Constraints on Dispatch Routines

The following guidelines briefly discuss how to avoid common programming errors in dispatch routines for file system filter drivers.

Note: For information about which types of IRPs are used in paging I/O, see Dispatch Routine IRQL and Thread Context.

  • Dispatch routines in the paging I/O path should never call IoCallDriver at any IRQL above APC_LEVEL. If a dispatch routine raises IRQL, it must lower it before calling IoCallDriver.

  • Dispatch routines in the paging path, such as read and write, cannot safely call any kernel-mode routines that require callers to be running at IRQL PASSIVE_LEVEL.

  • Dispatch routines that are in the paging file I/O path cannot safely call any kernel-mode routines that require a caller to be running at IRQL < DISPATCH_LEVEL.

  • Dispatch routines that are not in the paging I/O path should never call IoCallDriver at any IRQL above PASSIVE_LEVEL. If a dispatch routine raises IRQL, it must lower it before calling IoCallDriver.

Constraints on Processing IRPs

Constraints on Completing IRPs

  • When completing IRPs, file system filter drivers should use only success and error status values.

  • Although STATUS_PENDING is a success NTSTATUS value, it is a programming error to complete an IRP with STATUS_PENDING.

  • After a dispatch routine calls IoCompleteRequest, the IRP pointer is no longer valid and cannot safely be dereferenced.

Constraints on Setting a Completion Routine

Note: For information about setting completion routines, see Using Completion Routines.

  • When a dispatch routine calls IoSetCompletionRoutine, it can optionally pass a Context pointer to a structure for the completion routine to use when processing the given IRP. This structure must be allocated from nonpaged pool, because the completion routine can be called IRQL DISPATCH_LEVEL.

  • If a dispatch routine sets a completion routine that may return STATUS_MORE_PROCESSING_REQUIRED, it must do one of the following to prevent the I/O Manager from completing the IRP prematurely:

Constraints on Passing IRPs Down

  • After a dispatch routine calls IoCallDriver, the IRP pointer is no longer valid and cannot safely be dereferenced, unless the dispatch routine waits for the completion routine to signal that it has been called.

  • It is a programming error to call PoCallDriver from a file system filter driver. (PoCallDriver is used to pass IRP_MJ_POWER requests to lower-level drivers. File system filter drivers never receive IRP_MJ_POWER requests.)

Constraints on Returning Status

  • Except when completing an IRP, a dispatch routine that does not set a completion routine should always return the NTSTATUS value returned by IoCallDriver. Unless this value is STATUS_PENDING, it must match the value of Irp->IoStatus.Status set by the driver that completed the IRP.

  • When IoCallDriver returns STATUS_PENDING, the dispatch routine should also return STATUS_PENDING, unless it waits for the completion routine to signal an event.

  • When posting the IRP to a worker queue for later processing, the dispatch routine should mark the IRP pending and return STATUS_PENDING.

  • When setting a completion routine that might post the IRP to a worker queue for later processing, the dispatch routine should mark the IRP pending and return STATUS_PENDING.

  • A dispatch routine that marks an IRP pending must return STATUS_PENDING.

  • Oplock operations cannot be pended (posted to a worker queue), and dispatch routines cannot return STATUS_PENDING for them.

Constraints on Posting IRPs to a Work Queue

  • If a dispatch routine posts IRPs to a work queue, it must call IoMarkIrpPending before posting each IRP to the worker queue. Otherwise, the IRP could be dequeued, completed by another driver routine, and freed by the system before the call to IoMarkIrpPending occurs, thereby causing a crash.