Debian 13 : orages d’IRQ et latences étranges — vérifiez irqbalance et corrigez les interruptions

Cet article vous a aidé ?

Tout semble « correct » jusqu’à ce que ce ne soit plus le cas. Les graphiques de latence se mettent à mordre. Une base de données qui ronronnait depuis des mois commence à hoqueter toutes les 30–90 secondes. Le p99 part en vrille alors que l’« idle » CPU prétend encore être détendu. Puis vous regardez un cœur coincé à 100 % dans ksoftirqd et vous réalisez que vous ne chassez pas un problème applicatif du tout.

Sur Debian 13, les orages d’IRQ et le déséquilibre d’interruptions peuvent ressembler à de l’activité paranormale : les paquets arrivent, les disques terminent, mais votre ordonnanceur et vos files perdent la bataille. La solution n’est pas « redémarrer et prier ». Il s’agit de mesurer les interruptions sérieusement, de vérifier irqbalance, et de prendre des décisions intentionnelles sur l’affinité, le nombre de files, et les offloads.

Guide de diagnostic rapide

Si vous êtes de garde et que le pager fait des siennes, vous avez besoin d’une séquence qui restreint la recherche en quelques minutes. Ne commencez pas par du « tuning ». Commencez par prouver où le temps est passé.

Première étape : confirmer si les interruptions/softirqs sont le goulot

  • Vérifiez si un CPU est saturé et que ce n’est pas l’espace utilisateur.
  • Vérifiez les taux de softirq et les principaux contributeurs (NET_RX, BLOCK, TIMER).
  • Vérifiez si les interruptions s’accumulent sur CPU0 (classique) ou sur un seul nœud NUMA.

Deuxième étape : identifier quel périphérique génère la pression

  • Cartographiez les IRQ chaudes vers les périphériques (IRQ de files NIC, vecteurs MSI-X NVMe, lignes HBA).
  • Corrélez avec la chronologie de la charge (pics réseau, flush stockage, snapshot, sauvegarde).
  • Vérifiez si le périphérique dispose d’assez de files et si elles sont utilisées.

Troisième étape : décider entre « laisser irqbalance faire » ou « épingler délibérément »

  • Si c’est un serveur usage général avec des charges changeantes : préférez irqbalance avec des valeurs par défaut sensées.
  • Si c’est un système sensible à la latence avec CPU épinglés (DPDK, quasi-realtime, trading, audio, télécom) : désactivez irqbalance et épinglez les interruptions de façon intentionnelle.
  • Si vous utilisez l’isolation CPU (isolcpus, nohz_full) : les interruptions doivent rester hors des cœurs isolés, sinon vous avez construit une voiture de course et monté des roues de caddie.

Quatrième étape : vérifiez que vous avez bien amélioré le bon indicateur

  • Mesurez la latence end-to-end p95/p99, pas seulement « le CPU a l’air mieux ».
  • Confirmez que les IRQ chaudes sont réparties (ou épinglées correctement) et que les taux sont stables.
  • Surveillez les régressions : pertes de paquets, retransmissions accrues, taux d’interruption plus élevé après désactivation de la coalescence.

Ce que vous voyez réellement (orages d’IRQ, softirqs et latence)

Un « orage d’IRQ » n’est généralement pas un orage électrique littéral. C’est le noyau interrompu si fréquemment — ou traitant tellement de travail différé d’interruption — que le travail réel ne peut pas s’exécuter correctement. Les symptômes ressemblent souvent à des bizarreries applicatives : timeouts, IO gelé, courts « hoquets » et jitter qui ne correspond pas à la moyenne CPU ou au débit.

Sur Linux moderne, les interruptions matérielles (top halves) sont maintenues courtes. Le travail lourd est différé dans les softirqs (bottom halves) et les kthreads comme ksoftirqd/N. Si la charge softirq est élevée, le noyau peut exécuter le traitement softirq dans le contexte de la tâche interrompue (rapide, bon pour le débit) ou dans ksoftirqd (préemptable, mais peut prendre du retard). Si vous voyez ksoftirqd dominer un CPU, vous êtes en retard.

Le traitement réseau entrant (NET_RX) est un coupable fréquent : si les paquets arrivent assez vite, le système peut passer énormément de temps CPU juste à déplacer les paquets du NIC vers les buffers de socket, laissant moins de temps à l’application pour les vider. Le stockage peut aussi provoquer cela : les complétions NVMe sont rapides et fréquentes, et avec plusieurs files vous pouvez générer un tambourin constant d’interruptions.

Le déséquilibre d’interruptions est le cousin plus discret des orages. Le taux total d’interruptions peut être correct, mais s’il est concentré sur un CPU (souvent CPU0), ce cœur devient votre goulot par défaut. L’ordonnanceur peut afficher beaucoup d’idle ailleurs, ce qui mène au diagnostic erroné classique : « le CPU est ok ». Ce n’est pas vrai. Un cœur est en feu pendant que les autres prennent un brunch.

Deux schémas courants :

  • Surcharge CPU0 : de nombreux pilotes par défaut utilisent la file 0 ou un vecteur unique sauf si RSS/MSI-X et l’affinité sont configurés. Les affinités par défaut au démarrage peuvent aussi biaiser vers CPU0.
  • Mauvais appairage NUMA : les interruptions tournent sur des CPU éloignés de la localité mémoire du périphérique PCIe. Cela ajoute de la latence et consume la bande passante d’interconnexion. C’est la mort par mille cache-miss.

Il y a aussi le piège de la « modulation d’interruption » : si la coalescence est trop agressive, vous réduisez le taux d’interruption mais augmentez la latence car le NIC attend plus longtemps avant d’interrompre. Trop peu de coalescence, et vous pouvez faire fondre un cœur avec les interruptions. Le réglage est un compromis : vous décidez combien de jitter vous pouvez tolérer pour éviter la surcharge.

Une citation à garder en tête pendant ce travail : L’espérance n’est pas une stratégie. — General Gordon R. Sullivan

Blague n°1 : Les orages d’interruptions sont comme les réunions — si vous en avez trop, rien d’autre n’est fait.

