Transition de ION vers DMA-BUF Heaps

Restez organisé à l'aide des collections Enregistrez et classez les contenus selon vos préférences.

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 de n'importe quel tas nécessitait uniquement l'accès au périphérique /dev/ion .
  • Stabilité ABI : contrairement à ION, l'interface IOCTL du framework DMA-BUF heaps est garantie d'être stable ABI car elle est maintenue dans le noyau Linux en amont.
  • Normalisation : le framework de tas DMA-BUF offre une UAPI bien définie. ION autorisait des drapeaux personnalisés et des ID de tas qui empêchaient de développer un cadre 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 .

Fond

Voici 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 le tas.
  • Ils laissent tous deux chaque tas définir son propre répartiteur 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 de tas ION et DMA-BUF

Tas d'ions Tas DMA-BUF
Toutes les allocations ION sont effectuées avec /dev/ion . Chaque tas DMA-BUF est un périphérique de caractères présent dans /dev/dma_heap/<heap_name> .
ION prend en charge les drapeaux privés de tas. Les tas DMA-BUF ne prennent pas en charge les indicateurs privés de tas. Chaque type d'allocation différent est plutôt effectué à partir d'un tas différent. Par exemple, les variantes de tas système mis en cache et non mis en cache sont des tas distincts situés dans /dev/dma_heap/system et /dev/dma_heap/system_uncached .
L'ID/masque de tas et les drapeaux doivent être spécifiés pour l'allocation. Le nom du tas est utilisé pour l'allocation.

Les sections suivantes répertorient les composants qui traitent d'ION et décrivent comment les basculer vers l'infrastructure de tas DMA-BUF.

Transition des pilotes du noyau de ION vers les tas DMA-BUF

Pilotes du noyau implémentant des tas ION

Les tas ION et DMA-BUF permettent à chaque tas d'implémenter ses propres répartiteurs 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 ensemble d'API différent pour enregistrer le tas. Ce tableau présente les API d'enregistrement de tas ION et leurs API de tas DMA-BUF équivalentes.

Tas d'ions 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 prennent pas en charge les indicateurs privés de tas. Ainsi, chaque variante du tas doit ê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. Cet 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 dma-buf: heaps: example pour créer un tas DMA-BUF à partir de rien.

Pilotes du noyau allouant directement à partir des tas ION

L'infrastructure de tas DMA-BUF offre également une interface d'allocation pour les clients du noyau. Au lieu de spécifier le masque de tas et les drapeaux pour sélectionner le type d'allocation, l'interface offerte 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 passé en argument à l'API dma_heap_buffer_alloc() .

Tas d'ions Tas 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)

Pilotes du noyau qui utilisent les DMA-BUF

Aucune modification n'est requise pour les pilotes qui importent uniquement des DMA-BUF, 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 pour les clients de l'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 et, si ce n'est pas le cas, 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() . Cela est dû au fait que 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 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 prend un masque de tas et un argument d'indicateur, par l' BufferAllocator::Alloc() , qui prend un nom de tas à la place.

Ce tableau illustre ces modifications en montrant comment libion et libdmabufheap effectuent une allocation de tas système non mise en cache.

Type d'attribution libion libdmabufheap
Allocation mise en cache à partir du tas 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 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 mise en cache est en attente d'approbation en amont mais 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 aux paramètres de tas ION (nom/masque de tas et indicateurs) pour permettre à ces interfaces d'utiliser également des allocations basées sur le nom. 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 à utiliser par les clients C.

Implémentation de Gralloc de référence

L'implémentation Hikey960 gralloc 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 à l'appareil créé, ajoutez une nouvelle entrée au fichier ueventd.rc de l'appareil. Cet exemple de configuration ueventd pour prendre en charge les tas DMA-BUF montre comment cela est fait pour le tas système DMA-BUF.

Ajouts requis à la politique de sécurité

Ajoutez des autorisations sepolicy pour permettre à un client de l'espace utilisateur d'accéder à un nouveau tas DMA-BUF. Cet exemple d'ajout d'autorisations requises montre les autorisations sepolicy créées pour divers clients afin d'accéder au tas système DMA-BUF.

