0,
FALSE, // Not exclusive
&fdo);
If you try to create a second DebugPrint device, the same kernel device name is passed to
No special processing is required when you remove a device with a kernel device name.
The Read IRP queue is implemented using variables in the device extension, shown in Listing 14.6. If
// 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
The
If there are no trace events available, the IRP is queued. This simply means storing the IRP pointer in
IRPs that are not cancelled must remove their cancel routine using
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;
}
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'
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