Виртуальный обзор A/B

Android имеет два механизма обновления: A/B (бесшовные) обновления и не-A/B обновления. Чтобы уменьшить сложность кода и улучшить процесс обновления, в Android 11 эти два механизма объединены с помощью виртуального A/B, чтобы обеспечить бесшовные обновления на всех устройствах с минимальной стоимостью хранилища. Android 12 предлагает возможность виртуального сжатия A/B для сжатия моментальных снимков разделов. Как в Android 11, так и в Android 12 применимо следующее:

  • Виртуальные обновления A/B не вызывают затруднений , как и обновления A/B. Виртуальные обновления A/B сводят к минимуму время, в течение которого устройство находится в автономном режиме и не используется.
  • Виртуальные обновления A/B можно откатить . Если новая ОС не загружается, устройства автоматически возвращаются к предыдущей версии.
  • Виртуальные обновления A/B занимают минимум дополнительного места , дублируя только те разделы, которые используются загрузчиком. Создаются моментальные снимки других обновляемых разделов.

Предыстория и терминология

В этом разделе определяется терминология и описывается технология, поддерживающая виртуальный A/B.

Устройство-сопоставитель

Device-mapper — это слой виртуальных блоков Linux, часто используемый в Android. При использовании динамических разделов такие разделы, как /system , представляют собой стек многоуровневых устройств:

  • Внизу стека находится физический суперраздел (например, /dev/block/by-name/super ).
  • Посередине находится dm-linear устройство, указывающее, какие блоки суперраздела образуют данный раздел. Это отображается как /dev/block/mapper/system_[a|b] на устройстве A/B или /dev/block/mapper/system на устройстве без A/B.
  • Наверху находится устройство dm-verity , созданное для проверенных разделов. Это устройство проверяет правильность подписи блоков на устройстве dm-linear . Он выглядит как /dev/block/mapper/system-verity и является источником точки монтирования /system .

На рис. 1 показано, как выглядит стек в точке монтирования /system .

Partition stacking underneath system

Рис. 1. Стек под точкой монтирования /system

дм-моментальный снимок

Виртуальный A/B использует dm-snapshot , модуль сопоставления устройств для моментального снимка состояния устройства хранения. При использовании dm-snapshot в игре участвуют четыре устройства:

  • Базовое устройство — это устройство, на которое сделан снимок. На этой странице базовое устройство всегда является динамическим разделом, таким как системный или поставщик.
  • Устройство копирования при записи (COW) для регистрации изменений в базовом устройстве. Он может быть любого размера, но он должен быть достаточно большим, чтобы вместить все изменения базового устройства.
  • Устройство моментального снимка создается с использованием цели snapshot . Записи на устройство моментальных снимков записываются на устройство COW. Чтение с устройства моментального снимка выполняется либо с базового устройства, либо с устройства COW, в зависимости от того, были ли данные, к которым осуществляется доступ, были изменены моментальным снимком.
  • Исходное устройство создается с помощью цели snapshot-origin . Чтение на исходное устройство считывается непосредственно с базового устройства. Запись на исходное устройство записывается непосредственно на базовое устройство, но исходные данные резервируются путем записи на устройство COW.

Device mapping for dm-snapshot

Рисунок 2. Сопоставление устройств для dm-snapshot

Сжатые снимки

В Android 12, поскольку требования к пространству в разделе /data могут быть высокими, вы можете включить сжатые моментальные снимки в своей сборке, чтобы удовлетворить более высокие требования к пространству в разделе /data .

Виртуальные сжатые снимки A/B создаются на основе двух новых компонентов, доступных в Android 12:

  • dm-user , модуль ядра, похожий на FUSE, который позволяет пользовательскому пространству реализовывать блочные устройства.
  • snapuserd — демон пользовательского пространства для реализации нового формата моментальных снимков.

Эти компоненты включают сжатие. Другие необходимые изменения, внесенные для реализации возможностей сжатых моментальных снимков, приведены в следующих разделах: формат COW для сжатых моментальных снимков , dm-user и Snapuserd .

Формат COW для сжатых моментальных снимков

В Android 12 сжатые снимки используют новый формат COW. Подобно встроенному формату ядра, используемому для несжатых моментальных снимков, формат COW для сжатых моментальных снимков имеет чередующиеся разделы метаданных и данных. Метаданные исходного формата допускали только операции «замены»: замена блока X в базовом образе содержимым блока Y в моментальном снимке. Формат сжатых снимков COW более выразителен и поддерживает три операции:

  • Копировать — Блок X в базовом устройстве должен быть заменен блоком Y в базовом устройстве.
  • Заменить — блок X в базовом устройстве должен быть заменен содержимым блока Y в моментальном снимке. Каждый из этих блоков сжат gz.
  • Zero - Блок X в базовом устройстве должен быть заменен всеми нулями.

Полные OTA-обновления состоят только из операций замены и нуля . Добавочные обновления OTA могут дополнительно иметь операции копирования .

dm-пользователь в Android 12

Модуль ядра dm-user позволяет userspace реализовывать блочные устройства device-mapper. Запись таблицы dm-user создает разное устройство в /dev/dm-user/<control-name> . Процесс userspace может опрашивать устройство для получения запросов на чтение и запись от ядра. У каждого запроса есть связанный буфер для пользовательского пространства, которое нужно либо заполнить (для чтения), либо распространить (для записи).

