Каждый пакет приходит в процессор через прерывание, и на скорости 10 Гбит/с с мелкими пакетами это до полутора миллионов прерываний в секунду. Interrupt moderation собирает пакеты в пачки и выдаёт одно прерывание на несколько пакетов, радикально снижая нагрузку на ядра. Плата за это - задержка: пока таймер не истёк, пакет ждёт. Настройка баланса между пропускной способностью и латентностью через ethtool -C и есть главная задача этой статьи, плюс управление softirq, привязка прерываний и диагностика.

Механика moderation и параметров rx-usecs rx-frames

Interrupt moderation работает так: адаптер копит пакеты в кольце приёма и поднимает прерывание, когда истёк таймер rx-usecs микросекунд или собралось rx-frames пакетов, что наступит раньше. Аналогично на передаче: tx-usecs и tx-frames управляют прерываниями завершения отправки.

Текущее состояние показывает ethtool -c eth0:

ethtool -c eth0

В выводе важны adaptive-rx, adaptive-tx, rx-usecs, rx-frames, rx-usecs-irq, tx-usecs, tx-frames и диапазоны, поддерживаемые картой. Изменение делается через ethtool -C:

ethtool -C eth0 rx-usecs 64 rx-frames 64 tx-usecs 64 tx-frames 32

Значение rx-usecs 0 отключает таймер, прерывание идёт на каждый пакет или на каждое rx-frames. Карты Intel ограничивают минимум единицей на большинстве поколений, Mellanox разрешает ноль с отключением moderation через rx-frames 0.

Диапазоны параметров видны в конце вывода ethtool -c: например, rx-usecs от 0 до 8191 у igb, от 0 до 1023 у mlx5. Выход за диапазон возвращает ошибку EINVAL.

Баланс задержки, adaptive режим и его настройка

Малые rx-usecs дают низкую латентность и высокий расход CPU на прерывания. Большие - экономят ядра и добавляют задержку до rx-usecs микросекунд. Типовые точки настройки:

Для HFT и торговых систем: rx-usecs 1-8, rx-frames 1-8, adaptive off. Латентность от линка до приложения 3-5 мкс сверх сетевой.

Для веб-серверов и баз данных: rx-usecs 16-32, rx-frames 16-32. Компромисс: латентность до 32 мкс, CPU умеренный.

Для потокового трафика, бэкапов, репликации: rx-usecs 128-256, rx-frames 128-256. Ядра почти свободны от прерываний, латентность до четверти миллисекунды.

Для 10 Гбит/с мелкими пакетами без moderation ядро умирает от 14.88 млн прерываний в секунду, с rx-usecs 64 справляется одно ядро. Это ключевой аргумент в пользу включённого moderation на всех скоростях выше гигабита.

Замер баланса ведут так: запускают нагрузку, смотрят счётчик прерываний через watch -n1 "grep eth0 /proc/interrupts", и параллельно латентность ping или собственным бенчмарком. Поднимают rx-usecs, пока число прерываний перестанет заметно падать, затем откатывают чуть назад.

Большинство современных карт умеют адаптивный режим: адаптер сам поднимает rx-usecs при высокой нагрузке и опускает при низкой. Включение:

ethtool -C eth0 adaptive-rx on adaptive-tx on

У Intel adaptive учитывает занятость процессора и профиль трафика, у Mellanox adaptive-rx базируется на счётчиках очередей. Для смешанных нагрузок adaptive часто лучший выбор по умолчанию. Для фиксированных нагрузок ручная настройка предсказуемее.

У Intel есть и приватные ручки через ethtool --set-priv-flags: например, у igb InterruptThrottleRate управляется параметром модуля с значениями от 8000 до 48000, единица включает динамический режим, ноль отключает moderation совсем. У mlx5 приватный флаг mlx5e управляет счётчиками и режимами очередей, а также ethtool -C работает на уровне профилей очередей.

Мелкий нюанс: некоторые карты имеют раздельные значения rx-usecs-irq и tx-usecs-irq, применяемые пока обработчик прерывания активен, и rx-usecs-low/high для адаптивного диапазона. Эти поля заполняются автоматически и вручную трогают редко.

SoftIRQ NET_RX и привязка прерываний по NUMA

Прерывание только поднимает флаг, основная работа идёт в softirq NET_RX. Его раскладку по ядрам показывает /proc/softirqs:

watch -n1 "grep NET_RX /proc/softirqs"

Бюджет softirq ограничен: один проход обрабатывает до 300 пакетов, затем ядру ставится флаг и ksoftirqd завершает работу. Если ksoftirqd стабильно жжёт ядра, moderation слишком агрессивен: пачки больше бюджета не успевают пройти за один softirq. Лечение: поднять rx-usecs, чтобы пачки были крупнее, но реже, или ограничить поток через RPS.

Связка с оффлоадами: GRO склеивает пакеты внутри softirq, поэтому большие rx-frames без GRO менее эффективны, чем с GRO: 64 сырых пакета против 64 склеенных в один-два skb. Правильный порядок настройки: сначала включить GRO и checksum offload, затем подбирать moderation.

Прерывание сетевой карты лучше обрабатывать на том же NUMA-узле, где висит карта, и на том же ядре, где крутится приложение. Список прерываний:

grep eth0 /proc/interrupts

Привязка каждого вектора:

echo 2 > /proc/irq/128/smp_affinity_list

При работающем irqbalance привязка перетирается каждые несколько секунд, поэтому для ручной раскладки службу останавливают: systemctl stop irqbalance, а постоянную раскладку фиксируют в /etc/default/irqbalance через IRQBALANCE_BANNED_CPULIST или полностью отключают.

На серверах с драйверами, создающими очередь на каждое ядро, ядро само привязывает векторы к ядрам их очередей, и ручная раскладка не нужна. У virtual-функций и контейнеров привязку делают через cpuset или taskset на стороне приложения.

Типичные ошибки, диагностика и рекомендации по серверам

  1. adaptive-rx on поверх ручных значений, ручные настройки игнорируются, выбирают один режим;
  2. rx-usecs 0 на 10 Гбит/с, ядро захлёбывается прерываниями, поднимают до разумных значений;
  3. rx-frames 256 с бюджетом softirq 300, пачки не проходят за проход, снижают до 64-128 или включают GRO;
  4. irqbalance перетирает привязку, раскладка сбивается каждые несколько секунд, останавливают службу или бандят ядра;
  5. latency замеряют ping без учёта moderation, реальные задержки выше, замеряют приложением через perf или бенчмарком;
  6. priv flags конфликтуют с ethtool -C, значения перетирают друг друга, задают в одном месте;
  7. очереди не согласованы с NUMA, прерывания на чужом узле, привязка через smp_affinity_list.

Диагностика: ethtool -c показывает текущие значения, watch /proc/interrupts показывает частоту, /proc/softirqs показывает раскладку NET_RX, mpstat -P ALL 1 показывает %soft по ядрам. Если %soft выше 20-30% на ядре, moderation стоит усилить.

Файловый сервер или СХД с 25-100 Гбит/с: rx-usecs 128-256, rx-frames 128-256, GRO on, adaptive off. Ядра почти свободны, полоса максимальна.

База данных с клиентами по сети: rx-usecs 32, rx-frames 32, adaptive-rx on. Латентность до десятков микросекунд, прерывания под контролем.

Маршрутизатор или фильтр трафика: rx-usecs 8-16, rx-frames 32, включённый RPS на все ядра. Компромисс между латентностью и CPU.

Виртуализация с SR-IOV: moderation на VF настраивается так же, на PF выставляют большие значения, чтобы host-стек не мешал гостям.

Порядок внедрения: включить оффлоады, выставить стартовые значения по профилю сервера, замерить частоту прерываний и латентность, подстроить rx-usecs, зафиксировать конфигурацию в systemd-юните или скрипте запуска сети, настроить мониторинг /proc/interrupts и %soft в mpstat. Один вечер настройки обычно снижает нагрузку прерываниями в пять-десять раз при сохранении приемлемой латентности.
## Настройка на поколениях карт Intel и Mellanox

Драйверы Intel (igb, ixgbe, ice, i40e) применяют moderation через регистр ITR, Interrupt Throttle Rate, значение в прерываниях в секунду на вектор. Параметр модуля InterruptThrottleRate принимает значения от 8000 до 48000, единица включает динамический режим, ноль отключает moderation. Связь с ethtool -C прямая: rx-usecs 64 при пике трафика даёт примерно 15 тысяч прерываний в секунду на очередь, что соответствует ITR 15000.

