The most common uses for higher-level drivers’ IoCompletion routines are the following:
Any higher-level driver that allocates an IRP with either of these support routines must set its IoCompletion routine in such an IRP. The IoCompletion routine must call IoFreeIrp to dispose of these driver-allocated IRPs.
Highest-level NT drivers, such as file systems, are more likely to have IoCompletion routines that attempt to retry requests for which lower-level drivers have returned an error status than are intermediate drivers, except possibly class drivers layered above a closely coupled port driver. Nevertheless, any NT intermediate driver can retry requests from its IoCompletion routine(s) at the discretion of the driver designer.
In other words, an NT highest-level or intermediate driver’s DispatchReadWrite routine usually determines whether a given IRP requires the driver to set up an IoCompletion routine. At the discretion of the driver designer, such a driver’s DispatchDeviceControl or other Dispatch routine(s) also can set up an IoCompletion routine for any given IRP that is passed on to lower drivers.
For driver-allocated and reused IRPs, the Dispatch routine must call IoSetCompletionRoutine with the following Boolean parameters:
Usually, InvokeOnCancel is set to TRUE, whether an IRP might be returned with STATUS_CANCELLED or not, to ensure that the IoCompletion routine frees each driver-allocated IRP or checks the completion status of each reuse of an IRP.
Consider the following implementation guidelines for calling IoSetCompletionRoutine in higher-level drivers’ Dispatch routines
Such an underlying removable-media device driver might call IoSetHardErrorOrVerifyDevice, which references the pointer at Irp->Tail.Overlay.Thread, with a driver-allocated IRP. If the underlying device driver calls this support routine, the file system driver can send a popup to the appropriate user thread that prompts the user to cancel, retry, or fail the operation that the device driver could not satisfy.
For more information about handling removable media, see Chapter 16.
The driver’s IoCompletion routine should free all driver-allocated IRPs with IoFreeIrp before it calls IoCompleteRequest with the original IRP. When it completes the original IRP, the IoCompletion routine must free all driver-allocated IRPs before it returns control.
For example, a DispatchReadWrite routine must save the relevant transfer parameters of an input IRP for the IoCompletion routine before the DispatchReadWrite routine sets up a partial tranfer for the next-lower driver in that IRP, particularly if the DispatchReadWrite routine modifies any parameters that the IoCompletion routine needs to determine when the original request has been satisfied.
If the IoCompletion routine can retry the request, the Dispatch routine must set up a driver-determined upper limit on how many retries its IoCompletion routine should attempt before it completes the original IRP with an error.
The driver’s IoCompletion routine must maintain state about each reuse of an IRP until the original request is satisfied (or failed) and then call IoCompleteRequest with the IRP. The IoCompletion routine also might be responsible for reinitializing the common state area, or notifying another driver routine to dequeue the next IRP to be processed.
An IoCompletion routine that retries IRPs must maintain the retry count to it can determine when to fail an IRP if the requested operation cannot be completed successfully.
For example, if the Dispatch routine allocates an MDL with IoAllocateMdl and calls IoBuildPartialMdl for a partial-transfer IRP it allocates, the IoCompletion routine must release the MDL with IoFreeMdl. If it allocates resources to maintain state about the original IRP, it must free those resources, preferably before it calls IoCompleteRequest with the original IRP and definitely before it returns control.
In general, an IoCompletion routine should free any per-IRP resources such a Dispatch routine allocated before the corresponding IRP is freed or completed. Otherwise, the driver must maintain state about the resources to be freed before its IoCompletion routine returns control from completing the original request.