Dans Android 12, GKI 2.0 remplace l'outil d'allocation 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 de n'importe quel tas ne nécessitait que l'accès à l'appareil
/dev/ion
. - Stabilité de l'ABI: contrairement à ION, l'interface IOCTL du framework de tas DMA-BUF est stable en ABI, car elle est gérée dans le noyau Linux en amont.
- Standardisation: le framework de tas DMA-BUF offre une UAPI bien définie. ION permettait des indicateurs et des ID de tas personnalisés 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 désactivé CONFIG_ION
le 1er mars 2021.
Arrière-plan
Voici une brève comparaison entre les segments de mémoire ION et DMA-BUF.
Similarités 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 à chaque tas de définir son propre opérateur d'allocation et ses propres opérations 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 segments de mémoire 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 appareil de caractères présent dans /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 de tas privés. Chaque type d'allocation est effectué à partir d'un tas différent. Par exemple, les variantes de tas de mémoire système mis en cache et non mises en cache sont des segments de mémoire distincts, situés dans /dev/dma_heap/system et /dev/dma_heap/system_uncached .
|
L'ID/masque de tas et les indicateurs doivent être spécifiés pour l'allocation. | Le nom de la pile est utilisé pour l'allocation. |
Les sections suivantes énumèrent les composants qui traitent d'ION et décrivent comment les passer au framework de tas DMA-BUF.
Transition des pilotes de noyau d'ION vers les tas DMA-BUF
Pilotes de kernel implémentant des tas ION
Les tas ION et DMA-BUF permettent à chaque tas d'implémenter ses propres alloueurs et opérations DMA-BUF. Vous pouvez donc passer d'une implémentation de tas ION à une implémentation de tas DMA-BUF en utilisant un autre ensemble d'API pour enregistrer le tas. Ce tableau présente les API d'enregistrement de la pile ION et leurs API de pile DMA-BUF équivalentes.
Tas de mémoire 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 de tas privés. Chaque variante du tas de mémoire doit donc être enregistrée individuellement à l'aide de l'API dma_heap_add()
. Pour faciliter le partage de code, nous vous recommandons d'enregistrer toutes les variantes du même tas de mémoire dans le même pilote.
Cet exemple dma-buf: system_heap montre l'implémentation des variantes mises en cache et non mises en cache de la pile système.
Utilisez cet exemple de modèle dma-buf: heaps pour créer un tas DMA-BUF à partir de zéro.
Pilotes de kernel allouant directement à partir des tas ION
Le framework de segments de mémoire DMA-BUF offre également une interface d'allocation pour les clients du noyau. Au lieu de spécifier le masque de tas et les indicateurs pour sélectionner le type d'allocation, l'interface proposée par les tas DMA-BUF prend un nom de tas en entrée.
Vous trouverez ci-dessous 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 de mémoire. L'API renvoie un pointeur vers une instance de struct dma_heap, qui peut ensuite être transmise en tant qu'argument à l'API dma_heap_buffer_alloc()
.
Tas de mémoire ION | Tas DMA-BUF |
---|---|
struct dma_buf *ion_alloc(size_t len, unsigned int heap_id_mask, unsigned int flags)
|
|
Pilotes de kernel utilisant des DMA-BUF
Aucune modification n'est requise pour les pilotes qui n'importent que des DMA-BUF, car un tampon alloué à partir d'un tas de mémoire ION se comporte exactement de la même manière qu'un tampon alloué à partir d'un tas de mémoire DMA-BUF équivalent.
Faire passer les clients de l'espace utilisateur d'ION aux segments de mémoire DMA-BUF
Pour faciliter la transition des clients d'espace utilisateur d'ION, une bibliothèque d'abstraction appelée libdmabufheap
est disponible. libdmabufheap
prend en charge l'allocation dans les tas DMA-BUF et les tas ION. Il vérifie d'abord si un tas DMA-BUF du nom spécifié existe. Si ce n'est pas le cas, il utilise un tas ION équivalent, le cas échéant.
Les clients doivent initialiser un objet BufferAllocator
lors de l'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:
- Gardez une trace du nom de la pile à utiliser pour l'allocation, au lieu de l'ID/du masque de tête et de l'indicateur de pile.
- Remplacez l'API
ion_alloc_fd()
, qui accepte un masque de tas et un argument d'indicateur, par l'APIBufferAllocator::Alloc()
, qui accepte plutôt un nom de tas.
Ce tableau illustre ces modifications en montrant comment libion
et libdmabufheap
effectuent une allocation de tas système non mise en cache.
Type d'allocation | Libion | Libdmabufheap |
---|---|---|
Allocation en cache à partir du tas de mémoire du 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 du tas de mémoire du système non mis en cache est en attente d'approbation en amont, mais elle fait déjà partie de la branche android12-5.10
.
Pour prendre en charge 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 un nom. Voici un exemple d'allocation basée sur un nom.
La documentation de chaque API exposée par libdmabufheap
est disponible. La bibliothèque expose également un fichier d'en-tête destiné aux 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 obligatoires
Pour chaque tas DMA-BUF créé spécifique à l'appareil, ajoutez une entrée au fichier ueventd.rc
de l'appareil.
Cet 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 de sepolicy requis
Ajoutez des autorisations sepolicy pour permettre à un client d'espace utilisateur d'accéder à une nouvelle pile DMA-BUF. Cet exemple d'ajout d'autorisations requises montre les autorisations sepolicy créées pour différents clients afin d'accéder au tas système DMA-BUF.
Accéder aux segments de mémoire des fournisseurs à partir du code du framework
Pour garantir la conformité avec 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 de la part de ses 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 de mémoire à 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 de la pile du système de pile DMA-BUF par défaut peut être remplacée.
CONFIG_DMABUF_HEAPS_SYSTEM
est désactivé dansgki_defconfig
pour qu'il puisse s'agir d'un module de fournisseur.- Les tests de conformité VTS garantissent que la pile existe à
/dev/dma_heap/system
. Les tests vérifient également que la mémoire tampon peut être allouée à partir de l'espace utilisateur 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 de la pile système, bien que son existence ne soit pas obligatoire pour les appareils entièrement cohérents en E/S.
Tas à allouer à partir de la mémoire protégée
Les implémentations de tas sécurisées doivent être spécifiques au fournisseur, car le noyau commun Android n'est pas compatible avec 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 s'assurent qu'ils peuvent être alloués.
- Les composants du framework disposent d'un accès à ces tas afin de pouvoir activer leur utilisation via le HAL Codec2/les HAL du même processus non liés. Toutefois, les fonctionnalités génériques du framework Android ne peuvent pas en dépendre en raison de la variabilité des détails de leur implémentation. Si une implémentation de tas sécurisé générique est ajoutée au noyau commun Android à l'avenir, elle doit utiliser une ABI différente pour éviter les conflits avec la mise à niveau des appareils.
Allocator 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 des paramètres de tas de mémoire à partir du HAL C2 est disponible avec l'outil d'allocation de tas de mémoire DMA-BUF C2.
Exemple de flux de transition pour une pile ION
Pour faciliter la transition d'une pile ION vers une pile DMA-BUF, libdmabufheap
permet de basculer une pile à la fois. Les étapes suivantes présentent un workflow suggéré pour la transition d'une pile ION non obsolète nommée my_heap
qui accepte un indicateur, ION_FLAG_MY_FLAG
.
Étape 1:Créez des équivalents du tas de mémoire ION dans le framework DMA-BUF. Dans cet exemple, comme la pile ION my_heap
est compatible avec un indicateur ION_FLAG_MY_FLAG
, nous enregistrons deux piles DMA-BUF:
- Le comportement de
my_heap
correspond exactement à celui de la pile ION avec l'indicateurION_FLAG_MY_FLAG
désactivé. - Le comportement de
my_heap_special
correspond exactement à celui du tas de mémoire ION avec l'optionION_FLAG_MY_FLAG
activée.
Étape 2:Créez les modifications nécessaires pour les nouveaux tas de mémoire my_heap
et my_heap_special
DMA-BUF. À 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 de compilation 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>
à des 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>
vers des noms de tas DMA-BUF équivalents en définissant le paramètre de nom du tas ION sur vide.
Étape 4:remplacez les invocations ion_alloc_fd()
par BufferAllocator::Alloc()
en utilisant le nom de tas approprié.
Type d'allocation | Libion | libdmabufheap |
---|---|---|
Allocation à partir 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)
|
Allocation à 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 continue d'allouer à partir du tas ION, car il ne dispose pas des autorisations de stratégie de sécurité requises pour ouvrir le tas DMA-BUF.
Étape 5:Créez les autorisations sepolicy requises pour que le client puisse accéder aux nouveaux segments de mémoire DMA-BUF. Le client est maintenant entièrement équipé pour allouer des données à partir du nouveau tas de mémoire 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 de mémoire ION my_heap
dans le noyau. Si le code client n'a pas besoin de prendre en charge la mise à niveau des appareils (dont les noyaux ne prennent peut-être en charge que les tas ION), vous pouvez également supprimer les invocations MapNameToIonHeap()
.