Transizione da ION a DMA-BUF Heap

In Android 12, GKI 2.0 sostituisce l'allocatore ION con heap DMA-BUF per i seguenti motivi:

  • Sicurezza: poiché ogni heap DMA-BUF è un dispositivo a caratteri separato, l'accesso a ogni heap può essere controllato separatamente con sepolicy. Ciò non era possibile con ION perché l'allocazione da qualsiasi heap richiedeva solo l'accesso al dispositivo /dev/ion .
  • Stabilità ABI: a differenza di ION, l'interfaccia IOCTL del framework heap DMA-BUF è garantita come stabile ABI perché è mantenuta nel kernel Linux upstream.
  • Standardizzazione: il framework degli heap DMA-BUF offre una UAPI ben definita. ION consentiva flag personalizzati e ID heap che impedivano lo sviluppo di un framework di test comune perché l'implementazione ION di ciascun dispositivo poteva comportarsi in modo diverso.

Il ramo android12-5.10 del kernel comune Android ha disabilitato CONFIG_ION il 1° marzo 2021 .

Sfondo

Di seguito è riportato un breve confronto tra gli heap ION e DMA-BUF.

Somiglianze tra il framework degli heap ION e DMA-BUF

  • I framework heap ION e DMA-BUF sono entrambi esportatori DMA-BUF basati su heap.
  • Entrambi consentono a ciascun heap di definire il proprio allocatore e le operazioni DMA-BUF.
  • Le prestazioni di allocazione sono simili perché entrambi gli schemi necessitano di un singolo IOCTL per l'allocazione.

Differenze tra il framework degli heap ION e DMA-BUF

Cumuli di IONI Cumuli DMA-BUF
Tutte le allocazioni ION vengono eseguite con /dev/ion . Ogni heap DMA-BUF è un dispositivo a caratteri presente in /dev/dma_heap/<heap_name> .
ION supporta i flag privati ​​dell'heap. Gli heap DMA-BUF non supportano i flag privati ​​dell'heap. Ogni diverso tipo di allocazione viene invece eseguito da un heap diverso. Ad esempio, le varianti dell'heap di sistema memorizzato nella cache e non memorizzato nella cache sono heap separati situati in /dev/dma_heap/system e /dev/dma_heap/system_uncached .
È necessario specificare l'ID/maschera e i flag dell'heap per l'allocazione. Il nome dell'heap viene utilizzato per l'allocazione.

Le sezioni seguenti elencano i componenti che gestiscono ION e descrivono come passarli al framework di heap DMA-BUF.

Transizione dei driver del kernel dagli heap ION agli heap DMA-BUF

Driver del kernel che implementano gli heap ION

Sia gli heap ION che quelli DMA-BUF consentono a ciascun heap di implementare i propri allocatori e le operazioni DMA-BUF. Quindi puoi passare da un'implementazione dell'heap ION a un'implementazione dell'heap DMA-BUF utilizzando un diverso set di API per registrare l'heap. Questa tabella mostra le API di registrazione dell'heap ION e le relative API dell'heap DMA-BUF equivalenti.

Cumuli di IONI Cumuli DMA-BUF
void ion_device_add_heap(struct ion_heap *heap) struct dma_heap *dma_heap_add(const struct dma_heap_export_info *exp_info);
void ion_device_remove_heap(struct ion_heap *heap) void dma_heap_put(struct dma_heap *heap);

Gli heap DMA-BUF non supportano i flag privati ​​dell'heap. Quindi ogni variante dell'heap deve essere registrata individualmente utilizzando l'API dma_heap_add() . Per facilitare la condivisione del codice, si consiglia di registrare tutte le varianti dello stesso heap all'interno dello stesso driver. Questo esempio dma-buf: system_heap mostra l'implementazione delle varianti memorizzate nella cache e non memorizzate nella cache dell'heap di sistema.

Utilizza questo dma-buf: heaps: modello di esempio per creare un heap DMA-BUF da zero.

Driver del kernel che eseguono l'allocazione diretta dagli heap ION

Il framework degli heap DMA-BUF offre anche un'interfaccia di allocazione per i client in-kernel. Invece di specificare la maschera dell'heap e i flag per selezionare il tipo di allocazione, l'interfaccia offerta dagli heap DMA-BUF accetta un nome di heap come input.

Di seguito viene mostrata l'API di allocazione ION nel kernel e le relative API di allocazione heap DMA-BUF equivalenti. I driver del kernel possono utilizzare l'API dma_heap_find() per interrogare l'esistenza di un heap. L'API restituisce un puntatore a un'istanza della struttura dma_heap , che può quindi essere passata come argomento all'API dma_heap_buffer_alloc() .

Cumuli di IONI Cumuli DMA-BUF
struct dma_buf *ion_alloc(size_t len, unsigned int heap_id_mask, unsigned int flags)

struct dma_heap *dma_heap_find(const char *name)

