Skip to content

IRQLs

IRQLs

Stable

Audience: Everyone
Author: gummi

  IRQLs provide a centralized preemption and interrupt control mechanism.   IRQLs are software only, meaning they are strictly enforced only by   software and used as an abstraction in software.

  IRQLs are a feature of quite a few other kernels. Windows NT is the   most prominent of the many kernels that have IRQLs. Similar concepts exist   in other kernels, however.

  Each logical processor (or CPU) on the machine has its own IRQL.

  At each IRQL, the effects of lower IRQLs are also applied.   There are 5 IRQLs, ordered from the least restrictive to most restrictive:

  • PASSIVE (standard code execution, nothing blocked)
  • APC (APCs[^1] blocked, preemption blocked)
  • DISPATCH (DPCs[^2] blocked)
  • DEVICE (same as DISPATCH, currently reserved for future use)
  • HIGH (hardware interrupts blocked)

  To change the IRQL, a function call to raise or lower it must be made.   This is because the raise/lower operation must invoke otherfunctions   to block other events (such as preemption or interrupts). This means   that you cannot change the current IRQL willy-nilly and expect everything   to work out just fine.

  There is also another “pseudo-IRQL” that is used as a placeholder so that   IRQL functions can be called at early bootstages[^3] (making the IRQL   functions effectively no-ops), which is NONE.

  There are three primary IRQL related functions:

  irql_raise(), irql_lower(), and irql_get().

  irql_raise() raises the IRQL to a new IRQL, returning   the previous IRQL. If the machine has not reached the LATE_DEVICES   bootstage, it will not do anything and will return the NONE IRQL.

  If this new IRQL is equal to the current IRQL, nothing is done.   However, if the new IRQL is lower than the current IRQL, the function   will panic.

  irql_lower() lowers the IRQL to a new IRQL. If the bootstage has   not reached the LATE_DEVICES stage, or if the NONE IRQL is passed in,   the function will not do anything.

  If the new IRQL is equal to the current IRQL, nothing is done.   However, if the new IRQL is higher than the current IRQL, the function   will panic.

  irql_lower() will also check if any APCs and DPCs have came in, and   execute them if the new IRQL permits the execution of these procedures.

  irql_lower() will also check if a reschedule is needed, and appropriately   call scheduler_yield() if preemption is enabled.

  An example of a common IRQL usage pattern for disabling preemption:

  ```c   enum irql old_irql = irql_raise(IRQL_DISPATCH_LEVEL); // disable preemption

  // do critical work

  irql_lower(old_irql);   ```

  irql_get() will return the current IRQL of the caller’s logical   processor.

  No errors besides the possible panics described above are possible.

  IRQL usage interacts with many subsystems, but primarily interacts with   synchronization, interrupt handlers, and scheduling.

  The irql_raise() and irql_lower() functions cannot call themselves.   Thus, inside the functions, whenever interrupts need to be disabled,   they use the raw disable_interrupts() and enable_interrupts()   assembly routines.

  None

  To raise the IRQL, the necessary operations are performed depending on   the IRQL being raised to. (disable preemption, interrupts, etc.).

  To lower the IRQL, we simply re-enable the blocked event types, and   attempt DPC and APC execution if we are currently running in the   context of a thread.

  IRQLs were introduced as the preemption/interrupt control mechanism   of this kernel because of the structure and strict rules they   provide and enforce. DPCs and APCs also require IRQLs.

11/22/2025 - gummi: created file
11/23/2025 - gummi: move references
enum irql {
IRQL_PASSIVE_LEVEL = 0,
IRQL_APC_LEVEL = 1,
IRQL_DISPATCH_LEVEL = 2,
IRQL_DEVICE_LEVEL = 3,
IRQL_HIGH_LEVEL = 4,
IRQL_NONE = -1,
};
char irql_to_str(enum irql level);

irql_to_str referenced types: