Aperçu A/B virtuel

Android dispose de deux mécanismes de mise à jour : les mises à jour A/B (transparentes) et les mises à jour non A/B. Pour réduire la complexité du code et améliorer le processus de mise à jour, dans Android 11, les deux mécanismes sont unifiés via A/B virtuel pour apporter des mises à jour transparentes à tous les appareils avec un coût de stockage minimisé. Android 12 offre l'option de compression virtuelle A/B pour compresser les partitions instantanées. Dans Android 11 et Android 12, les conditions suivantes s'appliquent :

  • Les mises à jour A/B virtuelles sont transparentes comme les mises à jour A/B. Les mises à jour virtuelles A/B minimisent le temps pendant lequel un appareil est hors ligne et inutilisable.
  • Les mises à jour virtuelles A/B peuvent être annulées . Si le nouveau système d'exploitation ne démarre pas, les appareils reviennent automatiquement à la version précédente.
  • Les mises à jour virtuelles A/B utilisent un minimum d'espace supplémentaire en dupliquant uniquement les partitions utilisées par le chargeur de démarrage. Les autres partitions pouvant être mises à jour sont prises en instantané .

Contexte et terminologie

Cette section définit la terminologie et décrit la technologie qui prend en charge l'A/B virtuel.

Device-mapper

Device-mapper est une couche de bloc virtuelle Linux souvent utilisée dans Android. Avec les partitions dynamiques , les partitions telles que /system sont une pile de périphériques en couches :

  • Au bas de la pile se trouve la super partition physique (par exemple, /dev/block/by-name/super ).
  • Au milieu se trouve un périphérique dm-linear , spécifiant quels blocs de la super partition forment la partition donnée. Cela apparaît comme /dev/block/mapper/system_[a|b] sur un périphérique A/B, ou /dev/block/mapper/system sur un périphérique non-A/B.
  • En haut se trouve un périphérique dm-verity , créé pour les partitions vérifiées. Cet appareil vérifie que les blocs sur l'appareil dm-linear sont signés correctement. Il apparaît comme /dev/block/mapper/system-verity et est la source du point de montage /system .

La figure 1 montre à quoi ressemble la pile sous le point de montage /system .

Partition stacking underneath system

Figure 1. Empiler sous le point de montage /system

dm-instantané

Virtual A/B s'appuie sur dm-snapshot , un module de mappage de périphérique pour prendre un instantané de l'état d'un périphérique de stockage. Lorsque vous utilisez dm-snapshot , quatre appareils sont en jeu :

  • L'appareil de base est l'appareil qui a fait l'objet d'un instantané. Sur cette page, le périphérique de base est toujours une partition dynamique, telle que système ou fournisseur.
  • Le périphérique de copie sur écriture (COW), pour la journalisation des modifications apportées au périphérique de base. Il peut être de n'importe quelle taille, mais il doit être suffisamment grand pour s'adapter à toutes les modifications apportées à l'appareil de base.
  • Le périphérique d'instantané est créé à l'aide de la cible snapshot . Les écritures sur le périphérique d'instantané sont écrites sur le périphérique COW. Les lectures à partir de l'appareil d'instantané sont lues à partir de l'appareil de base ou de l'appareil COW, selon que les données auxquelles on accède ont été modifiées par l'instantané.
  • Le périphérique d'origine est créé à l'aide de la cible snapshot-origin . Les lectures vers l'appareil d'origine sont lues directement à partir de l'appareil de base. Les écritures sur le périphérique d'origine écrivent directement sur le périphérique de base, mais les données d'origine sont sauvegardées en écrivant sur le périphérique COW.

Device mapping for dm-snapshot

Figure 2. Mappage de périphérique pour dm-snapshot

Instantanés compressés

Dans Android 12 et versions ultérieures, étant donné que les besoins en espace sur la partition /data peuvent être élevés, vous pouvez activer les instantanés compressés dans votre build pour répondre aux besoins en espace plus élevés de la partition /data .

Les instantanés virtuels compressés A/B sont construits sur les composants suivants qui sont disponibles dans Android 12 et versions ultérieures :

  • dm-user , un module de noyau similaire à FUSE qui permet à l'espace utilisateur d'implémenter des périphériques de bloc.
  • snapuserd , un démon de l'espace utilisateur pour implémenter un nouveau format d'instantané.

Ces composants permettent la compression. Les autres modifications nécessaires apportées pour implémenter les fonctionnalités d'instantanés compressés sont indiquées dans les sections suivantes : format COW pour les instantanés compressés , dm-user et Snapuserd .

Format COW pour les instantanés compressés

