![[personal profile]](https://www.dreamwidth.org/img/silk/identity/user.png)
В продолжение темы.
2. Производительность.
Производительность роутера обсуждается в контексте трафика в сотни мегабит или единиц гигабит в секунду (не 10G).
- Не экономьте на хороших сетевых платах. Наилучшие из использованных мной плат для витой пары это платы на чипе Intel 82576 (драйвер igb) - они меньше всего нагружают процессор прерываниями плюс могут помогать операционной системе выравнивать загрузку ядер CPU, генерируя не одно, а несколько прерываний на материнских платах, поддерживающих технологию MSI-X (на материнских платах тоже не экономьте), таким образом, позволяя ОС обрабатывать входящий трафик с одной карты параллельно несколькими ядрами.
Интегрированные карты на основе Intel 82574L показали себя хуже (больше грузят процессор прерываниями), но в итоге сгодились и они.
Гигабитные сетевые карты Realtek, несмотря на зачаточный interrupt moderation и его поддержку драйвером re, показали полную негодность на современном Internet-трафике, где средний размер пакета - порядка 600 байт. Слишком сильно грузят процессор прерываниями, при этом неспособны справляться с соответствующим количеством пакетов в секунду (pps). - Кстати, о pps (пункт только для владельцев сетевых карт Intel). По умолчанию, драйвера em и igb программируют чипы генерировать не более чем 8000 прерываний в секунду, опасаясь перегрузить CPU прерываниями. 8000 это очень мало для современных процессоров, и, если не принять мер, процессор останется недогружен, а пакеты при этом будут застаиваться в буферах карты и дополнительные задержки будут расти до сотен или даже тысяч милисекунд на пустом месте.
В нынешней 8.2-STABLE драйвер igb поддерживает loader tunnable hw.igb.max_interrupt_rate с дефолтом 8000, позволяющий изменить этот дефолт. Драйвер em ничего такого не поддерживает. Оба драйвера не поддерживают изменения параметра через sysctl (без перезагрузки).
Патчи для em и для igb исправляют эту ситуацию: у em появляется поддержка аналогичного loader tunnable hw.em.max_interrupt_rate и у обоих поддержка sysctl dev.em.X.max_interrupt_rate (аналогично для igb), для которых loader tunnables задают значения по умолчанию. Патч сыроват в том смысле, что изменение sysctl не вступает в силу сразу, для этого нужно каким-то образом инициировать перепрограммирование чипа - например, это можно сделать через ifconfig down/up. При указании нужных значений в loader.conf этой проблемы нет, значения прописываются в чип при первом поднятии интерфейса. Для достижения цифр, указанных в более раннем посте, пришлось выставлять 32000 и для em, и для igb (16000 оказалось тоже мало).
Update 1.03.2012: обновление патчей для 8.3-PRERELEASE: em и igb. Работают и на 9.1.
Update 7.10.2013: обновление патча на em для 9.2-RELEASE: em. Патч для igb всё ещё годится старый, от 8.3 (выше).
Update 9.10.2014: обновление патча на igb для 9.3: igb.
Update 29.01.2015: обновление патча на igb для 9.3-STABLE: igb.
Update 14.02.2015: обновление патча на em для 9.3-STABLE: em.
Update 16.11.2015: патч для драйвера старых сетевых гигабиток Intel для 9.3-STABLE: lem; обновление патча на em для 10.2: em; обновление патча на igb для 10.2: igb.
Update 7.09.2016: обновление патчей для 11.0: lem, em, igb.
Патчи эти не отменяют использования штатного interrupt moderation, поддерживаемого всеми гигабитными чипами карт Intel. Для понимания этой технологии и указанных ниже sysctl совершенно необходимо прочитать и понять первоисточник: http://download.intel.com/design/network/applnots/ap450.pdf (или аналогичную документацию).
В /boot/loader.conf пишем:hw.em.rxd=4096
hw.em.txd=4096
hw.igb.rxd=4096
hw.igb.txd=4096
hw.igb.max_interrupt_rate=32000
hw.em.max_interrupt_rate=32000
В /etc/sysctl.conf:dev.em.0.rx_int_delay=200
dev.em.0.tx_int_delay=200
dev.em.0.rx_abs_int_delay=4000
dev.em.0.tx_abs_int_delay=4000
dev.em.0.rx_processing_limit=4096
dev.em.1.rx_int_delay=200
dev.em.1.tx_int_delay=200
dev.em.1.rx_abs_int_delay=4000
dev.em.1.tx_abs_int_delay=4000
dev.em.1.rx_processing_limit=4096
dev.igb.0.rx_processing_limit=4096
dev.igb.1.rx_processing_limit=4096 - Дополнительный тюнинг NETGRAPH.
В /etc/sysctl.conf также полезно увеличить и другие буферы для стабильной работы утилиты ngctl:net.graph.maxdgram=8388608
net.graph.recvspace=8388608 - Там же имеет смысл поднять длины некоторых других очередей, связанных с обработкой пакетов различными ядерными сетевыми подсистемами во избежание переполнения этих очередей:
# for rtsock
net.route.netisr_maxqlen=4096В /boot/loader.conf:
# for other protocols (IP & PPPoE?)
net.isr.defaultqlimit=4096
# default outgoing interface queue length
# used by lagg etc.
net.link.ifqmaxlen=10240
Через /etc/sysctl.conf отдаём порядка гигабайта ядерной памяти на сетевые буфера, так как нынешняя FreeBSD 8.2 может войти в "клинч" (не падает, но и не обслуживает абонентов) при их переполнении и не выйти из него самостоятельно.kern.ipc.nmbclusters=400000
kern.ipc.maxsockbuf=83886080 - Для использующих dummynet с высокими скоростями: в /etc/sysctl.conf имеет смысл увеличить максимально допустимую длину очереди шейпера и включить режим io_fast, уменьшающий задержки и разгружающий CPU за счет пропуска без шейпинга пакетов тех пользователей, которые не выбирают своей полосы:
net.inet.ip.dummynet.pipe_slot_limit=1000
net.inet.ip.dummynet.io_fast=1 - Прочий тюнинг /etc/sysctl.conf:
net.inet.ip.fastforwarding=1
# на тот случай, если работу netisr стабилизируют в будущем, увеличиваем длину очереди под входящие пакеты:
net.inet.ip.intr_queue_maxlen=10240 - Использование lagg.
Если трафик через роутер хотя бы в одну сторону приближается с гигабиту при использовании карт 1G, один из вариантов (кроме перехода на 10G :-) это использование lagg (etherchannel, portchannel, trunking, bonding - есть много терминов для одного и того же по сути).
lagg в 8.2 годится к использованию, но и тут прячутся грабли (update 1.03.2012: описанная в этом пункте проблема исправлена, начиная с 8.3-PRERELEASE и 9.0-STABLE). Начиная с версии 8.0, драйвер lagg научился обращать внимание на тег M_FLOWID, который драйвер сетевой карты, первоначально принявшей пакет, мог навесить на буфер, содержащий этот пакет. Такие теги на пакет могут навешивать драйвера сетевых карт, поддерживающие MSI-X и использующие больше одного вектора прерывания для информирования ядра о приёме данных. В метаданных такого пакета хранится "номер потока", аппаратно сгенерированный принявшей пакет сетевой картой и донесенный до сведения операционной системы посредством генерации прерывания с нужным номером.
Например, карта на чипе 82576, на материнской плате с поддержкой MSI-X и четырьмя ядрами CPU может использовать четыре вектора прерываний, и драйвер igb в зависимости от того, посредством какого прерывания пришел пакет, приклеивает пакету один из четырех "номеров потока".
Увидев такой пакет, драйвер lagg при выборе исходящего порта для пакета пропускает вычисление хеша для него (для экономии тактов CPU) и выбирает порт, используя остаток от деления номера потока на количество портов исходящего интерфейса. В теории всё прекрасно, но...- Смотрим datasheet на карту: http://download.intel.com/design/network/datashts/82576_Datasheet.pdf
Для распределения пакетов по очередям карта использует спецификацию Microsoft Receive-Side Scaling (RSS) и даташит ссылается на неё на странице 274, упоминая реализованную хеш-функцию, которая реализована в чипе аппаратно и результат которой определяет, какой вектор прерывания будет использован для пакета. - Смотрим спецификацию RSS от Microsoft: http://download.microsoft.com/download/5/d/6/5d6eaf2b-7ddf-476b-93dc-7cf0072878e6/ndis_rss.doc
На странице 7 спецификации сказано, от чего может считаться хеш: от IP-адресов (IPv4 или IPv6) и, опционально, портов TCP (но не UDP). Номера тегов 802.1q не используются. На странице 9 сказано, что если пакет не имеет указанных параметров, от которых брать хеш, то он не хешируется. - На практике это означает, что все фреймы PPPoE/GRE, приходящие через пучок vlan-ов и карты igb, попадают в одну "очередь" внутри карты и обслуживаются одним вектором прерываний и никакого распределения нагрузки по ядрам не получается.
- Как следствие, драйвер igb назначает им всем одинаковый (нулевой) "номер потока".
- Смотрим datasheet на карту: http://download.intel.com/design/network/datashts/82576_Datasheet.pdf
В итоге, драйвер lagg все такие пакеты посылает только в один из своих портов и нет также никакой балансировки нагрузки по сетевым картам на выход в сторону аплинка.Патч на lagg вводит новый sysctl net.link.lagg.use_flows с дефолтным значением 1. Изменив его на 0, получим старое поведение времен FreeBSD 7.x, когда lagg всегда самостоятельно вычисляет хеш от пакета. И получим восстановленное балансирование исходящего трафика по портам lagg. См. тут.
Этот патч тоже сыроват в том смысле, что вводит глобальный sysctl, хотя можно было бы ввести по одному sysctl для каждого lagg и немного сэкономить CPU за счет использования "хороших" номеров потоков, сгенерированных теми сетевыми картами, которые получают трафик не в виде фреймов PPPoE, а в виде простых пакетов IPoE. Пока руки не дошли переделать патч.
Ну и ещё мелкая проблема в lagg: он неправильно считает статистику unicast/non-unicast pps, поэтому при рисовании графика pps с интерфейсов lagg получаем нули для non-unicast. Эта же статистика по индивидуальным портам отдаётся драйверами em/igb корректно. Тоже не доходят руки поправить.
Продолжение следует.