Linux Hardware Interrupt Management with APIC
This article explores how the modern Linux operating system handles hardware interrupts through the Advanced Programmable Interrupt Controller (APIC) architecture. It covers the dual roles of the I/O APIC and Local APIC, the hardware-to-kernel routing pipeline, interrupt distribution across multi-core symmetric multiprocessing (SMP) systems, and the execution workflow from initial signal reception to deferred kernel processing.
The APIC Architecture: I/O APIC vs. Local APIC
Legacy systems relied on the 8259 PIC (Programmable Interrupt Controller), which supported limited interrupt lines and lacked multi-core coordination. The modern x86 APIC architecture replaces this legacy system by splitting responsibilities into two distinct components:
- Local APIC (LAPIC): Integrated directly into each CPU core. The LAPIC is responsible for delivering interrupts to its specific core, managing local interrupt priorities via registers, handling local timer interrupts, and dispatching Inter-Processor Interrupts (IPIs) for multi-core coordination (such as TLB shootdowns and CPU rescheduling).
- I/O APIC: Integrated into the chipset/system bus. The I/O APIC receives physical interrupt lines from external hardware peripherals (such as disk controllers and network cards) and routes these signals across the system bus to one or more LAPICs.
Modern high-performance devices often bypass the physical pin connections of the I/O APIC using Message Signaled Interrupts (MSI and MSI-X). MSI devices write interrupt vectors directly to target Local APICs over the PCIe bus, significantly reducing latency and eliminating physical pin contention.
Kernel Initialization and Hardware Discovery
During system boot, the Linux kernel determines the topology of the system's interrupt hardware using ACPI (Advanced Configuration and Power Interface) tables, specifically the Multiple APIC Description Table (MADT).
- Enumeration: Linux parses the MADT to locate the base memory-mapped I/O (MMIO) addresses for all available I/O APICs and Local APICs.
- Vector Mapping: The kernel assigns architectural interrupt vectors (ranging from 32 to 255 on x86, as 0 to 31 are reserved for CPU exceptions) to specific hardware IRQs.
- Redirection Table Programming: Each pin on an I/O APIC corresponds to an entry in its Redirection Table. Linux programs these registers with the target interrupt vector, the delivery mode, and the target Local APIC ID.
Interrupt Routing and CPU Affinity
In multi-core systems, the Linux kernel leverages the APIC structure to distribute interrupt processing loads across available cores.
- IRQ Affinity: Every IRQ registered in the kernel
exposes an entry in the virtual filesystem under
/proc/irq/[IRQ_NUMBER]/smp_affinity. Linux administrators or background daemons (such asirqbalance) write bitmasks to these files to specify which CPU cores are allowed to service a specific interrupt. - Delivery Modes: The I/O APIC can be configured for fixed delivery (sending the interrupt strictly to a designated LAPIC) or lowest-priority delivery (directing the interrupt to the processor running at the lowest priority according to its Task Priority Register).
The Interrupt Processing Lifecycle
When an external hardware device requires CPU attention, the execution follows a deterministic path through the APIC and the kernel:
- Signal Generation: The peripheral triggers an interrupt line on the I/O APIC or sends an MSI packet directly targeting a LAPIC.
- LAPIC Arbitrament: The target core's LAPIC checks the incoming vector against its Task Priority Register (TPR) and Processor Priority Register (PPR). If the priority is higher than the current execution level, the LAPIC interrupts the CPU core.
- IDT Lookup: The CPU uses the assigned vector number as an index into the Interrupt Descriptor Table (IDT). The IDT points directly to the kernel's low-level architecture interrupt entry point.
- Top-Half Execution (Hard IRQ): The kernel saves the current register state, disables local interrupts on that core, and executes the registered Interrupt Service Routine (ISR). This step performs only the minimum critical operations needed, such as reading device registers and acknowledging the hardware state.
- End of Interrupt (EOI): The kernel writes an End of Interrupt (EOI) command to the Local APIC's EOI register. This signals the APIC that the current interrupt has been accepted, unblocking subsequent interrupts of equal or lower priority.
- Bottom-Half Execution (Soft IRQ): To prevent system
latency, any extensive work (such as copying network packets into memory
structures) is deferred to "bottom-half" mechanisms—predominantly
softirqs,tasklets, or dedicated kernel threads known asthreaded_irqs. Once the top half finishes, local interrupts are re-enabled, and the kernel processes these deferred queues.