The IRQL at which a driver routine executes determines which kernel-mode driver support routines it can call. For example, some driver support routines require that the caller be running at IRQL DISPATCH_LEVEL. Others cannot be called safely if the caller is running any raised IRQL; that is, at any IRQL higher than PASSIVE_LEVEL.
Following is a list of IRQLs at which the most commonly implemented standard driver routines are called. The IRQLs are listed from lowest to highest priority.
Driver Routines Called at PASSIVE_LEVEL — DriverEntry, AddDevice, Reinitialize, Unload routines, most dispatch routines, driver-created threads, worker-thread callbacks.
Driver Routines Called at APC_LEVEL — Some dispatch routines (see Dispatch Routines and IRQLs).
Driver Routines Called at DISPATCH_LEVEL — StartIo, AdapterControl, AdapterListControl, ControllerControl, IoTimer, Cancel (while holding the cancel spin lock), DpcForIsr, CustomTimerDpc, CustomDPC routines.
Driver Routines Called at DIRQL — InterruptService, SynchCritSection routines.
The only difference between APC_LEVEL and PASSIVE_LEVEL is that a process executing at APC_LEVEL cannot get APC interrupts. But both IRQLs imply a thread context and both imply that the code can be paged out.
Lowest-level drivers process IRPs while running at one of three IRQLs:
DriverEntry, AddDevice, Reinitialize, and Unload routines also are run at PASSIVE_LEVEL, as are any driver-created system threads.
AdapterControl, AdapterListControl, ControllerControl, IoTimer, Cancel (while it holds the cancel spin lock), and CustomTimerDpc routines also are run at DISPATCH_LEVEL, as are DpcForIsr and CustomDpc routines.
Most higher-level drivers process IRPs while running at either of two IRQLs:
DriverEntry, Reinitialize, AddDevice, and Unload routines also are run at PASSIVE_LEVEL, as are any driver-created system threads or worker-thread callback routines or file system drivers.
IoTimer, Cancel, and CustomTimerDpc routines also are run at DISPATCH_LEVEL.
In some circumstances, intermediate and lowest-level drivers of mass-storage devices are called at IRQL APC_LEVEL. In particular, this can occur at a page fault for which a file system driver sends an IRP_MJ_READ request to lower drivers.
Most standard driver routines are run at an IRQL that allows them simply to call the appropriate support routines. For example, a device driver must call AllocateAdapterChannel while running at IRQL DISPATCH_LEVEL. Since most device drivers call these routines from a StartIo routine, usually they are running at DISPATCH_LEVEL already.
Note that a device driver that has no StartIo routine because it sets up and manages its own queues of IRPs is not necessarily running at DISPATCH_LEVEL IRQL when it should call AllocateAdapterChannel. Such a driver must nest its call to AllocateAdapterChannel between calls to KeRaiseIrql and KeLowerIrql so that it runs at the required IRQL when it calls AllocateAdapterChannel and restores the original IRQL when the calling routine regains control.
When calling driver support routines, be aware of the following.
A driver can be running at IRQL <= DISPATCH_LEVEL when it calls KeAcquireSpinLock but it must release that spin lock by calling KeReleaseSpinLock. In other words, it is a programming error to release a spin lock acquired with KeAcquireSpinLock by calling KeReleaseSpinLockFromDpcLevel.
A driver must not call KeAcquireSpinLockAtDpcLevel, KeReleaseSpinLockFromDpcLevel, KeAcquireSpinLock, or KeReleaseSpinLock while running at IRQL>DISPATCH_LEVEL.