Wechsel von ION- zu DMA-BUF-Haufen (nur 5.4-Kernel)

In Android 12 ersetzt GKI 2.0 den ION-Allocator aus folgenden Gründen durch DMA-BUF-Haufen:

  • Sicherheit: Da jeder DMA-BUF-Heap ein separates Zeichengerät ist, kann der Zugriff auf jeden Heap mit sepolicy separat gesteuert werden. Das war mit ION nicht möglich, da die Zuordnung aus einem beliebigen Heap nur Zugriff auf das /dev/ion-Gerät erforderte.
  • ABI-Stabilität: Im Gegensatz zu ION ist die IOCTL-Schnittstelle des DMA-BUF-Haufen-Frameworks ABI-stabil, da sie im Upstream-Linux-Kernel gepflegt wird.
  • Standardisierung: Das DMA-BUF-Heap-Framework bietet eine gut definierte UAPI. ION erlaubte benutzerdefinierte Flags und Heap-IDs, die die Entwicklung eines gemeinsamen Testframeworks verhinderten, da sich die ION-Implementierung jedes Geräts unterschiedlich verhalten konnte.

Der android12-5.10-Zweig des Android Common Kernel wurde am 1. März 2021CONFIG_ION deaktiviert.

Hintergrund

Im Folgenden wird ION mit DMA-BUF-Haufen verglichen.

Ähnlichkeiten zwischen dem ION- und dem DMA-BUF-Heap-Framework

  • Die ION- und DMA-BUF-Heap-Frameworks sind beide heapbasierte DMA-BUF-Exporteure.
  • Bei beiden kann jeder Heap seinen eigenen Allocator und DMA-BUF-Vorgänge definieren.
  • Die Allokationsleistung ist ähnlich, da beide Schemata für die Allokation eine einzelne IOCTL benötigen.

Unterschiede zwischen dem ION- und dem DMA-BUF-Heap-Framework

ION-Haufen DMA-BUF-Stapel
Alle ION-Zuweisungen erfolgen über /dev/ion. Jeder DMA-BUF-Heap ist ein Zeichengerät, das unter /dev/dma_heap/<heap_name> vorhanden ist.
ION unterstützt Flags für den privaten Heap. DMA-BUF-Haufen unterstützen keine Flags für private Haufen. Jede Art der Zuweisung erfolgt stattdessen aus einem anderen Heap. Beispielsweise sind die Varianten des System-Heaps mit und ohne Cache separate Heaps, die sich unter /dev/dma_heap/system und /dev/dma_heap/system_uncached befinden.
Für die Zuordnung müssen Heap-ID/-Maske und Flags angegeben werden. Der Heap-Name wird für die Zuordnung verwendet.

In den folgenden Abschnitten werden die Komponenten aufgeführt, die mit ION zu tun haben, und beschrieben, wie sie auf das DMA-BUF-Heap-Framework umgestellt werden.

Kerneltreiber von ION- zu DMA-BUF-Haufen migrieren

Kernel-Treiber, die ION-Stapel implementieren

Sowohl ION- als auch DMA-BUF-Stapel ermöglichen es jedem Stapel, eigene Allocatoren und DMA-BUF-Vorgänge zu implementieren. So können Sie von einer ION-Heap-Implementierung zu einer DMA-BUF-Heap-Implementierung wechseln, indem Sie zum Registrieren des Heaps eine andere Gruppe von APIs verwenden. In dieser Tabelle sind die ION-Heap-Registrierungs-APIs und ihre entsprechenden DMA-BUF-Heap-APIs aufgeführt.

ION-Haufen DMA-BUF-Stapel
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);

DMA-BUF-Haufen unterstützen keine Flags für private Haufen. Daher muss jede Variante des Heaps einzeln über die dma_heap_add() API registriert werden. Um die Codefreigabe zu erleichtern, wird empfohlen, alle Varianten desselben Heaps im selben Treiber zu registrieren. Dieses Beispiel dma-buf: system_heap zeigt die Implementierung der mit und ohne Cache verfügbaren Varianten des Systemheaps.

