Dans Android 12, GKI 2.0 remplace l'allocateur ION par des tas DMA-BUF pour les raisons suivantes :
- Sécurité : étant donné que chaque tas DMA-BUF est un périphérique de caractères distinct, l'accès à chaque tas peut être contrôlé séparément avec sepolicy. Cela n'était pas possible avec ION, car l'allocation à partir d'un tas ne nécessitait qu'un accès à l'appareil
/dev/ion
. - Stabilité de l'ABI : contrairement à ION, l'interface IOCTL du framework de tas DMA-BUF est stable au niveau de l'ABI, car elle est gérée dans le noyau Linux en amont.
- Normalisation : le framework de tas DMA-BUF offre une UAPI bien définie. ION autorisait les indicateurs personnalisés et les ID de tas qui empêchaient le développement d'un framework de test commun, car l'implémentation ION de chaque appareil pouvait se comporter différemment.
La branche android12-5.10
du noyau commun Android a été désactivée le 1er mars 2021.CONFIG_ION
Arrière-plan
Vous trouverez ci-dessous une brève comparaison entre les tas ION et DMA-BUF.
Similitudes entre le framework de tas ION et DMA-BUF
- Les frameworks de tas ION et DMA-BUF sont tous deux des exportateurs DMA-BUF basés sur des tas.
- Ils permettent tous deux à chaque tas de définir ses propres opérations d'allocateur et de DMA-BUF.
- Les performances d'allocation sont similaires, car les deux schémas nécessitent un seul IOCTL pour l'allocation.
Différences entre le framework des tas ION et DMA-BUF
Tas ION | Tas DMA-BUF |
---|---|
Toutes les allocations ION sont effectuées avec /dev/ion .
|
Chaque tas DMA-BUF est un périphérique à caractères présent à /dev/dma_heap/<heap_name> .
|
ION est compatible avec les indicateurs privés de tas. | Les tas DMA-BUF ne sont pas compatibles avec les indicateurs privés de tas. Chaque type d'allocation différent est effectué à partir d'un tas différent. Par exemple, les variantes de tas système mises en cache et non mises en cache sont des tas distincts situés à /dev/dma_heap/system et /dev/dma_heap/system_uncached .
|
L'ID/le masque et les indicateurs du tas doivent être spécifiés pour l'allocation. | Le nom du tas est utilisé pour l'allocation. |
Les sections suivantes listent les composants qui gèrent ION et décrivent comment les transférer vers le framework de tas DMA-BUF.
Transition des pilotes de noyau d'ION vers les tas DMA-BUF
Pilotes de noyau implémentant des tas ION
Les tas ION et DMA-BUF permettent à chaque tas d'implémenter ses propres allocateurs et opérations DMA-BUF. Vous pouvez ainsi passer d'une implémentation de tas ION à une implémentation de tas DMA-BUF en utilisant un ensemble différent d'API pour enregistrer le tas. Ce tableau présente les API d'enregistrement du tas ION et leurs API de tas DMA-BUF équivalentes.
Tas ION | Tas 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);
|
Les tas DMA-BUF ne sont pas compatibles avec les indicateurs privés de tas. Chaque variante du tas doit donc être enregistrée individuellement à l'aide de l'API dma_heap_add()
. Pour faciliter le partage de code, il est recommandé d'enregistrer toutes les variantes du même tas dans le même pilote.
L'exemple dma-buf: system_heap montre l'implémentation des variantes mises en cache et non mises en cache du tas système.
Utilisez ce modèle d'exemple de tas dma-buf pour créer un tas DMA-BUF à partir de zéro.
Pilotes de noyau allouant directement à partir des tas ION
Le framework de tas DMA-BUF propose également une interface d'allocation pour les clients dans le noyau. Au lieu de spécifier le masque et les indicateurs de tas pour sélectionner le type d'allocation, l'interface proposée par les tas DMA-BUF prend un nom de tas en entrée.
L'exemple suivant montre l'API d'allocation ION dans le noyau et ses API d'allocation de tas DMA-BUF équivalentes. Les pilotes du noyau peuvent utiliser l'API dma_heap_find()
pour interroger l'existence d'un tas. L'API renvoie un pointeur vers une instance de struct dma_heap, qui peut ensuite être transmis en tant qu'argument à l'API dma_heap_buffer_alloc()
.
Tas ION | Tas DMA-BUF |
---|---|
struct dma_buf *ion_alloc(size_t len, unsigned int heap_id_mask, unsigned int flags)
|
|
Pilotes de noyau qui utilisent des DMA-BUFs
Aucune modification n'est requise pour les pilotes qui importent uniquement des DMA-BUFs, car un tampon alloué à partir d'un tas ION se comporte exactement de la même manière qu'un tampon alloué à partir d'un tas DMA-BUF équivalent.
Transition des clients de l'espace utilisateur d'ION vers les tas DMA-BUF
Pour faciliter la transition des clients de l'espace utilisateur d'ION, une bibliothèque d'abstraction appelée libdmabufheap
est disponible. libdmabufheap
est compatible avec l'allocation dans les tas DMA-BUF et ION. Il vérifie d'abord si un tas DMA-BUF du nom spécifié existe et, dans le cas contraire, revient à un tas ION équivalent, s'il en existe un.
Les clients doivent initialiser un objet BufferAllocator
lors de leur initialisation au lieu d'ouvrir /dev/ion using
ion_open()
. En effet, les descripteurs de fichiers créés en ouvrant /dev/ion
et /dev/dma_heap/<heap_name>
sont gérés en interne par l'objet BufferAllocator
.
Pour passer de libion
à libdmabufheap
, modifiez le comportement des clients comme suit :
- Suivez le nom du tas à utiliser pour l'allocation, au lieu de l'ID/masque de tête et de l'indicateur de tas.
- Remplacez l'API
ion_alloc_fd()
, qui accepte un argument de masque de tas et d'indicateur, par l'APIBufferAllocator::Alloc()
, qui accepte un nom de tas.
Ce tableau illustre ces modifications en montrant comment libion
et libdmabufheap
effectuent une allocation de tas système non mis en cache.
Type d'allocation | libion | libdmabufheap |
---|---|---|
Allocation mise en cache à partir du tas de mémoire système | ion_alloc_fd(ionfd, size, 0, ION_HEAP_SYSTEM, ION_FLAG_CACHED, &fd)
|
allocator->Alloc("system", size)
|
Allocation non mise en cache à partir du tas de mémoire système | ion_alloc_fd(ionfd, size, 0, ION_HEAP_SYSTEM, 0, &fd)
|
allocator->Alloc("system-uncached", size)
|
La variante de tas système non mis en cache est en attente d'approbation en amont, mais fait déjà partie de la branche android12-5.10
.
Pour permettre la mise à niveau des appareils, l'API MapNameToIonHeap()
permet de mapper un nom de tas sur des paramètres de tas ION (nom ou masque de tas et indicateurs) afin de permettre à ces interfaces d'utiliser des allocations basées sur des noms. Voici un exemple d'allocation basée sur le nom.
La documentation de chaque API exposée par libdmabufheap
est disponible. La bibliothèque expose également un fichier d'en-tête pour une utilisation par les clients C.
Implémentation de référence de Gralloc
L'implémentation gralloc Hikey960 utilise libdmabufheap
. Vous pouvez donc l'utiliser comme implémentation de référence.
Ajouts ueventd requis
Pour tout nouveau tas DMA-BUF spécifique à un appareil créé, ajoutez une entrée au fichier ueventd.rc
de l'appareil.
L'exemple de configuration de ueventd pour prendre en charge les tas DMA-BUF montre comment procéder pour le tas système DMA-BUF.
Ajouts sepolicy requis
Ajoutez des autorisations sepolicy pour permettre à un client userspace d'accéder à un nouveau tas DMA-BUF. L'exemple add required permissions montre les autorisations sepolicy créées pour différents clients afin d'accéder au tas système DMA-BUF.
Accéder aux tas de fournisseurs à partir du code du framework
Pour garantir la conformité à Treble, le code du framework ne peut allouer que des catégories de tas de fournisseurs préapprouvées.
Sur la base des commentaires reçus des partenaires, Google a identifié deux catégories de tas de fournisseurs auxquels il faut accéder à partir du code du framework :
- Tas basés sur le tas système avec des optimisations de performances spécifiques à l'appareil ou au SoC.
- Tas à allouer à partir de la mémoire protégée.
Tas basés sur le tas système avec des optimisations de performances spécifiques à l'appareil ou au SoC
Pour prendre en charge ce cas d'utilisation, l'implémentation du tas du système de tas DMA-BUF par défaut peut être remplacée.
CONFIG_DMABUF_HEAPS_SYSTEM
est désactivé dansgki_defconfig
pour qu'il puisse être un module fournisseur.- Les tests de conformité VTS garantissent que le tas existe à l'adresse
/dev/dma_heap/system
. Les tests vérifient également que le tas peut être alloué et que le descripteur de fichier renvoyé (fd
) peut être mappé en mémoire (mmapped) à partir de l'espace utilisateur.
Les points précédents sont également valables pour la variante non mise en cache du tas système, bien que son existence ne soit pas obligatoire pour les appareils entièrement cohérents avec les E/S.
Tas à allouer à partir de la mémoire protégée
Les implémentations de tas sécurisé doivent être spécifiques au fournisseur, car le noyau commun Android ne prend pas en charge une implémentation de tas sécurisé générique.
- Enregistrez vos implémentations spécifiques au fournisseur en tant que
/dev/dma_heap/system-secure<vendor-suffix>
. - Ces implémentations de tas sont facultatives.
- Si les tas existent, les tests VTS garantissent que des allocations peuvent être effectuées à partir d'eux.
- Les composants du framework ont accès à ces tas afin de pouvoir activer leur utilisation via les HAL Codec2/HAL non binderisées et HAL de même processus. Toutefois, les fonctionnalités génériques du framework Android ne peuvent pas en dépendre en raison de la variabilité de leurs détails d'implémentation. Si une implémentation de tas sécurisé générique est ajoutée au noyau commun Android à l'avenir, elle devra utiliser une ABI différente pour éviter les conflits avec les appareils mis à niveau.
Allocateur de codec 2 pour les tas DMA-BUF
Un allocateur codec2 pour l'interface des tas DMA-BUF est disponible dans AOSP.
L'interface du magasin de composants qui permet de spécifier les paramètres de tas à partir du HAL C2 est disponible avec l'allocateur de tas DMA-BUF C2.
Exemple de flux de transition pour un tas ION
Pour faciliter la transition des tas ION vers les tas DMA-BUF, libdmabufheap
permet de changer un tas à la fois. Les étapes suivantes illustrent un workflow suggéré pour la transition d'un tas ION non hérité nommé my_heap
qui prend en charge un indicateur, ION_FLAG_MY_FLAG
.
Étape 1 : Créez des équivalents du tas ION dans le framework DMA-BUF. Dans cet exemple, étant donné que le tas ION my_heap
accepte un indicateur ION_FLAG_MY_FLAG
, nous enregistrons deux tas DMA-BUF :
- Le comportement de
my_heap
correspond exactement à celui du tas ION lorsque l'indicateurION_FLAG_MY_FLAG
est désactivé. - Le comportement de
my_heap_special
correspond exactement à celui du tas ION avec l'indicateurION_FLAG_MY_FLAG
activé.
Étape 2 : Créez les modifications ueventd pour les nouveaux tas DMA-BUF my_heap
et my_heap_special
. À ce stade, les tas sont visibles sous la forme /dev/dma_heap/my_heap
et /dev/dma_heap/my_heap_special
, avec les autorisations prévues.
Étape 3 : Pour les clients qui allouent à partir de my_heap
, modifiez leurs fichiers makefile pour les associer à libdmabufheap
. Lors de l'initialisation du client, instanciez un objet BufferAllocator
et utilisez l'API MapNameToIonHeap()
pour mapper la combinaison <ION heap name/mask, flag>
aux noms de tas DMA-BUF équivalents.
Exemple :
allocator->MapNameToIonHeap("my_heap_special" /* name of DMA-BUF heap */, "my_heap" /* name of the ION heap */, ION_FLAG_MY_FLAG /* ion flags */ )
Au lieu d'utiliser l'API MapNameToIonHeap()
avec les paramètres de nom et d'indicateur, vous pouvez créer le mappage de <ION heap mask, flag>
aux noms de tas DMA-BUF équivalents en définissant le paramètre de nom de tas ION sur une valeur vide.
Étape 4 : Remplacez les invocations ion_alloc_fd()
par BufferAllocator::Alloc()
en utilisant le nom de tas approprié.
Type d'allocation | libion | libdmabufheap |
---|---|---|
Attribution de my_heap avec l'option ION_FLAG_MY_FLAG non définie
|
ion_alloc_fd(ionfd, size, 0, ION_HEAP_MY_HEAP, 0, &fd)
|
allocator->Alloc("my_heap", size)
|
Attribution à partir de my_heap avec l'option ION_FLAG_MY_FLAG définie
|
ion_alloc_fd(ionfd, size, 0, ION_HEAP_MY_HEAP,
ION_FLAG_MY_FLAG, &fd)
|
allocator->Alloc("my_heap_special", size)
|
À ce stade, le client est fonctionnel, mais il alloue toujours à partir du tas ION, car il ne dispose pas des autorisations sepolicy requises pour ouvrir le tas DMA-BUF.
Étape 5 : Créez les autorisations sepolicy requises pour que le client puisse accéder aux nouveaux tas DMA-BUF. Le client est désormais entièrement équipé pour allouer à partir du nouveau tas DMA-BUF.
Étape 6 : Vérifiez que les allocations sont effectuées à partir du nouveau tas DMA-BUF en examinant logcat.
Étape 7 : Désactivez le tas ION my_heap
dans le noyau. Si le code client n'a pas besoin d'être compatible avec la mise à niveau des appareils (dont les noyaux ne sont compatibles qu'avec les tas ION), vous pouvez également supprimer les appels MapNameToIonHeap()
.