понедельник, 22 марта 2010 г.

gpart: изменение размера раздела

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

Немного о цели патчей. Были у меня несколько машин, на которые я уже давно для тестов ставил FreeBSD с ZFS. Они полностью установлены на ZFS, но было оставлено свободное пространство "на всякий случай". Там используется GPT "схема" со "стандартной" разбивкой на три партиции: freebsd-boot, freebsd-swap и freebsd-zfs. Свободное место было оставлено в конце диска. И вот, в один прекрасный момент захотелось использовать свободное место. Но создавать отдельную партицию не хотелось после успешных экспериментов по расширению пула ZFS.

После постановки задачи "как это сделать" очевидных решений найдено не было. Первое, что пришло на ум - поправить в таблице GPT размер партиции. Решил попробовать, подампил таблицу в файлы, поизучал формат... Решил посмотреть, что там в коде реализации. Оказалось, что "заготовка" базового метода resize уже имелась в коде класса. Только она ничего не делала, а возвращала ошибку ENOSYS.

Я связался с автором класса PART - Marcel Moolenaar. Обсудил с ним что и как лучше реализовать. После чего взялся за эксперименты. Что в итоге получилось, можно увидеть здесь. Ход работы можно проследить в perforce.

Если в кратце, то в начале пришлось заново вспомнить изучить для чего в дереве кода ядра используются файлы с расширением "*.m". Они применяются для создания подобия ООП в коде ядра. Как я уже упоминал ранее, структура класса PART интересна своей модульностью. Сам класс имеет базовые методы работы с партициями, а все детали реализации описываются в отдельных модулях схем. Так вот, в .m файлах описан программный "интерфейс" подобных модулей. Эти файлы обрабатываются awk скриптом во время компиляции ядра и генерируются файлы .c и .h.

Грубо говоря, благодаря им и создаётся иллюзия ООП. Хотя на мой взгляд всё довольно условно :) Итак, реализовав базовый алгоритм изменения размера в ядре и добавив новую команду в утилиту управления gpart, я добавил поддержку в GPT схему. Потестировал на md(4) дисках - вроде работает и даже не паникует :)
Решил попробовать на рабочей системе, попробовал - размер изменился, перезагрузился - пул "подхватил" свободное пространство и я счастлив :)

После этого добавил поддержку в оставшиеся схемы, немного потестировал на том же md(4) и вроде всё. Да, только в EBR не стал добавлять, там как-то не всё гладко в реализации.

Пример использования:
# gpart show md0
=>    34  409533  md0  GPT  (200M)
      34  204800    1  freebsd-zfs  (100M)
  204834  204733       - free -  (100M)

# gpart resize -s 150m -i 1 md0
md0p1 resized
# gpart show md0
=>    34  409533  md0  GPT  (200M)
      34  307200    1  freebsd-zfs  (150M)
  307234  102333       - free -  (50M)

# gpart resize -i 1 md0
md0p1 resized
# gpart show md0
=>    34  409533  md0  GPT  (200M)
      34  409533    1  freebsd-zfs  (200M)

У команды resize есть два параметра "-i index" и "-s size". Указание индекса обязательно, а вот размер может быть вычислен автоматически. Изменяться может только смещение конца партиции, смещение начала всегда остаётся неизменным.

И ещё важное замечание - изменяются только метаданные таблицы разделов, изменение размеров файловой системы нужно производить сторонними методами, например growfs для UFS. ZFS сама видит увеличение. Уменьшение разделов не запрещено и работает, но что на это скажет файловая система раздела - мне, честно говоря, не очень хочется узнавать :)

четверг, 18 марта 2010 г.

Использование gpart

Утилита gpart служит для управления GEOM классом PART. На самом деле это хардлинк на утилиту geom(8), которая является универсальной утилитой управления для многих GEOM классов.

Как я уже упоминал в предыдущей заметке, класс PART служит для работы с таблицами партиций и предоставляет доступ к провайдерам этих партиций. С появлением этой утилиты можно уже забыть такие утилиты как fdisk и bsdlabel, они морально устарели. Большинство их действий можно выполнять при помощи утилиты gpart. Нужно только понять и запомнить основные понятия - схема и партиция. Прежде чем создавать партиции, необходимо выбрать тип таблицы - схему. Например, для привычной разметки с использованием MBR необходимо выполнить команду 'gpart create' с указанием имени схемы в опции "-s mbr":
# gpart create -f x -s mbr md0
md0 created
# gpart show md0
=>     17  1023978  md0  MBR  (500M)
       17  1023978       - free -  (500M)

