Каждый пакет приходит в процессор через прерывание, и на скорости 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 на стороне приложения.
Типичные ошибки, диагностика и рекомендации по серверам
- adaptive-rx on поверх ручных значений, ручные настройки игнорируются, выбирают один режим;
- rx-usecs 0 на 10 Гбит/с, ядро захлёбывается прерываниями, поднимают до разумных значений;
- rx-frames 256 с бюджетом softirq 300, пачки не проходят за проход, снижают до 64-128 или включают GRO;
- irqbalance перетирает привязку, раскладка сбивается каждые несколько секунд, останавливают службу или бандят ядра;
- latency замеряют ping без учёта moderation, реальные задержки выше, замеряют приложением через perf или бенчмарком;
- priv flags конфликтуют с ethtool -C, значения перетирают друг друга, задают в одном месте;
- очереди не согласованы с 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 задолго до жалоб пользователей.