Показаны сообщения с ярлыком boot. Показать все сообщения
Показаны сообщения с ярлыком boot. Показать все сообщения

понедельник, 2 апреля 2012 г.

Проблемы в работе zfsloader во FreeBSD

Сначала небольшой оффтопик - поддержка LDM уже включена во FreeBSD 10-CURRENT и в ближайшие пару недель будет перенесена в STABLE. В отличие от первоначальных патчей, текущая реализация умеет работать с дисками, размеченными GPT. А в остальном всё осталось примерно так же.

Теперь о теме сообщения. Возможно некоторые из вас замечали, что при загрузке FreeBSD c ZFS в качестве корневой файловой системы, наблюдается некоторая задержка между идентификацией дисков и началом загрузки ядра. Эта задержка в ряде случаев (например, большое количество дисков; много разделов; система в гостевой виртуальной машине) может составлять довольно ощутимое время.

Потратив некоторое время на изучение кода в каталоге sys/boot, могу сделать вывод, что работа нашего загрузчика zfsloader далеко не оптимальна. И корни этой проблемы растут с давних времён.

Во-первых, на данный момент из-за "ограниченности vs. универсальности" интерфейса libstand(3) нет никакой возможности определить количество найденных дисков из кода инициализации ZFS. Поэтому ZFS просто последовательно пробует открыть каждый диск поддерживаемый загрузчиком, а это 32 штуки (от "disk0:" до "disk31:").

Во-вторых, опять же, нет никакой возможности из кода инициализации ZFS узнать тип таблицы разделов и количество разделов в ней, чтобы проверить наличие ZFS на этих разделах. Поэтому выполняется простой перебор всех возможных разделов из диапазона от 1 до 128, например, выполняется попытка откыть "disk0p1:", если не удалось, то делается попытка открыть "disk0s1:" и т.д. до 128.

В-третьих, из-за привязанности к интерфейсу libstand(3), код работы с дисковыми устройствами выполняет чтение таблицы разделов, её проверку и т.д. на каждый вызов open(2). Т.е. когда код инициализации ZFS открывает "disk0:", затем открывает каждый его раздел. Все эти действия приводят к повторному чтению таблицы разделов.

Это частично компенсируется наличием уровня блочного кэша (да, даже у загрузчика он есть). Но размер его всего 16 кБайт, поэтому промахи имеют место быть, особенно для случая с GPT, когда таблица разделов занимает от 34 секторов.

В-четвёртых, для случая с GPT не проверяется ни контрольная сумма, ни резервная таблица с заголовком. Т.е. возможен вариант, когда у второй ступени загрузочного кода gptzfsboot и zfsloader будут разные представления о таблицах разделов.

В итоге, я написал вот такой вот патч, который решает часть проблем, но всё же далеко не идеален. Хотя, судя по отзывам увеличивает скорость инициализации загрузчика на порядок.

В идеале же хочется выделить логику работы с таблицами разделов (а он встречается в sys/boot не один и не два раза) в отдельные файлы с необходимым интерфейсом, не зависящим от libstand(3). Но пока работа в этом направлении идёт неспешно.

четверг, 26 мая 2011 г.

Назначение различных образов загрузочного кода во FreeBSD

Со времени появления поддержки таблиц GPT и файловой системы ZFS во FreeBSD содержимое каталога /boot довольно сильно изменилось. Я имею ввиду, сколько различных образов загрузочного кода теперь там находится.