Faits et contexte intéressants (pourquoi cela se reproduit)

  • 1) « irqbalance » existe parce que le SMP a rendu le routage d’interruptions naïf pénible. Les premiers systèmes Linux multiprocesseurs envoyaient souvent les interruptions sur le CPU de boot, produisant des hotspots CPU0 qui donnaient l’impression que « Linux est lent ».
  • 2) MSI-X a changé la donne. Les Message Signaled Interrupts (et MSI-X) permettent aux périphériques de lever des interruptions via des messages en mémoire et supportent de nombreux vecteurs — parfait pour les NIC multi-queue et NVMe.
  • 3) NAPI a été inventé pour arrêter le livelock de réception de paquets. Le réseau Linux est passé à un modèle de sondage mitigant les interruptions (NAPI) car le RX purement piloté par interruption pouvait s’effondrer à haut débit de paquets.
  • 4) Les softirqs sont par CPU par conception. C’est bon pour la localité de cache, mais cela signifie aussi qu’« un CPU qui se noie dans NET_RX » peut affamer le travail sur ce CPU même quand les autres sont inactifs.
  • 5) « Orage d’IRQ » signifiait autrefois matériel défectueux plus souvent. Aujourd’hui, c’est fréquemment une mauvaise configuration des files/coalescence ou des changements de charge (par exemple un nouveau service envoyant des paquets de 64 octets à un million par seconde).
  • 6) La performance NVMe s’accompagne d’un volume de complétions important. Beaucoup de petits IO à haut IOPS peuvent générer un taux élevé d’événements de complétion ; les vecteurs MSI-X et le mapping des files comptent.
  • 7) Les fonctions d’isolation CPU rendent le placement des interruptions plus important. isolcpus et nohz_full peuvent améliorer la latence, mais seulement si vous gardez les interruptions et la maintenance du noyau hors des cœurs isolés.
  • 8) Les offloads modernes des NIC ne sont pas toujours vos amis. GRO/LRO/TSO peuvent réduire le CPU mais augmenter la latence ou le jitter, et certaines charges (petits RPC) détestent leur comportement de mise en tampon.
  • 9) « irqbalance » est devenu une politique, pas de la magie. Il prend des décisions basées sur des heuristiques de charge. Ces heuristiques peuvent être inadaptées pour des systèmes spécialisés.

Outils et principes : comment Debian 13 gère les interruptions

Debian 13 n’est que Linux avec des choix de packaging. Le noyau utilise /proc/interrupts comme vérité brute. Des outils comme irqbalance appliquent une politique. Les pilotes exposent des réglages via /sys et ethtool. Votre travail est de décider à quoi ressemble le « bien » pour votre charge, puis de l’imposer.

Hard IRQs vs softirqs : ce qui compte pour la latence

Le contexte d’interruption matérielle est extrêmement contraint. La plupart du travail est renvoyé aux softirqs. Quand la charge softirq est élevée, le noyau peut exécuter le traitement softirq dans le contexte de la tâche interrompue (rapide) ou dans ksoftirqd (préemptable mais susceptible de prendre du retard). Si vous voyez ksoftirqd dominer un CPU, vous êtes en retard.

Affinité : « quel CPU traite cette interruption ? »

Chaque IRQ a un masque d’affinité. Pour MSI-X, chaque vecteur est effectivement sa propre IRQ et peut être réparti sur les CPU. Pour les interruptions basées sur une ligne legacy, vos options sont limitées et le partage peut devenir moche.

Files : périphériques multi-queue et pourquoi une seule file est une tragédie

Pour les NIC, multi-queue + RSS permet aux flux entrants d’être hachés sur plusieurs files RX, chacune avec son propre vecteur d’interruption. Pour les périphériques bloc, blk-mq mappe les files IO aux CPU. Ce n’est pas qu’une histoire de débit. C’est aussi une histoire de latence : réduire la contention sur les locks et garder le chemin chaud local.

NUMA : ne payez pas la distance si vous n’en avez pas besoin

Sur des systèmes multi-socket, placer les interruptions du NIC sur des CPU locaux au root complex PCIe du NIC réduit le trafic mémoire inter-nœuds. Debian ne devinera pas toujours correctement votre topologie. Vous devez vérifier.

Tâches pratiques : commandes, sorties et décisions (12+)

Voici les actions qui résolvent des incidents réels. Chaque tâche inclut : une commande, ce que la sortie signifie, et la décision que vous en tirez. Lancez-les en root si nécessaire.

Task 1: Confirm the symptom is interrupt/softirq pressure

cr0x@server:~$ top -H -b -n 1 | head -n 25
top - 10:11:12 up 12 days,  3:44,  1 user,  load average: 6.14, 5.98, 5.22
Threads:  421 total,   3 running, 418 sleeping,   0 stopped,   0 zombie
%Cpu(s):  8.0 us,  2.1 sy,  0.0 ni, 78.4 id,  0.0 wa,  0.0 hi, 11.5 si,  0.0 st
PID   USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
  32  root      20   0       0      0      0 R  96.7   0.0  78:22.41 ksoftirqd/3

Ce que cela signifie : Un si élevé (softirq) et un thread ksoftirqd/N chaud est un coupable évident. Votre temps CPU est dépensé sur le travail différé d’interruption, souvent réseau.

Décision : Passez immédiatement à la mesure des softirqs et à la cartographie des interruptions vers les périphériques. Ne touchez pas encore à l’application.

Task 2: See which softirq classes are hot

cr0x@server:~$ cat /proc/softirqs
                    CPU0       CPU1       CPU2       CPU3
          HI:          0          0          0          0
       TIMER:   10234567    9123456    9234567    9012345
      NET_TX:       884      12933      11822      11540
      NET_RX:  90345678   1245678   1300045   1219990
       BLOCK:    112233     110998     111102     109876
    IRQ_POLL:          0          0          0          0
     TASKLET:      3333       2888       3011       2999
       SCHED:    400000     398000     402000     399000
     HRTIMER:       222       211        219        210
         RCU:    600000     590000     610000     605000

Ce que cela signifie : NET_RX massivement biaisé vers CPU0 crie « le traitement RX est concentré ». Cela correspond souvent à une interruption de file RX qui arrive sur CPU0.

Décision : Inspectez /proc/interrupts et la configuration des files du NIC. Votre objectif est de répartir les interruptions des files RX ou d’activer/vérifier RSS.

Task 3: Identify hot IRQ lines and whether they’re imbalanced

