Есть три вещи которые в основном влияют на потерю пакетов при приёме (допустим, нас не волнует как долго он будет проходить через нашу систему, то есть latency мы можем жертвовать), а наша задача - поймать все пакеты, не упустив ни одного и обработать.

Итак, вещи:

  • размер буфера сетевой карты
  • правильное распределение прерываний входящих очередей сетёвки по ядрам
  • мощность ядер процессора, обрабатывающего пакеты
  • различные значения rx-usecs

Разберём их по порядку и набросаем пару костылей которые позволят вам заставить компуктир работать на полную мощь!

Размер буфера сетевой карты

[root@centos ~]# ethtool -g eth1
Ring parameters for eth1:
Pre-set maximums:
RX:		4096
RX Mini:	0
RX Jumbo:	0
TX:		4096
Current hardware settings:
RX:		4096
RX Mini:	0
RX Jumbo:	0
TX:		256

Здесь мы видим выкрученный на максимум rx-буфер. По гуглу и описанию знакомых, значение RX-буфера по сути означает количество указателей на пакеты, которое сетевая карта может накопить до поступления прерывания по usecs’ам перед выплёвыванием далее в ядро. Подобрать значение довольно сложновато, ибо тут такая ситуация:

Есть два стула, на одном задержки точеные, на другом потери мощные.

Иными словами высокое значение RX-буфера - задержки, низкое - потери. Вообще, недавно у нас в компании была дискуссия на эту тему, о том чем всё же по факту является значение Current HW Settings RX - реальным потолком буфера пакетов или значением при превышении которого необходимо сигнализировать о том, пора обрабатывать эти пакеты очень срочно, а на самом деле они могут копиться до Pre-set maximums. Внятных аргументов я не услышал, но пометочку о том, что надо почитать документацию и исходники на эту тему себе сделал.

Пример команды для увеличения буфера:

ethtool -G eth1 rx 4096

Распределение прерываний

Многие сетевые карты имеют несколько очередей для входящих пакетов. Каждая очередь висит на ядре/списке ядер. На многих железках из коробки, несмотря на то, что в smp_affinity_list указан список 0-$cpucount все прерывания находятся на первом ядре процессора. Обойти это можно раскидав с помощью echo все прерывания на разные ядра.

По возможности используйте разные реальные ядра, допустим, дано:

  • 1 процессор с гипертредингом
  • 4 реальных ядра
  • 8 виртуальных ядер
  • 4 очереди сетевой карты, которые составляют 95% работы сервера

Раскинуть их на 0, 1, 2 и 3 ядра будет не так эффективно, как на 0, 2, 4 и 6.

Пример кода (костыльный и неуниверсальный), который раскидывает 8 очередей на 8 ядер (довольно простой случай).

#!/bin/bash

nic=eth1-TxRx-
cpucount=$(grep -c 'model name' /proc/cpuinfo)
grep $nic /proc/interrupts | while read irq $(eval echo cpu{1..$cpucount}) t queue t; do
  irq=${irq//:}
  /proc/irq/$irq/smp_affinity_list
  echo "${queue##*TxRx-}" > /proc/irq/$irq/smp_affinity_list
done

Мощность ядер процессора

[root@centos ~]# grep '' /sys/devices/system/cpu/cpu0/cpufreq/scaling_{min,cur,max}_freq 
/sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq:1600000
/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq:1600000
/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq:3201000

Суть видна - довольно мощный процессор работает в полсилы и даже не собирается напрягаться.

Заставить их напрячься можно так:

#!/bin/bash

cpucount=$(grep -c 'model name' /proc/cpuinfo)
sysdir=/sys/devices/system/cpu
for cpu in $(eval echo cpu{0..$((cpucount-1))}); do
    cat $sysdir/$cpu/cpufreq/scaling_max_freq > $sysdir/$cpu/cpufreq/scaling_min_freq
done

Различные значения rx-usecs

Не буду плодить энтропию, так что вот ссылка на хорошую статью (правда заточенную под маршрутизаторы больше): http://habrahabr.ru/post/108240/

В кратце - можно за счёт повышения нагрузки на процессор слегка снять напряги с сетёвки уменьшая. На большинстве машин использумых в моём случае оптимальным оказалось значение 1.

ethtool -C eth1 rx-usecs 1