Verwenden Sie diese dma-buf: heaps: Beispielvorlage, um einen DMA-BUF-Haufen von Grund auf neu zu erstellen.

Kernel-Treiber, die direkt aus ION-Haufen zuweisen

Das DMA-BUF-Heap-Framework bietet außerdem eine Allokationsschnittstelle für In-Kernel-Clients. Anstatt die Heap-Maske und Flags anzugeben, um die Art der Zuweisung auszuwählen, nimmt die von DMA-BUF-Heaps angebotene Schnittstelle einen Heap-Namen als Eingabe entgegen.

Im Folgenden sind die ION-Allokations-API im Kernel und die entsprechenden DMA-BUF-Heap-Allokations-APIs zu sehen. Kerneltreiber können die dma_heap_find() API verwenden, um die Existenz eines Heaps abzufragen. Die API gibt einen Verweis auf eine Instanz von struct dma_heap zurück, der dann als Argument an die dma_heap_buffer_alloc() API übergeben werden kann.

ION-Haufen DMA-BUF-Stapel
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)

Kerneltreiber, die DMA-BUFs verwenden

Für Treiber, die nur DMA-BUFs importieren, sind keine Änderungen erforderlich, da sich ein aus einem ION-Heap zugewiesener Puffer genau so verhält wie ein aus einem entsprechenden DMA-BUF-Heap zugewiesener Puffer.

Nutzerbereichs-Clients von ION auf DMA-BUF-Haufen umstellen

Um den Übergang für User-Space-Clients von ION zu erleichtern, steht eine Abstraktionsbibliothek namens libdmabufheap zur Verfügung. libdmabufheap unterstützt die Zuweisung in DMA-BUF- und ION-Heaps. Es wird zuerst geprüft, ob ein DMA-BUF-Heap mit dem angegebenen Namen vorhanden ist. Andernfalls wird auf einen entsprechenden ION-Heap zurückgegriffen, falls vorhanden.

Clients sollten während der Initialisierung ein BufferAllocator-Objekt initialisieren, anstatt /dev/ion using ion_open() zu öffnen. Das liegt daran, dass Dateideskriptoren, die durch das Öffnen von /dev/ion und /dev/dma_heap/<heap_name> erstellt werden, intern vom BufferAllocator-Objekt verwaltet werden.

Wenn Sie von libion zu libdmabufheap wechseln möchten, ändern Sie das Verhalten der Kunden so:

  • Behalten Sie den Heap-Namen im Auge, der für die Zuordnung verwendet werden soll, anstelle der Kopf-ID/-Maske und des Heap-Flags.
  • Ersetzen Sie die ion_alloc_fd() API, die eine Heapmaske und ein Flag-Argument annimmt, durch die BufferAllocator::Alloc() API, die stattdessen einen Heapnamen annimmt.

In dieser Tabelle werden diese Änderungen veranschaulicht, indem gezeigt wird, wie libion und libdmabufheap eine nicht im Cache gespeicherte Systemheap-Zuweisung vornehmen.

Art der Zuweisung libion libdmabufheap
Aus dem System-Heap im Cache zugewiesene Speicherbereiche ion_alloc_fd(ionfd, size, 0, ION_HEAP_SYSTEM, ION_FLAG_CACHED, &fd) allocator->Alloc("system", size)
Nicht zwischengespeicherte Zuweisung aus dem System-Heap ion_alloc_fd(ionfd, size, 0, ION_HEAP_SYSTEM, 0, &fd) allocator->Alloc("system-uncached", size)

Die Variante des nicht im Cache befindlichen System-Heaps wartet noch auf die Genehmigung, ist aber bereits Teil des android12-5.10-Branches.

Zur Unterstützung des Upgrades von Geräten können Sie mit der MapNameToIonHeap() API einen Heap-Namen auf ION-Heap-Parameter (Heap-Name oder Maske und Flags) abbilden, damit diese Schnittstellen namenbasierte Zuordnungen verwenden können. Hier ist ein Beispiel für eine namenbasierte Zuweisung.

