Übergang von ION zu DMA-BUF Heaps

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

  • Sicherheit: Da jeder DMA-BUF-Heap ein separates Zeichengerät ist, kann der Zugriff auf jeden Heap separat mit Sepolicy gesteuert werden. Dies war mit ION nicht möglich, da für die Zuweisung von einem beliebigen Heap nur 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 garantiert ABI-stabil, da sie im Upstream-Linux-Kernel verwaltet wird.
  • Standardisierung: Das DMA-BUF-Heaps-Framework bietet eine genau definierte UAPI. ION erlaubte benutzerdefinierte Flags und Heap-IDs, die die Entwicklung eines gemeinsamen Test-Frameworks verhinderten, da sich die ION-Implementierung jedes Geräts unterschiedlich verhalten konnte.

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

Hintergrund

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

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

  • Die ION- und DMA-BUF-Heaps-Frameworks sind beide Heap-basierte DMA-BUF-Exporteure.
  • Beide lassen jeden Heap seinen eigenen Allokator und seine eigenen DMA-BUF-Operationen definieren.
  • Die Zuteilungsleistung ist ähnlich, da beide Schemata einen einzigen IOCTL für die Zuteilung benötigen.

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

ION-Haufen 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> vorhanden ist.
ION unterstützt private Heap-Flags. DMA-BUF-Heaps unterstützen keine privaten Heap-Flags. Jede unterschiedliche Art der Zuweisung erfolgt stattdessen von einem anderen Heap aus. Beispielsweise sind die zwischengespeicherten und nicht zwischengespeicherten System-Heap-Varianten 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 Zuordnung verwendet.

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

Umstellung der Kernel-Treiber von ION auf DMA-BUF-Heaps

Kernel-Treiber, die ION-Heaps implementieren

Sowohl ION- als auch DMA-BUF-Heaps ermöglichen es jedem Heap, seine eigenen Allokatoren und DMA-BUF-Operationen zu implementieren. Sie können also von einer ION-Heap-Implementierung zu einer DMA-BUF-Heap-Implementierung wechseln, indem Sie einen anderen Satz von APIs zum Registrieren des Heaps verwenden. Diese Tabelle zeigt die ION-Heap-Registrierungs-APIs und ihre entsprechenden DMA-BUF-Heap-APIs.

ION-Haufen 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. Daher muss jede Variante des Heaps einzeln mit der API dma_heap_add() registriert werden. Um die Codefreigabe zu erleichtern, wird empfohlen, alle Varianten desselben Heaps innerhalb desselben Treibers zu registrieren. Dieses dma-buf:system_heap- Beispiel zeigt die Implementierung der zwischengespeicherten und nicht zwischengespeicherten Varianten des Systemheaps.

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

Kernel-Treiber, die direkt von ION-Heaps zuweisen

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

Im Folgenden werden die kerninterne ION-Zuteilungs-API und die entsprechenden DMA-BUF-Heap-Zuteilungs-APIs gezeigt. Kernel-Treiber können die API dma_heap_find() verwenden, um das Vorhandensein eines Heaps abzufragen. Die API gibt einen Zeiger auf eine Instanz von struct dma_heap zurück, der dann als Argument an die API dma_heap_buffer_alloc() übergeben werden kann.

ION-Haufen 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 von einem ION-Heap zugewiesener Puffer genauso verhält wie ein von einem entsprechenden DMA-BUF-Heap zugewiesener Puffer.

Umstellung der User-Space-Clients von ION auf DMA-BUF-Heaps

Um User-Space-Clients von ION den Übergang zu erleichtern, steht eine Abstraktionsbibliothek namens libdmabufheap zur Verfügung. libdmabufheap unterstützt die Zuordnung in DMA-BUF-Heaps und ION-Heaps. Es prüft zunächst, ob ein DMA-BUF-Heap mit dem angegebenen Namen existiert, und falls nicht, greift es auf einen entsprechenden ION-Heap zurück, sofern einer vorhanden ist.

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

Um von libion ​​zu libdmabufheap zu wechseln, ändern Sie das Verhalten der Clients wie folgt:

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

Diese Tabelle veranschaulicht diese Änderungen, indem sie zeigt, wie libion ​​und libdmabufheap eine nicht zwischengespeicherte System-Heap-Zuweisung durchführen.

Art der Zuteilung libion libdmabufheap
Zwischengespeicherte Zuordnung vom Systemheap ion_alloc_fd(ionfd, size, 0, ION_HEAP_SYSTEM, ION_FLAG_CACHED, &fd) allocator->Alloc("system", size)
Nicht zwischengespeicherte Zuordnung vom Systemheap ion_alloc_fd(ionfd, size, 0, ION_HEAP_SYSTEM, 0, &fd) allocator->Alloc("system-uncached", size)

Die nicht zwischengespeicherte System-Heap-Variante wartet auf die Genehmigung durch den Upstream, ist aber bereits Teil des android12-5.10 Zweigs.

Um die Aktualisierung von Geräten zu unterstützen, ermöglicht die MapNameToIonHeap() -API die Zuordnung eines Heap-Namens zu ION-Heap-Parametern (Heap-Name/Maske und Flags), damit diese Schnittstellen auch namensbasierte Zuordnungen verwenden können. Hier ist ein Beispiel für eine namensbasierte Zuordnung .

Die Dokumentation für jede von libdmabufheap bereitgestellte API ist verfügbar. Die Bibliothek stellt außerdem eine Header-Datei zur Verwendung durch C-Clients bereit.

