12.4  Points to Consider In Handling Cancelable IRPs

Keep the following points in mind when implementing a Cancel routine and handling cancelable IRPs:

·A Cancel routine is called with the cancel spin lock already held, so it must call IoReleaseCancelSpinLock before it returns control. It must not call IoAcquireCancelSpinLock unless it calls IoReleaseCancelSpinLock first, and it must make a reciprocal call to IoReleaseCancelSpinLock for each call it makes to IoAcquireCancelSpinLock.

·Unless a driver manages its own internal queue(s) of IRPs, its Cancel routine is called with an incoming IRP that could be either of the following:

·The CurrentIrp in the input target device object

·An entry in the device queue associated with the target device object

·Unless a driver manages its own internal queues of IRPs, its Cancel routine should call KeRemoveEntryDeviceQueue with the input IRP to test whether it is an entry in the device queue associated with the target device object. Such a driver’s Cancel routine cannot call KeRemoveDeviceQueue or KeRemoveByKeyDeviceQueue because it cannot assume that the given IRP is at any particular position in the device queue.

·If a driver does manage its own internal queue of IRPs and has a Cancel routine, the queue must be protected by an executive spin lock. Such a queue should be an interlocked queue so the driver can use the ExInterlocked..List routines.

·If a Cancel routine is called with an IRP for which the driver has already started I/O processing and the request will be completed soon, the Cancel routine should release the system cancel spin lock and return control.

·Provided that the current state of the input IRP is pending, a Cancel routine must do the following:

1.Set the input IRP’s I/O status block with STATUS_CANCELLED for Status and zero for Information.

2.Release any spin locks it is holding, including the system cancel spin lock.

3.Call IoCompleteRequest with the given IRP.

·Any driver routine that passes IRPs on for further processing by other driver routines when an IRP might be held in a cancelable state must call IoSetCancelRoutine to set its entry point for the Cancel routine in the IRP. Only then can that driver routine call any support routine that causes the IRP to be held in a cancelable state, such as IoStartPacket, IoAllocateController, or an ExInterlockedInsert..List routine.

·Any driver routine that subsequently processes cancelable IRPs must check whether an IRP has already been cancelled before it begins operations to satisfy the request. Such a routine must call IoSetCancelRoutine to reset its entry point for the Cancel routine to NULL in the . Only then can that routine begin its I/O processing for the input IRP.

Note that such a routine might have to reset the entry point for a Cancel routine in an IRP if it, too, passes IRPs on for further processing by other driver routines and those IRPs might be held in a cancelable state.

·Any higher-level driver that holds IRPs in a cancelable state must reset its Cancel entry point to NULL before it passes an IRP on to the next-lower driver with IoCallDriver.

·Any higher-level driver can call IoCancelIrp with an IRP that it has allocated and passed on for further processing by lower-level driver(s). However, such a driver cannot assume that the given IRP will be completed with STATUS_CANCELLED by lower driver(s).

·A driver can (or must, depending on its design) maintain additional state information in its device extension to track the cancelable status of IRPs. If this state is shared by driver routines running at IRQL <= DISPATCH_LEVEL, the shared data can be protected with a driver-allocated and initialized executive spin lock.

Such a driver should manage its acquisitions and releases of the system cancel spin lock and its own executive spin lock(s) carefully, and it should be designed to hold the system cancel spin lock for the shortest possible interval(s).

·If a device driver maintains state information about cancelable IRPs that various driver routines share with its ISR, these other routines must synchronize access to the shared state with the ISR. Only a driver-supplied SynchCritSection routine can access such shared-with-the-ISR state in a multiprocessor-safe way.

For more information about using spin locks, see Chapter 16. See Chapters 8 and 10, respectively, for more information about ISRs and SynchCritSection routines.