?

Log in

No account? Create an account
chuck

dadv


Choose your future

Choose to sysadmin


Previous Entry Share Flag Next Entry
Тюнинг FreeBSD 8.2. Часть 2. Производительность.
chuck
dadv

В продолжение темы.

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 назначает им всем одинаковый (нулевой) "номер потока".

    В итоге, драйвер 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 корректно. Тоже не доходят руки поправить.

Продолжение следует.



А что скажешь про 82575EB? У нас на нём потери пакетов (UDP) при ~600-800Mbit/s входящего трафика пакетами по ~1000 байт. Тут Solaris, буфера увеличили до максимума (аналог фряхиного rxd/txd), но от потерь полностью не избавились, только снизили.

Про солярку ничего не скажу. Возможно, там есть буфер для входящих пакетов типа netisr на фре? Надо его найти, и если найдется, увеличить :-)

Буфер не в сетевой карте, а в ядре.

теги забыл или не дописал? хочу ссылку нашим кинуть, на всякий случай.

Забыл, ага. Поправил. Ваши все это знают наверняка.

О! Вот ещё вопрос -- что кроме man ipfw читать про новый dummynet? Все мурзилки про старый, а новый разобран в мане плохо -- например, я так и не понял, является ли pipe синонимом для sched type fifo или нет, и всегда ли нужны все три компонента и размножается ли pipe когда размножается sched.

Я с новым обращаюсь ровно так же, как со старым. То есть, не изучал ещё отличия. Работает не трогай :-)

(Deleted comment)
Интересно, почему это так зачастую воспринимается?

(Deleted comment)
(Deleted comment)
(Deleted comment)
(Deleted comment)
(Deleted comment)
(Deleted comment)
(Deleted comment)
(Deleted comment)
(Deleted comment)
(Deleted comment)
(Deleted comment)
а intel 82573 ведет себя так же, как 82574?

Судя по RTFS, у 82573 только 32000 буферов буферов под пакеты против 40000 у 82574. Кроме того, у 82573 есть некоторые проблемы с latency на части систем. Плюс у 82573L размер JumboFrames ограничен 9238 байтами против 16128 у 82574, а 82573V/E вообще не поддерживают JumboFrames.

На практике я их не сравнивал.

Дурацкий вопрос, почему именно FreeBSD, а не центось например, для подобных BRAS - задач?

Отвечу вопросом на вопрос: а какую производительность даст центось для подобной задачи?

а какой HZ? И что вообще лучше из ядра выкинуть? Скажем тотже
options MAC # TrustedBSD MAC Framework

> а какой HZ?

Дефолтный, то есть 1000.

> И что вообще лучше из ядра выкинуть?

options FLOWTABLE, оно глючное и его всё равно до исправления скоро уберут из GENERIC. MAC я не выкидывал, не мешает.

А почему вы не используете fastforwarding, только через netisr?
Судя по RTFS он корректно отработает с тонной нод netgraph и ifpw.

BRAS'ов с mpd к сожалению у меня нет, но на роутерах/шейперах используется именно ff взамен netisr

Re: fastforwarding

Вы невнимательно читаете. fastforwarding включен и про это написано в пункте "Прочий тюнининг". И он не "взамен" netisr, они друг другу не противоречат. netisr касается обработки пакетов на уровне драйверов и ядер, а fastforwarding это алгоритмы стека TCP/IP.

У меня почти та же конфигурация: 8.2-STABLE FreeBSD
Нету таких переменных в ядре:

sysctl net.graph.maxdgram=8388608
sysctl: unknown oid 'net.graph.maxdgram'

sysctl net.graph.recvspace=8388608
sysctl: unknown oid 'net.graph.recvspace'

При загрузке в messages тоже падает такое:
/etc/rc.d/sysctl: WARNING: sysctl net.graph.recvspace does not exist.
/etc/rc.d/sysctl: WARNING: sysctl net.graph.maxdgram does not exist.

Может нужно модуль какой то подгрузить ?

У меня NETGRAPH вкомпилирован в ядро и эти переменные есть. Типа намёк :-)

Прошу совета. На новом сервере доступа поставил 8.2-release FreeBSD, и были проблемы в виде паник ядра с аптаймом до недели. Многое из описанного в постах делал так-же, в тч обновлялся до stable. После этого тестировал в течении двух недель iperf-ом и cpuburn, заодно проверил память. Падений не было, и я решил поставить сервер под реальную нагрузку (pppoe ~200 конектов, до 200Мбит трафика, не большая нагрузка) Сервер проработал 3 недели без нареканий, а на четвертую молча перезагрузился. В логах перед перезагрузкой ничего, дампа ядра тоже нету.

Тогда у меня был включен net.isr на 4 ядра, а ядро i386,не было некоторых модулей NETGRAPH и соответственно переменные NETGRAPH при загрузке не применялись. Сетевые em, драйвер родной, под повышение прерываний не патчил. Подскажите по вашему опыту, что из этого могло вызвать такую перезагрузку? Как можно заставить систему сообщить ошибку при ребуте?

Большая проблема для меня остается в тестировании. не хочется экспериментировать на живых людях, но как дешево сэмулировать большое количество pppoe конектов с трафиком я пока не придумал. Iperf и прочие не ложат систему и воспроизвести не получается. Как вы это решаете? Думаю, это даже на отдельный пост потянет, если есть решение.

