Von ION- auf DMA-BUF-Heaps umstellen

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 werden mit /dev/ion abgeschlossen. Jeder DMA-BUF-Haufen 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 privaten Flags für Haufen. Jede Art der Zuordnung erfolgt stattdessen über einen 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 Zuweisung müssen die Heap-ID/-Maske und die 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. Außerdem wird beschrieben, wie diese auf das DMA-BUF-Heap-Framework umgestellt werden.

Kerneltreiber von ION auf DMA-BUF-Heaps umstellen

Kerneltreiber, die ION-Heaps 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. Diese Tabelle zeigt die APIs für die ION-Heap-Registrierung und ihre entsprechenden DMA-BUF-Heap-APIs.

ION-Heaps 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 privaten Flags für 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 im Cache gespeicherten und nicht zwischengespeicherten Varianten des System-Heaps.

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

Kerneltreiber, die direkt aus ION-Haufen zuweisen

Das DMA-BUF-Heap-Framework bietet auch eine Zuweisungsschnittstelle 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 sehen Sie die IONS-Zuweisungs-API im Kernel und die entsprechenden DMA-BUF-Heap-Zuweisungs-APIs. Kernel-Treiber können die dma_heap_find() API verwenden, um das Vorhandensein 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-Heaps 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.

User-Space-Clients von ION auf DMA-BUF-Haufen umstellen

Um den Übergang für User-Space-Clients von ION zu erleichtern, ist eine Abstraktionsbibliothek namens libdmabufheap verfügbar. 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
Im Cache gespeicherte Zuordnung aus 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 nicht zwischengespeicherte System-Heap-Variante wartet auf Genehmigung in Upstream, ist aber bereits Teil des android12-5.10-Zweigs.

Damit Geräteupgrades unterstützt werden, ermöglicht die MapNameToIonHeap() API das Zuordnen eines Heap-Namens zu ION-Heap-Parametern (Heap-Name oder Maske und Flags), damit diese Schnittstellen namebasierte Zuweisungen verwenden können. Hier ist ein Beispiel für eine namenbasierte Zuweisung.

Die Dokumentation für jede API, die von libdmabufheap bereitgestellt wird, ist verfügbar. Die Bibliothek stellt auch eine Headerdatei für die Verwendung durch C-Clients bereit.

Referenzimplementierung von Gralloc

Die gralloc-Implementierung von Hikey960 verwendet libdmabufheap. Sie können sie 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. Dieses Beispiel zum Hinzufügen erforderlicher Berechtigungen zeigt die sepolicy-Berechtigungen, die für verschiedene Clients für den Zugriff auf den DMA-BUF-System-Heap erstellt wurden.

Über den 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. Zugewiesene Heaps aus dem geschützten Arbeitsspeicher

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

Für diesen Anwendungsfall kann die Heap-Implementierung des Standard-DMA-BUF-Heap-Systems überschrieben werden.

  • CONFIG_DMABUF_HEAPS_SYSTEM ist in gki_defconfig deaktiviert, damit es als Anbietermodul verwendet werden kann.
  • VTS-Compliance-Tests prüfen, ob der Heap unter /dev/dma_heap/system vorhanden ist. Die Tests prüfen auch, ob der Heap aus dem Heap zugeordnet werden kann und ob der zurückgegebene Dateideskriptor (fd) aus dem Nutzerbereich dem Arbeitsspeicher zugeordnet (mmapped) 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.

Zugewiesene Heaps aus dem geschützten Arbeitsspeicher

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 zugewiesen werden können.
  • Framework-Komponenten erhalten Zugriff auf diese Heaps, damit sie die Nutzung von Heaps über Codec2 HAL/nicht binderisierte HALs mit demselben Prozess ermöglichen können. Die allgemeinen Android-Framework-Funktionen können aufgrund der Abweichungen in ihren Implementierungsdetails jedoch nicht von ihnen abhängig sein. Wenn dem Android Common Kernel in Zukunft eine generische sichere Heap-Implementierung hinzugefügt wird, muss ein anderes ABI verwendet werden, um Konflikte mit Geräteupgrades zu vermeiden.

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

In AOSP ist ein Codec2-Allocator für die DMA-BUF-Heap-Schnittstelle 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 den Ü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 veralteten ION-Heaps mit dem Namen my_heap, der ein Flag (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. An dieser Stelle werden die Heaps als /dev/dma_heap/my_heap und /dev/dma_heap/my_heap_special mit den vorgesehenen Berechtigungen angezeigt.

Schritt 3:Ändern Sie die Makefiles für Clients, die von my_heap zuweisen, so, dass sie mit libdmabufheap verknüpft sind. 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“ leer setzen.

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

Zuweisungstyp Librion 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 ist zu diesem Zeitpunkt funktionsfähig, aber die Zuordnung erfolgt weiterhin über den ION-Heap, 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.