cr0x@server:~$ awk 'NR==1 || /eth0|nvme|i915|virtio|mlx|ixgbe|enp/ {print}' /proc/interrupts
            CPU0       CPU1       CPU2       CPU3
  35:   81234567     120332     110221     118877   PCI-MSI 524288-edge      eth0-TxRx-0
  36:      1200   22100333     118900     119010   PCI-MSI 524289-edge      eth0-TxRx-1
  37:      1100     111200   20300111     119100   PCI-MSI 524290-edge      eth0-TxRx-2
  38:       900     112300     120010   19899110   PCI-MSI 524291-edge      eth0-TxRx-3
  92:    900000     910000     905000     899000   PCI-MSI 1048576-edge      nvme0q0
  93:    120000     118000     119000     121000   PCI-MSI 1048577-edge      nvme0q1

Ce que cela signifie : Ici la queue 0 du NIC est absurdement chaude sur CPU0. Les autres queues semblent plus saines. C’est un déséquilibre classique.

Décision : Corrigez l’affinité du vecteur chaud et confirmez que RSS répartit les flux. Si la file 0 est effectivement la plus chargée à cause du hachage, augmentez les files ou ajustez l’indirection RSS.

Task 4: Confirm whether irqbalance is running and what it thinks it should do

cr0x@server:~$ systemctl status irqbalance --no-pager
● irqbalance.service - irqbalance daemon
     Loaded: loaded (/lib/systemd/system/irqbalance.service; enabled; preset: enabled)
     Active: active (running) since Mon 2025-12-29 08:12:01 UTC; 1 day 02:00 ago
   Main PID: 812 (irqbalance)
      Tasks: 1 (limit: 38121)
     Memory: 3.8M
        CPU: 21min 33.120s

Ce que cela signifie : irqbalance est en cours. Donc soit (a) il ne peut pas déplacer cette IRQ (certaines sont épinglées/inamovibles), (b) il est configuré pour éviter certains CPU, ou (c) il fait un mauvais choix pour votre charge.

Décision : Vérifiez la configuration d’irqbalance et les masques d’affinité des IRQ. Décidez s’il faut ajuster irqbalance ou remplacer manuellement certaines IRQ.

Task 5: Check current affinity mask for a specific IRQ

cr0x@server:~$ cat /proc/irq/35/smp_affinity_list
0

Ce que cela signifie : L’IRQ 35 est épinglée sur CPU0 seulement. irqbalance ne peut rien faire si quelque chose (ou quelqu’un) l’a hard-pinnée.

Décision : Si ce n’est pas intentionnel, changez-le. Si CPU0 est réservé pour la maintenance et que vous le souhaitez ainsi, épinglez ailleurs.

Task 6: Move a hot IRQ to another CPU (quick test)

cr0x@server:~$ echo 2 > /proc/irq/35/smp_affinity
cr0x@server:~$ cat /proc/irq/35/smp_affinity_list
1

Ce que cela signifie : L’IRQ est maintenant routée vers CPU1 (bit de masque 1). C’est un instrument brutal mais excellent pour prouver la causalité.

Décision : Si la latence s’améliore immédiatement et que ksoftirqd se calme, vous avez confirmé que le placement d’interruptions est le problème. Ensuite, faites la correction durable (mapping conscient des files, politique irqbalance, placement NUMA adéquat).

Task 7: Identify the NIC, driver, and bus location (NUMA hints)

cr0x@server:~$ ethtool -i eth0
driver: ixgbe
version: 6.1.0
firmware-version: 0x800003e2
bus-info: 0000:3b:00.0
supports-statistics: yes
supports-test: yes
supports-eeprom-access: yes
supports-register-dump: yes
supports-priv-flags: yes

Ce que cela signifie : bus-info est l’adresse PCI. Vous pouvez la mapper à un nœud NUMA et à la localité CPU.

Décision : Gardez les interruptions du NIC sur les CPU locaux au nœud NUMA du NIC autant que possible.

Task 8: Find the device’s NUMA node and local CPU list

cr0x@server:~$ cat /sys/bus/pci/devices/0000:3b:00.0/numa_node
1
cr0x@server:~$ cat /sys/devices/system/node/node1/cpulist
16-31

Ce que cela signifie : Le NIC est attaché au nœud NUMA 1, CPU locaux 16–31. Si vos IRQ sont sur CPU0–3, vous payez des coûts cross-node.

Décision : Placez les IRQ du NIC sur les CPU 16–31, et envisagez aussi de placer les charges lourdes NIC là-bas.

Task 9: Check NIC queue/channel counts (do you even have enough queues?)

cr0x@server:~$ ethtool -l eth0
Channel parameters for eth0:
Pre-set maximums:
RX:	16
TX:	16
Other:	0
Combined:	16
Current hardware settings:
RX:	0
TX:	0
Other:	0
Combined:	4

Ce que cela signifie : Le NIC supporte jusqu’à 16 canaux combinés mais utilise actuellement 4. Si vous avez beaucoup de cœurs et des taux de paquets élevés, 4 files peuvent être un goulot.

Décision : Augmentez les canaux combinés si la charge en bénéficie et si le système a du CPU disponible. Mais n’en faites pas trop ; plus de files peuvent signifier plus d’overhead et une pire localité cache.

Task 10: Increase NIC combined queues (carefully) and re-check interrupts

cr0x@server:~$ sudo ethtool -L eth0 combined 8
cr0x@server:~$ ethtool -l eth0
Channel parameters for eth0:
Pre-set maximums:
RX:	16
TX:	16
Other:	0
Combined:	16
Current hardware settings:
RX:	0
TX:	0
Other:	0
Combined:	8

Ce que cela signifie : Vous avez maintenant 8 paires de files. Cela devrait créer plus de vecteurs MSI-X et mieux répartir le travail — si RSS est configuré et que vos flux se hachent bien.

Décision : Revérifiez /proc/interrupts après 30–60 secondes sous charge. Si une file domine toujours, votre hachage/indirection ou le pattern de trafic peut être la contrainte.

Task 11: Inspect RSS indirection and hash key (is traffic being spread?)

cr0x@server:~$ ethtool -x eth0 | head -n 25
RX flow hash indirection table for eth0 with 8 RX ring(s):
    0: 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
   16: 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
   32: 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
   48: 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
