ION'dan DMA-BUF yığınlarına geçiş

Android 12'de GKI 2.0, ION ayırıcıyı aşağıdaki nedenlerle DMA-BUF yığınlarıyla değiştirir:

  • Güvenlik: Her DMA-BUF yığını ayrı bir karakter cihazı olduğundan her yığına erişim, sepolicy ile ayrı ayrı kontrol edilebilir. Yığınlardan ayırma için yalnızca /dev/ion cihazına erişim gerektiğinden bu işlem ION ile mümkün değildi.
  • ABI kararlılığı: ION'un aksine, DMA-BUF yığınları çerçevesinin IOCTL arayüzü, yayın öncesi Linux çekirdeğinde korunduğu için ABI kararlıdır.
  • Standartlaştırma: DMA-BUF yığınları çerçevesi, iyi tanımlanmış bir UAPI sunar. ION, her cihazın ION uygulaması farklı davranabileceğinden ortak bir test çerçevesi geliştirilmesini engelleyen özel işaretlere ve yığın kimliklerine izin veriyordu.

Android Ortak Çekirdeği'nin android12-5.10 şubesi 1 Mart 2021'de devre dışı bırakıldıCONFIG_ION.

Arka plan

Aşağıda, ION ve DMA-BUF yığınları arasındaki kısa bir karşılaştırma verilmiştir.

ION ve DMA-BUF yığınları çerçevesi arasındaki benzerlikler

  • ION ve DMA-BUF yığın çerçevelerinin ikisi de yığın tabanlı DMA-BUF dışa aktarıcılarıdır.
  • Her ikisi de her yığının kendi ayırıcısını ve DMA-BUF işlemlerini tanımlamasını sağlar.
  • Her iki şema da ayırma için tek bir IOCTL'ye ihtiyaç duyduğundan ayırma performansı benzerdir.

ION ve DMA-BUF yığın çerçevesi arasındaki farklar

ION yığınları DMA-BUF yığınları
Tüm ION ayırmaları /dev/ion ile yapılır. Her DMA-BUF yığını, /dev/dma_heap/<heap_name> adresinde bulunan bir karakter cihazıdır.
ION, yığın özel işaretlerini destekler. DMA-BUF yığınları, yığın özel işaretlerini desteklemez. Her farklı ayırma türü, farklı bir yığından yapılır. Örneğin, önbelleğe alınmış ve önbelleğe alınmamış sistem yığını varyantları, /dev/dma_heap/system ve /dev/dma_heap/system_uncached adreslerinde bulunan ayrı yığınlardır.
Ayırma için yığın kimliği/maskesi ve işaretlerin belirtilmesi gerekir. Yığın adı, tahsis için kullanılır.

Aşağıdaki bölümlerde, ION ile ilgili bileşenler listelenmiş ve bunların DMA-BUF yığınları çerçevesine nasıl geçirileceği açıklanmaktadır.

Çekirdek sürücülerini ION'dan DMA-BUF yığınlarına geçirme

ION yığınlarını uygulayan çekirdek sürücüleri

Hem ION hem de DMA-BUF yığınları, her yığının kendi ayırıcılarını ve DMA-BUF işlemlerini uygulamasına olanak tanır. Böylece, yığını kaydetmek için farklı bir API grubu kullanarak ION yığın uygulamasından DMA-BUF yığın uygulamasına geçebilirsiniz. Bu tabloda ION yığın kayıt API'leri ve bunların eşdeğer DMA-BUF yığın API'leri gösterilmektedir.

ION yığınları DMA-BUF yığınları
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 yığınları, yığın özel işaretlerini desteklemez. Bu nedenle, yığının her varyantı dma_heap_add() API'si kullanılarak ayrı ayrı kaydedilmelidir. Kod paylaşımını kolaylaştırmak için aynı yığının tüm varyantlarının aynı sürücüye kaydedilmesi önerilir. Bu dma-buf: system_heap örneği, sistem yığınının önbelleğe alınmış ve önbelleğe alınmamış varyantlarının uygulanmasını gösterir.

