Linux IRQs: Interrupt Requests, /proc/interrupts, and Device Interrupt Handling

Learn how Linux IRQs work, how to read /proc/interrupts, understand legacy IRQ numbers, MSI/MSI-X, shared interrupts, affinity, and basic diagnostics.

An interrupt request (IRQ) is an event notification that asks the processor and operating system to handle something promptly. A device can raise an interrupt when a keyboard key is pressed, network data arrives, a storage request completes, a timer expires, or hardware detects an error.

Instead of repeatedly checking every device, the CPU can run ordinary code until an interrupt arrives. It temporarily pauses the current execution path, runs kernel interrupt-handling code, and then resumes the interrupted work.

How an IRQ is handled in Linux

The general path is:

  1. A device detects an event, such as received network data.
  2. The device signals an interrupt through a legacy line or a message-signaled mechanism.
  3. An interrupt controller receives, prioritizes, and routes the event. Traditional PCs used a PIC, while modern x86 systems commonly use an APIC architecture.
  4. The controller delivers an interrupt to a CPU using an interrupt vector.
  5. The Linux kernel dispatches the event to an interrupt handler registered by the relevant device driver.
  6. The handler performs urgent work and schedules additional processing when necessary.
  7. After handling finishes, the CPU returns to the interrupted task.

An IRQ number is a kernel-visible identifier associated with an interrupt source or vector. It is not necessarily a permanent physical wire number. Firmware, the kernel, PCI routing, drivers, virtualization, and hardware configuration can all affect the identifier seen by Linux.

Top halves and deferred work

The first part of interrupt handling is often called the top half. It should be short: acknowledge the device, record essential state, and arrange follow-up work. Lengthy processing in a hard interrupt context can delay other interrupts and increase latency.

Linux can defer additional work using mechanisms such as threaded IRQ handlers, softirqs, workqueues, and, in older kernel contexts, tasklets. A softirq is deferred kernel processing commonly used for work related to networking and other high-frequency events. Workqueues run scheduled work in kernel threads and are useful when processing may sleep.

Legacy x86 IRQ numbering

Early IBM PC-compatible systems commonly exposed 16 interrupt lines, numbered IRQ 0 through IRQ 15. Two cascaded programmable interrupt controllers were used: the second controller connected through the cascade position represented by IRQ 2. This layout is useful historical background, but it is not a universal description of a current Linux system.

Legacy IRQ 0: System timer. Modern timer delivery may use APIC facilities rather than a simple legacy line.

Legacy IRQ 1: Keyboard controller.

Legacy IRQ 2: Cascade connection for the second legacy interrupt controller. On AT-compatible systems, its role is commonly represented through IRQ 9.

Legacy IRQ 3: Historically associated with serial port COM2.

Legacy IRQ 4: Historically associated with serial port COM1.

Legacy IRQ 5: Often available for expansion; depending on configuration it could be associated with a secondary sound device, printer, or another add-in device.

Legacy IRQ 6: Floppy disk controller.

Legacy IRQ 7: Parallel printer port, or an available legacy line.

Legacy IRQ 8: Real-time clock.

Legacy IRQ 9: ACPI or system-control use, and historically related to IRQ 2 redirection.

Legacy IRQ 10: Frequently allocated to an expansion device.

Legacy IRQ 11: Frequently allocated to an expansion device.

Legacy IRQ 12: PS/2 mouse controller.

Legacy IRQ 13: Floating-point unit exception on older systems.

Legacy IRQ 14: Primary ATA/IDE controller.

Legacy IRQ 15: Secondary ATA/IDE controller.

Viewing interrupt activity with procfs

procfs is the virtual /proc filesystem. It exposes information maintained by the kernel rather than ordinary files stored on disk. The primary interface for general interrupt inspection is /proc/interrupts.

cat /proc/interrupts

A typical modern output contains a header followed by entries. The exact formatting varies by kernel and hardware, but the main areas are:

IRQ identifier: The Linux-visible interrupt number, which may be greater than 15.

CPU counter columns: One counter per logical CPU. Each counter records interrupts handled on that CPU for the entry.