RSS hash key:
6d:5a:2c:...:91

Ce que cela signifie : La table semble uniformément distribuée. C’est bon. Si ce n’est pas le cas, ou si tout est en zéro, vous êtes effectivement en single-queue.

Décision : Si la distribution est mauvaise, ajustez la table d’indirection (avancé) ou corrigez les paramètres du pilote/firmware. Si votre charge est un flux unique (un gros flux TCP), RSS n’aidera pas beaucoup ; il faut segmenter l’application ou changer le transport.

Task 12: Check and tune interrupt coalescing (latency vs CPU trade)

cr0x@server:~$ ethtool -c eth0
Coalesce parameters for eth0:
Adaptive RX: on  TX: on
rx-usecs: 50
rx-frames: 0
tx-usecs: 50
tx-frames: 0

Ce que cela signifie : La coalescence adaptative est activée ; le NIC/driver changera la coalescence selon le trafic. C’est généralement acceptable pour usage général, mais cela peut introduire de la variance de latence.

Décision : Pour des charges strictes sur la queue, envisagez de désactiver la coalescence adaptative et de définir des valeurs fixes conservatrices. Validez avec de vraies mesures de latence, pas des impressions.

Task 13: Identify dropped packets or backlog overruns (symptom of NET_RX overload)

cr0x@server:~$ nstat | egrep 'TcpExtListenOverflows|IpInDiscards|UdpInErrors|TcpExtTCPBacklogDrop'
TcpExtTCPBacklogDrop          123
IpInDiscards                  456

Ce que cela signifie : Vous perdez des paquets dans la pile. Cela peut arriver quand le traitement softirq n’arrive pas à suivre, ou quand l’application accept/read ne peut pas vider assez vite.

Décision : Corrigez d’abord la distribution des interruptions. Ensuite, envisagez le tuning des backlogs de socket et la capacité applicative. Ne masquer pas un hotspot IRQ avec des buffers plus grands sauf si vous aimez les pannes différées.

Task 14: Look for IRQ “nobody cared” messages and kernel warnings

cr0x@server:~$ journalctl -k -b | egrep -i 'irq|nobody cared|soft lockup|hard lockup' | tail -n 20
Dec 30 09:55:18 server kernel: irq 35: nobody cared (try booting with the "irqpoll" option)
Dec 30 09:55:18 server kernel: Disabling IRQ #35

Ce que cela signifie : C’est sérieux. Une IRQ est devenue si bruyante ou défaillante que le noyau a décidé qu’elle était cassée et l’a désactivée. Cela peut mettre votre NIC ou votre stockage hors ligne en douceur.

Décision : Traitez cela d’abord comme un problème de pilote/firmware/matériel. Vérifiez les réglages BIOS, mettez à jour le firmware, vérifiez la stabilité MSI/MSI-X, et examinez si les paramètres de modulation d’interruption sont pathologiques.

Task 15: Verify NVMe queue/IRQ mapping is sane

cr0x@server:~$ ls -1 /proc/interrupts | head -n 0
cr0x@server:~$ grep -E 'nvme[0-9]q' /proc/interrupts | head -n 10
 92:    900000     910000     905000     899000   PCI-MSI 1048576-edge      nvme0q0
 93:    120000     118000     119000     121000   PCI-MSI 1048577-edge      nvme0q1
 94:    119000     121000     118000     120000   PCI-MSI 1048578-edge      nvme0q2

Ce que cela signifie : Plusieurs files NVMe existent et les interruptions sont relativement réparties. Si vous voyez seulement nvme0q0 chaud et les autres inactifs, le périphérique peut être limité à une file ou la charge est effectivement monothread.

Décision : Si les interruptions NVMe sont concentrées, vérifiez les réglages nvme_core.default_ps_max_latency_us, les paramètres du pilote, et si la couche bloc mappe les files sur les CPU comme prévu.

Task 16: Observe per-IRQ rate over time (not just totals)

cr0x@server:~$ for i in 1 2 3; do date; grep -E 'eth0-TxRx-0|eth0-TxRx-1|eth0-TxRx-2|eth0-TxRx-3' /proc/interrupts; sleep 1; done
Tue Dec 30 10:10:01 UTC 2025
 35:   81234567     120332     110221     118877   PCI-MSI 524288-edge      eth0-TxRx-0
 36:      1200   22100333     118900     119010   PCI-MSI 524289-edge      eth0-TxRx-1
Tue Dec 30 10:10:02 UTC 2025
 35:   81310222     120350     110240     118899   PCI-MSI 524288-edge      eth0-TxRx-0
 36:      1210   22155880     118920     119030   PCI-MSI 524289-edge      eth0-TxRx-1
Tue Dec 30 10:10:03 UTC 2025
 35:   81389001     120360     110255     118920   PCI-MSI 524288-edge      eth0-TxRx-0
 36:      1220   22210210     118940     119050   PCI-MSI 524289-edge      eth0-TxRx-1

Ce que cela signifie : Vous pouvez observer visuellement les deltas par seconde. Si une IRQ incrémente beaucoup plus vite que les autres, c’est votre hotspot. Les totaux cachent les changements de taux.

Décision : Ciblez l’IRQ à taux élevé pour l’équilibrage/épinglage et confirmez que la distribution s’améliore sous une charge représentative.

irqbalance sur Debian 13 : vérifier, ajuster et savoir quand désactiver

irqbalance est souvent soit aveuglément confié soit aveuglément blâmé. Aucun des deux n’est adulte. Traitez-le comme toute autre automatisation : c’est un moteur de politique avec des valeurs par défaut adaptées aux « serveurs typiques ». Si votre serveur n’est pas typique, il sera quand même traité comme typique à moins que vous n’interveniez.

Ce que irqbalance fait bien

  • Répartit les IRQ sur les CPU pour éviter que le CPU0 ne soit le souffre-douleur désigné.
  • Réagit aux charges changeantes sans que vous éditiez les masques d’affinité à 3 h du matin.
  • Travaille raisonnablement avec les périphériques MSI-X multi-queue.

