воскресенье, 30 сентября 2012 г.

loader(8): оптимизация и результаты.

Наконец-то я нашёл время и ресурсы на проведение оптимизации в загрузчике системы. Организовал стенд на базе VMWare ESXi, в которой я создал несколько виртуалок с FreeBSD 10, установленных с использованием различных комбинаций таблиц разделов и файловых систем. Расшарил по NFS рабочий каталог с исходниками системы со своей машины, подключил его к виртуалкам и начал отладку... Ну как "отладку", добавил несколько printf'ов в libstand и в драйвер диска biosdisk; включил отладку в недавно добавленном модуле с DISK API; в виртуалках создал файл /boot.config с опциями "-D -S115200", в VMWare настроил запись вывода с последовательного порта в файл и начал тесты.

Получив логи, начал смотреть код и искать способы уменьшения количества дисковых операций, которых оказалось великое множество. В итоге, в модуль с DISK API был добавлен код кеширования обращений к дискам. А нужно сказать, что почти каждое открытие файла с использованием libstand приводит к чтению метаданных таблиц разделов. Чтобы уменьшить это число обращений раньше использовался bcache - блочный кэш. Но, printf'ы внутри дискового драйвера показали, что его эффективность довольно низка, а при использовании GPT - совсем удручает.

Итак, новый кэш работает по другому. При открытии файла, запрос open() приводит к вызову функции devopen(), которая и "открывает" текущее устройство (обычно диск, с которого происходит загрузка), например, disk1p3. Открытие диска - это чтение его метаданных, поиск таблиц разделов и определение смещения, с которого начинается открываемый раздел. Каждое успешное открытие добавляет в кеш запись. Таким образом, при открытии следующего файла на этом диске, уже не нужно будет читать метаданные, а смещение будет прочитано из кэша. В результате, если раньше при загрузке с UFS чтение метаданных выполнялось примерно 50 раз (с учётом попаданий в блочный кеш - около 30), то сейчас это происходит 1 раз.

Далее меня заинтересовало почему при открытии, например, ядра или его модулей, в логе загрузки эта операция отображается несколько раз (от трёх до четырёх)? Т.е. конечно чтение всего ядра не происходит четыре раза, но каждое открытие сопровождается чтением некоторого объёма данных, и только последнее открытие читает заметно больший объём данных. Как оказалось, для архитектуры x86/x64 loader одновременно поддерживает несколько форматов ELF - 32-битные и 64-битные. Причём в массиве, где перечислены указатели на обработчики этих форматов первыми стоят 32-битные, а затем уже 64-битные. Поэтому loader при загрузке модулей ядра в цикле "пробует" модуль каждым обработчиком, пока не найдёт подходящий. И так как у меня тестовые машины 64-битные, то обработчик ядра стоит  3-им в этом массиве, а обработчик модулей ядра - 4-ым.

В общем, эта досадная несправедливость в отношении 64-битных систем была мной ликвидирована тоже. В итоге, я сравнил результаты по затратам времени загрузки с момента старта loader'а и до момента запуска ядра в разных конфигурациях системы:

UFS+GPTZFS+GPTZFS+GPT+несколько дисков
Старый loader7,203 сек20,584 сек26,079 сек
Обновлённый loader4,334 сек9,422 сек11,245 сек

Во всех случаях, кроме самого ядра загружалось ещё несколько модулей.

суббота, 1 сентября 2012 г.

FreeBSD mini-summit в Москве

25-го августа в Москве состоялся уже второй саммит разработчиков FreeBSD. Как и в прошлый раз он проходил в офисе компании Рамблер, правда сейчас это уже был новый офис :)
Формат встречи был прежним - доклады, вопросы, затем обсуждения. Так как народу в этот раз было поменьше, а места было побольше, то обсуждения получились интересными и продуктивными.

Первый доклад сделал Максим Евменкин из компании Netflix. Честно говоря, я об этой компании раньше слышал немного, а оказывается заграницей она довольно известна. Компания предлагает решения по доставке медиаконтента, причём организованы эти решения на основе FreeBSD. Сборка FreeBSD 9-STABLE, из которой убрано всё лишнее, установлена на зеркало из двух SSD внутри "чёрного ящика", в который дополнительно установлено ещё несколько десятков 3-4 ТБ жестких дисков. Неожиданным для меня стало то, что на этих дисках используется UFSJ. Как рассказал Максим, почти единственное, чем система отличается от обычной FreeBSD - несколько патчей к драйверам сетевых карт Intel, все остальные изменения и исправления они стараются возвращать в базовую систему. А патчи для карт Intel являются довольно специфичными и наталкиваются на непонимание/нежелание майнтейнера драйвера. 
Что ещё хотелось бы упомянуть - это используемое ими решение для автоматического обновления системы. Принцип такой: на SSD носителях используется GPT и два зеркалируемых раздела. Одно зеркало - это текущая рабочая система, второе - место, куда записывается новая "firmware". Перед перезагрузкой на разделы, куда записалась прошивка устанавливается FreeBSD-specific GPT атрибут bootonce, который говорит загрузчику gptboot пробовать загружаться сначала с указанного раздела. Если загрузка не удалась, либо произошла паника и т.п., то при следующей загрузке система будет загружаться снова с первого зеркала, которое является рабочим. Подробнее об этом атрибуте можно прочитать в странице gpart(8).

Глеб Смирнов подготовил доклад о работе, проделанной им над пакетным фильтром pf в отдельном бранче projects/pf. Теперь pf стал значительно эффективнее работать на SMP системах, что, например, можно увидеть на графике загрузки тестовой машины.

Константин Белоусов рассказал о причинах возникновения дэдлока между подсистемами VM и VFS, какие методы были опробованы им для решения этой проблемы, и как в итоге  проблема была решена.