DMA-BUF yığınını sıfırdan oluşturmak için bu dma-buf: heaps: example şablonunu kullanın.

Doğrudan ION yığınlarından ayırma yapan çekirdek sürücüleri

DMA-BUF yığın çerçevesi, çekirdek içi istemciler için bir ayırma arayüzü de sunar. DMA-BUF yığınları tarafından sunulan arayüz, ayırma türünü seçmek için yığın maskesini ve işaretlerini belirtmek yerine giriş olarak bir yığın adı alır.

Aşağıda, çekirdek içi ION tahsis API'si ve eşdeğeri DMA-BUF yığın tahsis API'leri gösterilmektedir. Çekirdek sürücüleri, bir yığının varlığını sorgulamak için dma_heap_find() API'yi kullanabilir. API, struct dma_heap örneğinin işaretçisini döndürür. Bu işaretçi daha sonra dma_heap_buffer_alloc() API'ye bağımsız değişken olarak iletilebilir.

ION yığınları DMA-BUF yığınları
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)

DMA-BUF kullanan çekirdek sürücüleri

ION yığınından ayrılan bir arabellek, eşdeğer bir DMA-BUF yığınından ayrılan bir arabellekle tam olarak aynı şekilde davrandığından yalnızca DMA-BUF'ları içe aktaran sürücülerde değişiklik yapılması gerekmez.

ION'ın kullanıcı alanı istemcilerini DMA-BUF yığınlarına geçirin

ION'ın kullanıcı alanı istemcileri için geçişi kolaylaştırmak amacıyla libdmabufheap adlı bir soyutlama kitaplığı mevcuttur. libdmabufheap, DMA-BUF yığınlarında ve ION yığınlarında tahsisi destekler. İlk olarak, belirtilen ada sahip bir DMA-BUF yığınının olup olmadığını kontrol eder ve yoksa eşdeğer bir ION yığınına (varsa) geçer.

İstemciler, başlatma sırasında /dev/ion using ion_open() öğesini açmak yerine bir BufferAllocator nesnesini başlatmalıdır. Bunun nedeni, /dev/ion ve /dev/dma_heap/<heap_name>'ün açılmasıyla oluşturulan dosya tanımlayıcılarının BufferAllocator nesnesi tarafından dahili olarak yönetilmesidir.

libion'ten libdmabufheap'e geçmek için istemcilerin davranışını aşağıdaki gibi değiştirin:

  • Başlık kimliği/maskesi ve yığın işareti yerine, ayırma için kullanılacak yığın adını takip edin.
  • Yığın maskesi ve işaret bağımsız değişkenini alan ion_alloc_fd() API'yi, bunun yerine yığın adı alan BufferAllocator::Alloc() API'siyle değiştirin.

Bu tabloda, libion ve libdmabufheap'ün önbelleğe alınmamış bir sistem yığını ayırma işlemini nasıl gerçekleştirdiğini göstererek bu değişiklikler açıklanmaktadır.

Tahsis türü libion libdmabufheap
Sistem yığınından önbelleğe alınan ayırma ion_alloc_fd(ionfd, size, 0, ION_HEAP_SYSTEM, ION_FLAG_CACHED, &fd) allocator->Alloc("system", size)
Sistem yığınından önbelleğe alınmamış ayırma ion_alloc_fd(ionfd, size, 0, ION_HEAP_SYSTEM, 0, &fd) allocator->Alloc("system-uncached", size)

Önbelleğe alınmamış sistem yığını varyantı yukarı akış için onay bekliyor ancak zaten android12-5.10 şubesinin bir parçası.

Cihazların yükseltilmesini desteklemek amacıyla MapNameToIonHeap() API, bir yığın adının ION yığın parametreleriyle (yığın adı veya maske ve işaretler) eşlenmesine izin verir. Böylece bu arayüzlerin ada dayalı ayırmalar kullanmasına izin verilir. Aşağıda ada dayalı bir tahsis örneği verilmiştir.