Controller or type information: Text describing a controller, interrupt class, or delivery method, such as APIC-related information or MSI.

Handler labels: Device, driver, queue, or kernel-subsystem names associated with the interrupt. A label may not exactly match the device name shown in a desktop interface.

These counters increase as interrupt events are handled. Per-CPU columns help show whether a device's interrupts are concentrated on one CPU or distributed across several CPUs.

watch -n 1 cat /proc/interrupts

Run the command while generating known activity, such as typing, transferring network data, or reading from storage. Compare the changing entries rather than relying only on a single snapshot.

Useful related interfaces include:

  • /proc/irq/ contains IRQ-specific information, including affinity controls where supported.
  • /proc/softirqs shows counters for deferred interrupt processing.
  • /proc/stat includes aggregate interrupt and CPU accounting fields.

Filtering likely device entries

grep -Ei 'eth|enp|ens|nvme|usb|i915|amdgpu' /proc/interrupts

This is only a convenient first filter. Names vary by driver, kernel version, queue configuration, and hardware.

Understanding modern interrupt entries

Current output may include local APIC timer events, interprocessor interrupts, PCI devices, storage queues, network queues, graphics drivers, USB controllers, and MSI or MSI-X vectors. One physical device can therefore appear in several entries.

Multiqueue devices commonly assign separate interrupt vectors to receive and transmit queues. A network interface may show several queue-related labels, and a storage controller may expose multiple completion queues. The labels describe the kernel handler, driver, queue, or subsystem—not always the marketing name of the device.

A floppy-controller label is a useful example of driver ownership, but it is absent on most current systems because they do not contain or enable a floppy controller.

Shared IRQs, MSI, and MSI-X

A legacy line-based interrupt can be shared by several devices. When the line fires, each registered handler checks whether its device caused the event. Handlers that did not cause it return without doing device-specific work.

Sharing allows limited interrupt lines to support more devices, but it adds diagnostic complexity and can create extra handler checks. A malfunctioning device on a shared line may also affect the apparent behavior of other devices.

MSI, or Message Signaled Interrupt, lets a PCI or PCIe device signal an interrupt with a memory write rather than asserting a shared physical interrupt line. MSI-X extends this model and allows multiple independently configurable interrupt vectors.

Signaling method: Legacy line IRQs assert a controller input; MSI/MSI-X use messages written by the device.

Sharing: Legacy lines may be shared; MSI/MSI-X vectors are generally allocated to a particular device function or queue.

Vector count: Legacy arrangements are constrained by available lines; MSI-X can provide many vectors.

Typical hardware era: Line-based IRQs are associated with older ISA and compatibility arrangements; MSI/MSI-X are common with PCIe devices.

Troubleshooting: For a legacy conflict, inspect multiple handlers on one line. For MSI/MSI-X, inspect vector allocation, queues, affinity, driver initialization, and PCI capability status.

Modern IRQ numbers can therefore be much larger than 15. They can also change after reboot, hardware changes, firmware changes, or driver reloads. Do not assume that a number observed today is a permanent hardware identity.

Interrupt affinity and performance

Interrupt affinity is the set of CPUs eligible to handle a particular IRQ. Good distribution can prevent one CPU from becoming a bottleneck when a network or storage device generates many events.

The irqbalance service can distribute suitable hardware interrupts across CPUs. Its availability, default state, and management commands depend on the Linux distribution. A device may also use multiple queues and MSI-X vectors, allowing interrupt work to be spread more effectively.

find /proc/irq -maxdepth 2 -name smp_affinity_list -print
cat /proc/irq/<IRQ_NUMBER>/smp_affinity_list

These commands discover and read the CPUs eligible for a selected IRQ. Manual changes are advanced runtime tuning. Record the original value, identify the exact device, measure a representative workload, and maintain a rollback plan before changing affinity.

On NUMA systems, CPU placement also affects memory and device locality. Keeping an interrupt and its processing near the device's NUMA node can help high-throughput workloads, but an incorrect placement can make performance worse.

Basic IRQ diagnostics

