When people first hear the term IRQL (pronounced Er-kel) their thoughts sometimes turn to the sitcom "Family Matters" and Jaleel White’s alter ego, Steve Urkel. However, we’re not going to be taking a trip down Television’s Memory Lane today. Instead we’re going to talk about Interrupt Request Levels – aka IRQL’s. If you develop device drivers or spend a lot of time debugging, IRQL’s are familiar territory for you. An interrupt request level (IRQL) defines the hardware priority at which a processor operates at any given time. In the Windows Driver Model, a thread running at a low IRQL can be interrupted to run code at a higher IRQL. The number of IRQL’s and their specific values are processor-dependent.
Processes running at a higher IRQL will pre-empt a thread or interrupt running at a lower IRQL. An IRQL of 0 means that the processor is running a normal Kernel or User mode process. An IRQL of 1 means that the processor is running an Asynchronous Procedure Call (APC) or Page Fault. IRQL 2 is used for deferred procedure calls (DPC) and thread scheduling. IRQL 2 is known as the DISPATCH_LEVEL. When a processor is running at a given IRQL, interrupts at that IRQL and lower are blocked by the processor. Therefore, a processor currently at DISPATCH_LEVEL can only be interrupted by a request from an IRQL greater than 2. A system will schedule all threads to run at IRQL’s below DISPATCH_LEVEL – this level is also where the thread scheduler itself will run. So if there is a thread that has an IRQL greater than 2, that thread will have exclusive use of the processor. Since the scheduler runs at DISPATCH_LEVEL, and that interrupt level is now blocked off by the thread at a higher IRQL, the thread scheduler cannot run and schedule any other thread. So far, this is pretty straightforward – especially when we’re talking about a single processor system.
On a multi-processor system, things get a little complicated. Since each processor can be running at a different IRQL, you could have a situation where one processor is running a driver routine (Device Interrupt Level – aka DIRQL), while another processor is running driver code at IRQL 0. Since more than one thread could attempt to access shared data at the same time, drivers should protect the shared data by using some method of synchronization. Drivers should use a lock that raises the IRQL to the highest level at which any code that could access the data can run. We’re not going to get too much into Locks and Deadlocks here, but for the sake of our discussion, an example would be a driver using a spin lock to protect data accessible at DISPATCH_LEVEL. On a single processor system, raising the IRQL to DISPATCH_LEVEL or higher would have the same effect, because the raising of the IRQL prevents the interruption of the code currently executing.
That will actually wrap it up for this post. It’s a fairly short post, but hopefully you now have a basic understanding of IRQL. Until next time …