Impossible OS Docs

IRQL, DPCs and APCs

What is it?

Impossible OS uses the Windows Interrupt Request Level (IRQL) to answer one question everywhere in the kernel: what is legal right now. KIRQL is a per-CPU level from PASSIVE_LEVEL (thread code, everything allowed) through APC_LEVEL, DISPATCH_LEVEL and device levels to HIGH_LEVEL (almost nothing allowed). A Deferred Procedure Call (DPC) is the bottom half at DISPATCH_LEVEL: an interrupt handler acknowledges its device, queues a DPC and returns, and the DPC does the rest moments later. An Asynchronous Procedure Call (APC) is the per-thread counterpart: work queued onto one thread that runs when that thread reaches a delivery point.

How does it work?

Each CPU keeps its own current_irql, starting at PASSIVE_LEVEL. KeRaiseIrql() rejects a raise to a lower level and writes the LAPIC Task Priority Register only when the mapped priority actually changes, so moving between PASSIVE_LEVEL and APC_LEVEL (both priority 0) costs no hardware write. KeLowerIrql() restores a saved level and, as it drops, drains pending DPCs and APCs (irql.c).

Interrupt entry in isr_handler() (idt.c) raises current_irql to the vector's level and restores it on exit, in software only; the LAPIC's own in-service priority already masks lower vectors during delivery.

A DPC is a KDPC (routine, context, two arguments and a queue link) queued on a per-CPU FIFO, each protected by an irqsave spinlock and cache-line aligned so the interrupt path does not false-share (dpc.h). KeInsertQueueDpc() allocates nothing and is safe from interrupt handlers; high-importance DPCs go to the head. KiDispatchDpc() raises to DISPATCH_LEVEL, takes up to 32 DPCs per pass (DPC_BATCH_LIMIT) under the lock and runs them after releasing it. It runs from the LAPIC and PIT timer interrupts after end-of-interrupt (lapic.c), and whenever KeLowerIrql() drops below DISPATCH_LEVEL.

Threaded DPCs (KeInitializeThreadedDpc) are for work that must block or page. The dispatcher moves them to a per-CPU threaded list and one worker thread, started at boot, runs every CPU's threaded DPCs at PASSIVE_LEVEL. KeFlushQueuedDpcs() is a completion barrier: it waits until every CPU's normal and threaded queues are empty and nothing is in flight, and bugchecks with DPC_WATCHDOG_VIOLATION rather than return early if a DPC never settles.

APCs follow the same design one level up. A KAPC (apc.h) is queued on the target thread under its lock: special kernel APCs to the head, normal ones to the tail, and nothing onto a thread that is exiting. When IRQL drops below APC_LEVEL, special APCs run; normal APCs additionally run at PASSIVE_LEVEL. KeEnterCriticalRegion holds off normal APCs and KeEnterGuardedRegion holds off special ones too. A dying thread's queued APCs get their rundown routine.

Two helpers sit on top. KeSetTimerEx() (ktimer.h) queues a DPC automatically when a timer expires. kworker (kworker.h) is a single thread for periodic callbacks of a second or more that may call slow paths such as UEFI runtime services.

A fairness layer watches both queues: a per-CPU budget of 256 DPCs per tick (DPC_BUDGET_PER_TICK, carry-over capped at 512) flags monopolization, and, on CPUs with an invariant TSC and RDTSCP, a watchdog measures each DPC after it returns and fires when one ran longer than 100 microseconds (DPC_WATCHDOG_SINGLE_DPC_US in dpc_config.h), warning by default or bugchecking in strict mode.

sequenceDiagram
    participant ISR as Interrupt handler
    participant Q as Per-CPU DPC queue
    participant D as KiDispatchDpc
    participant W as Threaded DPC worker
    ISR->>Q: KeInsertQueueDpc()
    Q->>D: timer tick, or KeLowerIrql below DISPATCH_LEVEL
    D->>D: run up to 32 DPCs at DISPATCH_LEVEL
    D->>W: hand threaded DPCs to the worker
    W->>W: run at PASSIVE_LEVEL, may block

What are its interfaces?

Interface Purpose
KeGetCurrentIrql(), KeRaiseIrql(), KeLowerIrql() Read and change the current CPU's IRQL (irql.h)
IRQL_REQUIRE_AT_MOST(level), IRQL_REQUIRE_AT_LEAST(level) Contract checks that log and count a violation, or bugcheck in strict mode
KeInitializeDpc(), KeInsertQueueDpc(), KeRemoveQueueDpc(), KeRequestDpcFromIsr() Create, queue, cancel and request DPCs (dpc.h)
KeSetTargetProcessorDpc(), KeSetImportanceDpc(), KeFlushQueuedDpcs() Target a CPU, set importance, wait for all DPCs
KeInitializeThreadedDpc() Run a DPC at PASSIVE_LEVEL on the worker thread
KeInitializeApc(), KeInsertQueueApc(), KeRemoveQueueApc() Per-thread APCs (apc.h)
KeEnterCriticalRegion(), KeEnterGuardedRegion(), KeAreApcsDisabled() Hold off APC delivery
KeInitializeInterrupt(), KeConnectInterrupt(), KeSynchronizeExecution() Interrupt objects and synchronizing with an interrupt handler (kinterrupt.h)
KeInitializeTimer(), KeSetTimerEx(), KeCancelTimer() Timers with an optional expiry DPC (ktimer.h)
kworker_register(), kworker_unregister() Long-period periodic callbacks (kworker.h)

How do I use it?

The IRQL, DPC and APC machinery is always on; there is no setting.

bash scripts/test.sh SUITE=sched    # IRQL, DPC, APC, timer and kworker tests

A driver's interrupt handler should acknowledge the device and call KeInsertQueueDpc(); work that must block uses a threaded DPC instead. Code that must run within an IRQL range should state it with IRQL_REQUIRE_AT_MOST or IRQL_REQUIRE_AT_LEAST, so a violation is logged instead of deadlocking silently. The tests live in test_sched.c and test_kworker.c.

What is not implemented yet?

How does it compare with Windows 11 and Linux?

Windows defines the model this page follows: KIRQL, KDPC at DISPATCH_LEVEL, KAPC, and KeSynchronizeExecution. Linux covers the same ground with preempt, softirq and hardirq contexts, softirqs, tasklets and NAPI for bottom halves, and signal delivery on return to user mode; it has no kernel APC equivalent.

Impossible OS matches the core contract: IRQL transitions, per-CPU DPC queues with automatic draining, targeting, four importance levels, threaded DPCs, interrupt objects, and kernel APCs with critical and guarded regions. Its violation counters, DPC budget watchdog and locked queue snapshots are always on, where Windows keeps similar checks in Driver Verifier. It is behind Windows on CPU-affine DPC workers and user-mode APCs.

See also

Impossible OS is free software under the GPL-3.0-only license. Last updated Edit this page on GitHub