The IRP cancel routine is changed for the case when the IRP is the current IRP. Listing 16.5 shows that in this case, the IRP is completed and
Listing 16.5 Alternative IRP cancel routine
if (Irp==fdo->CurrentIrp) {
DebugPrintMsg('WdmIoCancelIrp: IRP just dequeued for StartIo');
// IRP has just been dequeued but StartIo has not had a chance
// to remove this cancel routine yet. Irp->Cancel flag already set.
IoReleaseCancelSpinLock(Irp->CancelIrql);
// Cancel IRP and start next one
CompleteIrp(Irp, STATUS_CANCELLED);
IoStartNextPacket(fdo, TRUE);
} else
…
The start of the
In my mind, this technique has a potential race condition. When the Cancel routine completes, the IRP memory may disappear straightaway. However, the StartIo routine may be just about to run on another processor. Accessing the IRP
Listing 16.6 Alternative StartIo initial processing
// Check whether cancelled already KIRQL OldIrql;
IoAcquireCancelSpinLock(&OldIrql);
if (Irp->Cancel) {
IoReleaseCancelSpinLock(OldIrql);
// IoStartNextPacket called by cancel routine
return;
}
// Remove cancel routine
IoSetCancelRoutine(Irp, NULL);
IoReleaseCancelSpinLock(OldIrql);
Cleanup IRP Handling
The Cleanup IRP is issued to cancel any IRPs that are outstanding when a file handle is closed. Even if the IRPs have cancel routines, they are not called.
To handle it correctly, the driver ought to cancel only IRPs that belong to the correct file handle. Only queued IRPs whose
The WdmIo Cleanup IRP handler,
The code keeps extracting IRPs using
The Cleanup IRP handler then goes on to try to cancel the IRP currently being processed by
To handle the FileObject check correctly, you must still remove each IRP from the queue in turn. If the IRP's FileObject does not match the Cleanup FileObject, the IRP must be put in a temporary holding queue. At the end, all these IRPs must be reinserted in the main device queue.
Listing 16.7 WdmIo Cleanup IRP handling
NTSTATUS WdmIoDispatchCleanup(IN PDEVICE_OBJECT fdo, IN PIRP Irp) {
PWDMIO_DEVICE_EXTENSION dx = (PWDMIO_DEVICE_EXTENSION)fdo->DeviceExtension;
DebugPrintMsg('WdmIoDispatchCleanup');
KIRQL OldIrql;
IoAcquireCancelSpinLock(&OldIrql);
// Cancel all IRPs in the I/O Manager maintained queue in device object
PKDEVICE_QUEUE_ENTRY QueueEntry;
while((QueueEntry=KeRemoveDeviceQueue(&fdo->DeviceQueue)) != NULL) {
PIRP CancelIrp = CONTAINING_RECORD(QueueEntry, IRP, Tail.Overlay.DeviceQueueEntry);
CancelIrp->Cancel = TRUE;
CancelIrp->CancelIrql = OldIrql;
CancelIrp->CancelRoutine = NULL;
IoReleaseCancelSpinLock(OldIrql);
DebugPrint('WdmIoDispatchCleanup: Cancelling %I', CancelIrp);
UnlockDevice(dx);
CompleteIrp(CancelIrp, STATUS_CANCELLED);
IoAcquireCancelSpinLock(&OldIrql);
}
IoReleaseCancelSpinLock(OldIrql);
// Forceably cancel any in-progress IRP
if (dx->Timeout!=-1) {
if (KeSynchronizeExecution(dx->InterruptObject, (PKSYNCHRONIZE_ROUTINE)CancelCurrentIrpSynch, dx)) {
if (fdo->CurrentIrp!=NULL) {
DebugPrint('WdmIoDispatchCleanup: Cancelled in-progress IRP %I', fdo->CurrentIrp);
WdmIoDpcForIsr(NULL, fdo, fdo->CurrentIrp, dx);
}
}
}
return CompleteIrp(Irp, STATUS_SUCCESS);
}
static BOOLEAN CancelCurrentIrpSynch(IN PWDMIO_DEVICE_EXTENSION dx) {
if (dx->Timeout==-l) return FALSE;
dx->Timeout = –1;
dx->TxStatus = STATUS_CANCELLED;
return TRUE;
}
Testing, Cancelling, and Cleanup
I amended
The