means that some nonpaged memory must be allocated for the output data. The output data is copied back to the shared buffer and the temporary memory freed. The RunCmds routine performs this task[39].
If WdmIo has not connected to a hardware interrupt, the IOCTL_PHDICLRUN_CMDS handler in Listing 16.2 simply calls
In this case, the IOCTL_PHDIO_RUN_CMDS handler calls
Listing 16.3 RunCmdsSynch and RunCmds
BOOLEAN RunCradsSynch(IN PDEVICE_OBJECT fdo) {
return RunCmds(fdo, false);
}
BOOLEAN RunCmds(IN PDEVICE_OBJECT fdo, IN bool CanTrace) {
PWDMIO_DEVICE_EXTENSION dx = (PWDMIO_DEVICE_EXTENSION)fdo->DeviceExtension;
PIRP Irp = fdo->CurrentIrp;
PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp);
ULONG InputLength = IrpStack->Parameters.DeviceIoControl.InputBufferLength;
ULONG OutputLength = IrpStack->Parameters.DeviceIoControl.OutputBufferLength;
PUCHAR Buffer = (PUCHAR)Irp->AssociatedIrp.SystemBuffer;
PUCHAR OutBuffer = NULL;
if (OutputLength>0) {
OutBuffer = (PUCHAR)ExAllocatePool(NonPagedPool.OutputLength);
if (OutBuffer==NULL) return FALSE;
}
ProcessCmds(dx, Buffer, InputLength, OutBuffer, OutputLength, CanTrace);
if (OutBuffer!=NULL) {
RtlMoveMemory(Buffer, OutBuffer, OutputLength);
ExFreePool(OutBuffer);
}
return TRUE;
}
Cancelling Queued IRPs
Chapter 14 explained when Windows tries to cancel IRPs using IRP cancel routines. In addition, Windows issues the Cleanup IRP to cancel IRPs in some circumstances. Chapter 14 also showed how the DebugPrint driver provides a cancel routine for its one queued IRP.
As WdmIo queues IRP, it must also provide cancel routines for these IRPs. In addition, it handles the Cleanup IRP correctly so that IRPs are cancelled if a user mode application closes its file handle with overlapped I/O requests pending.
When considering a strategy for cancelling IRPs, two cases must usually be considered. In the first case, the IRP is still being held in the device queue. The second case is when the IRP has been removed from the device queue and is being processed by
The I/O Manager does not know whether an IRP is in the device queue[40]. It simply calls the cancel routine. The cancel routine must determine what to do. If the IRP pointer matches the FDO CurrentIrp field, the IRP is running in
The I/O Manager uses its Cancel spin lock to guard cancelling operations. An IRP's cancel routine is called at DISPATCH_LEVEL IRQL while holding the Cancel spin lock. The IRP CancelIrql field holds the old IRQL that should be passed to
I have already mentioned that a device queue includes a spin lock to ensure that all operations on the queue are handled safely in a multiprocessor environment. When cancel routines are involved, the Cancel spin lock must be held. This is to ensure that a Cancel routine is not called on one processor while the IRP is being dequeued on another processor.
The WdmIo driver cancels a queued IRP by removing it from the device queue and completing it with status STATLJS_CANCELLED.
If the IRP is being processed by
The DDK documentation does not say that the Cancel routine has to complete the IRP. In WdmIo, if the IRP is being processed by
Listing 16.4 shows the
If the IRP to be cancelled is not the current IRP,
Listing 16.4 WdmIoCancelIrp routine
VOID WdmIoCancelIrp(IN PDEVICE_OBJECT fdo, IN PIRP Irp) {
PWDMIO_DEVICE_EXTENSION dx = (PWDMIO_DEVICE_EXTENSION)fdo->DeviceExtension;
DebugPrint('WdmIoCancelIrp: Cancelling %I', Irp);
if (Irp==fdo->CurrentIrp) {
DebugPrintMsg('WdmIoCancelIrp: IRP running in StartIo');
// IRP is being processed by WdmIoStartIo.
// Irp->Cancel flag already set.
// WdmIoStartIo or timeout will detect Cancel flag
// and cancel IRP in due course
IoReleaseCancelSpinLock(Irp->CancelIrql);
} else {
DebugPrintMsg('WdmIoCancelIrp: IRP in StartIo queue');
// IRP is still in StartIo device queue.
// Just dequeue and cancel it. No need to start next IRP.
BOOLEAN dequeued = KeRemoveEntryDeviceQueue(&fdo->DeviceQueue, &Irp->Tail.Overlay.DeviceQueueEntry);
IoReleaseCancelSpinLock(Irp->CancelIrql);
if (dequeued) {
UnlockDevice(dx);
CompleteIrp(Irp, STATUS_CANCELLED);
}
}
}
Cancel Checking
The code in DeviceIo.cpp makes various checks to see if the I/O Manager has requested that the IRP be cancelled.
At the end of
The
The interrupt handling code also checks the IRP
An alternative IRP cancelling strategy is to remove an IRP's cancel routine as soon as it starts being processed in