# gpart commit md0
Опция "-f x" указывает классу не вносить изменения на провайдер носителя сразу. Т.е. если вы не уверены в своих действиях, вы можете с каждой командой указывать эту опцию и после того как будете уверены, выполнить команду `gpart commit`, либо отменить изменения командой `gpart undo`. Не имеет значения, какой флаг вы укажете после опции "-f", т. к. класс отслеживает только наличие опции и её отличие от значения, принятого по-умолчанию. Если не указывать опцию "-f", то её значение по-умолчанию будет установлено в "-f C", что приведёт к автоматическому выполнению команды `gpart commit`.

Таблица MBR может содержать максимум 4 записи, т.е. не более 4 партиций. После того как схема создана можно создавать партиции при помощи команды `gpart add`. Каждая партиция обладает несколькими обязательными свойствами - смещение начала, смещение конца, индекс в таблице и тип партиции. А так же одно дополнительное свойство, которым обладают партиции некоторых схем - символьная метка.  Все эти параметры видны пользователю, за исключением смещения конца. Смещение заменяется на вычисляемый из него размер.
К тому же, для создания партиции при помощи утилиты gpart нужен только один обязательный параметр - это тип партиции, остальные параметры могут быть вычеслены автоматически.

Тип партиции может быть задан символьным именем либо специальным номером, в зависимости от используемой схемы. Поддерживаемые символьные имена можно увидеть в мануале gpart(8). При необходимости задать какой-то специфический для схемы тип партиции, он указывается после знака восклицания. Например, для схемы MBR тип партиции FreeBSD представлен номером 165, который можно указать как "-t !165" (не забываем экранировать знак восклицания, если выполняем команду из shell - "\!165"), либо символьным именем "-t freebsd".

Размер и смещение начала партиции по-умолчанию задаются в блоках, размер которых обычно соответствует размеру физического сектора, т.е. 512 байт. Для того чтобы не вычислять количество блоков, эти параметры можно указывать с суффиксом размера: b, k, m, g, t, p, e - т.е. от байт до экзабайт. Но есть одна особенность реализации - парсер при разборе этих параметров ещё не знает какой размер сектора имеет носитель, поэтому если он отличается от 512 байт, то лучше указывать размер в секторах, иначе размер будет вычислен неправильно.  Размер партиции указывается после опции "-s", а смещение начала - после "-b". При опускании любого из этих параметров они выбираются автоматически: смещение начала - на первый доступный блок, размер - на максимально возможный из доступного свободного пространства.

Вычисление индекса партиции обычно можно оставить утилите gpart. Только если вам не нужна какая-нибудь особенная конфигурация, например, чтобы система устанавливалась на ad0s3 нужно указать параметр "-i 3".

Итак, схема создана, создадим партицию:
# gpart add -s 400m -t freebsd md0
md0s1 added
# gpart show md0
=>     17  1023978  md0  MBR  (500M)
       17   819196    1  freebsd  (400M)
   819213   204782       - free -  (100M)

Как видно, партиция была создана с типом "freebsd" и размером в 400 Мбайт, индекс и смещение начала были выбраны автоматически.

Теперь на созданной партиции можно сделать дополнительную разметку для BSD схемы:
# gpart create -s bsd md0s1
md0s1 created
# gpart show md0
=>     17  1023978  md0  MBR  (500M)
       17   819196    1  freebsd  (400M)
   819213   204782       - free -  (100M)

# gpart show md0s1
=>     0  819196  md0s1  BSD  (400M)
       0  819196         - free -  (400M)

Теперь добавим партиции, только вместо bsdlabel используем тот же gpart:
# gpart add -s 100m -t freebsd-ufs md0s1
md0s1a added
# gpart add -s 100m -t freebsd-swap md0s1
md0s1b added
# gpart add -t freebsd-ufs md0s1
md0s1d added
# gpart show md0s1
=>     0  819196  md0s1  BSD  (400M)
       0  204800      1  freebsd-ufs  (100M)
  204800  204800      2  freebsd-swap  (100M)
  409600  409596      4  freebsd-ufs  (200M)

Всё достаточно легко и просто. Для записи загрузочного кода можно использовать всю ту же утилиту. Для этого предназначена команда `gpart bootcode`. Например, аналогом команды `bsdlabel -B` будет:
# gpart bootcode -b /boot/boot md0s1
md0s1 has bootcode
Аналогично можно записать загрузочный код для схемы MBR:
# gpart bootcode -b /boot/mbr md0
md0 has bootcode
Если же необходимо установить менеджер загрузки, то нужно всего лишь выбрать другой образ загрузочного кода - /boot/boot0. Так же, не стоит забывать, что у схемы MBR есть аттрибут активного раздела, чтобы его установить нужно использовать команду `gpart set`:
# gpart set -a active -i 1 md0
md0s1 has active set
# gpart show md0
=>     17  1023978  md0  MBR  (500M)
       17   819196    1  freebsd  [active]  (400M)
   819213   204782       - free -  (100M)

