Synchronizing Cancellation in Driver Routines that Process IRPs

Any driver routine that dequeues or is called with an IRP that is held in a cancelable state, including a driver's StartIo routine, must do the following:

  1. Call IoAcquireCancelSpinLock.

  2. Check to make sure that Irp equals DeviceObject->CurrentIrp. If not, call IoReleaseCancelSpinLock and return control.

    If the two are not the same, the CurrentIrp might have been canceled between the time that IoStartPacket released the cancel spin lock and this routine acquired it.

  3. Call IoSetCancelRoutine with a NULL CancelRoutine pointer to remove the IRP from the cancelable state.

  4. Check the Irp->Cancel field to determine whether to cancel the IRP or to begin processing the I/O request.

    If Irp->Cancel is set to TRUE, do the following:

    • Call IoReleaseCancelSpinLock.

    • Set Irp->IoStatus.Status to STATUS_CANCELLED.

    • Set Irp->IoStatus.Information to 0.

    • Call IoStartNextPacket (in a StartIo routine) to start the next packet.

    • Call IoCompleteRequest with a priority boost of IO_NO_INCREMENT to complete the IRP.

    If Irp->Cancel is set to FALSE, call IoReleaseCancelSpinLock and start the requested processing the I/O request or pass the IRP to the next lower driver, as appropriate.

Drivers that manage their own queues of IRPs, rather than using the I/O manager-supplied device queue, do not need to acquire the cancel spin lock when calling IoSetCancelRoutine. However, these drivers should check the Cancel routine pointer that IoSetCancelRoutine returns to determine whether the cancel routine has already started.

In any driver that handles cancelable IRPs, every driver routine that processes an IRP before the underlying device has been programmed for the requested I/O operation should check the cancelable state of all incoming IRPs. Specifically, a highest-level device driver with both StartIo and ControllerControl routines should process incoming IRPs in both these driver routines as already described.