2026-08-18
Before PCIe, devices raised interrupts by asserting one of four physical wires (INTA#-INTD#). Everything on a bus shared those wires, which is why the kernel's interrupt handler always had to poll every driver on a shared line to ask "was that you?" Message Signaled Interrupts killed the wires: instead of asserting a pin, the device performs a memory write to a magic address that the CPU's interrupt controller (LAPIC) has claimed. The write's address encodes the target core, and the data encodes the vector number. No wires, no sharing, no polling.
But MSI and MSI-X are two very different capabilities, and the difference matters:
This asymmetry drives modern device design. A cheap SATA controller might advertise MSI with 4 vectors — fine, it has one queue. But a modern NIC like Intel's E810 needs one interrupt per RX queue per core, so on a 64-core system with 8 queues per core, it needs 512 distinct vectors, each pinned to a specific core. Only MSI-X can do that. NVMe drives are the extreme case: they'll allocate one MSI-X vector per submission/completion queue pair, letting each core drive its own queue with zero cross-core interrupt traffic.
The negotiation dance: at boot, the OS reads the device's PCI capability list, finds either the MSI (cap ID 0x05) or MSI-X (cap ID 0x11) block, sees how many vectors the device can support, then writes back how many it will actually enable. Devices commonly ask for more than the OS will grant — Linux caps allocations per-driver and often gives you fewer.
Rule of thumb: if your device has more than one hardware queue or you care about interrupt affinity, MSI-X is mandatory. If you see a network driver dropping to a single interrupt on all cores under load, check /proc/interrupts — it probably fell back to MSI because MSI-X allocation failed.
