Все виглядає «добре», поки раптом ні. Графіки затримок демонструють зуби. База даних, яка місяцями була нудною, починає заїдати кожні 30–90 секунд. p99 виходить за межі очікуваного, хоча CPU показує багато «idle». Потім ви бачите одне ядро, прикуване на 100% у ksoftirqd, і розумієте, що ви переслідуєте не проблему застосунку.
У Debian 13 IRQ-шторми та дисбаланс переривань виглядають як паранормальні явища: пакети надходять, диски завершуються, але ваш планувальник і черги програють бій. Вирішення — не «перезавантажити й помолитися». Потрібно виміряти переривання по CPU, перевірити irqbalance і свідомо вирішити питання стосовно affinity, кількості черг і offload-настроювань.
Швидкий план діагностики
Якщо ви на чергуванні і пейджер голосно, потрібна послідовність, що звужує пошук за кілька хвилин. Не починайте з «налаштувань». Почніть із доведення, куди йде час.
По-перше: підтвердіть, чи переривання/softirq — вузьке місце
- Перевірте, чи одне ядро завантажене й це не користувацький процес.
- Перевірте швидкість
softirqі провідні категорії (NET_RX, BLOCK, TIMER). - Перевірте, чи переривання накопичуються на CPU0 (классика) або в одному NUMA-вузлі.
По-друге: визначте, який пристрій створює навантаження
- Зіставте гарячі IRQ з пристроями (IRQ черг NIC, MSI-X вектори NVMe, лінії HBA).
- Корелюйте з графіком навантаження (мережеві сплески, скидання на диск, знімки, резервні копії).
- Перевірте, чи пристрій має достатньо черг і чи вони використовуються.
По-третє: вирішіть — «довірити irqbalance» чи «закріпити вручну»
- Якщо це сервер загального призначення з мінливими навантаженнями: віддайте перевагу
irqbalanceз розумними налаштуваннями за замовчуванням. - Якщо це чутлива до затримок система з прив’язаними ядрами (DPDK, realtime-ish, трейдинг, аудіо, телеко): відключіть
irqbalanceі явно закріпіть переривання. - Якщо ви використовуєте ізоляцію CPU (
isolcpus,nohz_full): переривання мають бути поза ізольованими ядрами, інакше ви збудували гоночний автомобіль і поставили йому колеса від візка.
По-четверте: перевірте, чи ви покращили потрібну метрику
- Вимірюйте end-to-end p95/p99, а не тільки «CPU виглядає краще».
- Підтвердіть, що гарячі IRQ розподілені (або закріплені правильно) і швидкості стабільні.
- Слідкуйте за регресіями: падіння пакетів, зростання ретрансляцій, підвищення частоти переривань через відключений coalescing.
Що ви насправді бачите (IRQ-шторми, softirqs і затримки)
«IRQ-шторм» зазвичай не означає буквальну електричну бурю. Це коли ядро переривають настільки часто — або коли обробка відкладених переривань така велика — що реальна робота не виконується плавно. Симптоми часто схожі на дивну поведінку застосунку: таймаути, зависання IO, короткі «підгальмовування» і джиттер, що не відповідає середньому CPU чи пропускній здатності.
У сучасному Linux «жорсткі» переривання (top halves) тримають коротко. Важка робота відкладається в softirqs (bottom halves) і kthreads, як-от ksoftirqd/N. Мережевий прийом (NET_RX) часто винен: якщо пакети приходять достатньо швидко, система може витрачати величезний час на переміщення пакетів з NIC у сокет-буфери, залишаючи менше часу для застосунку. Схожа річ може статися зі збереженням: завершення NVMe швидкі й часті, і з багатьма чергами це може створити постійний барабан переривань.
Дисбаланс переривань — тихіший родич штормів. Загальна швидкість переривань може бути прийнятною, але якщо вона сконцентрована на одному CPU (зазвичай CPU0), це ядро стає вашим де-факто вузьким місцем. Планувальник може показувати багато вільного часу на інших ядрах, що призводить до класичної хибної діагностики: «CPU в порядку». Насправді одне ядро горить, поки інші насолоджуються кавою.
Два поширені сценарії:
- Перевантаження CPU0: багато драйверів за замовчуванням використовують чергу 0 або один вектор, якщо RSS/MSI-X та affinity не налаштовані. Значення affinity під час завантаження також можуть змістити навантаження на CPU0.
- NUMA-невідповідність: переривання виконуються на CPU, віддалених від пам’яті пристрою PCIe. Це додає затримки і витрачає пропускну здатність між вузлами. Це смерть від тисячі промахів кешу.
Є ще пастка «модерації переривань»: якщо coalescing занадто агресивний, ви зменшуєте частоту переривань, але підвищуєте затримку, бо NIC довше чекає перед перериванням. Надто мало coalescing — і ви можете розплавити ядро від переривань. Налаштування — це компроміс: ви вирішуєте, скільки джиттера готові терпіти, щоб уникнути перевантаження.
Цитата, яку мусите тримати в голові під час цієї роботи: Надія — не стратегія.
— General Gordon R. Sullivan
Жарт №1: IRQ-шторми як наради — якщо їх забагато, нічого іншого не робиться.
Цікаві факти та контекст (чому це повторюється)
- 1) «irqbalance» з’явився, бо SMP зробив наївне маршрутування переривань болючим. Ранні багатопроцесорні Linux-системи часто за замовчуванням віддавали переривання на CPU завантаження, створюючи гарячі точки на CPU0, що виглядало як «Linux повільний».
- 2) MSI-X змінило правила гри. Message Signaled Interrupts (і MSI-X) дозволяють пристроям піднімати переривання через повідомлення в пам’яті та підтримують багато векторів — ідеально для multi-queue NIC та NVMe.
- 3) NAPI придумали, щоб зупинити livelock при прийомі пакетів. Мережа в Linux перейшла на модель опитування з пом’якшенням переривань (NAPI), бо чисто переривально-орієнтований RX міг колапсувати при високих швидкостях пакетів.
- 4) Softirqs — по ядру за дизайном. Це добре для локальності кешу, але також означає, що «одне ядро тоне в NET_RX» може позбавити CPU можливості обробляти іншу роботу, навіть коли інші ядра idle.
- 5) Раніше «IRQ-шторм» частіше означав апаратну проблему. Сьогодні частіше це погано налаштовані черги/coalescing або зміни в навантаженні (наприклад, нова служба надсилає 64-байтні пакети мільйон на секунду).
- 6) Продуктивність NVMe приходить з обсягом завершень переривань. Багато дрібних IO при високих IOPS можуть генерувати високу частоту подій завершення; мають значення MSI-X вектори і відображення черг.
- 7) Функції ізоляції CPU зробили розміщення переривань важливішим.
isolcpusіnohz_fullможуть покращити затримку, але лише якщо ви тримаєте переривання і служби ядра поза ізольованими ядрами. - 8) Сучасні NIC offload-опції не завжди ваш друг. GRO/LRO/TSO можуть зменшити навантаження на CPU, але підвищити затримку або джиттер; деякі навантаження (дрібні RPC) не люблять таке буферування.
- 9) «irqbalance» — це політика, а не магія. Воно приймає рішення на основі евристик навантаження. Ці евристики можуть бути некоректні для спеціалізованих систем.
Інструменти та принципи: як Debian 13 обробляє переривання
Debian 13 — це просто Linux з певними уподобаннями в пакуванні. Ядро використовує /proc/interrupts як сире джерело істини. Інструменти, як-от irqbalance, застосовують політику. Драйвери відкривають регулятори через /sys і ethtool. Ваше завдання — вирішити, що «гарно» для вашого навантаження, а потім застосувати це.
Hard IRQs vs softirqs: що важливе для затримки
Контекст жорсткого IRQ дуже обмежений. Більшість роботи віддається в softirqs. Коли навантаження softirq високе, ядро може виконувати обробку softirq у контексті перерваного завдання (швидко, добре для пропускної здатності) або в ksoftirqd (припустимо, але може відстати). Якщо ви бачите, що ksoftirqd домінує на CPU, ви відстаєте.
Affinity: «яке ядро обробляє це переривання?»
Кожне IRQ має маску affinity. Для MSI-X кожен вектор фактично є окремим IRQ і може бути розподілений по CPU. Для застарілих лінійних переривань варіантів менше і спільне обслуговування може стати проблемою.
Черги: multi-queue пристрої і чому одна черга — трагедія
Для NIC multi-queue + RSS дозволяють inbound-потокам розподілятися по RX-чергах, кожна з яких має свій вектор переривань. Для блочних пристроїв blk-mq відображає IO-черги на CPU. Це важливо не лише для пропускної здатності, а й для затримки: зменшення конфліктів блокувань і збереження локальності гарячого шляху.
NUMA: не платіть за відстань, якщо не потрібно
На мульти-сокетних системах розміщення IRQ на CPU, локальних до PCIe root complex NIC, зменшує крос-вузловий трафік пам’яті. Debian не завжди вгадує вашу топологію правильно. Потрібно перевіряти самому.
Практичні завдання: команди, результати та рішення (12+)
Ось ходи, що вирішують реальні інциденти. Кожне завдання містить: команду, що означає вивід, і рішення. Виконуйте від root, коли потрібно.
Завдання 1: Підтвердіть симптоми тиску від переривань/softirq
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
Що це означає: Високий si (softirq) разом з гарячим потоком ksoftirqd/N — явна ознака. Ваш CPU витрачає час на відкладену обробку переривань, зазвичай мережеву.
Рішення: Негайно переходьте до вимірювання softirq та зіставлення переривань з пристроями. Не чіпайте застосунок поки не доведете контекст.
Завдання 2: Подивіться, які класи softirq гарячі
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
Що це означає: NET_RX масово зсунуте в бік CPU0 — це кричить «обробка RX зосереджена». Часто це корелює з тим, що один RX-IRQ лендить на CPU0.
Рішення: Проінспектуйте /proc/interrupts та конфігурацію черг NIC. Мета — розподілити IRQ RX-черг або увімкнути/перевірити RSS.
Завдання 3: Визначте гарячі IRQ та чи вони дисбалансовані
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
Що це означає: Тут IRQ черги 0 NIC абсурдно гарячий на CPU0. Інші черги виглядають здоровішими. Це класичний дисбаланс.
Рішення: Виправити affinity для гарячого вектора і підтвердити, що RSS розподіляє потоки. Якщо черга 0 дійсно найзавантаженіша через хешування, збільште кількість черг або налаштуйте RSS indirection.
Завдання 4: Підтвердіть, чи irqbalance запущений і що воно вирішує
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
Що це означає: irqbalance працює, отже або (a) воно не може перемістити цей IRQ (деякі невідчеплювані), або (b) воно налаштоване уникати певних CPU, або (c) воно приймає погане рішення для вашого навантаження.
Рішення: Перевірте конфігурацію irqbalance і маски affinity IRQ. Розв’яжіть, чи налаштувати irqbalance або переоприділити конкретні IRQ вручну.
Завдання 5: Перевірте поточну маску affinity для конкретного IRQ
cr0x@server:~$ cat /proc/irq/35/smp_affinity_list
0
Що це означає: IRQ 35 прив’язано лише до CPU0. irqbalance не допоможе, якщо хтось жорстко закріпив це переривання.
Рішення: Якщо це не навмисно, змініть. Якщо CPU0 призначено для обслуговування і ви цього хочете, закріпіть його інакше.
Завдання 6: Перемістити гарячий IRQ на інше ядро (швидкий тест)
cr0x@server:~$ echo 2 > /proc/irq/35/smp_affinity
cr0x@server:~$ cat /proc/irq/35/smp_affinity_list
1
Що це означає: IRQ тепер маршрутується на CPU1 (біт маски 1). Це грубий інструмент, але відмінний для доведення причинності.
Рішення: Якщо затримка миттєво покращується і ksoftirqd заспокоюється, ви підтвердили, що проблема — розміщення переривань. Тоді зробіть стале виправлення (відображення черг, політика irqbalance, NUMA-усвідомлене розміщення).
Завдання 7: Визначте NIC, драйвер і розташування шини (підказки NUMA)
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
Що це означає: bus-info — це PCI-адреса. Ви можете зіставити її з NUMA-вузлом і локальністю CPU.
Рішення: Тримайте переривання на CPU, локальних до NUMA-вузла NIC, коли це можливо.
Завдання 8: Знайдіть NUMA-вузол пристрою та локальний список CPU
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
Що це означає: NIC підключено до NUMA-вузла 1, локальні CPU 16–31. Якщо ваші IRQ знаходяться на CPU0–3, ви платите крос-вузлові витрати.
Рішення: Розміщуйте IRQ NIC на CPU 16–31 і подумайте про розміщення робочих навантажень, що інтенсивно використовують NIC, на тій же локальності.
Завдання 9: Перевірте кількість черг/каналів NIC (чи досить у вас черг?)
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
Що це означає: NIC підтримує до 16 комбінованих каналів, але зараз використовується 4. Якщо у вас багато ядер і високі швидкості пакетів, 4 черги можуть бути вузьким місцем.
Рішення: Збільшіть combined channels, якщо навантаження виграє від цього і система має CPU-голову. Але не перебільшуйте; більше черг може означати більше накладних витрат і гіршу локальність кешу.
Завдання 10: Обережно збільшити комбіновані черги NIC і перевірити переривання
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
Що це означає: Тепер у вас 8 пар черг. Це має створити більше MSI-X векторів і краще розподілити навантаження — якщо RSS налаштовано і потоки хешуються добре.
Рішення: Повторно перевірте /proc/interrupts через 30–60 секунд під навантаженням. Якщо одна черга все ще домінує, причина може бути в хешуванні/індексації або в закономірності трафіку.
Завдання 11: Перевірте RSS indirection та hash key (чи трафік рівномірно розподіляється?)
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
Що це означає: Таблиця виглядає рівномірною. Це добре. Якщо вона не рівномірна або весь час нулі — ви фактично одночергові.
Рішення: Якщо розподіл поганий, налаштуйте indirection table (продвинуто) або виправте драйвер/параметри прошивки. Якщо ваше навантаження — одиничний потік (один великий TCP-потік), RSS мало допоможе; потрібно шардування застосунку або інша модель транспорту.
Завдання 12: Перевірте і налаштуйте coalescing переривань (компроміс затримка ↔ CPU)
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
Що це означає: Адаптивний coalescing увімкнений; NIC/драйвер буде змінювати coalescing залежно від трафіку. Це зазвичай не погано для загального використання, але може вводити варіативність затримки.
Рішення: Для строгих tail-latency навантажень розгляньте відключення adaptive coalescing і встановлення консервативних фіксованих значень. Перевіряйте за реальними метриками затримки, а не по відчуттях.
Завдання 13: Виявіть втрачені пакети або переповнення backlog (симптом NET_RX overload)
cr0x@server:~$ nstat | egrep 'TcpExtListenOverflows|IpInDiscards|UdpInErrors|TcpExtTCPBacklogDrop'
TcpExtTCPBacklogDrop 123
IpInDiscards 456
Що це означає: Ви втрачаєте пакети у стеку. Це може статися, коли обробка softirq не встигає або коли застосунок не встигає приймати/зчитувати дані.
Рішення: Виправте розподіл переривань спершу. Потім розгляньте налаштування backlog сокетів і пропускну здатність застосунку. Не заклеюйте гарячий IRQ великими буферами, якщо вам подобається відкладена відмова.
Завдання 14: Шукайте повідомлення ядра про «IRQ nobody cared» та попередження
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
Що це означає: Це серйозно. IRQ став настільки гучним або непрацездатним, що ядро вирішило, ніби він зламався, і відключило його. Це може поступово вивести NIC або сховище з ладу.
Рішення: Ставте це як апаратну/прошивкову/драйверну проблему першочергово. Перевірте налаштування BIOS, оновіть прошивку, перевірте стабільність MSI/MSI-X і дослідіть, чи модерація переривань не патологічна.
Завдання 15: Перевірте, що відображення черг/IRQ NVMe адекватне
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
Що це означає: Існують кілька черг NVMe і переривання відносно рівномірно розподілені. Якщо ви бачите тільки nvme0q0 гарячий, а інші сплять, можливо, пристрій обмежений однією чергою або навантаження ефективно однопотокове.
Рішення: Якщо NVMe IRQ сконцентровані, перевірте параметри живлення nvme_core.default_ps_max_latency_us, параметри драйвера і чи блочний шар відображає черги на CPU як очікується.
Завдання 16: Спостерігайте швидкість пер-IRQ у часі (не тільки сумарні показники)
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
Що це означає: Ви можете візуально побачити дельти за секунду. Якщо один IRQ інкрементується набагато швидше за інших — це ваша «гаряча» лінія. Сумарні показники приховують швидкість змін.
Рішення: Цілиться в високошвидкісний IRQ для балансування/пінінгу і підтвердьте, що розподіл швидкостей поліпшився під репрезентативним навантаженням.
irqbalance у Debian 13: перевірка, налаштування і коли відключати
irqbalance часто або бездумно довіряють, або бездумно звинувачують. Жодна з крайностей не є адекватною. Ставтеся до нього як до будь-якої автоматизації: це політичний двигун з налаштуваннями за замовчуванням для «типових серверів». Якщо ваш сервер не типовий, воно все одно обробить його як типовий, якщо ви не втрутитеся.
Що irqbalance робить добре
- Розподіляє IRQ по CPU, щоб CPU0 не був постійним страждальцем.
- Реагує на зміну навантаження без вашого ручного редагування масок affinity опівночі.
- Працює пристойно з MSI-X multi-queue пристроями.
Що irqbalance робить погано (або не може зробити)
- CPU-ізоляція, критична до затримки: воно може перемістити переривання на CPU, які ви хотіли тримати «чистими», якщо не налаштувати обачно.
- NUMA-чутливе розміщення: іноді воно не тримає переривання локальними до PCIe NUMA-вузла в бажаний спосіб.
- Нерухомі IRQ: деякі IRQ по суті прив’язані або обробляються так, що irqbalance не може змінити ситуацію.
Перевірте конфігурацію irqbalance
cr0x@server:~$ grep -v '^\s*#' /etc/default/irqbalance | sed '/^\s*$/d'
ENABLED="1"
OPTIONS=""
Що це означає: Конфіг за замовчуванням. Немає масок бану CPU, жодної спецповедінки.
Рішення: Якщо ви бачите прив’язання IRQ, щось інше його встановлює (скрипти драйвера, кастомне налаштування, хуки рантайму контейнера або залишки від старих «оптимізацій»).
Подивіться, які CPU онлайн і чи ви ізолювали деякі
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
Що це означає: Топологія чиста. Якщо ви також використовуєте параметри ізоляції ядра в командному рядку, перевірте /proc/cmdline.
Рішення: Якщо ви ізолювали CPU (наприклад, 4–31) для робочих навантажень, потрібно заборонити irqbalance використовувати ці CPU або керувати affinity вручну.
Перевірте kernel cmdline на параметри, пов’язані з ізоляцією
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
Що це означає: Ви оголосили CPUs 4–31 спеціальними. Чудово. Тепер тримайте переривання поза ними або ви порушите обіцянку, яку дали ядру.
Рішення: Налаштуйте irqbalance так, щоб уникати 4–31, або відключіть irqbalance і задайте явні маски для всіх релевантних IRQ.
Заборонити CPU для irqbalance (типовий шаблон ізоляції)
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
Що це означає: Приклад тільки: заборона IRQ 0 — це не те саме, що заборона CPU. Бан CPU зазвичай робиться через опції маски CPU в irqbalance (залежить від версії/збірки). Головна ідея: не налаштовуйте опції, які не розумієте.
Рішення: Для ізольованих CPU краще використовувати явні маски affinity на IRQ або конфігурацію cpu mask irqbalance відповідно до вашої версії. Підтверджуйте, читаючи /proc/irq/*/smp_affinity_list після рестарту.
Ось суб’єктивна порада: якщо ви керуєте типовим флотом серверів Debian — залишайте irqbalance включеним. Якщо у вас спеціалізована latency-скринька з ізольованими CPU — відключіть його і керуйте affinity явно через систему управління конфігурацією, щоб було відтворювано.
Переривання NIC: RSS, RPS/XPS, coalescing та перевірка multi-queue
Більшість «дивних затримок», що пахнуть перериваннями, пов’язані з мережею. Не тому, що збереження невинне, а тому, що пакети можуть надходити на лінійній швидкості і вимагати негайної уваги CPU. NIC може генерувати більше роботи в секунду, ніж ваш ядро встигне обробити, якщо ви налаштовуєте його як у 2009 році.
Почніть з RSS (апаратного розподілення прийому)
RSS розподіляє потоки по RX-чергах в апаратурі. Кожна RX-черга має власний вектор переривань. Якщо RSS вимкнено або існує лише одна черга, одне ядро робитиме більшість прийому. Пропускна здатність може виглядати прийнятною, тоді як tail-latency руйнується через черги й джиттер.
RPS/XPS: програмне спрямування, коли RSS недостатній
RPS (Receive Packet Steering) може розподіляти обробку пакетів по CPU, навіть якщо апаратний RSS обмежений. XPS може розподілити TX-обробку. Це функції на рівні CPU; вони можуть покращити баланс, але додають накладні витрати. Використовуйте їх, коли є підстава, а не тому, що читали пост 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
Що це означає: Існує multi-queue. Добре. Тепер підтвердіть, що IRQ-вектори відповідають цим чергам і не всі направлені на один і той же набір CPU.
Рішення: Вирівняйте кожен IRQ черги до CPU, локальних до NIC, і уникайте ізольованих CPU, якщо це застосовно.
Coalescing переривань: податок на затримку, який може вас кусати
Якщо ваш сервіс чутливий до затримки (RPC, бази даних, інтерактивні API), налаштування coalescing може радикально вплинути на tail-latency. Adaptive coalescing розроблений для загальної ефективності, а не детерміністичності. Іноді «загальна ефективність» — ваш ворог.
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
Що це означає: Ви зменшили час очікування перед спрацьовуванням переривань. Це може знизити затримку, але збільшити навантаження на CPU і частоту переривань.
Рішення: Якщо є запас CPU і затримка покращилась — тримайте. Якщо CPU плавиться або зростають втрати — відкотіть. Правильне налаштування — те, що задовольняє ваше SLO без марнування ядра.
Жарт №2: Налагодження coalescing NIC — як приправляння супу: замало — прісно, надто багато — п’єш морську воду.
Переривання збереження: NVMe, blk-mq і пастка «диск в порядку»
Проблеми із затримками збереження часто списують на диск. Іноді це так. Але часто ні. NVMe — надзвичайно швидкі, а отже вони можуть завершувати IO настільки швидко, що шлях завершення стає вузьким місцем: переривання, відображення черг і локальність CPU.
Перевірте, чи ви прив’язані до CPU у блочному шарі
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
Що це означає: Очікування на пристрій низьке, utilization помірний. Якщо затримки застосунку все одно жахливі — пристрій явно не є очевидним обмеженням; можливо, вузьке місце — обробка завершень, конкуренція або мережевий шлях.
Рішення: Корелюйте з метриками IRQ/softirq. Якщо NVMe IRQ високі і сконцентровані — виправляйте це спершу.
Перевірте, чи налаштування живлення NVMe не саботують вас
cr0x@server:~$ cat /sys/module/nvme_core/parameters/default_ps_max_latency_us
0
Що це означає: 0 часто означає «без обмеження» (дозволити низькі енергетичні стани). На серверах агресивне енергозбереження може додати латентності пробудження.
Рішення: Для систем, чутливих до затримки, розгляньте встановлення жорсткішого max latency через параметр ядра. Перевіряйте ретельно; політика живлення залежить від навантаження і платформи.
Крутихності віртуалізації: virtio, vhost і шумні сусіди
У VM ви налаштовуєте не лише Linux гостя, ви домовляєтесь з гіпервізором. virtio-net і vhost можуть бути відмінними, але поведінка переривань змінюється. Ви можете бачити «IRQ-шторми», що насправді є «exit-шторми» або проблемою черг на хості.
У гості: перевірте розподіл virtio IRQ
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
Що це означає: Якщо один virtio-вектор домінує і він закріплений — ви все одно потерпатимете від дисбалансу. Але в VM важливіше ще й те, як хост прив’язує vCPU і розподіляє IRQ.
Рішення: Виправляйте affinity гостя тільки після перевірки хостового пінінгу і NUMA-розміщення. Інакше ви переставляєте меблі в рухомому кузові вантажівки.
Перевірте steal time і тиск планування
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
Що це означає: Нетривіальний st (steal) вказує на хостову контенцію. Це може імітувати проблеми з перериваннями, бо гість не може виконатися, коли треба обслуговувати черги.
Рішення: Ескалюйте до рівня віртуалізації: пінінг CPU, маршрутизація хостових IRQ, пом’якшення шумних сусідів і забезпечення, щоб virtio multi-queue був увімкнений кінця в кінець.
Три корпоративні історії з передової
Інцидент: хибне припущення («CPU лише 30%, значить це не CPU»)
Середня компанія мала Debian-базований API шар за load balancer. Після оновлення ядра p99 затримки подвоїлися у пікові години. Дашборди показували середній CPU близько 30–40% з великою кількістю idle. Початкова реакція була передбачуваною: звинуватити новий реліз застосунку, БД, потім мережеву команду за «втрату пакетів».
On-call SRE нарешті подивився на використання по ядрах і побачив CPU0 прикутий. Список процесів не показував гарячих користувацьких потоків. Це був ksoftirqd/0. Тим часом CPU1–CPU31 відпочивали. Команда припустила, що «CPU%» — це скаляр. Це не так. Це розподіл.
/proc/interrupts зробив це болісно очевидним: основний RX/TX IRQ NIC майже виключно лендив на CPU0. RSS було налаштовано на кілька черг, але маска affinity була закріплена під час раннього «тюнінгу продуктивності», який ніколи не відкатили. irqbalance працював, але не може перевизначити pin, який ви приварили.
Виправлення було нудним: прибрати ручне прив’язування, перезапустити irqbalance, а потім явно закріпити IRQ черг NIC на CPU локальних до NUMA-вузла NIC. Затримки повернулися до бази без змін у застосунку. Постмортем був суворішим, ніж фікс: команда неправильно інтерпретувала метрики CPU і втратила години на міжкомандну біганину.
Оптимізація, що відкотилася: «Вимкнути coalescing, щоб знизити затримку»
Інша організація мала latency-сенситивний сервіс і нового інженера з добрими намірами. Він прочитав, що coalescing «додає затримку», тому відключив adaptive coalescing і встановив rx-usecs у 0 всюди. Графіки виглядали чудово в синтетичному тесті: менший медіан, відчутна швидкість, всі задоволені.
Через два тижні патерн трафіку змінився. Більше дрібних пакетів. Більше конкурентних з’єднань. Раптом підмножина серверів почала втрачати пакети і показувати періодичні сплески затримок, схожі на паузи GC. Знову середній CPU виглядав нормальним. Справжня історія була в тому, що частота переривань злетіла; одне ядро на хості стало «консьєржем переривань», а інші ядра недовикористовувалися, бо гаряче ядро не встигало обробляти навантаження.
Команда оптимізувала медіану і платою отримала хвіст. Вимкнення coalescing не тільки зменшило затримку; воно прибрало клапан безпеки. Під сильним потоком система проводила надто багато часу в шляхах переривань і softirq. Виправлення: знову увімкнути adaptive coalescing, потім встановити помірний фіксований baseline для RX/TX usecs відповідно до їхнього SLO. Також вони збільшили черги NIC і упевнилися, що IRQ відображаються у правильний NUMA-вузол.
Урок не в «ніколи не змінюйте coalescing». Він у тому, що coalescing — це регулятор, а не релігія. Якщо ви ставите його на «найнижче завжди», ви обираєте крихкість при сплесках навантаження.
Нудна, але правильна практика: мати базу IRQ/affinity і її застосовувати
Одна компанія запускала Debian 13 на вузлах з інтенсивним збереженням: NVMe + 100GbE. Вони мали просте правило: для кожного апаратного класу документована, версіонована «база переривань і черг». Коли новий сервер провізіонувався, система управління конфігурацією застосовувала її, а валідаційний скрипт перевіряв.
Скрипт був не витончений. Він знімав знімок /proc/interrupts, кількості черг через ethtool -l, NUMA-вузол з sysfs і поточні списки affinity для гарячих IRQ. Він також позначав CPU0 hotspots і будь-який IRQ, прив’язаний до ізольованих CPU. Якщо щось відхилялося, пайплайн падав і сервер не потрапляв у пул.
Під час іншого неприємного інциденту — високі затримки на підмножині вузлів після оновлення прошивки від вендора — ця база врятувала дні. Вони одразу бачили, які вузли відхилилися: в одній партії прошивка скинула черги NIC, і переривання зосередилися на двох векторах. Відкат або повторне застосування налаштувань швидко вирішили проблему.
Практика була непомітна. Ніхто не отримав доповідь на конференції. Але в проді «нудне і правильне» перемагає «хитромудре і крихке» майже завжди.
Типові помилки: симптом → причина → виправлення
1) p99 сплески затримки, середній CPU низький
Симптом: Хвостова затримка погана; дашборди CPU показують багато idle.
Причина: Одне ядро перевантажене перериваннями/softirq (часто CPU0). Середнє приховує зсув.
Виправлення: Перевірте top -H, /proc/softirqs і /proc/interrupts. Виправте affinity IRQ і впевніться, що RSS/multi-queue активні.
2) ksoftirqd прикутий, NET_RX величезний на одному CPU
Симптом: ksoftirqd/N гарячий; NET_RX зрушений.
Причина: RX-черга прив’язана до одного CPU; RSS не розподіляє; домінує один потік; або RPS неправильно налаштований.
Виправлення: Збільште черги, перевірте RSS indirection, розподіліть IRQ vectors по CPU, локальних до NIC, розгляньте RPS для впертих випадків.
3) «irqbalance працює, але нічого не змінюється»
Симптом: irqbalance активний; IRQ все ще прив’язаний до одного CPU.
Причина: IRQ вручну закріплено; драйвер примушує affinity; IRQ неможливо перемістити; або irqbalance обмежено масками banned CPU.
Виправлення: Проінспектуйте /proc/irq/*/smp_affinity_list. Приберіть ручні pins або відрегулюйте політику. Перевірте після рестарту irqbalance.
4) Затримка покращилася спочатку, а потім погіршилася
Симптом: Швидкий виграш, за яким пішла регресія під реальним трафіком.
Причина: Пінінг покращив локальність, але перевантажив підмножину CPU; змінилося розподілення/хешування; або ви створили конкуренцію з робочими потоками на тих же ядрах.
Виправлення: Узгодьте розміщення IRQ з CPU-стратегією планування: резервуйте ядра для переривань або вирівняйте робочі потоки з тим же NUMA-вузлом і уникайте суперництва за ті самі ядра.
5) Падіння пакетів збільшилось після «налаштування латентності»
Симптом: Ретрансміти/backlog drops після відключення coalescing/offloads.
Причина: Частота переривань надто висока; CPU не встигає; буфери переповнюються.
Виправлення: Заново ввімкніть adaptive coalescing або встановіть помірні значення; зберігайте RSS і multi-queue; підтвердіть, що жоден IRQ не домінує.
6) Збереження звинувачують NVMe, але iostat чистий
Симптом: Застосунок бачить затримки; NVMe метрики виглядають нормальними.
Причина: Бік CPU у шляху завершень або крос-NUMA обробка переривань; іноді IRQ насичення на CPU, який ділиться з мережею.
Виправлення: Перевірте розподіл IRQ NVMe і NUMA-локальність; уникайте змішування гарячих NIC і NVMe IRQ на тих самих CPU, якщо це створює суперництво.
Контрольні списки / покроковий план
Чеклист A: Зупинити кровотечу за 15 хвилин
- Зробіть докази:
top -H,/proc/softirqs,/proc/interruptsі графіки затримки одночасно. - Визначте найгарячішу лінію IRQ/вектор і зіставте її з ім’ям пристрою/черги.
- Перевірте її affinity:
cat /proc/irq/<IRQ>/smp_affinity_list. - Якщо вона закріплена на одному CPU і воно гаряче — перемістіть її як тест (один крок) і виміряйте p99 знову.
- Якщо переміщення допомагає — реалізуйте довготривалу політику: або налаштуйте irqbalance, або задайте постійні affinity правила.
- Переконайтесь, що немає падінь/регресій:
nstat, статистика драйвера і помилки застосунку.
Чеклист B: Зробіть виправлення довговічним (не покладайтеся на героїв)
- Задокументуйте інтендовану топологію CPU: які ядра для housekeeping, які ізольовані, які для робочих навантажень.
- Запишіть NUMA-локальність NIC і NVMe і забезпечте правильне розміщення IRQ.
- Встановіть явну кількість черг NIC (і перевіряйте після ребута/оновлення прошивки).
- Виберіть політику coalescing: adaptive для загальних серверів; фіксована для строгих latency при наявності запасу.
- Вирішіть щодо irqbalance: увімкнено для загального призначення; відключено + явна affinity для спеціалізованих вузлів.
- Автоматизуйте валідацію: блокувати провізіонування, якщо IRQ звалюються на CPU0 або на ізольовані ядра.
Чеклист C: Підтвердіть покращення реальними вимірюваннями
- Виміряйте end-to-end p95/p99 і помилки протягом принаймні одного бізнес-циклу (не 60 секунд надії).
- Підтвердіть розподіл переривань у часі, використовуючи дельти (не тільки сумарні значення).
- Слідкуйте за часом CPU softirq і резидентністю
ksoftirqd. - Переконайтесь, що нових втрат/ретрансмітів не з’явилося.
- Збережіть знімки
/proc/interruptsдо і після для постмортему.
FAQ
1) Що точно таке IRQ-шторм у Linux?
Це стан, коли активність переривань (жорсткі IRQ або робота softirq, яку вони запускають) перевантажує CPU, спричиняючи затримки, втрати і джиттер. Часто це «занадто багато переривань» або «переривання в неправильному місці».
2) Чому CPU0 завжди здається жертвою?
Значення за замовчуванням при завантаженні, спадкова маршрутизація переривань і випадкове прив’язування часто призводять до того, що робота потрапляє на CPU0. Також багато housekeeping-задач природно виконуються там. Якщо дозволити пристроям звалюватися на CPU0 — воно стане вашим вузьким місцем.
3) Чи варто просто відключити irqbalance?
На серверах загального призначення: ні, залишайте його. На спеціалізованих latency-системах з ізоляцією CPU або суворими вимогами до affinity: так, відключіть і керуйте affinity явно. Найгірший варіант — «відключити і нічого не робити».
4) Як зрозуміти — мережа чи сховище?
Подивіться /proc/softirqs і /proc/interrupts. Високий NET_RX і гарячі IRQ черг NIC вказують на мережу. Високі блок-пов’язані IRQ і вектори NVMe вказують на збереження. Потім корелюйте з часом навантаження.
5) Якщо я збільшу черги NIC, чи завжди покращиться затримка?
Ні. Більше черг може зменшити конкуренцію і розподілити навантаження, але також збільшити накладні витрати і погіршити локальність кешу. Це інструмент, а не гарантія. Міряйте після кожної зміни.
6) Чи може один TCP-потік перемогти RSS?
Так. RSS хешує потоки; один потік мапиться на одну RX-чергу. Якщо ваше навантаження доміноване кількома важкими потоками, черга може все одно стати гарячою. Вирішуйте шляхом шардування трафіку або іншої моделі розподілу.
7) У чому різниця між RSS і RPS?
RSS — апаратне розподілення в кілька RX-черг. RPS — програмне спрямування обробки пакетів по CPU. RSS зазвичай кращий, якщо доступний; RPS — це fallback або доповнення.
8) Чому мої налаштування зникли після перезавантаження або оновлення прошивки?
Багато налаштувань (кількість черг, coalescing, affinity) не зберігаються за замовчуванням. Оновлення прошивки може скинути стан NIC. Зробіть конфігурацію стійкою через systemd unit-и, udev правила або систему управління конфігурацією — і перевіряйте при завантаженні.
9) Як NUMA впливає на переривання і затримки?
Якщо пристрій підключений до NUMA-вузла 1, а його переривання виконуються на CPU вузла 0, ви підвищуєте крос-вузловий трафік пам’яті і промахи кешу. Це додає джиттер і зменшує запас. Розміщуйте IRQ на локальних CPU, коли можливо.
10) Я перемістив IRQ і проблема покращилася, але не повністю. Що далі?
Добре: ви довели причинність. Наступний крок — систематичний розподіл: переконайтесь, що всі черги використовуються, відобразіть вектори по набору CPU, перевірте coalescing і уникайте розміщення гарячих переривань на CPU, що вже обслуговують найбільш завантажені потоки застосунку, якщо це не задумано.
Висновок: наступні кроки, що реально знижують p99
IRQ-шторми і дисбаланс переривань не містять таємниць. Вони вимірювані. У Debian 13 у вас уже є необхідні інструменти: /proc/interrupts, /proc/softirqs, ethtool і холодна голова.
Зробіть це наступним чином, по порядку:
- Доведіть, чи ви обмежені перериваннями/softirq (
top -H,/proc/softirqs). - Зіставте гарячі IRQ з пристроями і чергами (
/proc/interruptsплюсethtool -i/-l). - Виправте розподіл і локальність: черги, RSS, маски affinity, NUMA-розміщення.
- Лише потім: налаштовуйте coalescing і offloads, базуючись на реальних вимірах затримки і плані відкату.
- Зробіть це постійним і перевірюваним, бо гірше за шторм — це шторм, що повернувся після ребута.