Referenzieren Sie die Gralloc-Implementierung

Die Hikey960-Gralloc-Implementierung verwendet libdmabufheap , sodass Sie es 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 zur Datei ueventd.rc des Geräts hinzu. Dieses Setup-Ueventd-Beispiel zur Unterstützung von DMA-BUF-Heaps zeigt, wie dies für den DMA-BUF-System-Heap durchgeführt wird.

Erforderliche seperate Ergänzungen

Fügen Sie Sepolicy-Berechtigungen hinzu, um einem Userspace-Client den Zugriff auf einen neuen DMA-BUF-Heap zu ermöglichen. Dieses Beispiel zum Hinzufügen erforderlicher Berechtigungen zeigt die Sepolicy-Berechtigungen, die für verschiedene Clients erstellt wurden, um auf den DMA-BUF-Systemheap zuzugreifen.

Zugriff auf Anbieter-Heaps über den Framework-Code

Um die Treble-Konformität sicherzustellen, kann der Framework-Code nur Zuweisungen aus vorab genehmigten Kategorien von Anbieter-Heaps vornehmen.

Basierend auf dem Feedback von Partnern hat Google zwei Kategorien von Anbieter-Heaps identifiziert, auf die über den Framework-Code zugegriffen werden muss:

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

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

Um diesen Anwendungsfall zu unterstützen, 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 stellen sicher, dass der Heap unter /dev/dma_heap/system vorhanden ist. Die Tests überprüfen auch, ob der Heap zugewiesen werden kann und ob der zurückgegebene Dateideskriptor ( fd ) vom Benutzerbereich aus dem Speicher zugeordnet (mmapped) werden kann.

Die vorstehenden Punkte gelten auch für die nicht zwischengespeicherte Variante des System-Heaps, obwohl dessen Existenz für vollständig IO-kohärente Geräte nicht zwingend erforderlich ist.

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

Secure-Heap-Implementierungen müssen herstellerspezifisch sein, da der Android Common Kernel keine generische Secure-Heap-Implementierung unterstützt.

  • Registrieren Sie Ihre herstellerspezifischen Implementierungen als /dev/dma_heap/system-secure<vendor-suffix> .
  • Diese Heap-Implementierungen sind optional.
  • Wenn die Heaps vorhanden sind, stellen VTS-Tests sicher, dass daraus Allokationen vorgenommen werden können.
  • Framework-Komponenten erhalten Zugriff auf diese Heaps, sodass sie die Heap-Nutzung über die Codec2-HALs/nicht gebundene HALs mit demselben Prozess ermöglichen können. Generische Android-Framework-Funktionen können jedoch aufgrund der Variabilität ihrer Implementierungsdetails nicht von ihnen abhängig sein. Wenn dem Android Common Kernel in Zukunft eine generische Secure-Heap-Implementierung hinzugefügt wird, muss diese eine andere ABI verwenden, um Konflikte mit der Aktualisierung von Geräten zu vermeiden.

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

Ein Codec2-Zuordner für die DMA-BUF-Heaps-Schnittstelle ist in AOSP verfügbar.

Die Komponentenspeicherschnittstelle, die die Angabe von Heap-Parametern aus der C2-HAL ermöglicht, ist mit dem C2-DMA-BUF-Heap-Allokator verfügbar.

Beispielübergangsfluss für einen ION-Heap

Um den Übergang von ION- zu DMA-BUF-Heaps zu erleichtern, ermöglicht libdmabufheap den Wechsel eines Heaps nach dem anderen. Die folgenden Schritte veranschaulichen einen vorgeschlagenen Workflow für den Übergang eines nicht veralteten ION-Heaps namens my_heap , der ein Flag unterstützt, ION_FLAG_MY_FLAG .

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

  • Das Verhalten my_heap entspricht genau dem Verhalten des ION-Heaps mit deaktiviertem Flag ION_FLAG_MY_FLAG .
  • Das Verhalten 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 DMA-BUF-Heaps my_heap und my_heap_special . Zu diesem Zeitpunkt sind die Heaps als /dev/dma_heap/my_heap und /dev/dma_heap/my_heap_special mit den vorgesehenen Berechtigungen sichtbar.

Schritt 3: Für Clients, die von my_heap zuweisen, ändern Sie ihre Makefiles, um eine Verknüpfung zu libdmabufheap herzustellen. Instanziieren Sie während der Client-Initialisierung ein BufferAllocator Objekt und verwenden Sie die MapNameToIonHeap() -API, um die Kombination <ION heap name/mask, flag> entsprechenden DMA-BUF-Heap-Namen zuzuordnen.

Zum 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 API MapNameToIonHeap() 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() unter Verwendung des entsprechenden Heap-Namens.

Zuordnungstyp libion libdmabufheap
Zuordnung von 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 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)

Zu diesem Zeitpunkt ist der Client funktionsfähig, weist jedoch weiterhin vom ION-Heap zu, da er nicht über die erforderlichen Sepolicy-Berechtigungen zum Öffnen des DMA-BUF-Heaps verfügt.

Schritt 5: Erstellen Sie die erforderlichen Sepolicy-Berechtigungen, damit der Client auf die neuen DMA-BUF-Heaps zugreifen kann. Der Client ist nun vollständig für die Zuweisung aus dem neuen DMA-BUF-Heap gerüstet.

Schritt 6: Überprüfen Sie, ob die Zuweisungen vom neuen DMA-BUF-Heap erfolgen, indem Sie logcat untersuchen.

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