13.2 IoCompletion Routine Required Functionality
On entry, an IoCompletion routine is called with DeviceObject, Irp, and Context pointers, as shown by the declaration. The Dispatch routine that called IoSetCompletionRoutine can pass a Context pointer to whatever driver-determined context it set up for the IoCompletion routine to use in processing the given IRP. Note that such a context area cannot be pageable because the IoCompletion routine can be called at IRQL DISPATCH_LEVEL.
When it is called, an IoCompletion routine is responsible for doing whatever additional IRP processing the driver writer chooses and any necessary cleanup operations for the request, as determined by how the Dispatch routine set up the request.
Consider the following implementation guidelines for IoCompletion routines
·If the input IRP was allocated by the Dispatch routine with IoAllocateIrp or IoBuildAsynchronousFsdRequest, the IoCompletion routine must call IoFreeIrp to release that IRP, preferably before it completes the original IRP.
·The IoCompletion routine must release any per-IRP resources the Dispatch routine allocated for such a driver-allocated IRP, preferably before it frees the corresponding IRP.
For example, if the Dispatch routine allocated an MDL for a partial-transfer IRP that the IoCompletion routine is processing, the IoCompletion routine must call IoFreeMdl to free that MDL before it returns control from completing the original request.
·If the IoCompletion routine cannot complete the original IRP with STATUS_SUCCESS, it must set the I/O status block in the original IRP to the value returned in the driver-allocated IRP that caused the IoCompletion routine to fail the original request.
·If the IoCompletion routine will complete the original request with STATUS_PENDING, it must call IoMarkIrpPending with the original IRP before it calls IoCompleteRequest.
·If the IoCompletion routine must fail the original IRP with an error STATUS_XXX, it can log an error. However, it is the responsibility of the underlying device driver to log any device I/O errors that occur, so IoCompletion routines usually do not log errors.
For more information about logging I/O errors, see Chapter 16.
·When it has processed such a driver-allocated IRP and freed it, the IoCompletion routine must return control with STATUS_MORE_PROCESSING_REQUIRED.
Returning STATUS_MORE_PROCESSING_REQUIRED from the IoCompletion routine forestalls the I/O Manager’s completion processing for a driver-allocated and freed IRP.
·If the IoCompletion routine reuses an incoming IRP to send one or more requests to lower drivers, it should update whatever context the IoCompletion routine maintains about each reuse (or retry) of the IRP before it sets up the next-lower driver’s I/O stack location again, calls IoSetCompletionRoutine with its own entry point, and calls IoCallDriver with the IRP.
·The IoCompletion routine should not call IoMarkIrpPending at each reuse or retry of the IRP.
The Dispatch routine already marked the original IRP as pending. Until all drivers in the chain complete the original IRP with IoCompleteRequest, it remains pending.
·Before it retries a request, the IoCompletion routine should reset the I/O status block with STATUS_SUCCESS for Status and zero for Information, possibly after saving the returned error information.
For each retry, the IoCompletion routine usually decrements a count set up by the Dispatch routine because the IoCompletion routine must fail such an IRP with IoCompleteRequest when some limited number of retries have failed.
·The IoCompletion routine must return STATUS_MORE_PROCESSING_REQUIRED after it calls IoSetCompletionRoutine and IoCallDriver with an IRP that is being reused or retried.
Returning STATUS_MORE_PROCESSING_REQUIRED from the IoCompletion routine forestalls the I/O Manager’s completion processing of a reused or retried IRP.
·If the IoCompletion routine cannot complete the original IRP with STATUS_SUCCESS, it must leave the I/O status block as returned by lower drivers for the reuse or retry operation that causes the IoCompletion routine to fail the IRP.
·If the IoCompletion routine will complete the original request with STATUS_PENDING, it must call IoMarkIrpPending with the original IRP before it calls IoCompleteRequest.
·If the IoCompletion routine must fail the original IRP with an error STATUS_XXX, it can log an error. However, it is the responsibility of the underlying device driver to log any device I/O errors that occur, so IoCompletion routines usually do not log errors.
For more information about logging I/O errors, see Chapter 16.
·The IoCompletion routine must release any resources the Dispatch routine allocated for processing the original IRP, preferably before the IoCompletion routine calls IoCompleteRequest with the original IRP and definitely before the IoCompletion routine returns control from completing the original IRP.
If any still-higher-level driver has set its IoCompletion routine in the original IRP, that driver’s IoCompletion routine is not called until all lower-level drivers have called IoCompleteRequest with the original IRP.