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

In Android 12 wird der ION-Allocator aus folgenden Gründen durch DMA-BUF-Heaps ersetzt:

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

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

Hintergrund

Im Folgenden finden Sie einen kurzen Vergleich zwischen ION- und DMA-BUF-Heaps.

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

  • Das ION- und das DMA-BUF-Heaps-Framework sind beide Heap-basierte DMA-BUF-Exporter.
  • Bei beiden kann für jeden Heap ein eigener Zuweisungs- und DMA-BUF-Vorgang definiert werden.
  • Die Zuweisungsleistung ist ähnlich, da für die Zuweisung in beiden Schemata ein einzelner IOCTL erforderlich ist.

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

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

In den folgenden Abschnitten werden die Komponenten aufgeführt, die sich mit ION befassen, und es wird beschrieben, wie Sie sie auf das DMA-BUF-Heaps-Framework umstellen.

Kernel-Treiber von ION auf DMA‑BUF-Heaps umstellen

Kernel-Treiber, die ION-Heaps implementieren

Sowohl ION- als auch DMA-BUF-Heaps ermöglichen es, dass für jeden Heap eigene Zuweisungen und DMA-BUF-Vorgänge implementiert werden. Sie können also von einer ION-Heap-Implementierung zu einer DMA-BUF-Heap-Implementierung wechseln, indem Sie einen anderen Satz von APIs verwenden, um den Heap zu registrieren. In dieser Tabelle sind die ION-Heap-Registrierungs-APIs und die entsprechenden DMA-BUF-Heap-APIs aufgeführt.

ION-Heaps DMA-BUF-Heaps
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-Heaps unterstützen keine privaten Heap-Flags. Jede Variante des Heaps muss also einzeln über die dma_heap_add()-API registriert werden. Um die gemeinsame Nutzung von Code zu erleichtern, wird empfohlen, alle Varianten desselben Heaps im selben Treiber zu registrieren. In diesem dma-buf: system_heap-Beispiel wird die Implementierung der zwischengespeicherten und nicht zwischengespeicherten Varianten des System-Heaps gezeigt.

Verwenden Sie diese Beispielvorlage für DMA-BUF-Heaps, um einen DMA-BUF-Heap von Grund auf neu zu erstellen.

Kernel-Treiber, die direkt aus ION-Heaps zuweisen

Das DMA-BUF-Heaps-Framework bietet auch eine Zuweisungsschnittstelle für In-Kernel-Clients. Anstatt die Heap-Maske und die Flags anzugeben, um den Zuweisungstyp auszuwählen, wird bei der von DMA-BUF-Heaps angebotenen Schnittstelle ein Heap-Name als Eingabe verwendet.

Im Folgenden sehen Sie die ION-Zuweisungs-API im Kernel und die entsprechenden DMA-BUF-Heap-Zuweisungs-APIs. Kernel-Treiber können mit der dma_heap_find() API abfragen, ob ein Heap vorhanden ist. Die API gibt einen Zeiger auf eine Instanz von struct dma_heap zurück, die dann als Argument an die dma_heap_buffer_alloc() API übergeben werden kann.

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

Kernel-Treiber, 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 wie ein aus einem entsprechenden DMA-BUF-Heap zugewiesener Puffer verhält.

Umstellung der Userspace-Clients von ION auf DMA-BUF-Heaps

Um den Übergang für Userspace-Clients von ION zu erleichtern, ist eine Abstraktionsbibliothek namens libdmabufheap verfügbar. libdmabufheap unterstützt die Zuweisung in DMA-BUF-Heaps und ION-Heaps. Zuerst wird geprüft, ob ein DMA-BUF-Heap mit dem angegebenen Namen vorhanden ist. Wenn nicht, wird auf einen entsprechenden ION-Heap zurückgegriffen, sofern einer vorhanden ist.

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, müssen Sie das Verhalten der Clients so ändern:

  • Behalten Sie den Heap-Namen bei, der für die Zuweisung verwendet werden soll, anstelle der Head-ID/Maske und des Heap-Flags.
  • Ersetzen Sie die ion_alloc_fd()-API, die eine Heap-Maske und ein Flag-Argument akzeptiert, durch die BufferAllocator::Alloc()-API, die stattdessen einen Heap-Namen akzeptiert.

In dieser Tabelle werden diese Änderungen veranschaulicht. Sie zeigt, wie libion und libdmabufheap eine nicht im Cache gespeicherte System-Heap-Zuweisung vornehmen.

Art der Zuweisung libion libdmabufheap
Zwischengespeicherte Zuweisung aus dem System-Heap 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 uncached system heap variant (uncached-System-Heap-Variante) wartet auf die Genehmigung durch das Upstream-Team, ist aber bereits Teil des android12-5.10-Branch.

Um das Upgraden von Geräten zu unterstützen, ermöglicht die MapNameToIonHeap()-API die Zuordnung eines Heap-Namens zu ION-Heap-Parametern (Heap-Name oder Maske und Flags), damit diese Schnittstellen namensbasierte Zuweisungen verwenden können. Beispiel für die namensbasierte Zuweisung

Die Dokumentation für jede von libdmabufheap bereitgestellte API ist verfügbar. Die Bibliothek stellt auch eine Headerdatei zur Verfügung, die von C-Clients verwendet werden kann.

Gralloc-Referenzimplementierung

Die Hikey960-gralloc-Implementierung verwendet libdmabufheap, sodass Sie sie als Referenzimplementierung verwenden können.

Erforderliche ueventd-Ergänzungen