libdmabufheap tarafından kullanıma sunulan her API ile ilgili dokümanlar mevcuttur. Kitaplık, C istemcileri tarafından kullanılacak bir üstbilgi dosyası da sağlar.

Referans Gralloc uygulaması

Hikey960 gralloc uygulamasında libdmabufheap kullanıldığı için bu uygulamayı referans uygulama olarak kullanabilirsiniz.

Gerekli ueventd eklemeleri

Oluşturulan cihaza özgü yeni DMA-BUF yığınları için cihazın ueventd.rc dosyasına yeni bir giriş ekleyin. Bu DMA-BUF yığınlarını desteklemek için ueventd'yi ayarlama örneği, bunun DMA-BUF sistem yığını için nasıl yapıldığını gösterir.

Gerekli sepolicy eklemeleri

Kullanıcı alanı istemcisinin yeni bir DMA-BUF yığınına erişmesini sağlamak için sepolicy izinleri ekleyin. Bu gerekli izinleri ekleme örneği, çeşitli istemcilerin DMA-BUF sistem yığınına erişmesi için oluşturulan sepolicy izinlerini gösterir.

Çerçeve kodundan tedarikçi firma yığınlarına erişme

Treble uyumluluğunu sağlamak için çerçeve kodu yalnızca önceden onaylanmış tedarikçi yığını kategorilerinden ayırt edebilir.

Google, iş ortaklarından alınan geri bildirimlere dayanarak çerçeve kodundan erişilmesi gereken iki tedarikçi firma yığını kategorisi belirledi:

  1. Cihaz veya SoC'ye özel performans optimizasyonları içeren sistem yığınına dayalı yığınlar.
  2. Korunan bellekten ayrılacak yığınlar.

Cihaza veya çip üzerinde sisteme (SoC) özel performans optimizasyonları içeren sistem yığınına dayalı yığınlar

Bu kullanım alanını desteklemek için varsayılan DMA-BUF yığın sisteminin yığın uygulaması geçersiz kılınabilir.

  • CONFIG_DMABUF_HEAPS_SYSTEM, tedarikçi modülü olması için gki_defconfig'te devre dışı bırakılır.
  • VTS uygunluk testleri, yığının /dev/dma_heap/system adresinde bulunduğundan emin olur. Testler ayrıca, yığının kullanıcı alanından ayrılıp ayrılamayacağını ve döndürülen dosya tanımlayıcısının (fd) kullanıcı alanından bellek haritasına eklenip eklenemeyeceğini de doğrular.

Önceki noktalar, sistem yığınının önbelleğe alınmamış varyantı için de geçerlidir. Ancak bu varyantın varlığı, tamamen G/Ç uyumlu cihazlar için zorunlu değildir.

Korunan bellekten ayrılacak yığınlar

Android Common Kernel, genel güvenli yığın uygulamasını desteklemediğinden güvenli yığın uygulamaları tedarikçiye özel olmalıdır.

  • Tedarikçiye özgü uygulamalarınızı /dev/dma_heap/system-secure<vendor-suffix> olarak kaydedin.
  • Bu yığın uygulamaları isteğe bağlıdır.
  • Yığınlar varsa VTS testleri, bunlardan tahsis yapılabileceğini doğrular.
  • Çerçeve bileşenleri, Codec2 HAL/binder uygulanmamış, aynı işlem HAL'ler üzerinden yığın kullanımını etkinleştirebilmeleri için bu yığınlara erişebilir. Bununla birlikte, uygulama ayrıntılarındaki değişkenlikler nedeniyle genel Android çerçevesi özellikleri bunlara bağlı olamaz. Gelecekte Android Ortak Çekirdeği'ne genel bir güvenli yığın uygulaması eklenirse yükseltme yapan cihazlarla çakışmayı önlemek için farklı bir ABI kullanmalıdır.

DMA-BUF yığınları için Codec 2 ayırıcı