Ce que irqbalance fait mal (ou ne peut pas faire)

  • Configurations isolation CPU sensibles à la latence : Il peut déplacer des interruptions sur des CPU que vous vouliez garder « propres » à moins d’une configuration soignée.
  • Placement NUMA sensible : Il peut ne pas toujours garder les interruptions locales au nœud PCIe comme vous le souhaitez.
  • Interruptions inamovibles : Certaines IRQ sont effectivement épinglées ou traitées de façon que irqbalance ne peut pas changer.

Vérifier la configuration d’irqbalance

cr0x@server:~$ grep -v '^\s*#' /etc/default/irqbalance | sed '/^\s*$/d'
ENABLED="1"
OPTIONS=""

Ce que cela signifie : Configuration par défaut. Pas de masques de CPU bannis, pas de comportement spécial.

Décision : Si vous observez tout de même un épinglage d’IRQ, autre chose définit l’affinité (scripts de pilote, tuning personnalisé, hooks de runtime container, ou vieux restes d’« optimisation »).

Voir quels CPU sont en ligne et si vous en avez isolé

cr0x@server:~$ lscpu | egrep 'CPU\(s\)|On-line CPU|NUMA node'
CPU(s):                               32
On-line CPU(s) list:                  0-31
NUMA node(s):                         2
NUMA node0 CPU(s):                    0-15
NUMA node1 CPU(s):                    16-31

Ce que cela signifie : Topologie claire. Si vous utilisez aussi des paramètres d’isolation du noyau, vérifiez la ligne de commande du noyau.

Décision : Si vous isolez des CPU (par ex. 4–31) pour des charges, vous devez bannir ces CPU des interruptions via les options d’irqbalance ou l’affinité manuelle.

Inspecter la ligne de commande du noyau pour les paramètres d’isolation

cr0x@server:~$ cat /proc/cmdline
BOOT_IMAGE=/vmlinuz-6.12.0 root=/dev/mapper/vg0-root ro quiet isolcpus=4-31 nohz_full=4-31 rcu_nocbs=4-31

Ce que cela signifie : Vous avez déclaré les CPU 4–31 spéciaux. Très bien. Maintenant gardez les interruptions hors d’eux, sinon vous avez fait une promesse que le noyau va casser à votre place.

Décision : Configurez irqbalance pour éviter 4–31, ou désactivez irqbalance et définissez des masques explicites pour toutes les IRQ pertinentes.

Bannir des CPU depuis irqbalance (schéma typique d’isolation)

cr0x@server:~$ sudo sed -i 's/^OPTIONS=.*/OPTIONS="--banirq=0"/' /etc/default/irqbalance
cr0x@server:~$ sudo systemctl restart irqbalance
cr0x@server:~$ systemctl status irqbalance --no-pager | head -n 12
● irqbalance.service - irqbalance daemon
     Loaded: loaded (/lib/systemd/system/irqbalance.service; enabled; preset: enabled)
     Active: active (running) since Tue 2025-12-30 10:18:01 UTC; 2s ago

Ce que cela signifie : Exemple seulement : bannir l’IRQ 0 n’est pas la même chose que bannir des CPU. Le bannissement de CPU se fait typiquement via les options masque CPU d’irqbalance (varie selon la version/build). Le point : ne touchez pas aux options au hasard sans comprendre.

