При создании PV получаем вот такой ивент от netlink pvcreate
В этот момент файл в backup не создается
При создании VG получаем вот такой ивент от netlink vgcreate
В этот момент LVM создает файл по пути /etc/lvm/backup/{vg-name} vgcreate
При создании LV получаем вот такие ивенты от netlink lvcreate
В этот момент LVM обновляет файл по пути /etc/lvm/backup/{vg-name} lvcreate
Источник: lib/label/label.c
При запуске pvs/vgs/lvs LVM выполняет label_scan — сканирует все блочные устройства в системе:
_scan_list() -> bcache_prefetch() -> bcache_get() -> _process_block()
При этом важно разделять общее поведение LVM и поведение конкретного запуска с ограничением устройств. В сохраненном trace для pvs с --devices утилита по-прежнему обходит части /dev и sysfs, но прямой open() по блочному устройству в этом запуске есть только для /dev/sdb. Детальный разбор вынесен в research/pvs/README.MD. Для нашей практической задачи это важно потому, что ограничение списка устройств может уменьшить число обращений к реальным дискам и снизить риск зависания, если один из посторонних дисков застрял в состоянии D.
Каждое устройство открывается через open(), и первые 4096 байт считываются в буфер headers_buf:
#define HEADERS_BUF_SIZE 4096
memcpy(headers_buf, bb->data, HEADERS_BUF_SIZE);В функции _find_lvm_header() LVM ищет метку LABELONE в первых 4-х секторах (по 512 байт) устройства:
for (sector = start_sector; sector < start_sector + LABEL_SCAN_SECTORS;
sector += LABEL_SIZE >> SECTOR_SHIFT) {
lh = (struct label_header *) (headers_buf + (sector << SECTOR_SHIFT));
if (!memcmp(lh->id, LABEL_ID, sizeof(lh->id))) {
// нашли LVM label
}
}По умолчанию метка находится во 2-м секторе (смещение 512 байт от начала устройства).
После нахождения метки LVM вызывает labeller->ops->read() (это _text_read()), которая читает:
- PV header — UUID физического тома, размер, расположение данных
- MDA header (Metadata Area) — находится по смещению 4096 байт от начала устройства, размер заголовка 512 байт
- Metadata text — текстовые метаданные в ASCII формате (~1 МиБ по умолчанию), следующие сразу за MDA header
Все найденные данные сохраняются в lvmcache — внутренний кэш LVM в оперативной памяти:
// Из комментария в label.c:
// ops->read() is _text_read() which reads the pv_header, mda
// locations, and metadata text. All of the info it finds about the PV
// and VG is stashed in lvmcache which saves it in the form of
// info/vginfo structs.После этого команды pvs/vgs/lvs форматируют данные из lvmcache и выводят на экран.
Смещение 0 -> Sector 0 (может быть пустой или содержать boot record)
Смещение 512 -> Sector 1: LABELONE + PV header (UUID, размеры, указатели на MDA)
Смещение 4096 -> MDA header (512 байт, включает checksum, version, offset на текст)
Смещение ~4608 -> Metadata text (ASCII, ~1 МиБ) — содержит описание VG, LV, PV
Источники:
type PVData struct {
PVName string `json:"pv_name,omitempty"`
VGName string `json:"vg_name,omitempty"`
PVSize resource.Quantity `json:"pv_size,omitempty"`
PVUsed string `json:"pv_used,omitempty"` -- нигде не используется
PVUuid string `json:"pv_uuid,omitempty"`
VGTags string `json:"vg_tags,omitempty"`
VGUuid string `json:"vg_uuid,omitempty"`
}type VGData struct {
VGAttr string `json:"vg_attr"` -- нигде не используется
VGFree resource.Quantity `json:"vg_free"`
VGName string `json:"vg_name"`
VGShared string `json:"vg_shared"`
VGSize resource.Quantity `json:"vg_size"`
VGTags string `json:"vg_tags"`
VGUUID string `json:"vg_uuid"`
}type LVData struct {
LVName string `json:"lv_name"`
VGName string `json:"vg_name"`
VGUuid string `json:"vg_uuid"`
LVAttr string `json:"lv_attr"`
LVSize resource.Quantity `json:"lv_size"`
PoolName string `json:"pool_lv"`
Origin string `json:"origin"` -- не используется
DataPercent string `json:"data_percent"`
MetadataPercent string `json:"metadata_percent"`
MovePv string `json:"move_pv"` -- не используется
MirrorLog string `json:"mirror_log"` -- не используется
CopyPercent string `json:"copy_percent"` -- не используется
ConvertLv string `json:"convert_lv"` -- не используется
LvTags string `json:"lv_tags"`
ThinID string `json:"thin_id"`
MetadataLv string `json:"metadata_lv"`
LVDmPath string `json:"lv_dm_path"`
}Подробнее: research/pvs/README.MD
pvs --devices /dev/sdb ограничивает прямое чтение блочных устройств до целевого диска, но discovery через /dev и /sys остаётся.
Подробнее: research/vgs/README.md
vgs test-vg(с именем VG) — не ограничивает набор сканируемых дисков (все блочные устройства открываются O_DIRECT), но убирает глобальный лок P_globalvgs --devices /dev/sdb test-vg— ограничивает обращения к единственному устройству, но выдаёт warning о missing PV для VG из нескольких дисков
Подробнее: research/lvs/README.md
Поведение полностью идентично vgs: targeted запрос не ограничивает диски, только убирает глобальный лок; --devices ограничивает.
Подробнее: research/alternatives/README.md
| Источник | label_scan | Блочные устройства | LVM-локи | D-State risk |
|---|---|---|---|---|
| pvs/vgs/lvs (full) | да | все | глобальный + VG | высокий |
| pvs/vgs/lvs (targeted) | да | все | VG only | средний |
| pvs/vgs/lvs (--devices) | ограниченный | 1 устройство | VG only | ниже |
| /etc/lvm/backup/ | нет | нет | нет | нет |
| dmsetup | нет | нет | нет | нет |
| udev database | нет | нет | нет | нет |
Подробнее: research/coverage-matrix.md
Комбинация /etc/lvm/backup/ + dmsetup + udev database покрывает 100% нужных полей PV/VG/LV без единого обращения к блочным устройствам и без LVM-локов.
Подробнее: research/strategy.md