AOSP'de DMA-BUF yığın arayüzü için bir codec2 ayırıcı mevcuttur.

C2 HAL'den yığın parametrelerinin belirtilmesine olanak tanıyan bileşen mağazası arayüzü, C2 DMA-BUF yığın ayırıcısıyla kullanılabilir.

ION yığını için örnek geçiş akışı

ION'dan DMA-BUF yığınlarına geçişi kolaylaştırmak için libdmabufheap, tek seferde bir yığının değiştirilmesine olanak tanır. Aşağıdaki adımlarda, my_heap adlı eski olmayan ve ION_FLAG_MY_FLAG işaretini destekleyen bir ION yığınını taşımak için önerilen iş akışı gösterilmektedir.

1. adım: DMA-BUF çerçevesinde ION yığınının eşdeğerlerini oluşturun. Bu örnekte, my_heap ION yığını ION_FLAG_MY_FLAG işaretini desteklediğinden iki DMA-BUF yığını kaydederiz:

  • my_heap davranışı, ION_FLAG_MY_FLAG işareti devre dışıyken ION yığınının davranışıyla tam olarak aynıdır.
  • my_heap_special davranışı, ION_FLAG_MY_FLAG işareti etkinken ION yığınının davranışıyla tam olarak eşleşir.

2. Adım: Yeni my_heap ve my_heap_special DMA-BUF yığınları için ueventd değişikliklerini oluşturun. Bu noktada, yığınlar istenen izinlerle /dev/dma_heap/my_heap ve /dev/dma_heap/my_heap_special olarak görünür.

3. Adım: my_heap öğesinden ayırma yapan istemcilerde, oluşturma dosyalarını libdmabufheap ile bağlantı kuracak şekilde değiştirin. İstemci başlatılırken bir BufferAllocator nesnesi oluşturun ve <ION heap name/mask, flag> kombinasyonunu eşdeğer DMA-BUF yığın adlarıyla eşlemek için MapNameToIonHeap() API'sini kullanın.

Örnek:

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

Ad ve işaret parametreleriyle MapNameToIonHeap() API'yi kullanmak yerine, ION yığın adı parametresini boş olarak ayarlayarak <ION heap mask, flag> ile eşdeğer DMA-BUF yığın adları arasında eşleme oluşturabilirsiniz.

4. adım: Uygun yığın adını kullanarak ion_alloc_fd() çağrılarını BufferAllocator::Alloc() ile değiştirin.

Ayırma türü libion libdmabufheap
ION_FLAG_MY_FLAG işareti ayarlanmamışken my_heap kaynağından tahsis ion_alloc_fd(ionfd, size, 0, ION_HEAP_MY_HEAP, 0, &fd) allocator->Alloc("my_heap", size)
ION_FLAG_MY_FLAG işareti ayarlanmış my_heap bölgesinden ayırma ion_alloc_fd(ionfd, size, 0, ION_HEAP_MY_HEAP, ION_FLAG_MY_FLAG, &fd) allocator->Alloc("my_heap_special", size)

Bu noktada istemci çalışır durumdadır ancak DMA-BUF yığınını açmak için gerekli sepolicy izinlerine sahip olmadığından hâlâ ION yığınından ayırma işlemi yapmaktadır.

5. Adım: İstemcinin yeni DMA-BUF yığınlarına erişmesi için gereken sepolicy izinlerini oluşturun. İstemci artık yeni DMA-BUF yığınından ayırmak için gerekli tüm özelliklere sahiptir.

6. adım: logcat'i inceleyerek ayırmaların yeni DMA-BUF yığınından yapıldığını doğrulayın.

7. Adım: Çekirdekteki ION yığınını (my_heap) devre dışı bırakın. Müşteri kodunun, yükseltme yapan cihazları (çekirdekleri yalnızca ION yığınlarını destekleyebilir) desteklemesi gerekmiyorsa MapNameToIonHeap() çağrılarını da kaldırabilirsiniz.