Resumen virtual A/B

Android tiene dos mecanismos de actualización: actualizaciones A/B (ininterrumpidas) y actualizaciones no A/B. Para reducir la complejidad del código y mejorar el proceso de actualización, en Android 11 los dos mecanismos se unifican a través de A/B virtual para brindar actualizaciones sin inconvenientes a todos los dispositivos con un costo de almacenamiento mínimo. Android 12 ofrece la opción de compresión A/B virtual para comprimir particiones instantáneas. Tanto en Android 11 como en Android 12, se aplica lo siguiente:

  • Las actualizaciones virtuales A/B son perfectas como las actualizaciones A/B. Las actualizaciones virtuales A/B minimizan el tiempo que un dispositivo está fuera de línea e inutilizable.
  • Las actualizaciones virtuales A/B se pueden revertir . Si el nuevo sistema operativo no se inicia, los dispositivos retroceden automáticamente a la versión anterior.
  • Las actualizaciones virtuales A/B usan un mínimo de espacio adicional al duplicar solo las particiones que usa el gestor de arranque. Se capturan instantáneas de otras particiones actualizables.

Antecedentes y terminología

Esta sección define la terminología y describe la tecnología que admite A/B virtual.

mapeador de dispositivos

Device-mapper es una capa de bloque virtual de Linux que se usa a menudo en Android. Con particiones dinámicas , las particiones como /system son una pila de dispositivos en capas:

  • En la parte inferior de la pila se encuentra la superpartición física (por ejemplo, /dev/block/by-name/super ).
  • En el medio hay un dispositivo dm-linear , que especifica qué bloques en la súper partición forman la partición dada. Aparece como /dev/block/mapper/system_[a|b] en un dispositivo A/B o /dev/block/mapper/system en un dispositivo que no es A/B.
  • En la parte superior reside un dispositivo dm-verity , creado para particiones verificadas. Este dispositivo verifica que los bloques en el dispositivo dm-linear estén firmados correctamente. Aparece como /dev/block/mapper/system-verity y es la fuente del punto de montaje /system .

La Figura 1 muestra cómo se ve la pila debajo del punto de montaje /system .

Partition stacking underneath system

Figura 1. Pila debajo del punto de montaje /system

dm-instantánea

Virtual A/B se basa en dm-snapshot , un módulo mapeador de dispositivos para tomar instantáneas del estado de un dispositivo de almacenamiento. Al usar dm-snapshot , hay cuatro dispositivos en juego:

  • El dispositivo base es el dispositivo del que se toma una instantánea. En esta página, el dispositivo base siempre es una partición dinámica, como un sistema o un proveedor.
  • El dispositivo de copia en escritura (COW), para registrar cambios en el dispositivo base. Puede ser de cualquier tamaño, pero debe ser lo suficientemente grande para acomodar todos los cambios en el dispositivo base.
  • El dispositivo de instantánea se crea utilizando el destino de la snapshot . Las escrituras en el dispositivo de instantáneas se escriben en el dispositivo COW. Las lecturas del dispositivo de instantánea se leen desde el dispositivo base o desde el dispositivo COW, dependiendo de si la instantánea ha cambiado los datos a los que se accede.
  • El dispositivo de origen se crea utilizando el destino snapshot-origin . Las lecturas en el dispositivo de origen se leen directamente desde el dispositivo base. Escribe en el dispositivo de origen escribe directamente en el dispositivo base, pero los datos originales se respaldan escribiendo en el dispositivo COW.

Device mapping for dm-snapshot

Figura 2. Asignación de dispositivos para dm-snapshot

Instantáneas comprimidas

En Android 12, debido a que los requisitos de espacio en la partición /data pueden ser altos, puede habilitar instantáneas comprimidas en su compilación para abordar los requisitos de espacio más altos de la partición /data .

Las instantáneas comprimidas virtuales A/B se basan en dos nuevos componentes que están disponibles en Android 12:

  • dm-user , un módulo del núcleo similar a FUSE que permite que el espacio de usuario implemente dispositivos de bloque.
  • snapuserd , un demonio de espacio de usuario para implementar un nuevo formato de instantánea.

Estos componentes permiten la compresión. Los otros cambios necesarios realizados para implementar las capacidades de instantáneas comprimidas se proporcionan en las siguientes secciones: formato COW para instantáneas comprimidas , dm-user y Snapuserd .

Formato COW para instantáneas comprimidas

En Android 12, las instantáneas comprimidas usan un nuevo formato COW. De manera similar al formato integrado del kernel que se usa para las instantáneas sin comprimir, el formato COW para las instantáneas comprimidas tiene secciones alternas de metadatos y datos. Los metadatos del formato original solo permitían operaciones de "reemplazo": Reemplace el bloque X en la imagen base con el contenido del bloque Y en la instantánea. El formato COW de instantáneas comprimidas es más expresivo y admite tres operaciones:

  • Copiar : el bloque X en el dispositivo base debe reemplazarse con el bloque Y en el dispositivo base.
  • Reemplazar : el bloque X en el dispositivo base debe reemplazarse con el contenido del bloque Y en la instantánea. Cada uno de estos bloques está comprimido en gz.
  • Cero : el bloque X en el dispositivo base debe reemplazarse con ceros.

Las actualizaciones OTA completas consisten únicamente en operaciones de reemplazo y puesta a cero . Las actualizaciones incrementales de OTA también pueden tener operaciones de copia .

dm-usuario en Android 12

El módulo de kernel dm-user permite que el userspace de usuario implemente dispositivos de bloque mapeador de dispositivos. Una entrada de la tabla dm-user crea un dispositivo misceláneo en /dev/dm-user/<control-name> . Un proceso de userspace de usuario puede sondear el dispositivo para recibir solicitudes de lectura y escritura del kernel. Cada solicitud tiene un búfer asociado para que el espacio de usuario se complete (para una lectura) o se propague (para una escritura).

