0,

 FALSE, // Not exclusive

 &fdo);

If you try to create a second DebugPrint device, the same kernel device name is passed to IoCreateDevice. As this device name is already being used, the call will fail. This problem does not occur if you pass NULL for the device name, as in earlier examples. If you definitely need more than one named kernel device, you must keep a (zero-based) count of devices and append it as a device number to the kernel device name.

No special processing is required when you remove a device with a kernel device name.

Read Queue

The Read IRP queue is implemented using variables in the device extension, shown in Listing 14.6. If ReadIrp is NULL, no IRP is queued. Otherwise, it contains a pointer to the queued IRP. A spin lock called ReadIrpLock is used to protect access to the read queue. These fields are initialized as follows in the DbpAddDevice routine in Pnp.cpp.

// Initialise 'read queue'

KeInitializeSpinLock(&dx->ReadIrpLock);

dx->ReadIrp = NULL;

Listing 14.6 DebugPrint driver device extension

typedef struct _DEBUGPRINT_DEVICE_EXTENSION {

 PDEVICE_OBJECT fdo;

 PDEVICE_OBJECT NextStackDevice;

 UNICODE_STRING ifSymLinkName;

 bool GotResources; // Not stopped

 bool Paused; // Stop or remove pending

 bool IODisabled; // Paused or stopped

 LONG OpenHandleCount; // Count of open handles

 LONG UsageCount; // Pending I/O Count

 bool Stopping; // In process of stopping

 KEVENT StoppingEvent; // Set when all pending I/O complete

 PIRP ReadIrp; // 'Read queue' of 1 IRP

 KSPIN_LOCK ReadIrpLock; // Spin lock to guard access to ReadIrp

 LIST_ENTRY EventList; // Doubly-linked list of written Events

 KSPIN_LOCK EventListLock; // Spin lock to guard access to EventList

} DEBUGPRINT_DEVICE_EXTENSION, *PDEBUGPRINT_DEVICE_EXTENSION;

Listing 14.7 shows the complete Read IRP handler. Its first job is to acquire the ReadIrpLock spin lock. If the ReadIrp field is not NULL, it means another Read IRP has been queued. The IRP is failed, not forgetting to release the spin lock first. Even though the Monitor program will never issue more than one read IRP, it is best to be on the safe side.

The ReadEvent routine is then called. If there are any trace events available straightaway, ReadEvent returns the event data in the IRP, completes the IRP, and returns true. If this happens, the read routine can just return straightaway without queuing the IRP.

If there are no trace events available, the IRP is queued. This simply means storing the IRP pointer in ReadIrp. The final job is to mark the IRP as pending using IoMarkIrpPending and set its cancel routine using IoSetCancelRoutine. The read routine must return STATUS_PENDING because it has queued its IRP.

IRPs that are not cancelled must remove their cancel routine using IoSetCancelRoutine before completing the IRP.

Listing 14.7 DebugPrint driver read dispatch routine

NTSTATUS DbpRead(IN PDEVICE_OBJECT fdo, IN PIRP Irp) {

 PDEBUGPRINT_DEVICE_EXTENSION dx = (PDEBUGPRINT_DEVICE_EXTENSION)fdo->DeviceExtension;

 if (!dx->IODisabled) return CompleteIrp(Irp, STATUS_DEVICE_NOT_CONNECTED, 0);

 if (!LockDevice(dx)) return CompleteIrp(Irp, STATUS_DELETE_PENDING, 0);

 // Get access to our Read IRP queue

 KIRQL irql;

 KeAcquireSpinLock(&dx->ReadIrpLock,&irql);

 // Only one listening read allowed at a time.

 if (dx->ReadIrp!=NULL) {

  KeReleaseSpinLock(&dx->ReadIrpLock,irql);

  UnlockDevice(dx);

  return CompleteIrp(Irp, STATUS_UNSUCCESSFUL, 0);

 }

 // See if there's data available

 if (ReadEvent(dx, Irp)) {

  KeReleaseSpinLock(&dx->ReadIrpLock,irql);

  UnlockDevice(dx);

  return STATUS_SUCCESS;

 }

 // No event is available, queue this read Irp

 dx->ReadIrp = Irp;

 KeReleaseSpinLock(&dx->ReadIrpLock,irql);

 // Mark Irp as pending and set Cancel routine

 Irp->IoStatus.Information = 0;

 IoMarkIrpPending(Irp);

 IoSetCancelRoutine(Irp.DbpCancelIrp);

 return STATUS_PENDING;

}

Cancelling IRPs

Any IRPs that are queued must have a cancel routine. A driver ought to also handle the IRP_MJ_CLEANUP Cleanup IRP.

Cancel and Cleanup Circumstances

First, let's be clear when IRP cancel routines are called and when the Cleanup IRP is sent.

Case 1 is a situation in which a user application calls the CancelIo Win32 function on a file handle. All IRPs with cancel routines have their cancel routine called. Only IRPs that have been issued by the current thread are effected.

Case 2 covers these three situations:

• a user mode program crashes with IRPs pending,

• it exits with overlapped I/O requests pending and without closing its file handle, and

• if Ctrl+C is pressed in console applications.

In this case, all IRPs with cancel routines have their cancel routines called first. If there are outstanding IRPs without cancel routines, the I/O Manager simply sets the IRPs' Cancel flag and waits until the IRPs complete. Finally, the Cleanup IRP is sent.

If an uncancellable IRP does not complete within five minutes, the IRP is forcibly detached from the user process so that it can terminate. However, the IRP is still left uncompleted. You will not be able to reinstall the driver, so a reboot will be necessary to try the fixed version of your driver.

Issuing the Cleanup IRP seems to perform no useful function in this case.

Case 3 is a situation in which a user mode programs closes its file handle with overlapped I/O requests pending

In this case, IRPs with cancel routines do not have their cancel routines called. Instead, the Cleanup IRP is issued to cancel all pending IRPs.

The Implications

Добавить отзыв
ВСЕ ОТЗЫВЫ О КНИГЕ В ИЗБРАННОЕ

0

Вы можете отметить интересные вам фрагменты текста, которые будут доступны по уникальной ссылке в адресной строке браузера.

Отметить Добавить цитату
×