10.2.2 Maintaining State About Interrupt-Driven I/O Operations
Drivers whose DpcForIsr, CustomDpc, ControllerControl, AdapterControl, IoTimer, or CustomTimerDpc routines share state in the device extension with the ISR also use various techniques to make their SynchCritSection routines run as quickly as possible.
For example, device drivers might use some of techniques described in this scenario to minimize the execution intervals of their SynchCritSection routines. Consider a device driver that maintains a timer counter in its device extension to check whether a device I/O operation has timed out and that does not overlap I/O operations on its device. Several of the driver’s routines might access such a timer counter, using a SynchCritSection routine, as follows:
·Its StartIo routine initializes the timer counter to a driver-determined time-out value for the current request. The driver adds a second to its device-time-out value, in case its IoTimer routine has just returned control.
·Its ISR must set this timer counter to minus one when it is called to handle an interrupt from the device for the requested operation.
·Its IoTimer routine is called once per second to read the value of this timer counter and determine whether the ISR has already reset it to minus one. If not, the IoTimer routine decrements this counter by calling KeSynchronizeExecution with a SynchCritSection_1 routine.
If the counter goes to zero, indicating that the request timed out on the device, the SynchCritSection_1 routine calls a SynchCritSection_2 routine to program a device reset. If the counter is set to minus one, the IoTimer routine simply returns.
For more information about IoTimer routines, see Chapter 14.
·If the driver’s DpcForIsr must have the device reprogrammed to begin another partial-transfer operation, it must reinitialize the timer counter as the StartIo routine did.
The DpcForIsr also must call KeSynchronizeExecution with the SynchCritSection_2 routine, or possibly a SynchCritSection_3 routine, to program the device for another transfer operation.
Note that such a driver has more than one SynchCritSection routine, each with discrete but specific responsibilities, one to maintain its timer counter, another (or others) to program the device. Thus, each SynchCritSection routine can return control as quickly as possible because each does a single discrete task and nothing else.
Note also that such a driver has a single SynchCritSection_1 routine that maintains the state to the timer counter, along with the driver’s ISR. Thus, there is no contention for access to the timer counter among several SynchCritSection routines and the ISR.
NT device driver writers should consider the following general guidelines for designing and implementing SynchCritSection routines that maintain state
·If a device driver maintains state about its interrupt-driven I/O operations in the device (or controller) extension, place all this state information in a subrange of the device extension. This allows other nonISR routines that share resources with each other to access “their” area of the device extension without possible contention from the ISR and SynchCritSection routines.
For example, a driver-managed interlocked queue cannot be accessed by an ISR or SynchCritSection routine because neither can call an ExInterlocked..List routine from DIRQL when other driver routines synchronize their accesses to the queue from IRQL DISPATCH_LEVEL. Such a queue header and its guardian spin lock are readily accessible to other driver routines if its ISR and SynchCritSection routines always find the state they modify within a defined subrange of the device extension that does not include any resources accessed by other driver routines.
On the other hand, if the ISR or SynchCritSection routines access state variables scattered throughout the device extension, conflicting attempts to access the area that contains the driver’s interlocked queue or other shared resources are much more likely.
·Give each SynchCritSection routine that maintains state information responsibility for a discrete set of state variables. That is, avoid writing SynchCritSection routines that maintain overlapping state information.
This prevents contention, and possibly race conditions, between SynchCritSection routines (and the ISR) trying to access the same state concurrently.
This also ensures that each SyncCritSection routine returns control as quickly as possible because one SynchCritSection routine never has to wait for another that updates some of the same state information to return control.
·An NT device driver should avoid having a single, large, general-purpose SynchCritSection routine that does more testing of conditions to determine what to do than actually doing useful work. On the other hand, a driver also should avoid having many SynchCritSection routines that never execute a conditional statement because each updates only a single byte of state information.
Every SynchCritSection routine must return control as quickly as possible, because running any SynchCritSection routine prevents the driver’s ISR from getting any work done if it is running concurrently.