El módulo de kernel dm-user proporciona una nueva interfaz visible para el usuario para el kernel que no forma parte de la base de código upstream kernel.org. Hasta que lo sea, Google se reserva el derecho de modificar la interfaz dm-user en Android.

Snapusuario

El componente de espacio de usuario snapuserd para dm-user implementa la compresión A/B virtual.

En la versión sin comprimir de Virtual A/B (ya sea en Android 11 y versiones anteriores, o en Android 12 sin la opción de instantánea comprimida), el dispositivo COW es un archivo sin formato. Cuando la compresión está habilitada, COW funciona como un dispositivo dm-user , que está conectado a una instancia del daemon snapuserd .

El kernel no usa el nuevo formato COW. Entonces, el componente snapuserd traduce las solicitudes entre el formato COW de Android y el formato integrado del kernel:

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

Figura 3. Diagrama de flujo de snapuserd como traductor entre los formatos Android y Kernel COW

Esta traducción y descompresión nunca ocurre en el disco. El componente snapuserd intercepta las lecturas y escrituras COW que ocurren en el kernel y las implementa usando el formato COW de Android.

Procesos de compresión A/B virtual

Estas secciones brindan detalles sobre los procesos utilizados en la compresión Virtual A/B: lectura de metadatos, fusión y realización de transiciones de inicio.

Lectura de metadatos

Los metadatos son construidos por un daemon snapuserd . Los metadatos son principalmente un mapeo de 2 ID, de 8 bytes cada uno, que representan los sectores que se fusionarán. En dm-snapshot se llama disk_exception .

struct disk_exception {
    uint64_t old_chunk;
    uint64_t new_chunk;
};

Se utiliza una excepción de disco cuando una parte antigua de datos se reemplaza por una nueva.

Un demonio Snapuserd lee el archivo COW interno a través de la biblioteca COW y construye los metadatos para cada una de las operaciones COW presentes en el archivo COW.

Las lecturas de metadatos se inician desde dm-snapshot en el kernel cuando se crea el dispositivo dm- dm- snapshot .

La siguiente figura proporciona un diagrama de secuencia para la ruta de E/S para la construcción de metadatos.

Sequence diagram, IO path for metadata construction

Figura 4. Flujo de secuencia para la ruta de E/S en la construcción de metadatos

fusión

Una vez que se completa el proceso de inicio, el motor de actualización marca la ranura como un inicio exitoso e inicia la combinación cambiando el destino dm-snapshot al destino dm-snapshot-merge .

dm-snapshot recorre los metadatos e inicia una E/S de combinación para cada excepción de disco. A continuación se muestra una descripción general de alto nivel de la ruta de E/S de combinación.

Merge IO path

Figura 5. Descripción general de la ruta Merge IO

Si el dispositivo se reinicia durante el proceso de combinación, la combinación se reanuda en el próximo reinicio y la combinación se completa.

transiciones de inicio

Al arrancar con instantáneas comprimidas, el init de primera etapa debe iniciar snapuserd para montar particiones. Esto plantea un problema: cuando se carga y aplica sepolicy , snapuserd se coloca en el contexto incorrecto y sus solicitudes de lectura fallan, con denegaciones de selinux.

Para abordar esto, las transiciones de snapuserd con init , de la siguiente manera:

  1. El snapuserd init el ramdisk y guarda un descriptor de archivo abierto en una variable de entorno.
  2. El init de primera etapa cambia el sistema de archivos raíz a la partición del sistema y luego ejecuta la copia del sistema de init .
  3. La copia del sistema de init lee la política combinada en una cadena.
  4. Init invoca mlock() en todas las páginas respaldadas por ext4. A continuación, desactiva todas las tablas del mapeador de dispositivos para dispositivos de instantáneas y detiene snapuserd . Después de esto, está prohibido leer de las particiones, ya que al hacerlo se produce un interbloqueo.
  5. Usando el descriptor abierto de la copia ramdisk de snapuserd , init relanza el daemon con el contexto selinux correcto. Se reactivan las tablas del mapeador de dispositivos para dispositivos de instantáneas.
  6. Init invoca munlockall() - es seguro realizar IO nuevamente.

uso del espacio

La siguiente tabla proporciona una comparación del uso del espacio para diferentes mecanismos de OTA que usan el sistema operativo de Pixel y los tamaños de OTA.

Impacto de tamaño no-A/B A/B A/B virtuales A/B virtual (comprimido)
Imagen original de fábrica Super de 4,5 GB (imagen de 3,8 G + 700 M reservados) 1 Super de 9GB (3.8G + 700M reservados, para dos ranuras) Super de 4,5 GB (imagen de 3,8 G + 700 M reservados) Super de 4,5 GB (imagen de 3,8 G + 700 M reservados)
Otras particiones estáticas /cache Ninguna Ninguna Ninguna
Almacenamiento adicional durante OTA (espacio devuelto después de aplicar OTA) 1,4 GB en/datos 0 3.8GB 2 en /datos 2.1GB 2 en /datos
Almacenamiento total requerido para aplicar OTA 5.9GB 3 (súper y datos) 9 GB (súper) 8.3GB 3 (súper y datos) 6.6GB 3 (súper y datos)

1 Indica el diseño asumido basado en el mapeo de píxeles.

2 Supone que la nueva imagen del sistema tiene el mismo tamaño que la original.

3 El requisito de espacio es transitorio hasta que se reinicia.

Para implementar Virtual A/B, o para usar capacidades de instantáneas comprimidas, consulte Implementación de Virtual A/B