dadv: (Default)
[personal profile] dadv

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

Абонентская база неуклонно растёт, место в стойках и мощности упсов не резиновые, а процессор E5507 - младшенький в линейке. Кроме того, top(1) врёт про потребление процессора dummynet'ом, показывая незначительные проценты. В то же время график, построенный по выдаче команды (выдаёт потребление CPU ядерным тредом dummynet в сотых долях секунды, монотоно возрастающий счетчик):

ps -Hxo time,lwp | awk -vT=$(procstat -t 0 | awk '/dummynet/ {print $2}')\
' $2 == T { split($1, a, /[:.]/); print a[1]*6000+a[2]*100+a[3]; }'

показывает, что dummynet грузит CPU до 25% в моих условиях, а вовсе не на единицы процентов. Поэтому стал заменять 4-ядерный E5507 на 6-ядерный X5675 - максимум, что можно воткнуть в тот же сервер, уложившись в лимит тепловыделения в 100 ватт.

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

Дело в том, что ядерная часть ipfw использует rwlocks для корректной мультитредовой работы, или, по-русски, модель "читатели-писатели": если какой-то "писатель" (writer) захватил блокировку, то другие "писатели", а также "читатели" (readers) при попытке захватить эту же блокировку, будут ждать; если же блокировку захватил "читатель", то другие "читатели" тоже могут получить блокировку, а "писатели" будут ждать разблокировки всех "читателей".

Небольшое отступление: исполняющийся на собранной в виде NanoBSD системе процесс syslogd пишет логи на локальный "диск" в оперативной памяти плюс шлет их по сети на центральный коллектор логов, который принимает их со всех таких "бездисковых" машин и пишет себе в архив. Посылает логи syslogd стандартным API для отправки UDP-дейтаграмм, функцией sendto(), которая является системным вызовом, приводящим к цепочке вложенных вызовов ядерных функций:

sendto > sendit > kern_sendit > sosend_dgram > udp_send > ip_output > pfil_run_hooks > ipfw_check_hook > ipfw_chk > ipfw_lookup_table > rn_match > ...

Как видно из этой цепочки, в какой-то момент отправка UDP-пакета приводит к проверке его правилами ipfw, которые выполняются с захваченным ipfw reader lock.
Очень редко происходит следующее: планировщик прекращает выполнение syslogd и работающего в его контексте ядерного кода на функции rn_match() из-за исчерпания кванта времени и даёт поработать другим процессам/ядерным тредам. Те из них, которые работают с сетью и нуждаются в ipfw writer-lock, оказываются заблокированными до того момента, пока syslogd/его ядерный тред не получат свой квант процессорного времени, чтобы завершить свою работу и отпустить блокировку.

И тут на сцену, весь в белом, выходит драйвер ipmi(4), который выставил себе приоритет реального времени. Это значит, что пока он исполняется на процессорном ядре и добровольно не отдаёт управление другим частям системы (считает, что у него всё ещё есть работа), планировщик не будет отбирать у него процессорное ядро, несмотря ни на какие кванты. Если в системе есть процессы/ядерные треды, жестко привязанные к тому же процессорному ядру, на котором изволит работать ipmi(4), они будут ждать, пока он не отдаст ядро. Выполняющиеся на этом же ядре, но жестко к нему не привязанные процесссы/треды при некоторых условиях могут быть перемещены планировщиком на другие процессорные ядра и продолжить работу там.

В моём случае уже дважды случалось следующее: syslogd прерван в указанном выше месте и что-то случилось в драйвере ipmi(4), который начинает крутить внутри себя вечный цикл. По какой-то причине syslogd, который жестко не привязан ни к какому ядру, тем не менее не получает своего кванта процессора в течение более чем получаса реального времени. В течение этого получаса практически все сетевые процессы замирают, ожидая разблокировки ipfw. Процессы, которые не обращаются к сети, продолжают работать.

Так как в FreeBSD 8.3-STABLE драйвер сетевых карт igb(4) по умолчанию привязывает свои ядерные треды к процессорным ядрам и делает это очень криво, в моих роутерах эти треды перепривязаны по CPU одним из стартовых скриптов: к CPU4 привязана обработка трафика, приходящего через интерфейс igb0, к CPU5 - igb1, к CPU2 - em0, к CPU3 - em1. dummynet "по старой памяти" привязан к CPU0. Все остальные процессы/ядерные треды ни к чему не привязаны и могут перемещаться планировщиком по свободным ядрам, но, по факту, уже и этого оказалось достаточно, чтобы всё стало колом и требовалась аварийная перезагрузка системы для продолжения работы.

Сам драйвер ipmi(4) используется для общения с платой IPMI внутри железки - например, снимать данные с датчиков. Пришлось в качестве временного выхода выгрузить драйвер из роутеров, а скрипты, снимающие данные с датчиков при помощи ipmitool переделать на работу с платой через IP, а не через локальный драйвер.

Больше технических подробностей в PR: kern/172166.

This account has disabled anonymous posting.
(will be screened if not validated)
If you don't have an account you can create one now.
HTML doesn't work in the subject.
More info about formatting

If you are unable to use this captcha for any reason, please contact us by email at support@dreamwidth.org

Profile

dadv: (Default)
Choose your future

July 2024

M T W T F S S
12 34567
891011121314
15161718192021
22232425262728
293031    

Tags

Style Credit

Powered by Dreamwidth Studios