Décision : Pour l’isolation CPU, préférez l’affinité explicite par IRQ ou la configuration du masque CPU d’irqbalance adaptée à votre version installée. Vérifiez en lisant /proc/irq/*/smp_affinity_list après le redémarrage.

Conseil opinionné : si vous gérez une flotte standard de serveurs Debian, laissez irqbalance activé. Si vous gérez une machine spécialisée pour la latence avec des CPU isolés, désactivez-le et gérez l’affinité explicitement via la gestion de configuration pour que ce soit reproductible.

Interruptions NIC : RSS, RPS/XPS, coalescence et sanity multi-queue

La plupart des cas de « latence bizarre » qui sentent l’interruption sont d’origine réseau. Ce n’est pas que le stockage soit innocent, mais parce que les paquets peuvent arriver au débit ligne et exiger une attention CPU immédiate. Un NIC peut livrer plus de travail par seconde que votre noyau ne peut digérer si vous le configurez comme en 2009.

Commencez par RSS (répartition matérielle de la réception)

RSS répartit les flux sur les files RX dans le matériel. Chaque file RX a son propre vecteur d’interruption. Si RSS est désactivé, ou si une seule file existe, un CPU finira par faire la plupart du travail de réception. Le débit peut sembler correct, tandis que la latence tail est détruite par la mise en file et le jitter.

RPS/XPS : direction logicielle quand RSS ne suffit pas

RPS (Receive Packet Steering) peut répartir le traitement des paquets sur les CPU même si RSS matériel est limité. XPS aide à répartir le traitement de transmission. Ce sont des mécanismes côté CPU ; ils peuvent améliorer l’équilibre mais ajoutent aussi de l’overhead. Utilisez-les quand vous en avez une raison, pas parce que vous avez lu un billet en 2016.

cr0x@server:~$ ls -1 /sys/class/net/eth0/queues/
rx-0
rx-1
rx-2
rx-3
tx-0
tx-1
tx-2
tx-3

Ce que cela signifie : Le multi-queue existe. Bien. Confirmez maintenant que les vecteurs IRQ alignent avec ces files et ne sont pas tous routés vers le même ensemble de CPU.

Décision : Alignez chaque IRQ de file sur des CPU locaux au NIC, et évitez les CPU isolés si applicable.

Coalescence d’interruption : la taxe de latence que vous payez peut-être

Si votre service est sensible à la latence (RPC, bases de données, API interactives), les réglages de coalescence peuvent faire osciller la latence tail. La coalescence adaptative est conçue pour être globalement efficace, pas déterministe. Parfois « globalement efficace » est l’ennemi.

cr0x@server:~$ sudo ethtool -C eth0 adaptive-rx off adaptive-tx off rx-usecs 25 tx-usecs 25
cr0x@server:~$ ethtool -c eth0 | egrep 'Adaptive|rx-usecs|tx-usecs'
Adaptive RX: off  TX: off
rx-usecs: 25
tx-usecs: 25

Ce que cela signifie : Vous avez réduit le temps d’attente avant que les interruptions se déclenchent. Cela peut réduire la latence mais augmenter l’usage CPU et le taux d’interruptions.

Décision : Si il y a de la marge CPU et que la latence s’améliore, conservez-le. Si le CPU fond ou que les pertes augmentent, revenez en arrière. Le réglage correct est celui qui respecte votre SLO sans gaspiller un cœur.

Blague n°2 : Tuner la coalescence NIC, c’est comme assaisonner une soupe — trop peu et c’est fade, trop et vous buvez de l’eau de mer.

Interruptions stockage : NVMe, blk-mq et le piège « le disque est ok »

Les problèmes de latence stockage sont souvent imputés au disque. Parfois c’est vrai. Souvent non. Les périphériques NVMe sont extrêmement rapides, ce qui signifie qu’ils peuvent compléter les IO si rapidement que le chemin de complétion devient le goulot : interruptions, mapping des files, et localité CPU.

Vérifiez si vous êtes CPU-bound côté couche bloc

cr0x@server:~$ iostat -x 1 3
Linux 6.12.0 (server) 	12/30/2025 	_x86_64_	(32 CPU)

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
           7.12    0.00    3.01    0.22    0.00   89.65

Device            r/s     rkB/s   rrqm/s  %rrqm r_await rareq-sz     w/s     wkB/s   wrqm/s  %wrqm w_await wareq-sz aqu-sz  %util
nvme0n1         1200.0  96000.0     0.0   0.00    0.55    80.00  800.0  64000.0     0.0   0.00    0.60    80.00   0.12  45.00

Ce que cela signifie : L’attente du périphérique est faible, l’utilisation est modérée. Si la latence applicative est mauvaise malgré tout, le périphérique n’est pas manifestement le limiteur ; le traitement des complétions, la contention, ou le chemin réseau peuvent l’être.

Décision : Corrélez avec les métriques IRQ/softirq. Si les IRQ NVMe sont élevées et concentrées, corrigez cela d’abord.

Vérifiez que les réglages d’état d’alimentation/latence NVMe ne vous sabordent pas

cr0x@server:~$ cat /sys/module/nvme_core/parameters/default_ps_max_latency_us
0

Ce que cela signifie : 0 signifie souvent « pas de limite » (autoriser les états basse consommation). Sur des serveurs, les économies d’énergie agressives peuvent ajouter une latence de réveil.

Décision : Pour des systèmes sensibles à la latence, envisagez de fixer une latence maximale plus stricte via le paramètre du noyau. Validez soigneusement ; les politiques d’énergie dépendent de la charge et de la plateforme.

Particularités de la virtualisation : virtio, vhost et voisins bruyants

Dans les VM, vous ne réglez pas seulement Linux ; vous négociez avec l’hyperviseur. virtio-net et vhost peuvent être excellents, mais le comportement d’interruption change. Vous pouvez voir des « orages d’interruptions » qui sont en fait des « exit storms » ou une contention côté hôte sur les files.

Dans le guest : vérifiez la distribution des IRQ virtio

cr0x@server:~$ grep -E 'virtio|vhost' /proc/interrupts | head -n 8
 40:   22112233    1100223    1099888    1101001   PCI-MSI 327680-edge      virtio0-input.0
 41:     110022   19888777     990001     980002   PCI-MSI 327681-edge      virtio0-output.0

Ce que cela signifie : Si un vecteur virtio domine et est épinglé, vous pouvez subir un déséquilibre. Mais dans les VM, l’épinglage CPU hôte et la topologie vCPU comptent tout autant.

Décision : Corrigez l’affinité dans le guest seulement après avoir vérifié l’épinglage/NUMA côté hôte. Sinon vous réarrangez les meubles dans un camion en mouvement.

Vérifiez le temps volé et la pression d’ordonnancement

cr0x@server:~$ vmstat 1 5
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 1  0      0 812345  12345 987654    0    0     1     3 1200 2400  6  2 90  0  2

Ce que cela signifie : Un st (steal) non négligeable suggère une contention hôte. Cela peut imiter des problèmes d’interruptions car votre guest ne peut pas s’exécuter quand il doit traiter des files.

Décision : Escaladez au niveau virtualisation : épinglage CPU, routage IRQ hôte, mitigation des voisins bruyants, et assurer que virtio multi-queue est activé de bout en bout.

Trois mini-récits d’entreprise depuis le terrain

Incident : la mauvaise hypothèse (« le CPU n’est qu’à 30 %, donc ce ne peut pas être le CPU »)

Une entreprise moyenne exploitait une couche API Debian derrière un load balancer. Après une mise à jour du noyau, la latence p99 a doublé pendant les heures de pointe. Les tableaux de bord montraient un CPU moyen autour de ~30–40 % avec beaucoup d’idle. La réponse initiale a été prévisible : blâmer la nouvelle release applicative, blâmer la base de données, puis blâmer l’équipe réseau pour « perte de paquets ».

L’SRE de garde a finalement regardé l’utilisation par cœur et a vu CPU0 épinglé. La liste des processus ne montrait aucun thread utilisateur chaud. C’était ksoftirqd/0. Pendant ce temps CPU1–CPU31 sirotaient du thé. L’équipe avait supposé que « %CPU » était un scalaire. Ce n’est pas le cas. C’est une distribution.

/proc/interrupts l’a rendu douloureusement évident : l’interruption principale RX/TX du NIC atterrissait presque exclusivement sur CPU0. RSS était configuré pour plusieurs files, mais le masque d’affinité IRQ avait été épinglé lors d’une tentative d’« optimisation » antérieure qui n’a jamais été annulée. irqbalance tournait, mais il ne peut pas outrepasser un épinglage que vous avez soudé en place.

La correction a été ennuyeuse : enlever l’épinglage manuel, redémarrer irqbalance, puis épingler explicitement les interruptions des files NIC sur des CPU locaux au nœud NUMA du NIC. La latence est revenue à la normale sans toucher l’application. Le postmortem fut plus sévère que la correction : l’équipe s’était trompée sur la métrique CPU, et cela a coûté des heures de tourmente inter-équipes.

Optimisation qui a mal tourné : « désactiver la coalescence pour réduire la latence »

Une autre organisation avait un service sensible à la latence et un nouvel ingénieur avec de bonnes intentions. Il a lu que la coalescence « ajoute de la latence », alors il a désactivé la coalescence adaptative et mis rx-usecs à 0 partout. Les graphes paraissaient incroyables dans un test synthétique : médiane plus basse, réponses vives, tout le monde content.

Deux semaines plus tard, le patron de trafic a changé. Plus de petits paquets. Plus de connexions concurrentes. Soudain un sous-ensemble de serveurs a commencé à perdre des paquets et montrer des pics de latence périodiques ressemblant à des pauses de garbage collection. Là encore, la moyenne CPU semblait correcte. L’histoire réelle était que le taux d’interruptions avait explosé ; un CPU par hôte était devenu concierge d’interruptions, et le reste de la machine était sous-utilisé parce que le CPU chaud ne pouvait pas suivre le rythme.

L’équipe avait optimisé pour la médiane et payé la queue. Désactiver la coalescence n’avait pas seulement réduit la latence ; cela avait enlevé une soupape de sécurité. Sous des pics de paquets, le système passait trop de temps dans les chemins d’interruptions et de softirq. La correction a été de réactiver la coalescence adaptative, puis de définir une base fixe modérée pour RX/TX usecs alignée sur leur SLO. Ils ont aussi augmenté les files NIC et assuré que les interruptions étaient mappées au bon nœud NUMA.

La leçon n’était pas « ne jamais changer la coalescence ». C’était : la coalescence est un bouton de contrôle, pas une religion. Si vous la mettez à « toujours le plus bas possible », vous choisissez la fragilité face aux charges en rafale.

Pratique ennuyeuse mais correcte : maintenir une baseline IRQ/affinité et l’appliquer

Une société exploitait Debian 13 sur des nœuds orientés stockage : NVMe + 100GbE. Ils avaient une règle simple : chaque classe matérielle avait une « baseline d’interruptions et de files » documentée et versionnée. Lorsqu’un nouveau serveur était provisionné, la gestion de configuration l’appliquait et un script de validation le vérifiait.

Le script n’était pas sophistiqué. Il capturait un snapshot de /proc/interrupts, les comptes de files via ethtool -l, le mapping NUMA depuis sysfs, et les listes d’affinité actuelles pour les IRQ chaudes. Il indiquait aussi les hotspots CPU0 et toute IRQ épinglée sur des CPU isolés. Si quelque chose dérivait, le pipeline échouait et le serveur ne rejoignait pas le pool.

Pendant un incident autrement pénible — forte latence sur un sous-ensemble de nœuds après une mise à jour firmware du vendor — cette baseline a sauvé des jours. Ils ont pu voir immédiatement quels nœuds déviaient : un lot avait vu ses files NIC réduites par le reset firmware, et les interruptions s’étaient effondrées sur deux vecteurs. Revenir en arrière ou réappliquer les réglages a corrigé rapidement le problème.

La pratique était peu glamoureuse. Personne n’a eu de conférence. Ça marchait pourtant. En production, « ennuyeux et correct » bat « brillant et fragile » presque toujours.

Erreurs fréquentes : symptôme → cause racine → correction

1) pics de latence p99, CPU moyen faible

Symptôme : la latence tail est mauvaise ; les tableaux CPU montrent beaucoup d’idle.

Cause racine : Un CPU est surchargé par des interruptions/softirqs (souvent CPU0). La moyenne masque le biais.

Correction : Vérifiez top -H, /proc/softirqs et /proc/interrupts. Corrigez l’affinité des IRQ et assurez-vous que RSS/multi-queue est actif.

2) ksoftirqd au maximum, NET_RX énorme sur un CPU

Symptôme : ksoftirqd/N est chaud ; NET_RX est biaisé.

Cause racine : interruption de file RX épinglée sur un CPU ; RSS ne répartit pas ; flux unique dominant ; ou RPS mal configuré.

Correction : Augmentez les files, vérifiez l’indirection RSS, répartissez les vecteurs IRQ sur des CPU locaux au NIC, envisagez RPS pour les cas récalcitrants.

3) « irqbalance tourne mais rien ne change »

Symptôme : irqbalance actif ; IRQ toujours bloquée sur un CPU.

Cause racine : l’IRQ est manuellement épinglée ; le pilote impose l’affinité ; l’IRQ est inamovible ; ou irqbalance est contraint par des masques de CPU bannis.

Correction : Inspectez /proc/irq/*/smp_affinity_list. Retirez les épinglages manuels ou ajustez la politique. Validez après redémarrage d’irqbalance.

4) la latence s’améliore brièvement après épinglage, puis empire

Symptôme : victoire rapide suivie d’une régression sous trafic réel.

Cause racine : l’épinglage a amélioré la localité mais surchargé un sous-ensemble de CPU ; la distribution/hachage des files a changé ; ou vous avez créé de la contention avec les threads applicatifs sur les mêmes CPU.

Correction : Faites correspondre le placement IRQ à la stratégie d’ordonnancement CPU : réservez des CPU pour les interruptions, ou alignez les threads applicatifs sur le même nœud NUMA et évitez de vous disputer les mêmes cœurs.

5) les pertes de paquets augmentent après le « tuning » de latence

Symptôme : retransmissions / drops de backlog après désactivation de la coalescence/offloads.

Cause racine : taux d’interruptions trop élevé ; le CPU ne suit pas ; les buffers débordent.

Correction : Réactivez la coalescence adaptative ou définissez des valeurs modérées de coalescence ; conservez RSS et multi-queue ; vérifiez qu’aucune IRQ unique ne domine.

6) latence stockage imputée au NVMe, mais iostat est propre

Symptôme : l’application subit des blocages ; les métriques NVMe semblent correctes.

Cause racine : traitement côté CPU des complétions ou gestion d’interruptions cross-NUMA ; parfois saturation d’IRQ sur un CPU partagé avec le réseau.

Correction : Vérifiez la distribution des IRQ NVMe et la localité NUMA ; évitez de mélanger des interruptions NIC et NVMe chaudes sur le même ensemble de CPU si cela crée de la contention.

Checklists / plan étape par étape

Checklist A : arrêter l’hémorragie en 15 minutes

  1. Capturez des preuves : top -H, /proc/softirqs, /proc/interrupts, et les graphes de latence au même timestamp.
  2. Identifiez la ligne/vecteur IRQ le plus chaud et mappez-la au nom du périphérique/file.
  3. Vérifiez son affinité : cat /proc/irq/<IRQ>/smp_affinity_list.
  4. Si elle est épinglée sur un CPU chaud, déplacez-la en test (une étape) et re-mesurez le p99.
  5. Si déplacer aide, implémentez une politique durable : ajustez irqbalance ou définissez des règles d’affinité persistantes.
  6. Vérifiez qu’il n’y a pas de pertes/regressions : nstat, stats pilote, et taux d’erreur applicatif.