Die Dokumentation für alle APIs, die von libdmabufheap freigegeben werden, ist verfügbar. Die Bibliothek stellt auch eine Headerdatei für die Verwendung durch C-Clients bereit.

Referenzimplementierung von Gralloc

Die Hikey960-Gralloc-Implementierung verwendet libdmabufheap. Sie können sie also als Referenzimplementierung verwenden.

Erforderliche ueventd-Ergänzungen

Fügen Sie für alle neu erstellten gerätespezifischen DMA-BUF-Haufen einen neuen Eintrag in die ueventd.rc-Datei des Geräts ein. In diesem Beispiel für die Einrichtung von ueventd zur Unterstützung von DMA-BUF-Haufen wird gezeigt, wie dies für den DMA-BUF-System-Haufen geschieht.

Erforderliche Sepolicy-Ergänzungen

Fügen Sie SEPolicy-Berechtigungen hinzu, damit ein Userspace-Client auf einen neuen DMA-BUF-Haufen zugreifen kann. In diesem Beispiel zum Hinzufügen erforderlicher Berechtigungen sind die Sepolicy-Berechtigungen zu sehen, die für verschiedene Clients zum Zugriff auf den DMA-BUF-System-Heap erstellt wurden.

Über Framework-Code auf Anbieter-Heaps zugreifen

Zur Einhaltung der Treble-Compliance kann Framework-Code nur aus vorab genehmigten Kategorien von Anbieter-Haufen zugewiesen werden.

Auf Grundlage des Feedbacks von Partnern hat Google zwei Kategorien von Anbieter-Heaps identifiziert, auf die über Framework-Code zugegriffen werden muss:

  1. Heaps, die auf dem System-Heap basieren, mit geräte- oder SoC-spezifischen Leistungsoptimierungen.
  2. Heaps, die aus geschütztem Arbeitsspeicher zugewiesen werden sollen.

Heaps basierend auf dem System-Heap mit geräte- oder SoC-spezifischen Leistungsoptimierungen

Zur Unterstützung dieses Anwendungsfalls kann die Heap-Implementierung des standardmäßigen DMA-BUF-Heap-Systems überschrieben werden.

  • CONFIG_DMABUF_HEAPS_SYSTEM ist in gki_defconfig deaktiviert, damit es als Anbietermodul verwendet werden kann.
  • Bei VTS-Compliance-Tests wird sichergestellt, dass der Heap unter /dev/dma_heap/system vorhanden ist. Außerdem wird geprüft, ob der Heap aus dem Nutzerbereich zugewiesen werden kann und ob der zurückgegebene Dateideskriptor (fd) aus dem Nutzerbereich in den Arbeitsspeicher (mmapped) kopiert werden kann.

Die oben genannten Punkte gelten auch für die nicht im Cache gespeicherte Variante des System-Heaps, obwohl ihre Existenz für vollständig IO-kohärente Geräte nicht obligatorisch ist.

Heaps, die aus geschütztem Arbeitsspeicher zugewiesen werden sollen

Implementierungen des sicheren Heaps müssen anbieterspezifisch sein, da der Android Common Kernel keine generische Implementierung des sicheren Heaps unterstützt.

  • Registrieren Sie Ihre anbieterspezifischen Implementierungen als /dev/dma_heap/system-secure<vendor-suffix>.
  • Diese Heap-Implementierungen sind optional.
  • Wenn die Heaps vorhanden sind, sorgen VTS-Tests dafür, dass sie für Zuordnungen verwendet werden können.
  • Framework-Komponenten erhalten Zugriff auf diese Heaps, damit sie die Heap-Nutzung über die Codec2 HAL/nicht gebundene HALs im selben Prozess aktivieren können. Generische Android-Framework-Funktionen können jedoch aufgrund der Variabilität ihrer Implementierungsdetails nicht davon abhängig sein. Wenn dem Android Common Kernel in Zukunft eine generische sichere Heap-Implementierung hinzugefügt wird, muss eine andere ABI verwendet werden, um Konflikte beim Upgraden von Geräten zu vermeiden.

Codec 2-Allocator für DMA-BUF-Haufen

In AOSP ist ein Codec2-Allocator für die DMA-BUF-Stapelschnittstelle verfügbar.