struct dma_buf *struct dma_buf *dma_heap_buffer_alloc(struct dma_heap *heap, size_t len, unsigned int fd_flags, unsigned int heap_flags)

Driver del kernel che utilizzano DMA-BUF

Non sono richieste modifiche per i driver che importano solo DMA-BUF, poiché un buffer allocato da un heap ION si comporta esattamente come un buffer allocato da un heap DMA-BUF equivalente.

Transizione dei client dello spazio utente di ION agli heap DMA-BUF

Per facilitare la transizione per i client dello spazio utente di ION, è disponibile una libreria di astrazione chiamata libdmabufheap . libdmabufheap supporta l'allocazione negli heap DMA-BUF e negli heap ION. Innanzitutto controlla se esiste un heap DMA-BUF con il nome specificato e, in caso contrario, ritorna a un heap ION equivalente, se ne esiste uno.

I client dovrebbero inizializzare un oggetto BufferAllocator durante l'inizializzazione invece di aprire /dev/ion using ion_open() . Questo perché i descrittori di file creati aprendo /dev/ion e /dev/dma_heap/<heap_name> sono gestiti internamente dall'oggetto BufferAllocator .

Per passare da libion ​​a libdmabufheap , modificare il comportamento dei client come segue:

  • Tieni traccia del nome dell'heap da utilizzare per l'allocazione, anziché dell'ID/maschera dell'intestazione e del flag dell'heap.
  • Sostituisci l'API ion_alloc_fd() , che accetta una maschera heap e un argomento flag, con l'API BufferAllocator::Alloc() , che accetta invece un nome heap.

Questa tabella illustra queste modifiche mostrando come libion ​​e libdmabufheap eseguono un'allocazione dell'heap di sistema non memorizzato nella cache.

Tipo di assegnazione libico libdmabufheap
Allocazione memorizzata nella cache dall'heap di sistema ion_alloc_fd(ionfd, size, 0, ION_HEAP_SYSTEM, ION_FLAG_CACHED, &fd) allocator->Alloc("system", size)
Allocazione non memorizzata nella cache dall'heap di sistema ion_alloc_fd(ionfd, size, 0, ION_HEAP_SYSTEM, 0, &fd) allocator->Alloc("system-uncached", size)

La variante dell'heap di sistema non memorizzato nella cache è in attesa di approvazione a monte, ma fa già parte del ramo android12-5.10 .

Per supportare l'aggiornamento dei dispositivi, l'API MapNameToIonHeap() consente di mappare un nome heap sui parametri heap ION (nome/maschera heap e flag) per consentire a tali interfacce di utilizzare anche allocazioni basate sul nome. Di seguito è riportato un esempio di allocazione basata sul nome .

È disponibile la documentazione per ogni API esposta da libdmabufheap . La libreria espone anche un file di intestazione utilizzabile dai client C.

Implementazione di Galloc di riferimento

L'implementazione gralloc di Hikey960 utilizza libdmabufheap , quindi puoi usarla come implementazione di riferimento .

Aggiunte uventd richieste

Per ogni nuovo heap DMA-BUF specifico del dispositivo creato, aggiungere una nuova voce al file ueventd.rc del dispositivo. Questo esempio di installazione di eventi per supportare gli heap DMA-BUF illustra come eseguire questa operazione per l'heap di sistema DMA-BUF.

Aggiunte sepolicy richieste

Aggiungi autorizzazioni sepolicy per consentire a un client dello spazio utente di accedere a un nuovo heap DMA-BUF. Questo esempio di aggiunta delle autorizzazioni richieste mostra le autorizzazioni sepolicy create per vari client per accedere all'heap di sistema DMA-BUF.

Accesso agli heap dei fornitori dal codice del framework

Per garantire la conformità Treble, il codice framework può allocare solo da categorie pre-approvate di heap del fornitore.

Sulla base del feedback ricevuto dai partner, Google ha identificato due categorie di heap dei fornitori a cui è necessario accedere dal codice del framework:

  1. Heap basati sull'heap di sistema con ottimizzazioni delle prestazioni specifiche del dispositivo o del SoC.
  2. Heap da allocare dalla memoria protetta.

Heap basati sull'heap del sistema con ottimizzazioni delle prestazioni specifiche del dispositivo o del SoC

Per supportare questo caso d'uso, è possibile sovrascrivere l'implementazione heap del sistema heap DMA-BUF predefinito.

  • CONFIG_DMABUF_HEAPS_SYSTEM è disattivato in gki_defconfig per consentirgli di essere un modulo del fornitore.
  • I test di conformità VTS assicurano che l'heap esista in /dev/dma_heap/system . I test verificano anche che l'heap possa essere allocato e che il descrittore di file restituito ( fd ) possa essere mappato in memoria (mmapped) dallo spazio utente.

I punti precedenti valgono anche per la variante senza cache dell'heap di sistema, sebbene la sua esistenza non sia obbligatoria per i dispositivi con coerenza I/O completa.

