On entry, an ISR is given pointers to the driver’s interrupt object and a ServiceContext pointer to whatever area the DriverEntry routine set up when it called IoConnectInterrupt. Most NT device drivers set the ServiceContext pointer to their device objects that represent physical devices that generate interrupts or to such a device object’s device extension. Such a driver uses the device extension to set up state information for the driver’s DpcForIsr routine, which usually does almost all of the I/O processing to satisfy each request that caused the device to interrupt.
In NT device drivers that do not overlap device I/O operations, the ISR is responsible for the following:
1.Determine whether the interrupt is spurious. If so, return FALSE immediately so the ISR of the device that interrupted will be called promptly. Otherwise, continue interrupt processing.
2.Stop the device from interrupting.
3.Gather whatever context information the DpcForIsr (or CustomDpc) routine will need to complete I/O processing for the current operation.
4.Store this context in an area accessible to the DpcForIsr or CustomDpc routine, usually in the device extension of the target device object for which processing the current I/O request caused the interrupt.
5.If the driver has a DpcForIsr routine, call IoRequestDpc with pointers to the current IRP, the target device object, and the saved context. IoRequestDpc queues the DpcForIsr routine to be run as soon as IRQL falls below DISPATCH_LEVEL on a processor.
If the driver has a CustomDpc routine, call KeInsertQueueDpc with a pointer to the DPC object (associated with the CustomDpc routine) and pointer(s) to any saved context the CustomDpc routine will need to complete the operation. Usually, the ISR also passes pointers to the current IRP and target device object. The CustomDpc routine is run as soon as IRQL falls below DISPATCH_LEVEL on a processor.
6.Return TRUE to indicate that its device generated the interrupt.
In general, an ISR does no actual I/O processing to satisfy an IRP. Instead, it stops its device from interrupting, sets up necessary state information, and queues the driver’s DpcForIsr or CustomDpc to do whatever I/O processing is necessary to satisfy the current request that caused the device to interrupt.
NT device driver writers should consider the following an implementation guideline:
An ISR must run at DIRQL for the shortest possible interval.
Following the preceding guideline increases I/O throughput for every device in the machine because running at DIRQL masks off all interrupts to which the system has assigned a lesser or equal IRQL value.
The SynchronizeIrql of the driver’s interrupt object(s), specified when the driver called IoConnectInterrupt, determines the DIRQL at which a device driver’s ISR and SynchCritSection routines are run.
When an NT device driver’s StartIo (or other) routine calls KeSynchronizeExecution with the driver’s SynchCritSection routine, the caller also passes the pointer to the interrupt object(s) associated with the ISR. Consequently, interrupts from the device are masked off on the processor running the SynchCritSection routine. Meanwhile, KeSynchronizeExecution holds the spin lock associated with the interrupt object(s) so that the ISR cannot access the device registers or shared state in the device extension from another processor until the driver’s SynchCritSection routine returns control.
For more information about StartIo and SynchCritSection routines, see Chapters 7 and 10, respectively. For more information about how the caller of KeSynchronizeExecution uses an interrupt spin lock, see also Chapter 16.