6.3.3.2 DispatchReadWrite Using Direct I/O

Any NT device driver that sets up its device objects for direct I/O satisfies a read request by returning data transferred from its device to system physical memory, which is described by the MDL at Irp->MdlAddress. It satisfies a write request by transferring data from system physical memory out to its device.

As already mentioned in Section 6.2.6, lower-level NT drivers must handle read/write requests asynchronously. Every NT device driver’s DispatchReadWrite routine must pass IRP_MJ_READ and IRP_MJ_WRITE IRPs with valid parameters on to other driver routines, as already described in Section 6.2.4.

For all read/write IRPs sent to lower-level NT drivers, the paged physical memory described by the MDL at Irp->MdlAddress has already been probed for the correct access rights to carry out the requested transfer and has already been locked down by the highest-level NT driver in the chain or by the I/O Manager. Any intermediate or lowest-level NT driver that sets up its device objects for direct I/O should not  call MmProbeAndLockPages because this has already been done. Such a lowest-level driver calls MmGetSystemAddressForMdl to get mapped system virtual addresses if its device uses PIO to transfer data.

Any intermediate or lowest-level device driver’s DispatchReadWrite routine should perform sanity checks on the parameters in its I/O stack location of read/write IRPs if it cannot trust a higher-level driver to pass down only IRPs with valid parameters. If such a driver’s DispatchReadWrite routine finds a parameter error in an incoming transfer IRP, it should complete the IRP with an appropriate error STATUS_XXX value as already described in Section 6.2.2. Otherwise, such an intermediate driver’s DispatchReadWrite routine must pass the request on for further processing, according to the guidelines in Section 6.3.3.3.

Any device driver’s DispatchReadWrite routine must call IoMarkIrpPending with the transfer request, pass the IRP on for further processing by other driver routines, and return STATUS_PENDING, as described in Section 6.2.4. Note that a device driver’s DispatchReadWrite routine can control the order in which IRPs are queued to its device for faster I/O throughput by calling IoStartPacket with a driver-determined Key value. Another routine in the driver dequeues the IRP later, determines whether the requested length must be split into partial-transfer operations, and programs the device to transfer data.

In general, an NT device driver that must split up large transfer requests to suit the limitations of its device should postpone these operations until just before setting up the device for a given transfer request. Such a device driver’s DispatchReadWrite routine should not check the I/O stack location of incoming IRPs for any device-specific transfer constraints, nor attempt to calculate partial-transfer ranges, when the driver can postpone these checks until just before its StartIo (or other driver routine) programs the device for a transfer operation.