Heap da allocare dalla memoria protetta

Le implementazioni dell'heap sicuro devono essere specifiche del fornitore poiché Android Common Kernel non supporta un'implementazione generica dell'heap sicuro.

  • Registra le implementazioni specifiche del tuo fornitore come /dev/dma_heap/system-secure<vendor-suffix> .
  • Queste implementazioni dell'heap sono facoltative.
  • Se gli heap esistono, i test VTS assicurano che da essi possano essere effettuate le allocazioni.
  • Ai componenti del framework viene fornito l'accesso a questi heap in modo che possano abilitare l'utilizzo degli heap tramite HAL Codec2/HAL non binderizzati e con lo stesso processo. Tuttavia, le funzionalità generiche del framework Android non possono dipendere da loro a causa della variabilità nei dettagli di implementazione. Se in futuro un'implementazione generica dell'heap sicuro viene aggiunta al kernel comune di Android, dovrà utilizzare un'ABI diversa per evitare conflitti con i dispositivi di aggiornamento.

Allocatore Codec 2 per heap DMA-BUF

Un allocatore codec2 per l'interfaccia heap DMA-BUF è disponibile in AOSP.

L'interfaccia dell'archivio componenti che consente di specificare i parametri heap dall'HAL C2 è disponibile con l'allocatore heap C2 DMA-BUF.

Flusso di transizione di esempio per un heap ION

Per facilitare la transizione dagli heap ION agli heap DMA-BUF, libdmabufheap consente di passare da un heap alla volta. I passaggi seguenti dimostrano un flusso di lavoro suggerito per la transizione di un heap ION non legacy denominato my_heap che supporta un flag, ION_FLAG_MY_FLAG .

Passaggio 1: creare equivalenti dell'heap ION nel framework DMA-BUF. In questo esempio, poiché l'heap ION my_heap supporta un flag ION_FLAG_MY_FLAG , registriamo due heap DMA-BUF:

  • Il comportamento my_heap corrisponde esattamente al comportamento dell'heap ION con il flag ION_FLAG_MY_FLAG disabilitato.
  • Il comportamento di my_heap_special corrisponde esattamente al comportamento dell'heap ION con il flag ION_FLAG_MY_FLAG abilitato.

Passaggio 2: creare le modifiche ueventd per i nuovi my_heap DMA-BUF my_heap e my_heap_special . A questo punto, gli heap sono visibili come /dev/dma_heap/my_heap e /dev/dma_heap/my_heap_special , con le autorizzazioni previste.

Passaggio 3: per i client che effettuano l'allocazione da my_heap , modificare i propri makefile per collegarsi a libdmabufheap . Durante l'inizializzazione del client, creare un'istanza di un oggetto BufferAllocator e utilizzare l'API MapNameToIonHeap() per mappare la combinazione <ION heap name/mask, flag> ai nomi heap DMA-BUF equivalenti.

Per esempio:

allocator->MapNameToIonHeap("my_heap_special" /* name of DMA-BUF heap */, "my_heap" /* name of the ION heap */, ION_FLAG_MY_FLAG /* ion flags */ )

Invece di utilizzare l'API MapNameToIonHeap() con i parametri name e flag, è possibile creare la mappatura da <ION heap mask, flag> ai nomi heap DMA-BUF equivalenti impostando il parametro del nome heap ION su vuoto.

Passaggio 4: sostituire le invocazioni ion_alloc_fd() con BufferAllocator::Alloc() utilizzando il nome heap appropriato.

Tipo di allocazione libico libdmabufheap
Allocazione da my_heap con flag ION_FLAG_MY_FLAG non impostato ion_alloc_fd(ionfd, size, 0, ION_HEAP_MY_HEAP, 0, &fd) allocator->Alloc("my_heap", size
Allocazione da my_heap con flag ION_FLAG_MY_FLAG impostato ion_alloc_fd(ionfd, size, 0, ION_HEAP_MY_HEAP, ION_FLAG_MY_FLAG, &fd) allocator->Alloc("my_heap_special", size)

A questo punto, il client è funzionante ma sta ancora effettuando l'allocazione dall'heap ION perché non dispone delle autorizzazioni sepolicy necessarie per aprire l'heap DMA-BUF.

Passaggio 5: creare le autorizzazioni sepolicy richieste affinché il client possa accedere ai nuovi heap DMA-BUF. Il client è ora completamente attrezzato per allocare dal nuovo heap DMA-BUF.

Passaggio 6: verificare che le allocazioni avvengano dal nuovo heap DMA-BUF esaminando logcat .

Passaggio 7: disabilitare l'heap ION my_heap nel kernel. Se il codice client non deve supportare l'aggiornamento dei dispositivi (i cui kernel potrebbero supportare solo heap ION), puoi anche rimuovere le invocazioni MapNameToIonHeap() .