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

10 комментариев:

  1. Я конечно наглею, но может к нему как то можно прикрутить загрузку и с GPT/UFS ну и совсем уж мечты об EUFI :)

    ОтветитьУдалить
  2. Шай-бу! Шай-бу! :)

    судя по этому тексту улучшение загрузчика очень правильная работа

    ОтветитьУдалить
  3. 2 Анонимный
    не то слово, в настоящее время имеем целую пачку загрузчиков, каждый из которых как то и при каких то условиях работает, но это как то не очень удобно, а порой до неприличия криво.

    ОтветитьУдалить
  4. Нужно различать загрузочный код, который помещается в специальные области диска и загрузчик (loader или zfsloader). Задача последнего предоставить интерактив, если нужно, и загрузить ядро с файловой системы. И поддержка GPT/UFS у zfsloader имеется уже, вернее zfsloader это тот же loader, только он ещё умеет находить zfs пулы.

    ОтветитьУдалить
  5. По поводу UEFI, кстати, во freebsd-hackers кто-то хотел начать писать код в рамках GSoC 2012

    ОтветитьУдалить
  6. прошу прощение за возможный оффтоп
    а так должно быть?

    # gpart show -r
    => 34 234492989 ada0 GPT (111G)
    34 134 1 83bd6b9d-7f41-11dc-be0b-001560b84f0f (67k)
    168 41943040 2 516e7cba-6ecf-11d6-8ff8-00022d09712b (20G)
    41943208 41943040 3 516e7cba-6ecf-11d6-8ff8-00022d09712b (20G)
    83886248 41943040 4 516e7cba-6ecf-11d6-8ff8-00022d09712b (20G)
    125829288 108663735 - free - (51G)

    => 34 3907029101 ada1 GPT (1.8T)
    34 6 - free - (3.0k)
    40 88 1 83bd6b9d-7f41-11dc-be0b-001560b84f0f (44k)
    128 209715200 2 516e7cb6-6ecf-11d6-8ff8-00022d09712b (100G)
    209715328 3697313800 3 516e7cba-6ecf-11d6-8ff8-00022d09712b (1.7T)
    3907029128 7 - free - (3.5k)

    => 34 3907029101 ada2 GPT (1.8T)
    34 6 - free - (3.0k)
    40 88 1 83bd6b9d-7f41-11dc-be0b-001560b84f0f (44k)
    128 209715200 2 516e7cb6-6ecf-11d6-8ff8-00022d09712b (100G)
    209715328 3697313800 3 516e7cba-6ecf-11d6-8ff8-00022d09712b (1.7T)
    3907029128 7 - free - (3.5k)

    одинаковые номера?

    ОтветитьУдалить
  7. Это GUID'ы типов партиций, они и должны быть одинаковыми, если это одни и те же типы.
    83bd6b9d-7f41-11dc-be0b-001560b84f0f = freebsd-boot
    516e7cba-6ecf-11d6-8ff8-00022d09712b = freebsd-zfs
    516e7cb6-6ecf-11d6-8ff8-00022d09712b = freebsd-ufs

    ОтветитьУдалить
    Ответы
    1. Скорее всего, метка никуда не девается. Это uid'ы GPT, они доступны в /dev/gptid/.
      Проблема тут в том, что у всех этих провайдеров, например, ada2p2, gpt/label и gptid/uid одинаковый размер, и ZFS берёт первую попавшуюся. Сейчас нет возможность сказать ZFS использовать какую-то одну из них. Если вам не нравяся gptuid'ы, их можно отключить через kern.geom.label.gptid.enable="0" в loader.conf.

      Удалить
    2. Я вас замучу )))

      >sysctl kern.geom.label
      kern.geom.label.debug: 0
      kern.geom.label.ext2fs.enable: 1
      kern.geom.label.iso9660.enable: 1
      kern.geom.label.msdosfs.enable: 1
      kern.geom.label.ntfs.enable: 1
      kern.geom.label.reiserfs.enable: 1
      kern.geom.label.ufs.enable: 1
      kern.geom.label.ufsid.enable: 1
      kern.geom.label.gptid.enable: 0
      kern.geom.label.gpt.enable: 1

      >gpart show -l
      => 34 3907029101 ada0 GPT (1.8T)
      34 6 - free - (3.0k)
      40 88 1 (null) (44k)
      128 209715200 2 (null) (100G)
      209715328 3697313800 3 beagle0 (1.7T)
      3907029128 7 - free - (3.5k)

      а в /dev/ нет gpt/ вообще

      Удалить
    3. Так работает GEOM_LABEL, я уже неоднократно писал почему так происходит, так что предлагаю погуглить :)

      Удалить