> ls /boot/*boot* /boot/*mbr*
/boot/boot              /boot/boot1             /boot/gptboot           /boot/pmbr
/boot/boot0             /boot/boot2             /boot/gptzfsboot        /boot/pxeboot
/boot/boot0sio          /boot/cdboot            /boot/mbr               /boot/zfsboot
В этой заметке я попробую восполнить недостаток документации и описания этих файлов, рассказав для чего они необходимы.

Итак, по-порядку. BIOS передаёт управление загрузочному коду, хранящемуся в MBR. Его задача - найти на диске загрузочный код следующей ступени, который для таблицы разделов MBR обычно находится в первом секторе активного раздела. Т.е. он должен прочитать таблицу разделов MBR, найти в ней раздел, помеченный активным и передать управление коду, находящемуся в первом секторе этого раздела. Эти задачи выполняет код, копия которого находится в файле /boot/mbr. Более продвинутая его версия - это менеджер загрузки /boot/boot0. Он имеет простейший функционал взаимодействия с пользователем - меню для выбора раздела или диска, с которого выполнять загрузку.

Далее вступает в работу загрузочный код первой ступени /boot/boot1. Он схож по функционалу с тем, что делает /boot/mbr. Разница в том, что он должен считать таблицу разделов BSD и код второй ступени загрузки /boot/boot2. Обычно они используются вместе в виде образа /boot/boot. Т.е. /boot/boot это склееные вместе /boot/boot1 и /boot/boot2.

Вторая ступень загрузки уже более функциональна и может считывать файлы с UFS. Задача этого кода загрузить и передать управление загрузчику /boot/loader (третьей ступени), либо самому ядру /boot/kernel/kernel.

Загрузочный код /boot/zfsboot выполняет те же функции, что и /boot/boot, но для файловой системы ZFS. Он тоже состоит из двух частей. Первая часть аналогично boot1 необходима для поиска и запуска второй ступени, которая уже может прочитать файлы с ZFS и запустить ядро, либо третью ступень /boot/zfsloader.

Теперь что касается GPT. Для таблицы GPT во FreeBSD пока нет подобного /boot/boot0 менеджера загрузки. Но для систем, которые не используют EFI, загрузка с GPT происходит аналогично тому, как это делается для MBR. Т.е. BIOS передаёт управление загрузочному коду, находящемуся в первом секторе диска. А там должна находиться копия /boot/pmbr. Этот код выполняет поиск раздела с типом "freebsd-boot" по таблице GPT. Если он находит его, то загружает его содержимое в память и передаёт туда управление.

Здесь уже могут быть варианты. Первый - /boot/gptboot. Его задача проверить корректность таблиц и заголовков GPT (посчитать и сверить контрольные суммы), а затем найти в таблице раздел с типом "freebsd-ufs", попытаться загрузить с этого раздела /boot/loader или ядро. Если это не получается, он ищет следующий раздел. Кроме того, не так давно pjd@ добавил поддержу специальных GPT-атрибутов bootme и bootonce, с помощью которых можно устанавливать приоритетность загрузки разделов, на случай, если несколько копий/версий FreeBSD находятся на одном диске.

Второй вариант - /boot/gptzfsboot. Этот код представляет собой комбинацию zfsboot  и gptboot. Он выполняет поиск GPT разделов с типом "freebsd-zfs", на которых он ищет пулы. На самом деле логика его работы немного сложнее, так как пулы могут состоять из нескольких устройств. Но цель его - загрузка /boot/zfsloader или ядра с ZFS пула.

В заключение несколько слов о том, где должен располагаться загрузочный код, для того чтобы он выполнил свои задачи. Думаю, не надо упоминать о том, что находясь в каталоге /boot он выполниться никак не сможет, поэтому эти образы должны быть записаны в определённый области на диске. Для записи загрузочного кода на диск обычно используется команда "gpart bootcode", но некоторые образы требуют специальных "манипуляций".

Например, образы mbr, boot0, pmbr должны быть записаны в первый сектор диска. Образ /boot/boot - в начало MBR раздела с типом "freebsd". Образы gptboot и gptzfsboot должны быть записаны на GPT раздел с типом "freebsd-boot". Специальные манипуляции нужны для загрузочного кода zfsboot. Первые 512 байт его должны быть записаны в начало раздела или диска, на котором создан ZFS пул. Оставшаяся часть должна быть записана со смещением в 512 кбайт от начала диска или раздела. Т.е. для случая, когда пул создан на целом диске ada0, загрузочный код записывается вот так:
# dd if=/boot/zfsboot of=/dev/ada0 count=1
# dd if=/boot/zfsboot of=/dev/ada0 skip=1 seek=1024