[make-initrd] [devel] syslinux

Alexey Gladkov legion at altlinux.ru
Mon Apr 22 03:04:50 MSK 2019


On Sun, Apr 21, 2019 at 05:44:14PM +0200, Michael A. Kangin wrote:
> On 04/21/2019 03:43 PM, Alexey Gladkov wrote:
> 
> >> А есть еще какой-то "payload" для тестирования сетевой загрузки?
> > 
> > Не очень понял вопроса.
> 
> Какая-нибудь фича, которая бы пользовалась сетью для обретения корневой 
> FS. Ну, чтобы система полностью могла загрузиться.
> 
> nfsroot не работает, CLB на новую версию не спортировано, netboot 
> (который выкачивал tgz и распаковывал в tmpfs) тоже не спортирован.
> А больше мне ничего на ум не приходит.

Никакие другие мне тоже не известны. Но раз вы подняли эту проблему, то,
возможно, мы сейчас сделаем такую фичу и починим nfsroot.

> > Попробую сделать это в ближайшее время. Буду признателен, если
> > заинтересованные в таком варианте протестируют перед релизом.
> 
> Разумеется.
> Думаю, было бы неплохо так же рассматривать NFS как транспорт для других 
> фич, по давним замерам производительности смонтировать squashfs по NFS 

Интересная мысль. Она хорошо согласуется с необходимостью монтирования
cd-диска, который тоже является промежуточной стадией. По сути нет разницы
между разными промежуточными стадиями. Правда пока этот механизм я не
придумал.

> может оказаться заметно быстрее, чем чистый корень:
> 
> https://lists.altlinux.org/pipermail/ltsp-server/2012-August/002532.html

Интересно.

> >> Еще большой вопрос - как написать фичу? Есть какая-то образцовая
> >> работающая фича? Или документация, с бест-практиками, примерами...
> > 
> > Каждая фича приносит какой-то новый функционал. У меня нет документации
> > для этого. Бест-практики появляются, когда достаточное количество людей
> > занимаются их написанием. В моём случае это не так.
> 
> Боюсь, тут может быть некоторый замкнутый круг.

Конечно есть.

> Чтобы появилось хоть некоторое количество таких людей, у них должно быть 
> понимание, как это делать. Или хотя бы с чего начать.

Дело в том, что для самого себя писать документацию сложно. А заставить
себя в такой ситуации ещё сложнее. Также вы же понимаете что когда один
разрабатываешь что-то, то многие вещи кажутся самоочевидными.

Я надеюсь, что благодаря вам и Леониду, вашим вопросам я смогу заполнить
пробелы.

> В старой версии M-I я попытался взять за основу упомянутый netboot и 
> написать свою фичу по его мотивам. Результат, конечно, так себе, и далёк 
> от любых бест-практик, но оно хотя бы заработало. И было некое 
> понимание, что скрипт в post/udev/ выполнится после работы udev'а, и в 
> рамках этого скрипта можно реализовать всю необходимую логику.

Сейчас добавление дополнительной функциональности стало ещё проще. Весь
рантайм выглядит как обычная система с сервисами. Встроиться можно куда
угодно.

> Сейчас, с новой версией M-I, нет ни понимания, ни документации, ни 
> работающего референсного примера.

Для каждой фичи встраиваться нужно в разные места. Если вы хотите
обработать новый тип загрузки (тот же nfs), то нужно встраиваться нужно в
одно место. Если же вам нужно загрузить консольный шрифт и раскладку, то в
другое. Всё это сильно зависит.

> Я смутно догадываюсь, что сейчас фичу нужно оформлять какими-то 
> udev-коллбеками, фильтрами и хуками, но абсолютно не представляю, с чего 
> начать.

udev-коллбэки это лишь часть эвентов. Они позволяют реагировать на
загрузку модулей. Сейчас через тот же механизм можно реагировать на этапы
конфигурации сети.

> Поэтому буду рад любым советам и намёкам (варианты for dummies, in a 
> nutshell вообще бесценны для снижения порога вхождения и сглаживания 
> лёрнинг курвы :)

Ок )) Я постараюсь описать некоторые моменты.

> >> Я думал попробовать взять за основу nfsroot, но, похоже, он вообще не
> >> "запускается". По крайней мере ни одного упоминания в /var/log нет, в
> >> dmesg только о загруженном модуле nfs.
> > 
> > Безотносительно работает nfsmount или нет эта фича хорошо показывает как
> > добавляются новые варианты загрузки.
> 
> Меня только смущает, что эта фича даже не пробует начать работать в boot 
> runtime, по крайней мере, я не никакого признака не заметил - в логах 
> пусто, в /.initrd/ ничего не грепается и не ищется.

Ни md_run, ни nfsroot ничего не пишут в процессе работы. Это неприятно.
Поэтому я двигаюсь в сторону отказа от них.

> rules.mk, config.mk - тут в принципе понятно. Но я наткнулся на странный 
> результат с модулем nfs. В первый раз, когда я делал образ initrd с этой 
> фичей, в него попал модуль nfs, согласно
> NFS_PRELOAD	= af_packet nfs
> ...
> MODULES_PRELOAD	+= $(NFS_PRELOAD)
> 
> Однако, после нескольких попыток разобраться, почему она не работает, и 

