In this lesson, you will learn how to resolve network throughput bottlenecks by distributing SoftIRQ processing across multiple CPU cores.
The SoftIRQ Bottleneck
When a high-speed network interface receives packets, it triggers hardware interrupts (hardirqs) that notify the CPU. However, the heavy lifting—decapsulating headers, passing data to the TCP/IP stack, and delivering it to sockets—happens in SoftIRQ context (specifically NET_RX_SOFTIRQ).
If all incoming traffic is steered to a single CPU core by the NIC’s hardware queues, that core becomes a bottleneck. You will observe this in top or mpstat as one CPU hitting 100% si (software interrupt) utilization while others remain idle, even though the system has plenty of total capacity.
Distributing the Load
To resolve this, we manipulate the smp_affinity for the interrupt lines associated with your network interface. By default, the irqbalance daemon attempts to manage this, but for high-performance networking, manual tuning or specific RSS (Receive Side Scaling) configurations are often required.
First, identify which CPU cores are handling your NIC interrupts:
# View current mapping of IRQs to CPUs
cat /proc/interrupts | grep eth0
If you find that eth0-txrx-0 is pinned to CPU 0, you can redistribute the load by modifying the smp_affinity file for that specific IRQ. You provide a hexadecimal bitmask to tell the kernel which CPUs are allowed to handle that interrupt:
# Allow IRQ 124 to be handled by CPU 1 only
echo 2 > /proc/irq/124/smp_affinity
By spreading different IRQ lines across different physical cores (or NUMA nodes, as you learned in previous lessons), you ensure that no single core becomes a serialization point for network traffic. This spreads the SoftIRQ burden, allowing the kernel to parallelize packet processing across your entire multi-core system. When you observe high si usage, verify if your irqbalance is fighting your manual affinity settings or if the NIC hardware is failing to spread traffic across its queues.
Key Takeaway: SoftIRQ balancing prevents packet drops by ensuring that network processing workload is distributed across multiple CPUs, transforming a single-core bottleneck into a scalable parallel operation.