В принципе, этого набора команд достаточно для работы. Если иметь понятие, что и для чего нужно сделать - то сложностей в использовании вообще не вижу. Даже если такая необходимость возникает нечасто, все команды утилиты gpart имеют описание:
# gpart 
usage: gpart add [-b start] [-s size] -t type [-i index] [-l label] [-f flags] geom
       gpart bootcode [-b bootcode] [-p partcode] [-i index] [-f flags] geom
       gpart commit geom
       gpart create -s scheme [-n entries] [-f flags] provider
       gpart delete -i index [-f flags] geom
       gpart destroy [-f flags] geom
       gpart modify -i index [-l label] [-t type] [-f flags] geom
       gpart set -a attrib -i index [-f flags] geom
       gpart show [-lr] [geom ...]
       gpart undo geom
       gpart unset -a attrib -i index [-f flags] geom
       gpart help
       gpart list [name ...]
       gpart status [-s] [name ...]
       gpart load [-v]
       gpart unload [-v]

среда, 10 марта 2010 г.

Немного о GEOM классе PART

В 7-ую версию FreeBSD был добавлен GEOM класс PART, который заменил собой несколько классов, реализующих поддержку таблиц партиций, таких как BSD, MBR, PC98.

Не слишком углубляясь в технические подробности реализации назначение класса PART можно представить в виде некоторого модульного механизма ядра для управления различными таблицами партиций.

Каждый пользователь FreeBSD в той или иной мере знаком как минимум с двумя типами таблиц партиций - это MBR и BSD, а в последнее время, стал пользоваться популярностью ещё один тип - GPT. В терминологии класса PART эти типы таблиц представлены "схемами". А модульность реализации позволяет включать поддержку различных схем в виде отдельных модулей ядра. Для этого реализация класса включает в себя инфраструктуру с базовыми действиями схожими для различных типов таблиц партиций. А реализация особенностей каждой из "схем" содержится в отдельных модулях, которые благодаря интерфейсу kobj(9) подключаются в базовую инфраструктуру класса.

Когда подсистема GEOM инициализирует класс PART, базовая инфраструктура класса выполняет "пробу" для каждой зарегистрированной схемы. Т.е. класс выполняет поиск метаданных схемы на носителях. Так, например, схема MBR реализует поддержку таблицы основных разделов, содержащейся в главной загрузочной записи MBR. Если "схема" обнаружит свои метаданные на носителе, она создаёт "провайдеры", через которые осуществляется доступ к партициям. Для пользователя партиции этой схемы выглядят как <имя_провайдера>s%d, где %d - номер индекса партиции в таблице. Т.е. привычные всем имена типа ad0s1 можно разделить на имя провайдера носителя ad0 и имя партиции s1, где 1 - это номер партиции в таблице.

Схема BSD реализует поддержку bsd-разделов. Это те, которые раньше создавались утилитой bsdlabel (или ещё раньше disklabel). Имена партиций этой схемы именуются латинскими буквами, например, ad0s1a. Здесь ad0 - имя провайдера носителя, s1 - имя провайдера схемы MBR, т.е. на диске ad0 была создана схема MBR и уже на первой партиции этой схемы была создана схема BSD c партицией, определяемой именем "a".

Схема GPT реализует поддержку GUID таблицы разделов. Имена партиций при использовании этой схемы формируются в зависимости от типа. Тип задаётся при создании партиции, о нём я расскажу чуть позже. Но в общем, имя может быть либо "p%d" либо "s%d", где %d - номер индекса в таблице. Обычно это "p", имя "s" используется только для типа партиции "freebsd", обозначающего, что на данной партиции будет использоваться BSD схема.

Ещё одна знакомая схема - EBR. Она используется для разметки партиций внутри расширенных разделов. И осталось ещё три неупомянутых схемы, поддержка которых реализована на сегодняшний день в классе PART: APM используемая в Apple Mac, PC98 используемая в NEC PC-9801, и VTOC8 используемая  на Sun'овских UltraSPARC.

Это краткое описание я решил написать после нескольких часов, проведённых за изучением исходного кода этого класса. И в целом, его реализация мне понравилась. Можно сказать, что она довольно красива и в духе всей подсистемы GEOM.