Device Drivers & Kernel Modules

NAPI

/ NAH-pee /

A fast network card can deliver millions of packets per second. If the driver took one interrupt per packet, the machine would spend all its time entering and leaving interrupt handlers and could grind to a halt under load — a real failure mode called receive livelock. NAPI is the clever compromise that fixes this: when packets start arriving, take ONE interrupt, then switch off the card's interrupts and POLL to scoop up packets in batches, only re-enabling interrupts once the flood subsides.

Concretely, when the first packet arrives the card raises an interrupt. The NAPI driver does the minimum in that handler: it disables further receive interrupts on the card and schedules a poll. The kernel then calls the driver's poll function, which pulls up to a budget of packets (say 64) out of the receive ring in one go and pushes them up the stack. If there were exactly that many or more waiting, the kernel keeps polling; if the ring drained below the budget, the driver re-enables interrupts and goes back to sleep. So under light traffic it behaves like interrupt-driven I/O (one packet, one interrupt, low latency), and under heavy traffic it behaves like polling (no interrupts, high throughput, packets handled in efficient batches).

NAPI matters because it is how Linux survives high-rate networking — it gives low latency when idle and high throughput when busy, automatically, without the driver author choosing one mode forever. The honest framing: NAPI is not magic, it is a deliberate hybrid of the two classic strategies, adaptively switching between them based on load. The same interrupt-then-poll idea now appears beyond networking (for example in some storage and block paths), wherever events can arrive faster than per-event interrupts can be afforded.

// in the interrupt: disable IRQ, schedule a poll disable_rx_irq(dev); napi_schedule(&dev->napi); // the poll function, called by the kernel: int my_poll(struct napi_struct *n, int budget) { int done = drain_rx_ring(n, budget); // grab up to 'budget' if (done < budget) { // ring is empty napi_complete(n); enable_rx_irq(dev); // back to interrupts } return done; }

NAPI: one interrupt disables further interrupts and schedules a poll; the poll batches packets and re-arms interrupts only when the ring drains.

NAPI is not a separate magic mode — it is an adaptive blend of interrupts (low latency when idle) and polling (high throughput when busy). It exists chiefly to avoid receive livelock under a packet flood.

Also called
New APIinterrupt-then-poll中斷後輪詢