Verify that a device is active

  1. Display /proc/interrupts.
  2. Generate known activity, such as network traffic, keyboard input, or a storage operation.
  3. Display the file again or use watch.
  4. Compare counters and note which CPU columns increase.
  5. Correlate the labels with the driver and device.

An interrupt counter that does not change can mean the device is idle, the wrong label was selected, the device uses another queue or vector, the driver uses polling, the driver failed to initialize, or the hardware or connection has a fault. A counter must be interpreted together with a controlled activity test.

Correlate interrupts with PCI devices and logs

lspci -nnk
lspci -vv

lspci -nnk identifies PCI and PCIe devices and their active kernel drivers. lspci -vv provides detailed capability information, including MSI or MSI-X status when reported.

journalctl -k -b

Kernel logs can reveal driver initialization failures, device resets, interrupt warnings, or hardware errors that interrupt counters alone cannot explain.

Investigate high interrupt rates

A rapidly increasing entry can represent legitimate high I/O, but excessive interrupt activity can consume CPU time, increase latency, or cause an interrupt storm. Possible causes include poor interrupt coalescing settings, a malfunctioning device or driver, or unnecessary checks on a shared legacy line.

Measure interrupt growth, workload rate, CPU usage, and application latency together. Then inspect the owning driver and kernel logs. Avoid changing generic IRQ settings blindly; driver-specific queue and coalescing controls may be more appropriate.

Common troubleshooting situations

The expected device counter never rises

  • Generate a known workload for the device.
  • Check all related queue and vector labels, not just the interface name.
  • Use lspci -nnk or the relevant bus utility to confirm driver ownership.
  • Review journalctl -k -b for initialization and hardware errors.
  • Consider polling or a different interrupt mechanism if no counter changes.

One CPU receives most interrupts

  • Read the IRQ's smp_affinity_list.
  • Check whether irqbalance is unavailable, disabled, or unsuitable for the workload.
  • Determine whether the device has multiple queues or MSI-X vectors.
  • Compare throughput, CPU utilization, and latency before changing affinity.
  • Check NUMA locality on high-throughput systems.

A legacy IRQ number is not present

This is usually normal on modern hardware. Use the live /proc/interrupts output and lspci information as the system-specific sources of truth. A larger IRQ number or an MSI-X label indicates that modern routing is being used, not necessarily an IRQ conflict.

Safety and scope

Firmware, the Linux kernel, and device drivers primarily manage IRQ allocation and routing. Most users should inspect interrupt activity rather than manually assign IRQs.

IRQ conflicts are much less common on modern PCI and PCIe systems than on manually configured legacy hardware. Do not apply the IRQ 0–15 reference table as a universal rule.

Practical command summary

  • cat /proc/interrupts — view interrupt counters and handler labels.
  • watch -n 1 cat /proc/interrupts — refresh counters while testing activity.
  • cat /proc/softirqs — inspect deferred interrupt-processing counters.
  • find /proc/irq -maxdepth 2 -name smp_affinity_list -print — discover affinity-list files.
  • cat /proc/irq/<IRQ_NUMBER>/smp_affinity_list — read an IRQ's eligible CPUs.
  • lspci -nnk — correlate PCI devices with kernel drivers.
  • lspci -vv — inspect detailed PCI interrupt capabilities.
  • journalctl -k -b — review current-boot kernel messages.

Key points to remember

  • An IRQ notifies the CPU and kernel that prompt device-related work is needed.
  • Drivers register interrupt handlers, and Linux may defer substantial processing.
  • IRQ numbers are operating-system identifiers, not permanent physical wiring labels.
  • IRQ 0 through IRQ 15 describe traditional PC hardware, not every modern Linux system.
  • /proc/interrupts shows counts per CPU and labels for handlers or interrupt sources.
  • Shared line IRQs differ fundamentally from PCI/PCIe MSI and MSI-X.
  • Affinity and irqbalance influence where interrupt work runs, but tuning should be measured and reversible.

For continued study, review Linux IRQ and interrupt request fundamentals alongside Linux device-driver, procfs, PCIe, and CPU-affinity material.