Panoramica A/B virtuale

Android dispone di due meccanismi di aggiornamento: aggiornamenti A/B (senza interruzioni) e aggiornamenti non A/B. Per ridurre la complessità del codice e migliorare il processo di aggiornamento, in Android 11 i due meccanismi sono unificati tramite A/B virtuale per portare aggiornamenti senza interruzioni su tutti i dispositivi con un costo di archiviazione ridotto al minimo. Android 12 offre l'opzione della compressione A/B virtuale per comprimere le partizioni con snapshot. Sia in Android 11 che in Android 12 si applica quanto segue:

  • Gli aggiornamenti A/B virtuali sono fluidi come gli aggiornamenti A/B. Gli aggiornamenti A/B virtuali riducono al minimo il tempo in cui un dispositivo è offline e inutilizzabile.
  • È possibile eseguire il rollback degli aggiornamenti A/B virtuali. Se il nuovo sistema operativo non si avvia, i dispositivi ripristinano automaticamente la versione precedente.
  • Gli aggiornamenti A/B virtuali utilizzano un minimo di spazio aggiuntivo duplicando solo le partizioni utilizzate dal bootloader. Viene eseguita l'istantanea delle altre partizioni aggiornabili.

Contesto e terminologia

Questa sezione definisce la terminologia e descrive la tecnologia che supporta l'A/B virtuale.

Mappatore di dispositivi

Device-mapper è un livello di blocchi virtuali Linux utilizzato spesso in Android. Con le partizioni dinamiche , le partizioni come /system sono una pila di dispositivi a più livelli:

  • In fondo allo stack c'è la super partizione fisica (ad esempio, /dev/block/by-name/super ).
  • Nel mezzo c'è un dispositivo dm-linear , che specifica quali blocchi nella super partizione formano la partizione data. Appare come /dev/block/mapper/system_[a|b] su un dispositivo A/B o /dev/block/mapper/system su un dispositivo non A/B.
  • Nella parte superiore risiede un dispositivo dm-verity , creato per partizioni verificate. Questo dispositivo verifica che i blocchi sul dispositivo dm-linear siano firmati correttamente. Appare come /dev/block/mapper/system-verity ed è l'origine del punto di montaggio /system .

La Figura 1 mostra l'aspetto dello stack sotto il punto di montaggio /system .

Partition stacking underneath system

Figura 1. Stack sotto il punto di montaggio /system

dm-istantanea

L'A/B virtuale si basa su dm-snapshot , un modulo di mappatura dei dispositivi per creare istantanee dello stato di un dispositivo di archiviazione. Quando si utilizza dm-snapshot , sono quattro i dispositivi in ​​gioco:

  • Il dispositivo di base è il dispositivo di cui è stato eseguito lo snapshot. In questa pagina, il dispositivo di base è sempre una partizione dinamica, ad esempio sistema o fornitore.
  • Il dispositivo copy-on-write (COW), per la registrazione delle modifiche sul dispositivo di base. Può avere qualsiasi dimensione, ma deve essere abbastanza grande da contenere tutte le modifiche apportate al dispositivo di base.
  • Il dispositivo snapshot viene creato utilizzando la destinazione snapshot . Le scritture sul dispositivo snapshot vengono scritte sul dispositivo COW. Legge dal dispositivo snapshot legge dal dispositivo di base o dal dispositivo COW, a seconda che i dati a cui si accede siano stati modificati dallo snapshot.
  • Il dispositivo di origine viene creato utilizzando la destinazione snapshot-origin . Legge sul dispositivo di origine legge direttamente dal dispositivo di base. Le scritture sul dispositivo di origine scrivono direttamente sul dispositivo di base, ma viene eseguito il backup dei dati originali scrivendo sul dispositivo COW.

Device mapping for dm-snapshot

Figura 2. Mappatura del dispositivo per dm-snapshot

Istantanee compresse

In Android 12 e versioni successive, poiché i requisiti di spazio sulla partizione /data possono essere elevati, puoi abilitare gli snapshot compressi nella build per soddisfare i requisiti di spazio più elevati della partizione /data .

Gli snapshot compressi A/B virtuali si basano sui seguenti componenti disponibili in Android 12 e versioni successive:

  • dm-user , un modulo del kernel simile a FUSE che consente allo spazio utente di implementare dispositivi a blocchi.
  • snapuserd , un demone in spazio utente per implementare un nuovo formato di snapshot.

Questi componenti consentono la compressione. Le altre modifiche necessarie apportate per implementare le funzionalità degli snapshot compressi sono fornite nelle sezioni successive: formato COW per snapshot compressi , dm-user e Snapuserd .

Formato COW per istantanee compresse

In Android 12 e versioni successive, le istantanee compresse utilizzano il formato COW. Similmente al formato integrato del kernel utilizzato per le istantanee non compresse, il formato COW per le istantanee compresse presenta sezioni alternate di metadati e dati. I metadati del formato originale consentivano solo operazioni di sostituzione : sostituisci il blocco X nell'immagine di base con il contenuto del blocco Y nello snapshot. Il formato COW delle istantanee compresse è più espressivo e supporta le seguenti operazioni:

  • Copia : il blocco X nel dispositivo di base deve essere sostituito con il blocco Y nel dispositivo di base.
  • Sostituisci : il blocco X nel dispositivo di base deve essere sostituito con il contenuto del blocco Y nello snapshot. Ciascuno di questi blocchi è compresso con gz.
  • Zero : il blocco X nel dispositivo di base deve essere sostituito con tutti zeri.
  • XOR : il dispositivo COW memorizza byte compressi XOR tra il blocco X e il blocco Y. (Disponibile in Android 13 e versioni successive.)

Gli aggiornamenti OTA completi consistono solo in operazioni di sostituzione e zero . Gli aggiornamenti OTA incrementali possono inoltre avere operazioni di copia .

utente dm in Android 12

Il modulo del kernel dm-user consente userspace di implementare dispositivi a blocchi device-mapper. Una voce della tabella dm-user crea un dispositivo vario in /dev/dm-user/<control-name> . Un processo userspace può interrogare il dispositivo per ricevere richieste di lettura e scrittura dal kernel. Ogni richiesta ha un buffer associato per lo spazio utente da popolare (per una lettura) o propagare (per una scrittura).

Il modulo del kernel dm-user fornisce una nuova interfaccia visibile all'utente al kernel che non fa parte del codice base kernel.org upstream. Fino a quando non lo sarà, Google si riserva il diritto di modificare l'interfaccia dm-user in Android.

snapuserd

Il componente userspace snapuserd per dm-user implementa la compressione A/B virtuale.

Nella versione non compressa di Virtual A/B (in Android 11 e versioni precedenti o in Android 12 senza l'opzione di snapshot compresso), il dispositivo COW è un file raw. Quando la compressione è abilitata, COW funziona invece come un dispositivo dm-user , connesso a un'istanza del demone snapuserd .

Il kernel non utilizza il nuovo formato COW. Quindi il componente snapuserd traduce le richieste tra il formato Android COW e il formato integrato del kernel:

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

Figura 3. Diagramma di flusso di snapuserd come traduttore tra i formati Android e Kernel COW

Questa traduzione e decompressione non avviene mai sul disco. Il componente snapuserd intercetta le letture e le scritture COW che si verificano nel kernel e le implementa utilizzando il formato Android COW.

Compressione XOR

Per i dispositivi avviati con Android 13 e versioni successive, la funzionalità di compressione XOR, abilitata per impostazione predefinita, consente agli snapshot dello spazio utente di archiviare byte compressi XOR tra blocchi vecchi e nuovi. Quando vengono modificati solo pochi byte in un blocco in un aggiornamento A/B virtuale, lo schema di archiviazione di compressione XOR utilizza meno spazio rispetto allo schema di archiviazione predefinito perché gli snapshot non archiviano byte 4K completi. Questa riduzione delle dimensioni dello snapshot è possibile perché i dati XOR contengono molti zeri e sono più facili da comprimere rispetto ai dati a blocchi grezzi. Sui dispositivi Pixel, la compressione XOR riduce le dimensioni dell'istantanea dal 25% al ​​40%.

Per i dispositivi che eseguono l'aggiornamento ad Android 13 e versioni successive, la compressione XOR deve essere abilitata. Per i dettagli, vedere Compressione XOR .

Processi di compressione A/B virtuale

Questa sezione fornisce dettagli sul processo di compressione A/B virtuale utilizzato in Android 13 e Android 12.

Lettura dei metadati (Android 12)

I metadati sono costruiti da un demone snapuserd . I metadati sono principalmente una mappatura di due ID, 8 byte ciascuno, che rappresentano i settori da unire. In dm-snapshot si chiama disk_exception .

struct disk_exception {
    uint64_t old_chunk;
    uint64_t new_chunk;
};

Un'eccezione del disco viene utilizzata quando una vecchia porzione di dati viene sostituita da una nuova.

Un demone snapuserd legge il file COW interno tramite la libreria COW e costruisce i metadati per ciascuna delle operazioni COW presenti nel file COW.

Le letture dei metadati vengono avviate da dm-snapshot nel kernel quando viene creato il dispositivo dm- snapshot .

La figura seguente fornisce un diagramma di sequenza per il percorso IO per la costruzione dei metadati.

Sequence diagram, IO path for metadata construction

Figura 4. Flusso di sequenza per il percorso IO nella costruzione dei metadati

Unione (Android 12)

Una volta completato il processo di avvio, il motore di aggiornamento contrassegna lo slot come avvio riuscito e avvia l'unione cambiando la destinazione dm-snapshot con la destinazione dm-snapshot-merge .

dm-snapshot esamina i metadati e avvia un I/O di unione per ogni eccezione del disco. Di seguito è mostrata una panoramica di alto livello del percorso IO di unione.

Merge IO path

Figura 5. Panoramica del percorso IO di unione

Se il dispositivo viene riavviato durante il processo di unione, l'unione riprenderà al riavvio successivo e sarà completata.

Stratificazione del mappatore del dispositivo

Per i dispositivi avviati con Android 13 e versioni successive, i processi di snapshot e unione di snapshot nella compressione A/B virtuale vengono eseguiti dal componente userspace snapuserd . Per i dispositivi che eseguono l'aggiornamento ad Android 13 e versioni successive, questa funzione deve essere abilitata. Per i dettagli, consulta Unione dello spazio utente .

Di seguito viene descritto il processo di compressione A/B virtuale:

  1. Il framework monta la partizione /system da un dispositivo dm-verity , che è impilato sopra un dispositivo dm-user . Ciò significa che ogni I/O dal file system root viene instradato a dm-user .
  2. dm-user instrada l'I/O al demone snapuserd dello spazio utente, che gestisce la richiesta I/O.
  3. Una volta completata l'operazione di unione, il framework comprime dm-verity sopra dm-linear ( system_base ) e rimuove dm-user .

Processo di compressione A/B virtuale

Figura 6. Processo di compressione A/B virtuale

Il processo di unione degli snapshot può essere interrotto. Se il dispositivo viene riavviato durante il processo di unione, il processo di unione riprenderà dopo il riavvio.

Transizioni iniziali

Quando si esegue l'avvio con istantanee compresse, l'init di prima fase deve avviare snapuserd per montare le partizioni. Ciò pone un problema: quando sepolicy viene caricata e applicata, snapuserd viene inserito nel contesto sbagliato e le sue richieste di lettura falliscono, con dinieghi selinux.

Per risolvere questo problema, le transizioni snapuserd vanno in sequenza con init , come segue:

  1. La prima fase init avvia snapuserd dal ramdisk e vi salva un descrittore di file aperto in una variabile di ambiente.
  2. La prima fase init trasferisce il filesystem root nella partizione di sistema, quindi esegue la copia di sistema di init .
  3. La copia di sistema di init legge la sepolicy combinata in una stringa.
  4. Init invoca mlock() su tutte le pagine supportate da ext4. Quindi disattiva tutte le tabelle di mappatura dei dispositivi per i dispositivi snapshot e interrompe snapuserd . Dopodiché è proibito leggere dalle partizioni, poiché ciò causerebbe un deadlock.
  5. Utilizzando il descrittore open sulla copia ramdisk di snapuserd , init rilancia il demone con il contesto selinux corretto. Le tabelle di mappatura dei dispositivi per i dispositivi snapshot vengono riattivate.
  6. Init invoca munlockall() : è sicuro eseguire nuovamente l'IO.

Utilizzo dello spazio

La tabella seguente fornisce un confronto tra l'utilizzo dello spazio per diversi meccanismi OTA utilizzando il sistema operativo e le dimensioni OTA di Pixel.

Impatto sulle dimensioni non A/B A/B A/B virtuale A/B virtuale (compresso)
Immagine di fabbrica originale Super 4,5 GB (immagine 3,8 G + 700 MB riservati) 1 9GB super (3.8G + 700M riservati, per due slot) Super 4,5 GB (immagine 3,8 G + 700 MB riservati) Super 4,5 GB (immagine 3,8 G + 700 MB riservati)
Altre partizioni statiche /cache Nessuno Nessuno Nessuno
Spazio di archiviazione aggiuntivo durante l'OTA (spazio restituito dopo l'applicazione dell'OTA) 1,4 GB su /dati 0 3,8 GB 2 su /dati 2,1 GB 2 su /dati
Spazio di archiviazione totale richiesto per applicare OTA 5,9 GB 3 (super e dati) 9 GB (eccellente) 8,3 GB 3 (super e dati) 6,6 GB 3 (super e dati)

1 Indica il layout presunto basato sulla mappatura dei pixel.

2 Si presuppone che la nuova immagine del sistema abbia le stesse dimensioni dell'originale.

3 Il requisito di spazio è temporaneo fino al riavvio.

Per implementare Virtual A/B o utilizzare funzionalità di snapshot compresse, consulta Implementazione di Virtual A/B