Why You Cannot Do Everything in the Handler
When the doorbell rings, you do not invite the delivery driver inside, make them coffee, and spend 20 minutes inspecting the package. You open the door, say "thanks," take the package, and close the door. You deal with the package later.
The same logic applies to interrupt handlers. The CPU cannot spend milliseconds processing a network packet inside an ISR. While it is doing so, the next packet arrives, and the next, and the next — and they are all dropped because the hardware buffer overflows. The interrupt handler must be as short as possible.
Linux solves this with a two-phase design called Top Half and Bottom Half.
Top Half — The Hard IRQ
The Top Half is the actual ISR that runs immediately when the hardware interrupt fires. It operates with interrupts disabled on the current CPU core, meaning nothing can preempt it. Its responsibilities are strictly limited:
- Acknowledge the hardware ("I received your signal; stop asserting the IRQ line").
- Copy raw data from the hardware buffer into a kernel memory buffer (e.g., a socket buffer, or
sk_buffin networking). - Schedule the Bottom Half for later processing.
- Return as fast as possible — typically in microseconds.
The Top Half cannot sleep, cannot block, and cannot call any function that might sleep. Violating this rule causes kernel panics or deadlocks.
Bottom Half — The Deferred Work
The Bottom Half is scheduled by the Top Half but runs later, when the CPU has a moment. It runs with interrupts re-enabled, so it can be preempted. Its job is the heavy lifting:
- Parse protocol headers (TCP, IP, Ethernet).
- Walk firewall rules (netfilter/iptables).
- Reassemble fragmented packets.
- Hand data off to the waiting application via the socket layer.
Linux implements Bottom Halves through three mechanisms, in order of age and flexibility:
| Mechanism | Scheduled per | Can sleep? | Use case |
|---|---|---|---|
| SoftIRQ | CPU core | No | Networking, block I/O (performance-critical) |
| Tasklet | CPU core | No | General drivers (built on SoftIRQ) |
| Workqueue | Kernel thread | Yes | Anything needing sleep/blocking |
For networking, Linux uses NET_RX_SOFTIRQ and NET_TX_SOFTIRQ — two of the ten fixed SoftIRQ slots in the kernel.
The architectural insight: This split is why Linux networking can sustain millions of packets per second. The Top Half is a thin, fast acknowledgment layer. The Bottom Half is where the real work happens, but it runs in a context where it can be preempted and where multiple cores can work in parallel.