Карты Intel также поддерживают раздельные профили по типам трафика: Rx profile, Tx profile, Tx-Rx combined. При combined профиле один ITR обслуживает и приём, и передачу, что упрощает настройку. Раздельные профили нужны, когда приём и передача асимметричны: бэкап-сервер шлёт мало, принимает много, и tx-usecs можно ставить большим, а rx-usecs умеренным.

Ice-драйвер на E810 добавляет контекстные профили: для каждой очереди можно задать свой ITR через sysfs-путь /sys/class/net/eth0/queues/rx-N/rx_itr. Это позволяет на одной карте держать латентностную очередь с rx-usecs 4 для торгового трафика и фоновую очередь с rx-usecs 256 для репликации.

Счётчики необработанных прерываний видны в ethtool -S eth0: rx_no_buffer_count, rx_missed_errors показывают, что адаптер не успел уведомить ядро и пакеты упали в NIC. Рост этих счётчиков сигнализирует о слишком агрессивном moderation или о нехватке дескрипторов кольца.

Драйвер mlx5 использует профили moderation, задаваемые в ethtool -C: rx-usecs, rx-frames, и отдельные tx-usecs, tx-frames. Adaptive режим у mlx5 включается adaptive-rx on и динамически поднимает интервал при загрузке очереди выше порога. Профили переключаются на лету без сброса линка, что удобно для A/B замеров.

Для mlx5 есть нюанс: минимальный rx-usecs зависит от прошивки и обычно равен 8, а ноль работает только с отключённым moderation, когда прерывание идёт на каждый пакет. Скорость реакции проверяется замером ping по RDMA или UDP: с rx-usecs 8 латентность растёт на 4-8 мкс относительно отключённого moderation.

CQ moderation на MLX5 настраивается и из libibverbs при создании CQ через ibv_modify_cq, что позволяет приложению менять профиль динамически: в фазе стартапа низкие значения, в steady-state повышенные. Счётчики очередей через ethtool -S показывают cq_overrun при переполнении очереди завершения, что лечится увеличением глубины CQ или снижением пачки.

Замеры латентности и автоматизация конфигурации

Честный замер латентности с moderation ведут по схеме: фиксированный профиль, UDP-эхо бенчмарком с разрешением наносекунд, серия из миллиона пакетов, отчёт p50/p99/p999. Сравнение профилей:

| rx-usecs | p50 | p99 | прерываний/с на 10G |
|----------|-----|-----|---------------------|
| 0 | 4 мкс | 6 мкс | 1.4 млн |
| 8 | 11 мкс | 15 мкс | 210 тыс |
| 32 | 38 мкс | 44 мкс | 58 тыс |
| 128 | 135 мкс | 142 мкс | 15 тыс |

Пропускная способность при этом почти не меняется до rx-usecs 128, а выше начинает слегка расти за счёт укрупнения пачек. Отсюда практический вывод: латентностные сервисы живут с rx-usecs 8-16, массовый трафик с 64-256, и разница в полосе между этими режимами меньше процента.

Для нагрузки с пиками полезен гибрид: adaptive-rx on с базой rx-usecs 32. Адаптер опустит интервал до 8-16 при всплеске мелких пакетов и поднимет до 256 в спокойные периоды. Счётчик adaptive_rx_low/high в ethtool -S показывает фактический диапазон работы адаптера.

Настройки moderation не переживают перезагрузку, поэтому их фиксируют в сетевой конфигурации. В Debian и Ubuntu: строки post-up ethtool -C eth0 rx-usecs 64 rx-frames 64 в /etc/network/interfaces. В systemd-networkd: юнит-файл .link с параметром RxInterruptModeration, где поддерживаются значения adaptive или числом микросекунд. В NetworkManager: dispatcher-скрипт с ethtool -C.

Для серверов с несколькими интерфейсами и разными профилями удобно сделать скрипт /usr/local/bin/apply-moderation.sh со списком интерфейсов и значений, вызываемый из systemd-юнита oneshot до поднятия сети. В скрипте полезно проверять поддержку параметров через ethtool -c и пропускать неподдерживаемые значения с логом, иначе одна старая карта сломает загрузку.

Мониторинг в продакшене: экспорт числа прерываний по интерфейсам в Prometheus через текстовый коллектор node_exporter, алерты на рост числа прерываний выше порога и на %soft в узле выше 30%. Эти два сигнала ловят деградацию moderation задолго до жалоб пользователей.