Dans Android 12 et versions ultérieures, les instantanés compressés utilisent un format COW. Semblable au format intégré du noyau utilisé pour les instantanés non compressés, le format COW pour les instantanés compressés comporte des sections alternées de métadonnées et de données. Les métadonnées du format d'origine n'autorisaient que les opérations de remplacement : remplacez le bloc X dans l'image de base par le contenu du bloc Y dans l'instantané. Le format COW des instantanés compressés est plus expressif et prend en charge les opérations suivantes :

  • Copie : le bloc X dans l'appareil de base doit être remplacé par le bloc Y dans l'appareil de base.
  • Remplacer : le bloc X dans l'appareil de base doit être remplacé par le contenu du bloc Y dans l'instantané. Chacun de ces blocs est compressé gz.
  • Zéro : le bloc X dans l'appareil de base doit être remplacé par tous les zéros.
  • XOR : Le périphérique COW stocke les octets compressés XOR entre le bloc X et le bloc Y . (Disponible sur Android 13 et supérieur.)

Les mises à jour OTA complètes consistent uniquement en des opérations de remplacement et zéro . Les mises à jour OTA incrémentielles peuvent également avoir des opérations de copie .

dm-utilisateur dans Android 12

Le module de noyau dm-user permet userspace d'implémenter des périphériques de bloc de mappeur de périphériques. Une entrée de table dm-user crée un périphérique divers sous /dev/dm-user/<control-name> . Un processus userspace peut interroger le périphérique pour recevoir des demandes de lecture et d'écriture du noyau. Chaque demande a un tampon associé pour que l'espace utilisateur soit rempli (pour une lecture) ou propagé (pour une écriture).

Le module de noyau dm-user fournit une nouvelle interface visible par l'utilisateur pour le noyau qui ne fait pas partie de la base de code en amont de kernel.org. En attendant, Google se réserve le droit de modifier l'interface dm-user d'Android.

snapuser

Le composant d'espace utilisateur snapuserd de dm-user implémente la compression Virtual A/B.

Dans la version non compressée de Virtual A/B (soit sous Android 11 et versions antérieures, soit sous Android 12 sans l'option d'instantané compressé), le périphérique COW est un fichier brut. Lorsque la compression est activée, le COW fonctionne à la place comme un périphérique dm-user , qui est connecté à une instance du démon snapuserd .

Le noyau n'utilise pas le nouveau format COW. Ainsi, le composant snapuserd traduit les requêtes entre le format Android COW et le format intégré du noyau :

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

Figure 3. Organigramme de snapuserd en tant que traducteur entre les formats Android et Kernel COW

Cette traduction et cette décompression ne se produisent jamais sur le disque. Le composant snapuserd intercepte les lectures et écritures COW qui se produisent dans le noyau et les implémente à l'aide du format Android COW.

Compression XOR

Pour les appareils lancés avec Android 13 et versions ultérieures, la fonction de compression XOR, qui est activée par défaut, permet aux instantanés de l'espace utilisateur de stocker des octets compressés XOR entre les anciens blocs et les nouveaux blocs. Lorsque seuls quelques octets d'un bloc sont modifiés dans une mise à jour Virtual A/B, le schéma de stockage de compression XOR utilise moins d'espace que le schéma de stockage par défaut, car les instantanés ne stockent pas les octets 4K complets. Cette réduction de la taille de l'instantané est possible car les données XOR contiennent de nombreux zéros et sont plus faciles à compresser que les données de bloc brutes. Sur les appareils Pixel, la compression XOR réduit la taille de l'instantané de 25 % à 40 %.

Pour les appareils mis à niveau vers Android 13 et versions ultérieures, la compression XOR doit être activée. Pour plus de détails, voir Compression XOR .

Processus de compression A/B virtuels

Cette section fournit des détails sur le processus de compression Virtual A/B utilisé dans Android 13 et Android 12.

Lecture des métadonnées (Android 12)

Les métadonnées sont construites par un démon snapuserd . Les métadonnées sont principalement un mappage de deux identifiants, de 8 octets chacun, qui représentent les secteurs à fusionner. Dans dm-snapshot il s'appelle disk_exception .

struct disk_exception {
    uint64_t old_chunk;
    uint64_t new_chunk;
};

Une exception de disque est utilisée lorsqu'un ancien bloc de données est remplacé par un nouveau.

Un démon snapuserd lit le fichier COW interne via la bibliothèque COW et construit les métadonnées pour chacune des opérations COW présentes dans le fichier COW.

Les lectures de métadonnées sont lancées à partir du dm-snapshot dans le noyau lorsque le périphérique dm- snapshot est créé.

La figure ci-dessous fournit un diagramme de séquence pour le chemin d'E/S pour la construction des métadonnées.