Accéder aux tas des fournisseurs à partir du code du framework

Pour garantir la conformité Treble, le code de structure ne peut être alloué qu'à partir de catégories pré-approuvées de tas de fournisseurs.

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 :

  1. Tas basés sur le tas du système avec des optimisations de performances spécifiques à l'appareil ou au SoC.
  2. Heaps à allouer à partir de la mémoire protégée.

Tas basés sur le tas du 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 tas du système de tas DMA-BUF par défaut peut être remplacée.

  • CONFIG_DMABUF_HEAPS_SYSTEM est désactivé dans gki_defconfig pour lui permettre d'être un module fournisseur.
  • Les tests de conformité VTS garantissent que le tas existe dans /dev/dma_heap/system . Les tests vérifient également que le tas peut être alloué depuis et que le descripteur de fichier renvoyé ( fd ) peut être mappé en mémoire (mmappé) depuis l'espace utilisateur.

Les points précédents sont également vrais pour la variante non mise en cache du tas système, bien que son existence ne soit pas obligatoire pour les périphériques entièrement cohérents avec les E/S.

Heaps à 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 sous /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 faites à partir d'eux.
  • Les composants de la structure ont accès à ces tas afin qu'ils puissent activer l'utilisation des tas via les HAL Codec2/HAL non liés, même processus. Cependant, les fonctionnalités génériques du framework Android ne peuvent pas en dépendre en raison de la variabilité de leurs détails de mise en œuvre. Si une implémentation générique de tas sécurisé est ajoutée au noyau commun Android à l'avenir, elle doit utiliser un ABI différent pour éviter les conflits avec la mise à niveau des appareils.

Allocateur de codec 2 pour les tas DMA-BUF

Un répartiteur codec2 pour l'interface de tas DMA-BUF est disponible dans AOSP.

L'interface de magasin de composants qui permet de spécifier les paramètres de tas à partir de la couche HAL C2 est disponible avec l'allocateur de tas C2 DMA-BUF.

Exemple de flux de transition pour un tas ION

Pour faciliter la transition des tas ION aux tas DMA-BUF, libdmabufheap permet de changer de tas à la fois. Les étapes suivantes illustrent un flux de travail 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 cadre DMA-BUF. Dans cet exemple, étant donné que le tas ION my_heap prend en charge un indicateur ION_FLAG_MY_FLAG , nous enregistrons deux tas DMA-BUF :

  • Le comportement de my_heap correspond exactement au comportement du tas ION avec le drapeau ION_FLAG_MY_FLAG désactivé.
  • Le comportement my_heap_special correspond exactement au comportement du tas ION avec le drapeau ION_FLAG_MY_FLAG activé.

Étape 2 : créez les modifications ueventd pour les nouveaux segments de mémoire DMA-BUF my_heap et my_heap_special . À ce stade, les tas sont visibles sous les noms /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 makefiles pour les lier à 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.

Par example:

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 name et flag, 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 de tas ION sur vide.

Étape 4 : Remplacez les invocations ion_alloc_fd() par BufferAllocator::Alloc() en utilisant le nom de tas approprié.

Type d'attribution libion libdmabufheap
Allocation de my_heap avec l'indicateur ION_FLAG_MY_FLAG non défini ion_alloc_fd(ionfd, size, 0, ION_HEAP_MY_HEAP, 0, &fd) allocator->Alloc("my_heap", size
Allocation de my_heap avec l'indicateur ION_FLAG_MY_FLAG défini 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 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 accède aux nouveaux tas DMA-BUF. Le client est maintenant entièrement équipé pour allouer à partir du nouveau tas DMA-BUF.

Étape 6 : Vérifiez que les allocations se produisent à 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 de prendre en charge la mise à niveau des périphériques (dont les noyaux peuvent uniquement prendre en charge les tas ION), vous pouvez également supprimer les invocations MapNameToIonHeap() .