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.