Sequence diagram, IO path for metadata construction

Figure 4. Flux de séquence pour le chemin IO dans la construction des métadonnées

Fusion (Android 12)

Une fois le processus de démarrage terminé, le moteur de mise à jour marque l'emplacement comme démarrage réussi et lance la fusion en basculant la cible dm-snapshot vers la cible dm-snapshot-merge .

dm-snapshot parcourt les métadonnées et lance une fusion IO pour chaque exception de disque. Une vue d'ensemble de haut niveau du chemin d'E/S de fusion est présentée ci-dessous.

Merge IO path

Figure 5. Vue d'ensemble du chemin d'E/S de fusion

Si le périphérique est redémarré pendant le processus de fusion, la fusion reprend au redémarrage suivant et la fusion est terminée.

Superposition de mappeurs d'appareils

Pour les appareils lancés avec Android 13 et versions ultérieures, les processus de fusion d'instantané et d'instantané dans la compression virtuelle A/B sont effectués par le composant d'espace utilisateur snapuserd . Pour les appareils mis à niveau vers Android 13 et versions ultérieures, cette fonctionnalité doit être activée. Pour plus de détails, voir Fusion de l'espace utilisateur .

Ce qui suit décrit le processus de compression Virtual A/B :

  1. Le framework monte la partition /system à partir d'un périphérique dm-verity , qui est empilé sur un périphérique dm-user . Cela signifie que chaque E/S du système de fichiers racine est acheminée vers dm-user .
  2. dm-user achemine les E/S vers le démon snapuserd de l'espace utilisateur, qui gère la demande d'E/S.
  3. Lorsque l'opération de fusion est terminée, le framework réduit dm-verity au-dessus de dm-linear ( system_base ) et supprime dm-user .

Processus de compression A/B virtuel

Figure 6. Processus de compression A/B virtuel

Le processus de fusion d'instantanés peut être interrompu. Si le périphérique est redémarré pendant le processus de fusion, le processus de fusion reprend après le redémarrage.

Transitions d'initialisation

Lors du démarrage avec des instantanés compressés, l'init de première étape doit démarrer snapuserd pour monter les partitions. Cela pose un problème : lorsque sepolicy est chargé et appliqué, snapuserd est placé dans le mauvais contexte et ses requêtes de lecture échouent, avec des refus de selinux.

Pour résoudre ce problème, snapuserd effectue la transition en lock-step avec init , comme suit :

  1. La première étape init lance snapuserd à partir du disque virtuel et y enregistre un descripteur de fichier ouvert dans une variable d'environnement.
  2. La première étape init bascule le système de fichiers racine vers la partition système, puis exécute la copie système de init .
  3. La copie système de init lit la sepolicy combinée dans une chaîne.
  4. Init invoque mlock() sur toutes les pages basées sur ext4. Il désactive ensuite toutes les tables de mappeur de périphériques pour les périphériques d'instantané et arrête snapuserd . Après cela, il est interdit de lire à partir des partitions, car cela provoque un blocage.
  5. En utilisant le descripteur ouvert de la copie du disque virtuel de snapuserd , init relance le démon avec le contexte selinux correct. Les tables de mappeur de périphériques pour les périphériques d'instantané sont réactivées.
  6. Init invoque munlockall() - il est sûr d'effectuer à nouveau des E/S.

Utilisation de l'espace

Le tableau suivant fournit une comparaison de l'utilisation de l'espace pour différents mécanismes OTA utilisant les tailles de système d'exploitation et d'OTA de Pixel.

Incidence sur la taille non A/B UN B A/B virtuel A/B virtuel (compressé)
Image d'usine d'origine Super 4,5 Go (image 3,8 Go + 700 Mo réservés) 1 9 Go super (3.8G + 700M réservés, pour deux slots) Super 4,5 Go (image 3,8 G + 700 M réservés) Super 4,5 Go (image 3,8 G + 700 M réservés)
Autres partitions statiques / cache Aucun Aucun Aucun
Stockage supplémentaire pendant l'OTA (espace restitué après l'application de l'OTA) 1,4 Go sur /données 0 3,8 Go 2 sur / données 2,1 Go 2 sur /données
Stockage total requis pour appliquer l'OTA 5,9 Go 3 (super et données) 9 Go (super) 8,3 Go 3 (super et données) 6,6 Go 3 (super et données)

1 Indique une mise en page supposée basée sur le mappage des pixels.

2 Suppose que la nouvelle image système a la même taille que l'original.

3 L'espace requis est transitoire jusqu'au redémarrage.

Pour implémenter Virtual A/B ou pour utiliser les fonctionnalités d'instantanés compressés, voir Implémentation de Virtual A/B