Сначала я хотел бы поделиться парой слов о том, как работает pcap — общеизвестный способ захвата пакетов. Он используется в таких популярных утилитах как iftop, tcpdump, arpwatch. Кроме этого, он отличается очень высокой нагрузкой на процессор.
Итак, Вы открыли им интерфейс и ждете пакетов от него используя обычный подход — bind/recv. Ядро в свою очередь получает данные из сетевой карты и сохраняет в пространстве ядра, после этого оно обнаруживает, что пользователь хочет получить его в юзер спейсе и передает через аргумент команды recv, адрес буфера куда эти данные положить. Ядро покорно копирует данные (уже второй раз!). Выходит довольно сложно, но это не все проблемы pcap.
Кроме этого, вспомним, что recv — это системный вызов и вызываем мы его на каждый пакет приходящий на интерфейс, системные вызовы обычно очень быстры, но скорости современных 10GE интерфейсов (до 14.6 миллионов вызовов секунду) приводят к тому, что даже легкий вызов становится очень затратным для системы исключительно по причине частоты вызовов.
Также стоит отметить, что у нас на сервере обычно более 2х логических ядер. И данные могут прилететь на любое их них! А приложение, которое принимает данные силами pcap использует одно ядро. Вот тут у нас включаются блокировки на стороне ядра и кардинально замедляют процесс захвата — теперь мы занимаемся не только копированием памяти/обработкой пакетов, а ждем освобождения блокировок, занятых другими ядрами. Поверьте, на блокировки может зачастую уйти до 90% процессорных ресурсов всего сервера.
Хороший списочек проблем? Итак, мы их все геройски попробуем решить!
Итак, для определенности зафиксируем, что мы работаем на зеркальных портах (что означает, что откуда-то извне сети нам приходит копия всего трафика определенного сервера). На них в свою очередь идет трафик — SYN флуд пакетами минимального размера на скорости 14.6 mpps/7.6GE.
Сетевые ixgbe, драйверы с SourceForge 4.1.1, Debian 8 Jessie. Конфигурация модуля: modprobe ixgbe RSS=8,8 (это важно!). Процессор у меня i7 3820, с 8ю логическими ядрами. Поэтому везде, где я использую 8 (в том числе в коде) Вы должны использовать то число ядер, которое есть у Вас.
Распределим прерывания по имеющимся ядрам
Обращаю внимание, что нам на порт прилетают пакеты, целевые MAC адреса которых не совпадают с MAC адресом нашей сетевой карты. В противном случае включится в работу TCP/IP стек Linux и машина подавится трафиком. Этот момент очень важен, мы сейчас обсуждаем только захват чужого трафика, а не обработку трафика, который предназначается данной машине (хотя для этого мой метод подходит с легкостью).
Теперь проверим, сколько трафика мы можем принять, если начнем слушать весь трафик.
Включаем promisc режим на сетевой карте:
ifconfig eth6 promisc
После этого в htop мы увидим очень неприятную картину — полную перегрузку одного из ядер:
1 [||||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%] 2 [ 0.0%] 3 [ 0.0%] 4 [ 0.0%] 5 [ 0.0%] 6 [ 0.0%] 7 [ 0.0%] 8 [ 0.0%]
Для определения скорости на интерфейсе воспользуемся спец-скриптом pps.sh: gist.github.com/pavel-odintsov/bc287860335e872db9a5
Скорость на интерфейсе при этом довольно маленькая — 4 миллиона пакетов секунду:
bash /root/pps.sh eth6
TX eth6: 0 pkts/s RX eth6: 3882721 pkts/s TX eth6: 0 pkts/s RX eth6: 3745027 pkts/s
Чтобы решить эту проблему и распределить нагрузку по всем логическим ядрам (у меня их 8) нужно запустить следующей скрипт: gist.github.com/pavel-odintsov/9b065f96900da40c5301 который распределит прерывания от всех 8 очередей сетевой карты по всем имеющимся логическим ядрам.
Прекрасно, скорость сразу вылетела до 12mpps (но это не захват, это лишь показатель того, что мы можем прочесть трафик на такой скорости из сети):
bash /root/pps.sh eth6 TX eth6: 0 pkts/s RX eth6: 12528942 pkts/s TX eth6: 0 pkts/s RX eth6: 12491898 pkts/s TX eth6: 0 pkts/s RX eth6: 12554312 pkts/s
А нагрузка на ядра стабилизировалась:
1 [||||| 7.4%] 2 [||||||| 9.7%] 3 [|||||| 8.9%] 4 [|| 2.8%] 5 [||| 4.1%] 6 [||| 3.9%] 7 [||| 4.1%] 8 [||||| 7.8%]
Сразу обращаю внимание, что в тексте будут использоваться два примера кода, вот они:
AF_PACKET, AF_PACKET + FANOUT: gist.github.com/pavel-odintsov/c2154f7799325aed46ae
AF_PACKET RX_RING, AF_PACKET + RX_RING + FANOUT: gist.github.com/pavel-odintsov/15b7435e484134650f20
Это законченные приложения с максимальным уровнем оптимизаций. Промежуточные (заведомо более медленные версии кода) я не привожу — но все флажки для управления всеми оптимизациями выделены и объявлены в коде как bool — можете легко повторить мой путь у себя.
Первая попытка запуска AF_PACKET захвата без оптимизаций
Итак, запускаем приложение по захвату трафика силами AF_PACKET:
We process: 222048 pps We process: 186315 pps
И нагрузка в потолок:
1 [|||||||||||||||||||||||||||||||||||||||||||||||||||||||| 86.1%] 2 [|||||||||||||||||||||||||||||||||||||||||||||||||||||| 84.1%] 3 [|||||||||||||||||||||||||||||||||||||||||||||||||||| 79.8%] 4 [||||||||||||||||||||||||||||||||||||||||||||||||||||||||| 88.3%] 5 [||||||||||||||||||||||||||||||||||||||||||||||||||||||| 83.7%] 6 [||||||||||||||||||||||||||||||||||||||||||||||||||||||||| 86.7%] 7 [|||||||||||||||||||||||||||||||||||||||||||||||||||||||||| 89.8%] 8 [|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||90.9%]
Причина тому, что ядро утонуло в блокировках, на которые тратит все процессорное время:
Samples: 303K of event 'cpu-clock', Event count (approx.): 53015222600 59.57% [kernel] [k] _raw_spin_lock 9.13% [kernel] [k] packet_rcv 7.23% [ixgbe] [k] ixgbe_clean_rx_irq 3.35% [kernel] [k] pvclock_clocksource_read 2.76% [kernel] [k] __netif_receive_skb_core 2.00% [kernel] [k] dev_gro_receive 1.98% [kernel] [k] consume_skb 1.94% [kernel] [k] build_skb 1.42% [kernel] [k] kmem_cache_alloc 1.39% [kernel] [k] kmem_cache_free 0.93% [kernel] [k] inet_gro_receive 0.89% [kernel] [k] __netdev_alloc_frag 0.79% [kernel] [k] tcp_gro_receive
Оптимизация AF_PACKET захвата с помощью FANOUT
Итак, что же делать? Немного подумать :) Блокировки возникают тогда, когда несколько процессоров пытаются использовать один ресурс. В нашем случае это происходит из-за того, что у нас один сокет и его обслуживает одно приложение, что вынуждает остальные 8 логических процессоров стоять в постоянном ожидании.
Тут нам на помощь придет отличная функция — FANOUT, а если по-русски — разветвления. Для AF_PACKET мы можем запустить несколько (разумеется, наиболее оптимальным в нашем случае будет число процессов равное числу логических ядер). Кроме этого, мы можем задать алгоритм по которому данные будут распределяться по этим сокетам. Я выбрал режим PACKET_FANOUT_CPU, так как в моем случае данные очень равномерно распределяются по очередям сетевой карты и это, на мой взгляд, наименее ресурсоемкий вариант балансировки (хотя тут не ручаюсь — рекомендую посмотреть в коде ядра).
Корректируем в примере кода bool use_multiple_fanout_processes = true;
И снова запускаем приложение.
О чудо! 10 кратное ускорение:
We process: 2250709 pps We process: 2234301 pps We process: 2266138 pps
Процессоры, конечно, по прежнему загружены по полной:
1 [|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||92.6%] 2 [|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||93.1%] 3 [|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||93.2%] 4 [|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||93.3%] 5 [|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||93.1%] 6 [|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||93.7%] 7 [|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||93.7%] 8 [|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||93.2%]
Но карта perf top выглядит уже совершенно иначе — никаких локов больше нет:
Samples: 1M of event 'cpu-clock', Event count (approx.): 110166379815 17.22% [ixgbe] [k] ixgbe_clean_rx_irq 7.07% [kernel] [k] pvclock_clocksource_read 6.04% [kernel] [k] __netif_receive_skb_core 4.88% [kernel] [k] build_skb 4.76% [kernel] [k] dev_gro_receive 4.28% [kernel] [k] kmem_cache_free 3.95% [kernel] [k] kmem_cache_alloc 3.04% [kernel] [k] packet_rcv 2.47% [kernel] [k] __netdev_alloc_frag 2.39% [kernel] [k] inet_gro_receive 2.29% [kernel] [k] copy_user_generic_string 2.11% [kernel] [k] tcp_gro_receive 2.03% [kernel] [k] _raw_spin_unlock_irqrestore
Кроме этого, у сокетов (хотя я не уверен про AF_PACKET) есть возможность задать буфер приема, SO_RCVBUF, но на моем тест-стенде это не дало никаких результатов.
Оптимизация AF_PACKET захвата с помощью RX_RING — кольцевого буфера
Что же делать и почему все равно медленно? Ответ тут в функции build_skb, она означает, что внутри ядра по-прежнему производится два копирования памяти!
Теперь попробуем разобраться с выделением памяти за счет использования RX_RING.
И ура 4 MPPS вершина взята!!!
We process: 3582498 pps We process: 3757254 pps We process: 3669876 pps We process: 3757254 pps We process: 3815506 pps We process: 3873758 pps
Такой прирост скорости был обеспечен тем, что копирование памяти из буфера сетевой карты теперь производится лишь однажды. И при передачей из пространства ядра в пространство пользователя повторное копирование не производится. Это обеспечивается за счет общего буфера выделенного в ядре и пропущенного в user space.
Также меняется подход с работой — нам теперь нельзя висеть и слушать когда придет пакет (помните — это оверхед!), теперь с помощью вызова poll мы можем ожидать сигнала, когда будет заполнен целый блок! И после этого начинать его обработку.
Оптимизация AF_PACKET захвата с помощью RX_RING силами FANOUT
Но все равно у нас проблемы с блокировками! Как же их победить? Старым методом — включить FANOUT и выделить блок памяти для каждого потока-обработчика!
Samples: 778K of event 'cpu-clock', Event count (approx.): 87039903833 74.26% [kernel] [k] _raw_spin_lock 4.55% [ixgbe] [k] ixgbe_clean_rx_irq 3.18% [kernel] [k] tpacket_rcv 2.50% [kernel] [k] pvclock_clocksource_read 1.78% [kernel] [k] __netif_receive_skb_core 1.55% [kernel] [k] sock_def_readable 1.20% [kernel] [k] build_skb 1.19% [kernel] [k] dev_gro_receive 0.95% [kernel] [k] kmem_cache_free 0.93% [kernel] [k] kmem_cache_alloc 0.60% [kernel] [k] inet_gro_receive 0.57% [kernel] [k] kfree_skb 0.52% [kernel] [k] tcp_gro_receive 0.52% [kernel] [k] __netdev_alloc_frag
Итак, подключаем FANOUT режим для RX_RING версии!
УРА! РЕКОРД!!! 9 MPPS!!!
We process: 9611580 pps We process: 8912556 pps We process: 8941682 pps We process: 8854304 pps We process: 8912556 pps We process: 8941682 pps We process: 8883430 pps We process: 8825178 pps
perf top:
Samples: 224K of event 'cpu-clock', Event count (approx.): 42501395417 21.79% [ixgbe] [k] ixgbe_clean_rx_irq 9.96% [kernel] [k] tpacket_rcv 6.58% [kernel] [k] pvclock_clocksource_read 5.88% [kernel] [k] __netif_receive_skb_core 4.99% [kernel] [k] memcpy 4.91% [kernel] [k] dev_gro_receive 4.55% [kernel] [k] build_skb 3.10% [kernel] [k] kmem_cache_alloc 3.09% [kernel] [k] kmem_cache_free 2.63% [kernel] [k] prb_fill_curr_block.isra.57
К слову, справедливости ради обновление на ядро 4.0.0 ветки особенного ускорения не дало. Скорость держалась в тех же пределах. Но нагрузка на ядра упала значительно!
1 [||||||||||||||||||||||||||||||||||||| 55.1%] 2 [||||||||||||||||||||||||||||||||||| 52.5%] 3 [|||||||||||||||||||||||||||||||||||||||||| 62.5%] 4 [|||||||||||||||||||||||||||||||||||||||||| 62.5%] 5 [||||||||||||||||||||||||||||||||||||||| 57.7%] 6 [|||||||||||||||||||||||||||||||| 47.7%] 7 [||||||||||||||||||||||||||||||||||||||| 55.9%] 8 [||||||||||||||||||||||||||||||||||||||||| 61.4%]
В заключение хотелось бы добавить, что Linux является просто потрясающей платформой для анализа трафика даже в окружении, где нельзя собрать какой-либо специализированный модуль ядра. Это очень и очень радует. Есть надежда, что в уже ближайших версиях ядра можно будет обрабатывать 10GE на полноценной wire-speed в 14.6 миллионов/пакетов секунды используя процессор на 1800 мегагерц :)
Рекомендуемые к прочтению материалы:
www.kernel.org/doc/Documentation/networking/packet_mmap.txt
man7.org/linux/man-pages/man7/packet.7.html
Комментарии (77)
pavelodintsov Автор
25.06.2015 16:41+3irqbalance штука довольно cложная и временами делает только хуже. Но есть отличная реализация грамотного подхода к задаче балансировки прерываний, это проект Birq: libcode.org/projects/birq от Serj Kalichev. Я его довольно активно использую там, где нагрузка не распределяется так красиво.
По поводу coalescing — нет, не пробовал. Тут есть более интересная тема, мне обещают дать патч на ixgbe для поддержки batch режима для извлечения пакетов из сетевой, который добавили в 3.19, это должно поидее дать очень хороший прирост скорости.
DustCn
25.06.2015 17:29+2Когда переедете на двухсокетный сервер у вас будет ближний и дальний (по отношению к сетевухе) сокет. Это тоже надо будет учитывать…
pavelodintsov Автор
25.06.2015 17:36+4Ага, поэтому предпочитаю односокетные машинки с очень мощными ядрами класса.
Вообще, выбор машины под захват пакетов дело целой статьи. Но я постараюсь — либо самосборное либо SuperMicro. Dell/HP замечены на очень серьезном уродовании референсных платформ от Intel. Что выливается, например, в невозможность настроить VT-D, сменить режим энергосбережения процессора без заходи в BIOS. Также очень любят отключать системные регистры и приводить в негодность тулзы класса PCM.
По части шедулинга прерываний — Birq понимает, когда сокет удаленный и с учетом этого распределяет прерывания.
invented
25.06.2015 18:03+9Оу, для меня это первая полезная статья на хабре за многие годы ) Спасибо тебе автор )
degs
25.06.2015 22:36Очень интересно, сам AF_PACKET конечно не новость, но оптимизация и анализ просто замечательны.
Кстати у меня ядро 3.13, однако PACKET_FANOUT присутствует в хедерах, хотя в манах не упоминается. Тем не менее все сбилдилось и работает. Спасибо.pavelodintsov Автор
25.06.2015 22:393.13 думаю тоже без проблем подходит. А вот младше 3.6 все плохо — home.regit.org/2012/12/af-packet-oops git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=c0de08d04215031d68fa13af36f347a6cfa252ca
maydjin
25.06.2015 23:52есть возможность задать буфер приема, SO_RCVBUF, но на моем тест-стенде это не дало никаких результатов.
А вы не проверяли код возврата setsockopt? Ибо на той же 7ой центоси этот буффер по умолчанию ограничен 512kb, когда как на практике для подобных процессоров знающие люди рекомендуют от 4mb.
SergeyT
26.06.2015 05:34+4Название статьи показалось до боли знакомым.
Не вот эта ли статья была источником вдохновения: How to receive a million packets per second?pavelodintsov Автор
26.06.2015 10:05+1Буквально неделю назад общался с Мареком по немного смежной теме — по захвату с netmap/pf_ring.
Но у нас все же разные статьи, Марек обрабатывает данные и пропускает их глубоко в стек. Я же наоборот отключаю как можно больше прикладной обработки пакетов ядром, чтобы сделать это в user space.
Но в ряде случаев (UDP сервисы там), мой подход можно использовать как замену подхода Марека на очень больших скоростях.
nckma
26.06.2015 11:21+1Извините — может глупый вопрос: «Картинка в статье имеет отношение к статье или нет?»
Я почему спрашиваю — вроде бы на картинке в сервер заходит несколько сетевых кабелей (5?), но прием пакетов происходит из нескольких интерфейсов или только из одного eth6?pavelodintsov Автор
26.06.2015 11:36Картинка просто для привлечения внимания =) Там всего 4 1GE линка в LACP. Тест стенд не такой фотогеничный, ибо собран из десктоп оборудования, это оказалось самым гибким вариантом по части полного доступа ко всем фичам Intel платформы.
TaHKucT
26.06.2015 18:25Раз пошла такая пьянка: нижний сервер стоит «задом наперед» или это модель, в которую диски с 2х сторон ставятся?
pavelodintsov Автор
26.06.2015 18:27Supermicro это :)
TaHKucT
26.06.2015 23:50это то понятно. Но почему он в сторону горячего коридора повернут дисками(если мы считаем что верхний сервер стоит «правильно»)?
pavelodintsov Автор
27.06.2015 12:48
drwatson32
26.06.2015 14:07+2Павел,
Спасибо за статью.
>>Моя статья расскажет Вам как принять 10 миллионов пакетов в секунду
>>без использования таких библиотек как Netmap, PF_RING, DPDK
Но ведь DPDK делает это на одном ядре.pavelodintsov Автор
26.06.2015 14:11Да, PF_RING/NETMAP/DPDK могут реально принять ~10-14mpps на одном ядре этого же процессора. Но это все же очень специфичный софт, имеющий ряд ограничений (архитектуры — arm/powerpc/avrXX, версии ядер, сам факт сборки модулей ядра часто нудоступен, PF_RING/NETMAP поддерживаются только на Intel сетевых), а AF_PACKET — это технология которая доступна на всем многообразии железа и платформ, где только работает Линукс, пусть и медленнее, но универсальность зашкаливает.
drwatson32
26.06.2015 14:39Согласен, что как универсальное решение ваш подход интересен.
Особенно при разработке виртуальных сетевых функций,
без акселерации на уровне гипервизора и не требующий использования
специализированных SDK.
По поводу PF_RING/NETMAP/DPDK я читал интересный отчет по анализу
http://www.net.in.tum.de/fileadmin/bibtex/publications/theses/2014-gallenmueller-high-speed-packet-processing.pdf.
Много думал. Для себя мы выбрали DPDK.pavelodintsov Автор
26.06.2015 14:46Блин, он на немецком =) Да, DPDK хорош если речь идет про новый проект. Если есть унаследованный код — тут PF_RING/Netmap впереди, ибо работают в рамках обычного окружения Linux API, а не строят свой абсолютно чуждый Nix программисту API.
pavelodintsov Автор
26.06.2015 15:04А что за проект Вы делаете на DPDK, если не секрет? Я use case коллекционирую :)
drwatson32
26.06.2015 15:53+1Не не не, там только первые страницы на немецком =).
Начиная с 10-го там английским по белому написано.
На DPDK делаем коммутатор с интерфейсами в lxc/kvm.
Замену для openvswitch в openstack.pavelodintsov Автор
26.06.2015 16:09Да, форвардеры/роутеры одно удовольствие писать на DPDK — там почти все для этого есть и ворох примеров :)
xdeller
26.06.2015 18:07А инициатива по классификаторам OF в tc вам не кажется более привлекательной? Менять овс можно либо на чистый юзерспейс, либо на них, в общем-то (как кажется). И какие в первую очередь задачи преследуете?
drwatson32
28.06.2015 17:45>>А инициатива по классификаторам OF в tc вам не кажется более привлекательной?
Мне кажется просто привлекательной, с точки зрения рекламы OpenFlow,
как SDN протокола. Ну и вообще мне нравится идея держать это в одном месте.
А так, насколько мне известно, все кому не лень пользуют классификатор из kernel модуля OVS.
>>Менять овс можно либо на чистый юзерспейс, либо на них, в общем-то (как кажется).
Мы на SP рынке, ни чистый userspace, ни kernel тупо не вытягивают нагрузку.
DPDK развивается и обещает чтение на wire speed на 40Gb/s.
У нас пока нечем такой траффик сгенерить, но мы работаем над этим ©.
OVS не подходит, так как нет поддержки chained groups, и впилить
архитектурно трудновыполнимо. Также там некоторые проблемы с meters,
select algs и т.п. Не реализованы dpdk bond и т.д.
В общем после анализа, определили свои фишки, посмотрели на опыт BigSwitch
который форкнул OVS в IVS, потом родил свой SwitchLight,
и начали делать.
А задачи у нас простые, нужен виртуальный свитч под наши решения,
конкретно под NFV ферму на x86 серверах.
Ну и попутно framework для наших партнеров по коммутаторам
(без dpdk, а со своим datapath).
И чтобы WindRiver с его виртуальным свитчем без поддержки OpenFlow
и 12mpps на 2 ядра (в составе его Titanium Server) оказался менее
производительным.
xdeller
28.06.2015 18:23>> Ну и попутно framework для наших партнеров по коммутаторам
Не совсем понятно — у коммутаторов обычно софтверный датапэс как класс отсутствует, я даже сходу не могу привести обратный пример. Или все же речь про общий апи?drwatson32
28.06.2015 19:15скажем так, ofagent с openflow 1.3/1.4/1.5 и с api, для прогрузки правил в hardware pipeline через SDK вендора асиков.
Просто опять же из плотной работы выяснилось, что и у pica8, и у Intel ONS есть ньюансы (оба на базе ovs).xdeller
28.06.2015 20:22Ну ONS насколько мне известно забил на софтовую платформу и делает упор на апи матрицы, а овс, когда я там его тестировал, был очень глючным. Вообще идея втыкать of-правила в матрицу, казавшаяся очень привлекательной два года назад, продолжает разбиваться о фактические возможности недорогих матриц — затруднена цепочечная группировка правил в нужных масштабах, много чего в железе просто не поддерживается и тд. Кумулюс года полтора назад казался достаточно провальным с его тогдашними объемами поддержки железа, а сейчас напротив — ONIE сертифицировался на большом числе железок, на него же подтянулась pica и будущее кажется довольно неплохим, плюс ребята реально много делают плюшек в хост-стеке. Pica же, напротив, с текущими тенденциями развития матриц не имеет, на мой взгляд, прорывного будущего. Втыкать полную спецификацию OF в обычный ToR-девайс очень сложно и избыточно, поскольку ему надо правил порядка 1..10 * число портов как максимум, а процессинг трафика лучше распределять по софтсвичам на вычнодах. На абсолютную истину мои высказывания не претендуют, просто развитие матриц все сильнее корректирует взгляды на то, как это все должно (будет) работать.
pavelodintsov Автор
28.06.2015 20:24Друзья, позвольте вклинится. А есть свичевые матрицы с достаточно широким каналом к контрол плейну и мощным железом на контрол плейне (хотя бы i7/e3)?
Уж очень хочется идею выявления атак силами github.com/FastVPSEestiOu/fastnetmon затащить даже в свич.xdeller
28.06.2015 22:04Это задача SUME-подобных вещей, если даже свич твоим требованиям удовлетворит (тот же ONS), на все порты его просто не хватит.
drwatson32
28.06.2015 22:36Я могу сказать за SDN свитчи (openflow/cumulus). В основном у всех control plane на atom (если x86). Максимум что я видел — это i3, на Intel ONP FM6K (etegro 410i). Зимой в 2014 говорили, что будет FM10K, с мощным control plane, но на NFV воркшопе Intel Network Builders в мае никто в курсе не был. Но вообще у нас как раз и задача вынести control plane со свитчей, поэтому особо эту тему не копал.
pavelodintsov Автор
28.06.2015 22:57Все порты, понятно, не засунуть. А вот хотя бы гигабит завернут на контрол плейн да там внимательно посмотреть его и проанализировать было бы хорошо.
drwatson32
28.06.2015 23:08Ну вон pica если нормализовать не может в hardware table, заворачивает на CPU. Выглядит на атоме это очень грустно :(
Я бы посмотрел в сторону коробок от A10, мультипроцессорные системы с большим количеством портов. Они на них CGNAT выпускают. По сути обычные серваки.pavelodintsov Автор
28.06.2015 23:12А что за сетевки они ставят?
drwatson32
29.06.2015 00:25Это хороший вопрос, мне идентифицировать не удалось. Видел в чужой лабе, там было 16x10GB/s SFP+.
Судя по дата шитам, внутри есть PCI-E expansions.pavelodintsov Автор
29.06.2015 09:53Ого, 16 штук, это круто! :)
pavelodintsov Автор
29.06.2015 09:55Что-то вот такое www.trentonsystems.com/pci-express-expansion/ttx5100-pci-express-expansion-chassis?
pavelodintsov Автор
29.06.2015 09:58Похоже, оно и есть www.sarsen.net/csi/426377216/f/pdf/pcieexpansionconfigurations.pdf в ТТХ заявлено 20GB/s между хост-машиной и этим экспандером, как раз 160 Gpbs, 16*10GE =)
drwatson32
28.06.2015 23:04of старше 1.1 на старых асиках с линейным pipeline (типа firebolt, trident, trident+) на мой взляд утопия. Но в принципе tomahawk уже разрабатывался с оглядкой на openflow. Мы реализуем подход похожий на описанный вами, потому что как не крути, tcam лимитирован. Только у нас траффик доводится на вычноду и там отдаётся в вычсервис, в зависимости от типа траффика, клиента, потребителя и т.п.
ilukyanov
29.06.2015 13:41Приятно видеть живого человека, имеющего компетенции в OVS и OpenFlow, да еще и пишущего на русском :)
OVS не подходит, так как нет поддержки chained groups, и впилить архитектурно трудновыполнимо
Так вроде у них были реализованы группы, но они в итоге их отломали их относительно недавно из-за некоторого бага с дедлоками. Трудновыполним именно фикс этого бага?
Также там некоторые проблемы с meters, select algs и т.п. Не реализованы dpdk bond и т.д.
А что за проблемы с meters и select algs? Год назад когда смотрел на meters — они вообще не были реализованы.drwatson32
29.06.2015 16:50>> Приятно видеть живого человека, имеющего компетенции в OVS и OpenFlow, да еще и пишущего на русском :)
Я кстати тоже приятно удивлен, что есть с кем поговорить :-)
>> Так вроде у них были реализованы группы
Да, у них был chaining, но они выпилили его году в 2014, объяснив
потенциальным readlock.
>> Трудновыполним именно фикс этого бага?
Скажу так, костыли есть, можно обойтись, но вот именно фикс,
гарантирующий работу group chaining в рамках архитектуры OVS — нетривиален.
В рамках координат моих знаний о его архитектуре, естественно.
Но если честно, то на мой взгляд OVS достаточно громоздок для baremetal
коммутаторов, и унификация под разные реализации datapath для x86
платформ тоже имеет свои минусы.
У него отличное сильное комьюнити, его используют множество проектов, но
конкретно нам надо решать наши сугубо прикладные задачи, и быстро :-(
Текущие реализации OVS и его портов типа Pica8 требуют костылей со стороны
контроллера, что повышает трудозатраты (разработчиков и инженеров) и
прозрачность кода.
>>А что за проблемы с meters и select algs?
meters — там есть pull реквест (годичной давности), когда проверял в феврале
— в develop ветке его не было.
select algs — там для select групп, раньше можно было делать select по dl_dst only.
Сейчас вроде еще есть 5-tuple hash, и он даже работал для tcp, udp flow,
но нет никакого механизма включить его для select группы в dpdk режиме.
Только правками в коде.
Оценив количество костылей, решили делать свой.
Потому что даже в случае выкладывания в открытый доступ ядра, мы останемся
владельцами проекта, в плане решений, что мерджить, а что нет.
Ну и изначально ориентируемся на легкий framework для baremetal с версией для x86
серверов. А не на виртуальный свитч, который портируют на baremetal.xdeller
29.06.2015 17:52> Текущие реализации OVS и его портов типа Pica8 требуют костылей со стороны
контроллера, что повышает трудозатраты (разработчиков и инженеров) и
прозрачность кода.
Гм, там вроде нет особенных костылей, если не говорить именно про пику.
> Ну и изначально ориентируемся на легкий framework для baremetal с версией для x86
серверов.
Тем не менее, фоллбекать процессинг на менеджмент энжин, при отсутствии возможности сделать требуемое матрицей, вы будете или вместо этого ограничите возможности стандарта per-матрица?
BTW выше вы с Игорем имеете в виду разные чейнинги.drwatson32
29.06.2015 19:29>>Гм, там вроде нет особенных костылей
Я имею ввиду костыли, которые приходится писать в сетевых приложениях для контроллера.
Вот пишешь ты допустим обработку mpls. Все правила, которые генерит контроллер
OpenFlow 1.3 spec conforming.
На практике:
OVS — три метки максимум (меняется в коде), push mpls метки на пакет с vlan вызывает drop.
push трех меток в userspace отрабатывал, в kernel нет (пофиксили в upstream).
Pica — если dl_dst не указал и vlan — процессинг на CPU
(а если в разных таблицах actions, то вообще drop) и две метки, Intel FM — одна метка,
так как старый билд OVS.
Ну и подобные мелочи…
>>Тем не менее, фоллбекать процессинг на менеджмент энжин…
Будем фоллбекать, но не совсем так как Pica.
Сейчас просто делаем research на эту тему, нужно подтверждение наших
идей, ждем ответ вендора матриц.
simpleadmin
26.06.2015 16:08Блин, он на немецком =)
Хм, а читается как английский :)
drwatson32, спасибо, весьма занимательное чтиво
k06a
01.07.2015 17:58ЕМНИП netmap легко дает 14.88 Mpps на 10G — info.iet.unipi.it/~luigi/netmap
netmap can forward packets at line rate (14.88 Mpps) at 1.7 GHz without touching data, and slightly slower with full data copies. As a comparison, native packet forwarding using the in-kernel bridge does about 700Kpps on the same hardware. Though the comparison is a bit unfair because our bridge and testpcap don't do address lookups; however we have some real forwarding code (a modified version of openvswitch) that does almost 3Mpps using netmap.
pavelodintsov Автор
01.07.2015 18:00Так и есть — он очень быстр. Но 1) Он на Linux не стандартный и планов по его интеграции нету 2) требует патчинга драйверов
k06a
01.07.2015 18:05Раз уж стоит вопрос заниматься этим профессионально и на пределе возможностей, почему бы не взять FreeBSD? Нужно же подходящие инструменты использовать под задачи. Я про целесообразность погружения статьи. Статья отличная и я проплюсовал как мог, но все же :)
pavelodintsov Автор
01.07.2015 18:08Задача — захват трафика на максимальной скорости, она очень универсальна и требовать от пользователя смены ОС для работы приложения — довольно странно.
AF_PACKET гибче netmap, если про то зашла речь, аналогов ему в FreeBSD нету. Netmap полностью отключает сетевую от стека, что в ряде случае недопустимо и трафик нужно смотреть на живом интерфейсе, подключенном к сетевому стеку ОС.
divert сокеты медленны и подобных AF_PACKET'у скоростей не дадут.
janatem
30.06.2015 17:02А где можно узнать про аппаратный (или программно-аппаратный) захват трафика? Я представляю, например, такую схему: имеется ПЛИС с 10GE- и PCIe-модулем, а прошивка реализует захват ethernet-трафика и его передачу в системную память посредством PCI Exporess. Кажется, такой подход позволит поднять скорость до полной утилизации пропускной способности 10GE.
pavelodintsov Автор
30.06.2015 17:06+1Полной скорости добиться можно без таких сложностей, PF_RING/NETMAP/DPDK дают полные 14.6 MPPS на захват. А вот 40GE — это уже иной мир, там все сложнее :)
YourChief
Очень интересная статья. Вообще возможности Си поражают в смысле производительности. Недавно у меня была задача преобразовать базу maxmind из бинарного формата в список префиксов по странам, получилось что на питоне программа отрабатывала за 14 часов, на pypy за 7-8, на Go за полтора часа, на Си за полторы минуты. Запускал на 16тиядерном сервере.
Возникли вопросы:
Для балансирования обработчиков прерываний от очередей сетевой карты не разумнее ли воспользоваться irqbalance?
Не пробовали ли вы тюнить сетевую карту и настраивать coalescing (
ethtool -C
) чтобы снизить частоту срабатывания обработчика прерывания?neolink
разница между go и c в 60! раз, подсказывает мне что код был не совсем одинаковый
pavelodintsov Автор
Кстати, для Go есть отличная библиотека для захвата пакетов — github.com/google/gopacket
YourChief
на си я пользовался libmaxminddb напрямую и имел возможность не делать многократное преобразование запрашиваемого адреса и запрашивал только нужную информацию вместо полной структуры. тем не менее в Go я разочарован: во-первых, разница не настолько большая по сравнению с питоном, чтобы стоило морочиться со статической типизацией и сравнительно небольшим набором языковых инструментов; во-вторых, сборка программы компилятором Go 1.4.2 вместо 1.2.1 сделало её медленнее еще на 30%. это ужасно
pavelodintsov Автор
Кстати, такой эффект может давать преобразования бинарщины в текстовый вид, вот даже народ умные либы для оптимизации пишет: github.com/h2o/qrintf
YourChief
вообще да. либу я бы не потащил, но на последовательный вывод из нескольких буферов заменил бы
neolink
как-то у меня не вяжется первая часть со второй. вы вроде признаете что постоянно что-то преобразовывали хотя это не нужно было, но потом делаете вывод о производительности.
про 30% это видимо время на GC какойнить map[string] *GeoData
YourChief
geoip2-golang принимает запрашиваемый адрес только в строковом виде, который у меня берётся из счётчика, который просто long. затем сам запрос внутрях libmaxminddb из строкового вида преобразуется в число. это преобразования, которые диктует окружение, увы.
мапов у меня нет нигде, это дефект новой версии golang. вот так вот замечательно развивается язык.
DLag
Так а в чем конкретно дефект?
И не совсем ясно почему не расширили структуру библиотеки для того чтобы она принимала long…
Делается это очень просто.
DLag
1. Используйте maxminddb-golang, вместо непонятной кривой надстройки
2. Посмотрите в вызов Lookup, и увидите там в первых строках ipV4Address := ipAddress.To4()
3. Допишите свою функцию без преобразований
4. profit!
YourChief
Для каждого языка я пользовался тем, что предлагает maxmind на сайте в разделе API.
DLag
Ну не стоит же рассматривать их как последнюю инстанцию в вопросах софта, который они не тестировали…
А на странице пакета, который вы использовали, во втором абзаце написано следующее:
This library is built using the Go maxminddb reader. All data for the database record is decoded using this library. If you only need several fields, you may get superior performance by using maxminddb's Lookup directly with a result struct that only contains the required fields.
Т.е. про конкретно ваш случай и пишут.
YourChief
Многократное преобразование это одно, насчёт maxminddb-golang я согласен
Есть отдельная проблема, что после компилирования той же самой проги новым компилером 1.4.2 (вместо 1.2.1) время работы стало не полтора часа, а два. Только от обновления версии компилятора. При такой пропорции это явный дефект.
DLag
На своем софте я заметил только ускорение с переходом на новую версию.
Сборщик отлично работать начал.
Посмотрите профайлинг, зафайлите баг разработчикам со статистикой.
Там адекватные и отзывчивые люди.
andyN
Посмотрите на Nim — околопитонячий синтаксис плюс производительность Си.
DLag
Нет там производительности Си, и не может быть по определению.
Он транслируется в C, но не более.
andyN
1) Не всегда сгенерированный Си-код будет сильно медленнее написанного руками с нуля кода на Си. Большинство тестов показывают вполне высокую производительность несложного кода на Nim.
2) Не всегда программист имеет квалификацию/время на написание производительного Си-кода руками. В некоторых (скорее всего редких, но все же) случаях, как мне кажется, Nim создаст даже более производительный код, чем низкоквалифицированный программист.
DLag
Слишком много допущений в ваших словах и ниодного пруфа.
Xazzzi
Медленность Go vs C таки не удивительна (сборщик мусора же), а вот разницу C c Rust было бы интересно оценить, ведь последний именно как системный язык позиционируется.
Latobco
Разница неудивительная, ведь Go намного медленнее С. По вашей логике, код на Python должен содержать специальные sleep и тоже не быть таким же, как C!
neolink
в смысле по моей логике? Go и С как-бы оба компилируемые языки и производительность абсолютно одинакового кода (в цикле число сложить) будет одинаковой и я как бы пишу на обоих, Go до 2-3х раз проигрывает C в задачах разбора бинарных потоков данных, например изображений, т.к. в C можно просто указатель двигать, а в Go будут постоянные проверки на границы при обращении, а также не так удобно сказать что у меня в таком то адресе байт, слово, структура и т.п.
Так вот это по сути максимальная разница для примерно одного и того же кода. (для сравнения в отдаче файлика по http у меня получалось ~ 0.7x от производительности nginx, то есть сервер который я написал на 5 минут vs дорабатываемый годами сервер 30% разницы)
Разница в 60! раз это 2 разные программы чуть более чем полностью
Viacheslav01
А можно код ради интереса покопаюсь?
YourChief
Да, пожалуйста:
C
Go
Вывод и мелочи чуть отличаются, так как Си-версия ушла эволюционно дальше. Запускаю для всего пространства IPv4 вот так: