Because the NT I/O Manager supports asynchronous I/O within a multitasking and multithreaded system, I/O requests to a device can come in faster than its driver can process them to completion, particularly in Windows NT multiprocessor machines. Consequently, IRPs bound to any particular device must be queued in the driver when its device is already busy processing another IRP.
In other words, lowest-level NT drivers either must have a StartIo routine or must hold IRPs queued for further processing by their other driver routines whenever IRPs come in faster than such a device driver can satisfy them.
Only an NT device driver that can satisfy and complete every possible IRP in its Dispatch routines needs no StartIo routine and/or no driver-managed queue(s) for IRPs.
Higher-level NT drivers almost never have StartIo routines. Most NT intermediate drivers also do not have internal queues because such a driver can usually pass IRPs with valid parameters on from its Dispatch routines and do whatever postprocessing is required for any IRP in its IoCompletion routine(s).
This section describes, in general, some of the design considerations for determining whether to implement a StartIo routine with or without internal, driver-managed queues for IRPs.
StartIo Routines in NT Drivers
Most NT device drivers have StartIo routines because most PC peripheral devices are capable of handling only one device I/O operation at a time. For such a device driver with a StartIo routine, the NT I/O Manager provides IoStartPacket and IoStartNextPacket to manage the queueing of IRPs in the driver, as shown in Chapter 4.
For more information about StartIo routines in device drivers, see Section 7.2.
Higher-level NT drivers usually do not have StartIo routines. For more information about the tradeoffs of implementing StartIo routines in higher-level NT drivers, see Section 7.2.4.
Internal Queues for IRPs in NT Drivers
If an underlying device can support more than one concurrent I/O operation, an NT device driver must set up internal request queues and manage its own queueing of IRPs. For example, the system serial driver maintains separate queues for read, write, purge, and wait operations on its device(s) because it supports full-duplex serial device(s).
A higher-level NT driver that sends requests to some number of underlying device drivers also might maintain internal queues of IRPs. For example, NT file system drivers almost always have internal queues for IRPs.
For more information about managing internal queues, see Section 7.3.
Internal Queues with StartIo Routines in NT Drivers
An NT driver that manages its own internal queues can have a StartIo routine at the discretion of the driver designer. Most NT device drivers either have a StartIo routine or manage their own queueing of IRPs.
An exception to this is the NT SCSI port driver, which both has a StartIo routine and manages internal queues of IRPs. IRPs are queued to its StartIo routine in the device queue associated with the driver-created device object that represents a SCSI HBA. This driver also sets up separate device queue objects for and manages the queueing of IRPs to each target device (corresponding to a SCSI logical unit) on any HBA-driven SCSI bus in the machine.
The NT SCSI port driver uses its supplemental device queues to hold IRPs sent down from the NT SCSI class drivers in LU-specific queues whenever any device on a SCSI bus is particularly busy. In effect, this driver’s supplemental, LU-specific device queues allow the NT SCSI port driver to serialize operations for heterogeneous SCSI devices through an HBA while keeping each device on that HBA’s SCSI bus(es) as busy as possible.
Internal Queues and Driver Threads
NT drivers with device-dedicated threads and highest-level NT drivers that use executive worker threads, including most NT file system drivers, usually set up an interlocked queue for IRPs in the device extensions of their device objects. Such a queue is shared by the driver thread or driver-supplied worker thread callback and by other driver routines that process IRPs.
Such a driver’s Dispatch routines insert IRPs into the interlocked queue and a driver-created thread or the driver’s worker-thread callback removes them by calling the ExInterlocked..List support routines.
For example, the system floppy controller driver, as mentioned in Chapter 2, uses such an interlocked queue. Its device-dedicated thread handles the same processing of IRPs that is done by other NT device drivers’ StartIo routines and some of the same processing of IRPs that is done by other NT device drivers’ DpcForIsr routines.