> что из этого могло вызвать такую перезагрузку?

Отключение net.isr.direct в первую очередь.

> Как можно заставить систему сообщить ошибку при ребуте?

Включение крешдампов по Handbook и организация записи всего вывода ядра на консоль обязательны. Последнее делается через serial console (опять же читаем Handbook) либо средствами IPMI, либо если нет - физическим кабелем с COM1 на консольный сервер (в его роли может быть любой другой сервер) и записью всего вывода с COM1 в текстовый файл.

> Большая проблема для меня остается в тестировании. не хочется экспериментировать на живых людях, но как дешево сэмулировать большое количество pppoe конектов с трафиком я пока не придумал.

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

По поводу стресс-теста.

Конфигурацию сервера следует немного изменить. Во-первых, если у вас авторизация пользователей идёт через RADIUS, это следует закомментировать, чтобы убрать влияние задержек ответов радиуса на тестирование, ну и чтобы не грузить его тысячами бессмысленных запросов.

Во-вторых, удобно переместить назначение IP-адресов на клиентскую часть, чтобы сервер "авторизовал" одного и того же пользователя по паролю в mpd.secret, но соглашался назначать на линк адреса, предложенные клиентом. Это минимизирует изменения настроек серверной части и концентрирует всю тест-логику на стороне клиента.

На стороне клиента вся логика реализована в двух небольших скриптах, которые я не стал вылизывать к публикации - работают и так :-)

Один скрипт - назван generate - принимает в командной строке один аргумент - количество одновременных клиентских подключений и генерирует клиентский /usr/local/etc/mpd5/mpd.conf для этого количества, используя фиксированный кусок конфига из /usr/local/etc/mpd5/mpd.conf.tmpl (готовите сами) и дописывая к нему нужное количество клиентских bundle и link.

Все клиенты находятся в одном vlan (для теста не требуется прокидывать транк до сервера), в моём случае это был vlan18, вам придётся поправить это место в скрипте generate под себя.

IP-адреса на каждый линк я назначал вида 10.100.M.N (клиент) и 10.100.109-M.N (сервер), где M.N разные для разных клиентов. Это тоже можете поправить под себя. Логин и пароль у всех клиентов один, pctest/testpc, это тоже в этом скрипте "зашито", как и имя PPPoE-сервиса.

Скрипт generate можно запустить руками, чтобы проверить полученный mpd.conf перед стартом теста, но во время самого теста его вручную пускать не надо - его запускает второй скрипт по имени test, в котором количество создаваемых клиентов указано в тексте в переменной count.

Скрипты положить в один каталог и стресс-тест запускать командой ./test, прерывать нажатием Ctrl-C (тест бесконечный).

Во время тестирования формируется конфигурация для клиента, запускается mpd с этой конфигурацией и ожидается установление всех клиентских соединений. Из-за внутренней проблемы в клиентской части mpd он может установить только часть соединений из указанного количества, поэтому скрипт может закачить очередной цикл тестирования, если в течение 5 секунд не было изменений в количестве установленных соединений. По окончании цикла клиентский mpd убивается, все потроха netgraph подчищаются и клиентский mpd стартует снова, заходя на следующий цикл.

Гонять тест следует в течение нескольких суток, посматривая, не упал ли сервер. Упасть, кстати, может и клиентская машина, если на ней не выполнен тот же тюнинг.

Падение тест провоцирует даже при полном отсутствии IP-трафика в PPPoE-линках, но при желании можно и погонять по ним трафик. Я делал это поднятием FTP-сервера на клиенте и использованием на сервере скрипта ip-up, который запускает в фоне и в цикле fetch фиксированного файла с клиента в /dev/null, то есть создаёт трафик нужного направления - от клиента к серверу (именно такой трафик дополнительно провоцирует паники сервера из-за netisr). Ну и ip-down, который убивает запущенный из ip-up фоновый процесс и его подпроцесс fetch. К сожалению, этот скрипт у меня не сохранился, но он не очень-то и важен - основные паники легко провоцируются и без него, а проблема с netisr исключается неиспользованием режима indirect.

Скрипты тут.

net.graph.maxdgram=8388608
net.graph.recvspace=8388608


Вот при таких настройках у меня на 8.2 STABLE netgraph сразу же умер по буферам.

hw.igb.rxd=4096
hw.igb.txd=4096

При этом второй igb у меня умер, видимо, 4096 - общий бюджет, а ключик ставит per-port, и на вторую карту уже не хватает.

> netgraph сразу же умер по буферам.

Ну, я же писал, сколько у меня физической памяти, 4GB. И kern.ipc.nmbclusters задран сильно, чтобы буферов хватало всем.

> При этом второй igb у меня умер

У меня igb0 и igb1, никто не умирает, все трудятся. Но amd64.

(Deleted comment)

Re: lagg0 и lagg1 ошибки.

Не понял вопроса. У кого "у вас"? И что значит "не накручивается"?

(Deleted comment)
Привет! Очень бы хотелось увидеть пост про тюнинг сетевой производительности FreeBSD 9. Был опыт использования в продакшене?

Я не ставлю .0-релизы в продакшн.