Ich zeige Schritt für Schritt, wie ich Linux SoftIRQ-Auslastung messe, Engpässe sichtbar mache und mit wenigen Kernel-Schrauben wieder Kontrolle bekomme. Dabei priorisiere ich messbare Effekte: kürzere Latenzen, ausgeglichene CPU-Kerne und stabile Paketverarbeitung unter hoher Netzwerklast.
Zentrale Punkte
- Messpunkte verstehen: /proc/softirqs, softnet_stat, interrupts
- Symptome erkennen: ksoftirqd-Last, Paketverluste, Latenzspitzen
- Tuning steuern: netdev_budget und netdev_budget_usecs
- Verteilung sichern: IRQ-Affinität, RSS, Queue-Mapping
- Monitoring durchziehen: mpstat, perf, Tracing
SoftIRQs im Überblick: Wie der Kernel arbeitet
Nach einem Hardware-Interrupt verschiebt der Kernel Teile der Arbeit in sogenannte SoftIRQs, damit kritische Pfade schnell frei werden und die Abarbeitung planbar bleibt. Besonders im Netzwerkpfad sammeln NAPI-Handler Pakete aus den NIC-Ringen ein, stoßen Protokollarbeit an und übergeben die Daten dem Netzwerk-Stack. Steigt das Ereignisaufkommen, springen per-CPU-Threads wie ksoftirqd/cpuN ein und übernehmen Polling sowie Nacharbeit. Diese Entkopplung verbessert die Gesamtdurchsatzleistung, kann aber bei Überlast zu langen SoftIRQ-Laufzeiten auf einzelnen Kernen führen. Ich beobachte deshalb frühzeitig, ob NET_RX- und NET_TX-Pfade dominieren und ob ksoftirqd sichtbar CPU-Zeit frisst. So erkenne ich, wann SoftIRQs zum Engpass werden und weitere Optimierungen nötig sind.
Typische Symptome hoher SoftIRQ-Last erkennen
Ausgeprägte SoftIRQ-Last sehe ich zuerst an dauerhaft hoher Kernel-CPU und ksoftirqd-Prozessen, die über viele Sekunden hinweg Spitzen halten. Parallel steigen Latenzen für Netzwerk- und Block-I/O, was sich in zähen TLS-Handshakes oder schleppenden APIs äußert. Häufig häufen sich Paketverluste, während Ringe der Netzwerkkarte überlaufen und Backlogs wachsen. Wenn Interrupts schlecht verteilt sind, leidet oft CPU 0 stark, weil viele IRQ-Lines samt Folgearbeit auf einem Kern landen. Diese Ein-Kern-Bindung treibt Wartezeiten für Dienste nach oben und reduziert den effektiven Durchsatz. Ich prüfe darum, ob das Muster systematisch ist oder nur zu Peaks führt.
Zentrale Messpunkte: /proc und Tools richtig lesen
Den Start mache ich mit /proc/softirqs, denn dort sehe ich pro CPU und Typ, wie stark etwa NET_RX, NET_TX, TIMER oder BLOCK ansteigen. In /proc/net/softnet_stat prüfe ich die Zeilen mit Fokus auf Felder, die verpasste Budgets oder Drops signalisieren, was bei stetigem Anstieg klar auf zu kurze Polling-Zyklen hindeutet. /proc/interrupts offenbart danach, ob Hardware-Interrupts ungleich auf die CPUs fallen und welche IRQs die lautesten sind. Werkzeuge wie mpstat, top oder htop helfen mir, ksoftirqd/cpuN zu identifizieren und die Verteilung der Softirq-Zeiten pro Kern zu beurteilen. Bei Bedarf liefert perf Hotspots im Stack, damit ich Handler und Treiberpfade mit hohem Zeitanteil finde. Die folgende Tabelle fasst die wichtigsten Messpunkte, Indikatoren und typischen Aktionen zusammen.
| Messpunkt | Wichtigste Felder/Indikatoren | Interpretation | Aktion |
|---|---|---|---|
| /proc/softirqs | NET_RX, NET_TX, BLOCK je CPU | Ungleichmäßige Lastverteilung erkennbar | IRQ-Affinität anpassen, RSS aktivieren |
| /proc/net/softnet_stat | Budget-/Drop-Zähler, dritte Spalte | Budgets zu klein, Pakete bleiben liegen | netdev_budget/usecs erhöhen, RPS prüfen |
| /proc/interrupts | IRQ-Lines pro CPU, Queue-Mapping | Zu viele IRQs auf wenigen Kernen | irqbalance prüfen, smp_affinity setzen |
| mpstat / perf | %soft, Hotspots, Stacks | Dominante Handler und Kerne sichtbar | Treiber- und Stack-Tuning priorisieren |
Ursachen und Muster hoher Last
Spitzen entstehen oft durch sehr hohen Durchsatz, viele parallele Verbindungen oder UDP-Bursts, die NET_RX dominieren. Manchmal sehen Treiber-Defaults kleine Batches vor, wodurch zu viele Interrupts entstehen und ksoftirqd überlastet wird, während GRO/LRO ungenutzt bleibt. Ungünstige Affinitäten bündeln Arbeit auf CPU 0, obwohl mehrere Queues verfügbar wären und RSS die Verteilung erleichtern könnte. In virtuellen Maschinen belasten vNICs den Host-Kernel, was SoftIRQ-Zeiten im Host auf Kosten der Gäste steigert. Container-Overlays legen zusätzlich Pakete auf den Stack, wodurch aus leichten Flows plötzlich anspruchsvollere Pfade werden. Erst die Kombination aus Verteilung, Budget und Batching ergibt ein rundes Bild.
Gezielt überwachen: SoftIRQs sichtbar machen
Für ein tragfähiges Monitoring lese ich regelmäßig /proc-Schnittstellen aus und verknüpfe sie mit Host-Metriken wie Load und Sched-Latenzen. Ich korreliere NET_RX-Anstiege mit Drop-Zählern, um zu klären, ob nur Durchsatz steigt oder Pakete auf der Strecke bleiben. mpstat liefert mir pro CPU die Zeitanteile für SoftIRQs, während top/htop die auffälligen ksoftirqd/cpuN-Threads zeigen. Mit perf record/perf top isoliere ich teure Pfade, zum Beispiel Checksum-Offloads, GRO-Zusammenführung oder qdisc-Arbeit. eBPF- oder ftrace-basierte Traces zeigen Start und Ende der Handler, womit ich Handler-Laufzeiten und Scheduling-Effekte bewerten kann. So entsteht ein klares Lagebild aus Metriken, Zeitverläufen und Hotspots.
Tuning mit netdev_budget und netdev_budget_usecs
Greift der NAPI-Pfad zu kurz, erhöhe ich schrittweise net.core.netdev_budget und net.core.netdev_budget_usecs, um mehr Pakete pro Polling-Zyklus zu verarbeiten. Dabei beobachte ich die dritte Spalte in /proc/net/softnet_stat; sinkt der Anstieg, treffen die Änderungen ins Schwarze und Latenzen werden kürzer. Ich steigere die Werte moderat, beispielsweise von 300 auf 600 Pakete und von 2000 auf 4000 Mikrosekunden, und kontrolliere, ob andere Tasks ausreichend CPU-Zeit behalten. Ein Zuviel blockiert den Scheduler, weshalb ich Lastspitzen, Kontextwechsel und Runqueue-Längen eng begleite. Zusätzlich lohnt sich das Prüfen von RPS/RFS, GRO/LRO und der MTU, um Batching und Paketgrößen sinnvoll zu nutzen. Für verringerte Interrupt-Fluten berücksichtige ich Interrupt Coalescing und gleiche Feinheiten mit den NIC-Treibern ab, falls dieser Hebel verfügbar ist.
Interrupt-Verteilung und IRQ-Affinität optimieren
Um Ein-Kern-Engpässe zu vermeiden, verteile ich IRQs auf mehrere CPUs, entweder per irqbalance oder mit manuellen smp_affinity-Masken. Dabei orientiere ich mich an den vorhandenen NIC-Queues und aktiviere RSS, damit die Hardware eingehende Flows gleichmäßig streut und jeder Kern arbeitserleichternde Batches erhält. Ich achte darauf, Steuer-IRQs nicht mit heißen Datenpfaden zu vermischen, um Cache-Lokalität und Planbarkeit zu wahren. Korrekt gesetzte Affinitäten senken Latenzen und verringern Drops, weil SoftIRQ-Nacharbeit nicht mehr auf einem Kern stecken bleibt. Treiber zeigen oft Queue-zu-CPU-Zuordnungen in sysfs; dort prüfe ich, ob jede Queue einen passenden Kern hat und keine Asymmetrien entstehen. Tiefergehend orientiere ich mich an Leitfäden wie IRQ-Affinität, um auch NUMA-Aspekte und Cache-Wirkung zu berücksichtigen.
Praxisleitfaden: Von Symptomen zur Lösung
Am Anfang verifiziere ich die Symptome: ksoftirqd/cpuN in top, SoftIRQ-Anteile je Kern in mpstat und auffällige NET_RX-Stürme. Danach sammele ich harte Fakten aus /proc/softirqs, /proc/net/softnet_stat und /proc/interrupts, um dominante Pfade und schiefe Verteilungen festzuhalten. Anschließend setze ich kleine Tuning-Schritte, zunächst an den netdev-Budgets, gefolgt von IRQ-Affinität und RSS, jeweils mit engmaschiger Kontrolle. Bleiben Drops sichtbar, prüfe ich Treibereinstellungen, Coalescing-Optionen, Offloads und GRO/LRO-Verhalten. In VM- oder Container-Hosts evaluiere ich zusätzlich, wie die vNICs mit dem physischen Host-Stack interagieren und wo die Hotspots wirklich liegen. Jede Änderung bewerte ich über Zeitreihen, bis Metriken und Latenzen stabil auf gutem Niveau landen.
Best Practices für nachhaltige Performance
Ich verankere regelmäßiges Monitoring der SoftIRQ-Zähler, denn nur konstante Transparenz verhindert Rückfälle in Engpässe. Aktuelle Kernel-Versionen zahlen sich aus, weil NAPI und Stack intern weiter verbessert werden und damit Reserven für harte Lasten schaffen. Eine ausgewogene Verteilung über mehrere Kerne bleibt Pflicht, ebenso wie sinnvolle Budgets, die genug Pakete abholen, ohne den Scheduler zu knebeln. Für Hosting-Profile mit viel HTTPS und API-Verkehr lohnt sich ein Blick auf SoftIRQ im Hosting, denn dort zeigt sich, wie stark die Wahl von NICs, Queues und Tuning die Servicequalität hebt. In der Kapazitätsplanung berücksichtige ich CPU-Kerne, NIC-Features, Speicher und NUMA-Zonen, damit Reserven vorhanden sind, bevor Spitzen eintreffen. So bleibt die Plattform belastbar und reagiert sauber auf saisonale oder kampagnengetriebene Verkehrsspitzen.
softnet_stat im Detail: Was die Zahlen wirklich bedeuten
Um gezielt nachzuschärfen, lese ich /proc/net/softnet_stat im Verlauf und interpretiere insbesondere die ersten Spalten. Die Anfangsfelder zählen verarbeitete und verworfene Pakete pro CPU, die dritte Spalte weist auf Zeitdruck hin (verkürzt: zu wenig Budget/Zeitscheibe, NAPI muss abbrechen). Wachsen die Drops oder der Zeitdruck linear mit der Last, sind Budgets oder Coalescing die ersten Hebel. Sehe ich dagegen Peaks ohne nachhaltigen Anstieg, verdichten Bursts lediglich kurzfristig die Arbeit – dann hilft Batching (GRO) häufiger als große Budgets. Neuere Kernel erweitern die Statistiken um Felder für RPS/RFS und Flow-Limits; steigen diese, verteile ich bewusster über RPS oder reduziere RFS, wenn seine Lookups teurer werden als ihr Nutzen. Ich korreliere die Zähler stets mit /proc/softirqs: Steigt NET_RX auf einzelnen Kernen zusammen mit Zeitdruck in softnet_stat, fokussiere ich zuerst auf Verteilung (IRQ/RSS) und erst im zweiten Schritt auf größere Budgets.
RPS/RFS und XPS: Software-Steering und Queue-Tuning im Griff
Wenn Hardware-RSS fehlt oder nicht ausreicht, setze ich RPS (Receive Packet Steering), um RX-Last auf mehrere Kerne zu verteilen. Ich weise den RX-Queues über rps_cpus solche Kerne zu, die zu den aktiven Workern passen und nach Möglichkeit NUMA-nah liegen. Bei vielen Flows ergänze ich RFS (Receive Flow Steering), damit eingehende Pakete dort enden, wo die zugehörigen Sockets verarbeitet werden – gut für Cache-Lokalität, solange die Flow-Tabellen nicht zum Flaschenhals werden. Auf der Senderseite hilft XPS (Transmit Packet Steering), die Wahl der TX-Queue an die CPU-Bindung der Anwendung anzugleichen. Ziel ist, dass ein Flow konsistent über dieselbe RX/TX-Queue und denselben Kern läuft, wodurch Latenzen sinken und GRO-Batches größer werden. Ich teste Verteilungen immer schrittweise: erst RPS an wenigen Queues aktivieren, Wirkung messen (Drops, %soft, Latenzen), dann RFS/XPS hinzunehmen. Werden Kerne durch RPS überfüllt oder verschlechtert sich der L3-Hitrate, reduziere ich die CPU-Masken wieder oder binde Queues enger an die Kerne der betreffenden Dienste.
NUMA, CPU-Isolation und Scheduler-Wechselwirkungen
Die besten Budgets und Verteilungen nützen wenig, wenn Speicherzugriffe weite NUMA-Wege nehmen. Ich achte darauf, dass NIC-Interrupts, NAPI-Nacharbeit und die anfragenden Prozesse möglichst innerhalb derselben NUMA-Domäne bleiben. In Setups mit dedizierten Echtzeit- oder Latenz-Kernen isoliere ich diese per CPU- und Cgroup-Policies und halte dort SoftIRQ-Arbeit bewusst fern. ksoftirqd sollte nicht auf isolierten Kernen landen, sonst stauen sich Pakete unbemerkt. Umgekehrt dürfen isolierte Kerne nicht völlig ohne IRQ-Servicing dastehen, wenn sie Datenpfade terminieren – eine klare Affinitäts- und Housekeeping-Strategie ist Pflicht. Für Workloads mit strengen SLOs meide ich überaggressive SCHED_FIFO/RR-Prioritäten, die NAPI-Ausführung verdrängen könnten. Ich beobachte Runqueue-Längen, Wakeups und Preemption-Raten: Wenn SoftIRQ-Zeiten mit wachsender Interaktivität der App ansteigen, justiere ich Granularität und Affinitäten, statt pauschal Budgets aufzudrehen.
qdisc, Offloads und Busy-Poll: Latenz und Durchsatz ausbalancieren
Auf dem Egress-Pfad kostet jede qdisc-Operation CPU-Zeit. Ich wähle die Disziplin passend zum Profil: fq_codel hilft bei Pufferbloat und glättet Bursts, während mq-Varianten Multi-Queue-NICs gerecht werden. Bei reinem Datendurchsatz auf stabilen Links kann eine leichtere qdisc Latenzspitzen minimieren. Auf dem Ingress lohnt die Feinabstimmung von GRO/TSO/GSO: Größere Batches drücken die SoftIRQ-Rate, erhöhen aber im Grenzfall die Paketzeit im Stack. Ich messe, ob GRO-Flush-Intervalle oder Hardware-Offloads zu großen Aggregaten führen, die der Anwendung schaden. Für sehr latenzkritische Pfade setze ich busy_poll und busy_read dosiert ein, um Pakete aktiv aus dem Treiber zu ziehen – jedoch nur mit enger Beobachtung, damit andere Tasks nicht verhungern. Ebenso stimme ich Interrupt Coalescing auf Burstanstiege ab: etwas mehr Mikrosekunden koalieren hilft dem Durchsatz, zu viel verzögert ACKs und verlängert Handshakes. Wichtig ist, je Änderung separat zu bewerten: netemulierte Bursts, echte Produktionsspitzen und Leerlaufzeiten zeigen oft unterschiedliche Latenzprofile.
Diagnose-Checkliste und sicheres Rollback
Ich arbeite Änderungen konsequent über eine kurze Checkliste ab: 1) Symptome belegen (ksoftirqd, %soft, Drops). 2) Verteilung prüfen (/proc/interrupts, Queue->CPU, RSS/RPS-Status). 3) Budgets anpassen, Wirkung in softnet_stat beobachten (Zeitdruck fällt, Drops stagnieren). 4) Offloads/Coalescing feinjustieren, qdisc sichten. 5) NUMA/CPU-Bindungen und cgroups gegenprüfen. Jeder Schritt endet mit einer klaren Metrikverbesserung oder dem Rollback auf den letzten guten Stand. Ich dokumentiere Ziel- und Istwerte (Latenz-P95/P99, %soft je Kern, Dropraten, Kontextwechsel), damit spätere Iterationen nicht im Nebel erfolgen. Führen mehrere kleine Gewinne nicht zur Entlastung, breche ich ab und suche strukturelle Ursachen (Queue-Bottlenecks, App-Blöcke, Storage-Einflüsse). Diese Disziplin verhindert Fehlkorrelationen und schützt vor Tuning-Spiralen, die zwar Durchsatzzahlen erhöhen, aber Interaktivität und Stabilität verschlechtern.
Grenzfälle und Workload-Profile sauber trennen
Ich unterscheide bewusst zwischen Bulk-Transfer, latenzkritischen APIs und burstigem UDP-Traffic. Für Bulk-Daten ziehe ich Batching und Coalescing früher an, solange Paketverluste ausbleiben. Bei API-Verkehr priorisiere ich gleichmäßige Verteilung, begrenzte Batches und stabile E2E-Latenzen, selbst wenn der Maximaldurchsatz nominal etwas sinkt. UDP-Bursts bekämpfe ich vorzugsweise über Queue-Verbreiterung und Affinitäten – zu große Budgets steigern sonst nur Head-of-Line-Blocking. Nutzt eine Umgebung viele Container- oder Overlay-Hops, plane ich zusätzliche Stack-Arbeit ein und streue SoftIRQ-Last breiter. Ich bewerte außerdem Firewall-/Conntrack-Overheads separat: Wenn Tabellen an Grenzen stoßen, steigt die SoftIRQ-Last zwangsläufig, egal wie gut die IRQ-Verteilung ist. Erst wenn die Pfade je Profil konsistent schlank sind, lohnt sich das Feintuning der letzten Prozente.
SoftIRQs in Cloud- und Container-Umgebungen
In virtualisierten Szenarien verschiebt sich Last durch vSwitches, Overlay-Netze und Host-Stacks, weshalb ich sowohl Gast- als auch Host-Metriken analysiere. Hohe SoftIRQ-Zeiten im Host bremsen Container und VMs unmittelbar aus, selbst wenn die Gastsysteme scheinbar entspannt wirken. Ich prüfe daher Offloads und Coalescing auf dem physischen NIC, während RPS/RFS im Host den Software-Pfad besser verteilt. Für Container-Workloads schaue ich, ob die Cgroup-Limits für CPU und IRQ-Nacharbeit sinnvoll gesetzt sind, damit wichtige Dienste nicht in Warteschlangen verhungern. Multi-Queue-fähige vNICs mit RSS verbessern die Parallelität, sofern Affinitäten und Queue-Mappings stimmen. Mit dieser Sicht halte ich die Datenpfade kurz, stabilisiere Latenzen und sichere reproduzierbare Performance.
Zusammenfassung: SoftIRQ-Analyse souverän meistern
Wer SoftIRQ-Last sauber analysiert, nutzt klare Messpunkte, betrachtet Verteilungen und setzt abgestufte Schritte. Ich starte bei /proc/softirqs und softnet_stat, korreliere mit ksoftirqd und mpstat und ziehe daraus die Reihenfolge meiner Maßnahmen. Zuerst justiere ich netdev_budget und netdev_budget_usecs, danach optimiere ich IRQ-Affinität, RSS sowie Batching-Optionen wie GRO und Offloads. Jeder Eingriff bleibt klein, wird gemessen und nur bei positiver Wirkung fortgeführt, bis Drops verschwinden und Latenzen sinken. Diese Disziplin verhindert Seiteneffekte, bewahrt Interaktivität auf der CPU und hält Dienste auch unter Verkehrsspitzen reaktionsfähig. So bleibt die Linux-Performance transparent, belastbar und anpassbar, ohne dass versteckte Hotspots die Stabilität gefährden.


