понедельник, 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). Но пока работа в этом направлении идёт неспешно.