Модуль ядра dm-user предоставляет новый видимый пользователю интерфейс для ядра, который не является частью исходной кодовой базы kernel.org. Пока это не так, Google оставляет за собой право изменять интерфейс dm-user в Android.

Snapuserd

Компонент пользовательского пространства snapuserd для dm-user реализует виртуальное сжатие A/B.

В несжатой версии Virtual A/B (либо в Android 11 и ниже, либо в Android 12 без опции сжатого моментального снимка) устройство COW представляет собой необработанный файл. Когда сжатие включено, COW функционирует как устройство dm-user , которое подключено к экземпляру демона snapuserd .

Ядро не использует новый формат COW. Таким образом, компонент snapuserd переводит запросы между форматом Android COW и встроенным форматом ядра:

Snapuserd component translating requests between Android COW format and kernel built-in format

Рисунок 3. Блок-схема snapuserd как переводчика между форматами Android и Kernel COW

Этот перевод и распаковка никогда не происходят на диске. Компонент snapuserd перехватывает операции чтения и записи COW, происходящие в ядре, и реализует их с использованием формата Android COW.

Виртуальные процессы сжатия A/B

В этих разделах содержится подробная информация о процессах, используемых при виртуальном сжатии A/B: чтение метаданных, слияние и выполнение переходов инициализации.

Чтение метаданных

Метаданные создаются демоном snapuserd . Метаданные — это прежде всего сопоставление двух идентификаторов, по 8 байтов каждый, которые представляют собой объединяемые сектора. В dm-snapshot это называется disk_exception .

struct disk_exception {
    uint64_t old_chunk;
    uint64_t new_chunk;
};

Дисковое исключение используется, когда старый блок данных заменяется новым.

Демон Snapuserd считывает внутренний файл COW через библиотеку COW и создает метаданные для каждой из операций COW, представленных в файле COW.

Чтение метаданных инициируется из dm-snapshot в ядре при dm- snapshot устройства dm-snapshot.

На рисунке ниже представлена ​​диаграмма последовательности пути ввода-вывода для создания метаданных.

Sequence diagram, IO path for metadata construction

Рис. 4. Последовательность действий для пути ввода-вывода при построении метаданных

Объединение

После завершения процесса загрузки механизм обновления помечает слот как успешную загрузку и инициирует слияние, переключая цель dm-snapshot на цель dm-snapshot-merge .

dm-snapshot просматривает метаданные и инициирует операцию ввода-вывода слияния для каждого исключения диска. Общий обзор пути ввода-вывода слияния показан ниже.

Merge IO path

Рис. 5. Обзор пути слияния ввода-вывода

Если устройство перезагружается во время процесса слияния, слияние возобновляется при следующей перезагрузке и завершается.

Начальные переходы

При загрузке со сжатыми моментальными снимками первый этап инициализации должен запускать snapuserd для монтирования разделов. Это создает проблему: когда sepolicy загружается и применяется, snapuserd помещается в неправильный контекст, и его запросы на чтение терпят неудачу с отказами selinux.

Чтобы решить эту проблему, snapuserd переходит в синхронный режим с init следующим образом:

  1. init первого этапа запускает snapuserd с виртуального диска и сохраняет в нем открытый файловый дескриптор в переменной окружения.
  2. Первый этап init переключает корневую файловую систему на системный раздел, а затем выполняет системную копию init .
  3. Системная копия init считывает комбинированную политику sepolicy в строку.
  4. Init вызывает mlock() на всех страницах, поддерживаемых ext4. Затем он деактивирует все таблицы сопоставления устройств для устройств моментальных снимков и останавливает snapuserd . После этого запрещается читать из разделов, так как это приводит к взаимоблокировке.
  5. Используя дескриптор open копии snapuserd на виртуальном диске, init перезапускает демон с правильным контекстом selinux. Таблицы сопоставления устройств для устройств моментальных снимков повторно активируются.
  6. Init вызывает munlockall() - безопасно снова выполнить ввод-вывод.

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

В следующей таблице представлено сравнение использования пространства для различных механизмов OTA с использованием ОС Pixel и размеров OTA.

Влияние размера не-A/B А/Б Виртуальный А/Б Виртуальный A/B (сжатый)
Оригинальное заводское изображение 4,5 ГБ супер (3,8 ГБ изображения + 700 МБ зарезервировано) 1 9GB super (3.8G + 700M зарезервировано, для двух слотов) 4,5 ГБ супер (3,8 ГБ изображения + 700 МБ зарезервировано) 4,5 ГБ супер (3,8 ГБ изображения + 700 МБ зарезервировано)
Другие статические разделы /кэш Никто Никто Никто
Дополнительное хранилище во время OTA (пространство возвращается после применения OTA) 1,4 ГБ в / данные 0 3,8 ГБ 2 в / данные 2,1 ГБ 2 в / данные
Общий объем хранилища, необходимый для применения OTA 5,9 ГБ 3 (супер и данные) 9 ГБ (супер) 8,3 ГБ 3 (супер и данные) 6,6 ГБ 3 (супер и данные)

1 Обозначает предполагаемую компоновку, основанную на сопоставлении пикселей.

2 Предполагается, что новый образ системы имеет тот же размер, что и исходный.

3 Требование к пространству является временным до перезагрузки.

Чтобы реализовать Virtual A/B или использовать возможности сжатых моментальных снимков, см. Реализация Virtual A/B.