nfsroot работало. Возможно, какого-то модуля ядра не хватает.

> в частности после попытки применения рецепта с вики DISABLE_GUESS = root 
> (https://www.altlinux.org/Make-initrd#nfsroot)

Просто для справки, я стараюсь вынести документацию с этой вики на github.

> этот модуль попадать в образ initrd перестал, даже с явным указанием
> MODULES_ADD += nfs в /etc/initrd.mk.

Пожалуйста сделайте пример конфига, где это можно воспроизвести. Это очень
странно.

> Получается, сборка образа initrd не совсем воспроизводима, и от раза к 
> разу может давать разные результаты?

Сборка образа отталкивается от того как был сконфигурён корень. Тут всё
довольно просто.

> Может, есть где сказать нечто вроде "make clean"? Я впервые сталкиваюсь
> с таким поведением, и даже не знаю, куда посмотреть.

Одна попытка сборки с другой никак не связана и не пересекается.

> /etc/initrd/cmdline.d/nfsroot - тут, как я полагаю, нужно 
> зарегистрировать все-все boot parameters, которыми фича собирается 
> пользоваться.

Верно.

Выяснилось, что некоторые пользователи жалуются на ошибки, когда указывают
rootdelay=five.

Из этого вылилась система валидации параметров cmdline.

> Чем отличается register_parameter от register_array (и, судя по 
> исходникам, есть еще register_alias), в каких случаях пользоваться той 
> или иной функцией, какой синтаксис для _array - указывать несколько раз 
> один и тот же параметр, или внутри одного параметра что-то перечислить 
> через запятую? Разглядывание лаконичного кода этих функций в исходниках 
> просветления, увы, не принесло...

Вот как это как раз пример того, что мне казалось очевидным, а
оказывается, что это не так.

Семантика у этих функций такая:

register_parameter <type> <varname> [<default>]

Функция регистрирует параметр <varname> и типом <type>. Также можно
указать значение по умолчанию.

register_array <type> <varname>

Позволяет создать "массив" с определённым типом. По правилам /proc/cmdline
параметр с тем же именем, находящийся правее переписывает предыдущие. В
некоторых случаях хочется иметь возможность получить их все как массив
значений. Пример такого параметра IP. Каждое последующее его указание не
отменяет предыдущий, а конфигурирует другой интерфейс.

register_alias <varname> <another-name> [<another-name> ...]

Эта функция создана для указания более одного имени для одно и того же
параметра. Это полезно если возникает необходимость переименовать
параметр или сделать слой совместимости, например, с dracut по некоторым
параметрам.

type может быть  string, number, bool.

varname это не оригинальное имя, которое может быть указано в
/proc/cmdline, а имя после обработки. Дело в том, что в /proc/cmdline
параметры совсем не shell-like. Они могут содержать дефисы или точки.
Поэтому make-initrd приводит имена параметров к uppercase и заменяет
дефисы и точки на подчёркивания. В такой форме они и регистрируются.

> Как дальше пользоваться этими параметрами? делать в каждом скрипте 
> инклюд . /.initrd/initenv, и ссылаться на переменную параметра, как уже 
> существующую?

Сервис data/etc/rc.d/init.d/cmdline отвечает за разбор cmdline параметров.
Он же создаёт /.initrd/initenv. Это необходимо в том числе и из-за того,
что некоторые параметры используются более чем в одном месте и имеют
значения по умолчанию. Производить анализ cmdline каждый раз, где нужен
параметр очень неудобно по понятным причинам. Поэтому анализ производится
лишь один раз и результат записывается в /.initrd/initenv, который
импортируют все остальные скрипты.

> В cmdline писать параметр маленькими буковками, а 
> переменную ожидать, что она существует с именем заглавными буквами?

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

> /etc/udev/rules.d/99-nfsroot.rules - так, тут какое-то правило udev.
> SUBSYSTEM=="net", ACTION=="online", RUN+="/lib/uevent/filters/nfsroot"
> т.е. когда сеть будет в онлайне, начнёт действовать фильтр событий 
> /lib/uevent/filters/nfsroot, если я правильно трактую.

Да. Вы всё правильно поняли.

> Наверное, для всех сетевых фич это будет более-менее однотипно.
> "Пользовательским фичам" рекомендуется использовать номер 99-?

Пока полиси на эту тему у меня нет. У вас есть идеи ?
 
> /lib/uevent/filters/nfsroot - этот файл уже значительно менее понятен.
> [ -n "$NFSROOT" ] || [ "$ROOT" != '/dev/nfs' ] ||
> 	NFSROOT='auto'
> 
> [ -n "$NFSROOT" ] ||
> 	exit 0
> 
> exit 0 нужно использовать, когда мы недовольны качеством предоставленных 
> параметров, еще чем-нибудь, и не хотим оказывать услугу?

Как вы уже правильно заметили этот скрипт выполняется udev'ом. Не нужно
завершаться с ненулевым кодом возврата, если не было ошибки. Если
написать:

[ -n "$NFSROOT" ] || exit

то exit без аргумента будет использовать код возврата от предыдущей
операции, а это 1 так как переменная NFSROOT пуста.

> event="$(make_event)"
> showenv -q > "$event"
> release_event nfsroot "$event"
> 
> Эти заклинания использовать, как_есть?

Заклинания не нужно выполнять никогда. Лучше знать, что происходит. Я
попробую коротко рассказать что это и почему оно так.

udev не может выполнять долгие задания. В udev(1) для RUN написано:

This can only be used for very short-running foreground tasks. Running an
event process for a long period of time may block all further events for
this or a dependent device.

И так было с самого начала. Поэтому обработка событий разделена на две
части и скрипты (filters) из udev ничего сами не делают. Они лишь
подготавливают условия для реальных обработчиков (handlers). Фильтры
выполняются udev'ом параллельно и формируют очередь, которая разбирается
демоном ueventd (User EVENT Daemon). ueventd делает это последовательно
так как порядок выполнения handlers важен.

Теперь зная это возвращаемся к этим трём строчкам. Эти функции описаны в 
data/bin/uevent-sh-functions. 

Функция make_event создаёт временный файл для эвента и печатает его имя.
Далее showenv печатает в этот файл текущие переменные окружения. Это
делается из-за того, что udev передаёт параметры устройства через
окружение. Таким образом handler получит то же самое окружение, что и
filter. Ну и наконец сформированный эвент передаётся на обработку ueventd
функцией release_event.

> Я посмотрел доступные файлы в /lib/uevent/filters, но никакой системы 
> использования понять не сумел, все они достаточно разношерстные.

Они подготавливают окружение для разных handler'ов, да, они все разные.

> /lib/uevent/handlers/040-nfsroot - это уже похоже на скрипт, в котором 
> делается реальная работа.

Это handler.

> Всю логику необходимо помещать внутрь функции handler() как я понимаю.

Да. Но нужно помнить, что пока этот скрипт не завершит работу следующий не
будет запущен.

> for e in "$eventdir"/nfsroot.*; do
>          [ -f "$e" ] || break
>          ( . "$e"; handler; ) ||
>                  rc=1
>          done_event "$e"
> done
> Эти заклинания будут неизменны?

Эти "заклинания" будут зависеть от логики обработчика.

> Во всех просмотренных хандлерах ссылка на эвенты выглядит как
> "$eventdir"/nfsroot.*, "$eventdir/network.$ev_type", 
> "$eventdir"/mountdev.*, etc.
> Однако, внутри этой директории /.initrd/uevent-events находятся файлы вида:
> done.network.addr.update.29.84.XXXNRubtr
> done.network.addr.update.29.85.XXXHB5F1u
> done.network.config.update.13.56.XXXPsND17
> done.network.config.update.13.59.XXXJrrdH9
> done.network.hostname.update.29.84.XXXwup0dr
> 
> т.е. они не подпадают под ожидаемые маски. Это нормально?

Это выполненные задания (см. uevent-sh-functions). Так как initrd
удаляется быстро, то нет смысла их удалять. Но для отладки они очень важны.

> В некоторых фичах оформляется еще служба init.d, однако, в nfsroot 
> например такой службы нет. В каких случаях делать, в каких не надо?

Всё также как и в обычной системе. Если вам нужна отдельная служба, то
нужно делать сервис, если нет, то не нужно.

> Если есть init.d/service, как можно в рантайме сделать на него ссылки в 
> rcN.d (есть ли местный аналог chkconfig)?

В initrd используется lsb заголовки для сервисов. Сервисы могут
предоставлять как реальное имя так и виртуальное имя. Это очень старый
механизм и о нём я много рассказывать не буду. Подробнее здесь [1][2].

Ссылки создаются в процессе создания обарза утилитой sort-lsb.

> Если в рантайме нужно делать какие-то мелкие действия, обязательно ли 
> оформлять для этого init.d/сервис, или есть какой-то аналог rc.local? 

Не понял вопроса.

> Работают ли еще старые механизмы pre/ post/ скриптов?

Нет. Раньше были стадии перед которыми можно было вклиниться, но с v2
функционально их не стало и даже для обратной совместимости неоткуда их
вызвать.

Я очень долго просил откликнуться тех у кого есть фичи, чтобы помочь в
адаптации. Всем кто откликнулся я помог.

> Замечания, появляющиеся в ходе тестирования сети, предпочтительней 
> оформлять багами или для начала лучше писать сюда (для обсуждения и 
> понимания, баг это или фича)?

На релиз в сизифе баги стоит вешать в багзиллу. До релиза пишите в личку
или на github.

[1] https://github.com/legionus/make-initrd/blob/master/docs/InitramfsServices.md
[2] https://github.com/legionus/make-initrd/blob/master/docs/BootRuntime.md

P.S. Уф. У вас длинное письмо. Я постарался ответить на всё. Извините,
если получилось слишком много.

P.P.S. Если вы найдёте время помочь мне с документацией того, что мы тут
обсуждаем, то вы мне очень поможете сделать всё понятнее.

-- 
Rgrds, legion



More information about the Make-initrd mailing list