Checklist B : rendre la correction durable (ne comptez pas sur des héros)

  1. Documentez la topologie CPU prévue : quels CPU pour la maintenance, lesquels sont isolés, lesquels pour la charge.
  2. Consignez la localité NUMA des NIC et NVMe et assurez-vous que les IRQ sont placées en conséquence.
  3. Fixez explicitement le nombre de files NIC (et vérifiez après reboot / update firmware).
  4. Choisissez une politique de coalescence : adaptative pour les serveurs généraux ; fixe pour la latence stricte avec marge CPU.
  5. Décidez de l’usage d’irqbalance : activé pour usage général ; désactivé + affinité explicite pour nœuds spécialisés.
  6. Automatisez la validation : échouer le provisioning si les IRQ s’effondrent sur CPU0 ou des cœurs isolés.

Checklist C : confirmer les améliorations avec de vraies mesures

  1. Mesurez p95/p99 end-to-end et les taux d’erreur sur au moins un cycle métier (pas 60 secondes d’espoir).
  2. Confirmez la distribution des interruptions dans le temps en utilisant des deltas (pas seulement les totaux).
  3. Surveillez le temps softirq CPU et la résidence de ksoftirqd.
  4. Validez qu’aucune nouvelle perte/retransmission n’apparaît.
  5. Conservez un snapshot avant/après de /proc/interrupts pour le postmortem.

FAQ

1) Qu’est-ce exactement qu’un orage d’IRQ sur Linux ?

C’est une situation où l’activité d’interruption (hard IRQs ou le travail softirq qu’elles déclenchent) surcharge les CPU, provoquant latence, pertes et jitter. Souvent c’est « trop d’interruptions » ou « des interruptions mal placées ».

2) Pourquoi CPU0 semble toujours être la victime ?

Les valeurs par défaut au démarrage, le routage legacy des interruptions, et l’épinglage accidentel amènent souvent le travail sur CPU0. De plus, de nombreuses tâches de maintenance tournent naturellement là. Si vous laissez les périphériques se ruer sur CPU0, il devient votre cœur goulot.

3) Dois-je simplement désactiver irqbalance ?

Sur des serveurs usage général : non, laissez-le. Sur des systèmes spécialisés pour la latence avec isolation CPU ou exigences strictes d’affinité : oui, désactivez-le et gérez l’affinité explicitement. La pire option est « le désactiver et ne rien faire d’autre ».

4) Comment savoir si c’est réseau ou stockage ?

Regardez /proc/softirqs et /proc/interrupts. Un NET_RX élevé et des IRQ chauds de files NIC pointent vers le réseau. Des IRQ liées au bloc élevées et des vecteurs NVMe chauds pointent vers le stockage. Ensuite corrélez avec la chronologie de la charge.

5) Si j’augmente les files NIC, la latence va-t-elle toujours s’améliorer ?

Non. Plus de files peuvent réduire la contention et répartir la charge, mais peuvent aussi augmenter l’overhead et dégrader la localité cache. C’est un outil, pas une garantie. Mesurez après chaque changement.

6) Un seul flux TCP peut-il vaincre RSS ?

Oui. RSS hache les flux ; un flux mappe sur une file RX. Si votre charge est dominée par quelques gros flux, vous pouvez toujours surcharger une file. Corrigez en shardant le trafic ou en changeant la façon dont la charge se répartit.

7) Quelle est la différence entre RSS et RPS ?

RSS est une répartition matérielle dans plusieurs files RX. RPS est une direction logicielle du traitement des paquets entre CPU. RSS est généralement préférable quand disponible ; RPS est une solution de secours ou un complément.

8) Pourquoi mes réglages ont-ils disparu après un reboot ou une mise à jour firmware ?

Beaucoup de réglages (nombre de files, coalescence, affinité) ne sont pas persistants par défaut. Les mises à jour firmware peuvent réinitialiser l’état du NIC. Rendre la configuration persistante via unit systemd, règles udev ou gestion de configuration — et vérifiez au démarrage.

9) Comment NUMA affecte les interruptions et la latence ?

Si un périphérique est attaché au nœud NUMA 1 mais que ses interruptions tournent sur des CPU du nœud 0, vous augmentez le trafic cross-node et les cache-miss. Cela ajoute du jitter et réduit la marge. Placez les IRQ sur des CPU locaux quand c’est possible.

10) J’ai déplacé une IRQ et le problème s’est amélioré, mais pas complètement. Et maintenant ?

Bien : vous avez prouvé la causalité. L’étape suivante est une distribution systématique : assurez-vous que toutes les files sont utilisées, mappez les vecteurs sur un ensemble de CPU, vérifiez la coalescence, et évitez de placer des interruptions chaudes sur des CPU qui exécutent déjà les threads applicatifs les plus occupés sauf si c’est voulu.

Conclusion : prochaines étapes qui réduisent vraiment le p99

Les orages d’IRQ et le déséquilibre d’interruptions ne sont pas mystérieux. Ils sont mesurables. Sur Debian 13, vous avez déjà ce qu’il faut : /proc/interrupts, /proc/softirqs, ethtool, et un esprit clair.

Faites ceci ensuite, dans l’ordre :

  1. Prouvez si vous êtes lié aux interruptions/softirqs (top -H, /proc/softirqs).
  2. Mappez les IRQ chaudes aux périphériques et aux files (/proc/interrupts plus ethtool -i/-l).
  3. Corrigez la distribution et la localité : files, RSS, masques d’affinité, placement NUMA.
  4. Ensuite seulement : ajustez la coalescence et les offloads, avec de vraies mesures de latence et des plans de rollback.
  5. Rendez cela persistant et validé, car la seule chose pire qu’un orage est un orage qui revient après un reboot.
← Précédent
Ubuntu 24.04 : Secure Boot bloque les pilotes — réparez sans tout casser la configuration
Suivant →
Debian 13 : la limitation thermique détruit le débit — démontrez-le et corrigez le refroidissement/les limites d’alimentation

Laisser un commentaire