Die Component Store-Oberfläche, mit der Heap-Parameter über die C2 HAL angegeben werden können, ist mit dem C2 DMA-BUF-Heap-Allocator verfügbar.

Beispiel für einen Übergangsablauf für einen ION-Heap

Um den Übergang von ION- zu DMA-BUF-Haufen reibungslos zu gestalten, ermöglicht libdmabufheap das Umschalten jeweils eines Haufens. Die folgenden Schritte veranschaulichen einen vorgeschlagenen Workflow für die Umstellung eines nicht älteren ION-Haufens mit dem Namen my_heap, der ein Flag namens ION_FLAG_MY_FLAG unterstützt.

Schritt 1:Erstellen Sie Entsprechungen des ION-Heaps im DMA-BUF-Framework. In diesem Beispiel registrieren wir zwei DMA-BUF-Haufen, da der ION-Haufen my_heap ein Flag ION_FLAG_MY_FLAG unterstützt:

  • Das Verhalten von my_heap entspricht genau dem des ION-Heaps, wenn das Flag ION_FLAG_MY_FLAG deaktiviert ist.
  • Das Verhalten von my_heap_special entspricht genau dem des ION-Heaps mit aktiviertem Flag ION_FLAG_MY_FLAG.

Schritt 2:Erstellen Sie die ueventd-Änderungen für die neuen my_heap- und my_heap_special-DMA-BUF-Haufen. Zu diesem Zeitpunkt sind die Heaps als /dev/dma_heap/my_heap und /dev/dma_heap/my_heap_special mit den gewünschten Berechtigungen zu sehen.

Schritt 3:Ändern Sie die Makefiles für Clients, die von my_heap aus zuweisen, sodass sie mit libdmabufheap verknüpft werden. Instanziere während der Clientinitialisierung ein BufferAllocator-Objekt und ordne mithilfe der MapNameToIonHeap() API die <ION heap name/mask, flag>-Kombination den entsprechenden DMA-BUF-Heapnamen zu.

Beispiel:

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

Anstatt die MapNameToIonHeap() API mit den Parametern „Name“ und „Flag“ zu verwenden, können Sie die Zuordnung von <ION heap mask, flag> zu entsprechenden DMA-BUF-Heap-Namen erstellen, indem Sie den Parameter „ION-Heap-Name“ auf „leer“ setzen.

Schritt 4:Ersetzen Sie ion_alloc_fd()-Aufrufe durch BufferAllocator::Alloc() mit dem entsprechenden Heap-Namen.

Zuweisungstyp libion libdmabufheap
Zuweisung von my_heap ohne Flag ION_FLAG_MY_FLAG ion_alloc_fd(ionfd, size, 0, ION_HEAP_MY_HEAP, 0, &fd) allocator->Alloc("my_heap", size)
Zuweisung von my_heap mit gesetztem Flag ION_FLAG_MY_FLAG ion_alloc_fd(ionfd, size, 0, ION_HEAP_MY_HEAP, ION_FLAG_MY_FLAG, &fd) allocator->Alloc("my_heap_special", size)

Der Client funktioniert zwar, weist aber weiterhin Speicherplatz aus dem ION-Heap zu, da er nicht über die erforderlichen SEPolicy-Berechtigungen zum Öffnen des DMA-BUF-Heaps verfügt.

Schritt 5:Erstellen Sie die Sepolicy-Berechtigungen, die der Client zum Zugriff auf die neuen DMA-BUF-Haufen benötigt. Der Client ist jetzt vollständig für die Zuweisung aus dem neuen DMA-BUF-Heap gerüstet.

Schritt 6:Prüfen Sie anhand von logcat, ob die Zuordnungen aus dem neuen DMA-BUF-Heap erfolgen.

Schritt 7:Deaktivieren Sie den ION-Heap my_heap im Kernel. Wenn der Clientcode keine Aktualisierung von Geräten unterstützen muss, deren Kernel möglicherweise nur ION-Haufen unterstützen, können Sie auch die MapNameToIonHeap()-Aufrufe entfernen.