Fügen Sie für alle neu erstellten gerätespezifischen DMA-BUF-Heaps 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-Heaps wird gezeigt, wie dies für den DMA-BUF-System-Heap erfolgt.

Erforderliche sepolicy-Ergänzungen

Fügen Sie sepolicy-Berechtigungen hinzu, damit ein Userspace-Client auf einen neuen DMA-BUF-Heap zugreifen kann. In diesem Beispiel zum Hinzufügen erforderlicher Berechtigungen werden die für verschiedene Clients erstellten sepolicy-Berechtigungen für den Zugriff auf den DMA-BUF-System-Heap gezeigt.

Über Framework-Code auf Anbieter-Heaps zugreifen

Um die Treble-Konformität zu gewährleisten, kann Framework-Code nur aus vorab genehmigten Kategorien von Vendor-Heaps zugewiesen werden.

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

  1. Heaps, die auf dem System-Heap mit geräte- oder SoC-spezifischen Leistungsoptimierungen basieren.
  2. Heaps, aus denen Speicher im geschützten Speicherbereich zugewiesen werden soll.

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 ein Anbietermodul sein kann.
  • VTS-Konformitätstests sorgen dafür, dass der Heap unter /dev/dma_heap/system vorhanden ist. Die Tests prüfen auch, ob der Heap zugewiesen werden kann und ob der zurückgegebene Dateideskriptor (fd) aus dem Nutzerbereich gemappt werden kann.

Die oben genannten Punkte gelten auch für die nicht im Cache gespeicherte Variante des System-Heaps, obwohl deren Existenz für vollständig E/A-kohärente Geräte nicht zwingend erforderlich ist.

Heaps, die aus dem geschützten Speicher zugewiesen werden sollen

Sichere Heap-Implementierungen müssen anbieterspezifisch sein, da der Android Common Kernel keine generische sichere Heap-Implementierung 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, wird durch VTS-Tests sichergestellt, dass Zuweisungen aus ihnen vorgenommen werden können.
  • Framework-Komponenten haben Zugriff auf diese Heaps, sodass sie die Heap-Nutzung über die Codec2-HAL/nicht-binderisierte HALs im selben Prozess ermöglichen können. Allgemeine Android-Framework-Funktionen können jedoch aufgrund der unterschiedlichen Implementierungsdetails nicht von ihnen abhängig sein. Wenn dem Android Common Kernel in Zukunft eine generische Implementierung für sichere Heaps hinzugefügt wird, muss sie eine andere ABI verwenden, um Konflikte mit der Aktualisierung von Geräten zu vermeiden.

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

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

Die Komponentenspeicher-Schnittstelle, 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-Heaps zu erleichtern, ermöglicht libdmabufheap das Umschalten eines Heaps nach dem anderen. Die folgenden Schritte zeigen einen vorgeschlagenen Workflow für die Umstellung eines nicht mehr unterstützten ION-Heaps mit dem Namen my_heap, der ein Flag unterstützt, ION_FLAG_MY_FLAG.

Schritt 1:Erstellen Sie Entsprechungen des ION-Heaps im DMA-BUF-Framework. Da der ION-Heap my_heap in diesem Beispiel das Flag ION_FLAG_MY_FLAG unterstützt, registrieren wir zwei DMA-BUF-Heaps:

  • Das Verhalten von my_heap entspricht genau dem Verhalten des ION-Heaps, wenn das Flag ION_FLAG_MY_FLAG deaktiviert ist.
  • Das Verhalten von my_heap_special entspricht genau dem Verhalten 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-Heaps. Die Heaps sind jetzt als /dev/dma_heap/my_heap und /dev/dma_heap/my_heap_special mit den gewünschten Berechtigungen sichtbar.

Schritt 3:Ändern Sie für Clients, die aus my_heap zuweisen, die Makefiles, um eine Verknüpfung zu libdmabufheap herzustellen. Instanziieren Sie bei der Clientinitialisierung ein BufferAllocator-Objekt und verwenden Sie die MapNameToIonHeap() API, um die <ION heap name/mask, flag>-Kombination entsprechenden DMA-BUF-Heap-Namen zuzuordnen.

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 für den ION-Heap-Namen auf leer setzen.

Schritt 4:Ersetzen Sie ion_alloc_fd()-Aufrufe durch BufferAllocator::Alloc() und verwenden Sie dabei den entsprechenden Heap-Namen.

Zuweisungstyp libion libdmabufheap
Zuweisung aus my_heap mit nicht gesetztem 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 dem 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)

An diesem Punkt ist der Client funktionsfähig, aber er weist immer noch Speicher aus dem ION-Heap zu, da er nicht die erforderlichen sepolicy-Berechtigungen zum Öffnen des DMA-BUF-Heaps hat.

Schritt 5:Erstellen Sie die für den Client erforderlichen sepolicy-Berechtigungen, damit er auf die neuen DMA-BUF-Heaps zugreifen kann. Der Client ist jetzt vollständig für die Zuweisung aus dem neuen DMA-BUF-Heap ausgestattet.

Schritt 6:Prüfen Sie, ob die Zuweisungen aus dem neuen DMA-BUF-Heap erfolgen, indem Sie logcat untersuchen.

Schritt 7:Deaktivieren Sie den ION-Heap my_heap im Kernel. Wenn der Clientcode keine Unterstützung für das Aktualisieren von Geräten benötigt, deren Kernel möglicherweise nur ION-Heaps unterstützen, können Sie auch die MapNameToIonHeap()-Aufrufe entfernen.