ION'dan DMA-BUF Yığınlarına Geçiş

Android 12'de GKI 2.0, aşağıdaki nedenlerden dolayı ION ayırıcısını 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. Bu ION ile mümkün değildi çünkü herhangi bir yığından tahsis yalnızca /dev/ion cihazına erişim gerektiriyordu.
  • ABI kararlılığı: ION'dan farklı olarak, DMA-BUF yığın çerçevesinin IOCTL arayüzünün ABI kararlılığı garanti edilir çünkü yukarı akışlı Linux çekirdeğinde korunur.
  • Standardizasyon: DMA-BUF yığın çerçevesi iyi tanımlanmış bir UAPI sunar. ION, her cihazın ION uygulamasının farklı davranabilmesi nedeniyle ortak bir test çerçevesi geliştirmeyi engelleyen özel bayraklara ve yığın kimliklerine izin verdi.

Android Common Kernel'in android12-5.10 dalı , 1 Mart 2021'de CONFIG_ION devre dışı bıraktı.

Arka plan

Aşağıda ION ve DMA-BUF yığınları arasında kısa bir karşılaştırma yer almaktadır.

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

  • ION ve DMA-BUF yığın çerçevelerinin her ikisi de yığın tabanlı DMA-BUF dışa aktarıcılarıdır.
  • İkisi de her yığının kendi ayırıcısını ve DMA-BUF operasyonlarını tanımlamasına izin veriyor.
  • Tahsis performansı benzer çünkü her iki şema da tahsis için tek bir IOCTL'ye ihtiyaç duyuyor.

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

İYON yığınları DMA-BUF yığınları
Tüm ION tahsisleri /dev/ion ile yapılır. Her DMA-BUF yığını /dev/dma_heap/<heap_name> konumunda bulunan bir karakter aygıtıdır.
ION, yığın özel bayraklarını destekler. DMA-BUF yığınları, yığın özel işaretlerini desteklemez. Bunun yerine her farklı tahsis türü farklı bir yığından yapılır. Örneğin, önbelleğe alınmış ve önbelleğe alınmamış sistem yığını çeşitleri, /dev/dma_heap/system ve /dev/dma_heap/system_uncached konumunda bulunan ayrı yığınlardır.
Tahsis için yığın kimliği/maskesi ve bayrakların belirtilmesi gerekir. Yığın adı tahsis için kullanılır.

Aşağıdaki bölümlerde ION ile ilgili bileşenler listelenmekte ve bunların DMA-BUF yığın çerçevesine nasıl aktarılacağı 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 operasyonlarını uygulamasına olanak tanır. Böylece, yığını kaydetmek için farklı bir API kümesi kullanarak bir ION yığın uygulamasından DMA-BUF yığın uygulamasına geçiş yapabilirsiniz. Bu tablo, ION yığın kaydı API'lerini ve bunların eşdeğer DMA-BUF yığın API'lerini gösterir.

İYON 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. Dolayısıyla yığının her bir çeşidinin dma_heap_add() API'si kullanılarak ayrı ayrı kaydedilmesi gerekir. Kod paylaşımını kolaylaştırmak için aynı yığının tüm değişkenlerinin 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ış değişkenlerinin uygulanmasını gösterir.

Sıfırdan bir DMA-BUF yığını oluşturmak için bu dma-buf: heaps: örnek şablonunu kullanın.

ION yığınlarından doğrudan tahsis edilen çekirdek sürücüleri

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

Aşağıda çekirdek içi ION ayırma API'si ve eşdeğer DMA-BUF yığın ayırma API'leri gösterilmektedir. Çekirdek sürücüleri bir yığının varlığını sorgulamak için dma_heap_find() API'sini kullanabilir. API, struct dma_heap örneğine bir işaretçi döndürür; bu daha sonra dma_heap_buffer_alloc() API'sine argüman olarak iletilebilir.

İYON 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'leri kullanan çekirdek sürücüleri

Yalnızca DMA-BUF'leri içe aktaran sürücüler için herhangi bir değişiklik yapılmasına gerek yoktur çünkü bir ION yığınından tahsis edilen bir arabellek, eşdeğer bir DMA-BUF yığınından tahsis edilen bir arabellek ile tam olarak aynı şekilde davranır.

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

ION'un kullanıcı alanı istemcileri için geçişi kolaylaştırmak amacıyla libdmabufheap adı verilen bir soyutlama kitaplığı mevcuttur. libdmabufheap DMA-BUF yığınlarında ve ION yığınlarında tahsisi destekler. Öncelikle belirtilen adda bir DMA-BUF yığınının var olup olmadığını kontrol eder ve eğer yoksa eşdeğer bir ION yığınına (varsa) geri döner.

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

libion libdmabufheap geçiş yapmak için istemcilerin davranışını aşağıdaki gibi değiştirin:

  • Tahsis için kullanılacak yığın adının, baş kimliği/maskesi ve yığın bayrağı yerine takip edin.
  • Bir yığın maskesi ve bayrak bağımsız değişkeni alan ion_alloc_fd() API'sini, bunun yerine bir yığın adı alan BufferAllocator::Alloc() API'si ile değiştirin.

Bu tablo, libion ​​ve libdmabufheap önbelleğe alınmamış bir sistem yığın tahsisini nasıl yaptığını göstererek bu değişiklikleri göstermektedir.

Tahsis türü libion libdmabufheap
Sistem yığınından önbelleğe alınmış tahsis 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ış tahsis ion_alloc_fd(ionfd, size, 0, ION_HEAP_SYSTEM, 0, &fd) allocator->Alloc("system-uncached", size)

Önbelleğe alınmamış sistem yığını çeşidi yukarı yönde onay bekliyor ancak zaten android12-5.10 dalının bir parçası.

Cihazların yükseltilmesini desteklemek için, MapNameToIonHeap() API'si, bir yığın adının ION yığın parametreleriyle (yığın adı/maskesi ve bayrakları) eşleştirilmesine ve bu arayüzlerin aynı zamanda isme dayalı ayırmalar kullanmasına olanak sağlar. Burada isme dayalı bir tahsis örneği verilmiştir.

libdmabufheap tarafından kullanıma sunulan her API'nin belgeleri mevcuttur. Kitaplık ayrıca C istemcileri tarafından kullanılacak bir başlık dosyasını da kullanıma sunar.

Referans Gralloc uygulaması

Hikey960 gralloc uygulaması libdmabufheap kullanır, dolayısıyla onu referans uygulaması olarak kullanabilirsiniz.

Gerekli olay ve eklemeler

Oluşturulan cihaza özel yeni DMA-BUF yığınları için cihazın ueventd.rc dosyasına yeni bir giriş ekleyin. DMA-BUF yığınlarını desteklemeye yönelik bu Kurulum örneği, bunun DMA-BUF sistem yığını için nasıl yapıldığını gösterir.

Gerekli sepolitika 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.

Satıcı yığınlarına çerçeve kodundan erişme

Treble uyumluluğunu sağlamak için çerçeve kodu yalnızca önceden onaylanmış satıcı yığın kategorilerinden tahsis edilebilir.

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

  1. Cihaza veya SoC'ye özgü performans optimizasyonlarına sahip sistem yığınını temel alan yığınlar.
  2. Korumalı bellekten ayrılacak yığınlar.

Cihaza veya SoC'ye özgü performans optimizasyonlarına sahip sistem yığınını temel alan yığınlar

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

  • CONFIG_DMABUF_HEAPS_SYSTEM , satıcı modülü olmasına izin vermek için gki_defconfig kapatılmıştır.
  • VTS uyumluluk testleri, yığının /dev/dma_heap/system konumunda mevcut olmasını sağlar. Testler ayrıca yığının tahsis edilebileceğini ve döndürülen dosya tanımlayıcısının ( fd ) kullanıcı alanından bellek eşlemeli (mmapped) yapılabileceğini doğrular.

Yukarıdaki noktalar aynı zamanda sistem yığınının önbelleğe alınmamış değişkeni için de geçerlidir; ancak bunun varlığı tamamen GÇ uyumlu cihazlar için zorunlu değildir.

Korumalı bellekten ayrılacak yığınlar

Android Common Kernel genel bir güvenli yığın uygulamasını desteklemediğinden, güvenli yığın uygulamaları satıcıya özel olmalıdır.

  • Satıcıya özel uygulamalarınızı /dev/dma_heap/system-secure<vendor-suffix> olarak kaydedin.
  • Bu yığın uygulamaları isteğe bağlıdır.
  • Yığınlar mevcutsa, VTS testleri tahsislerin bunlardan yapılabileceğini garanti eder.
  • Çerçeve bileşenlerine, Codec2 HAL/bağlayıcı olmayan, aynı işlem HAL'leri aracılığıyla yığın kullanımını etkinleştirebilmeleri için bu yığınlara erişim sağlanır. Ancak genel Android çerçeve özellikleri, uygulama ayrıntılarındaki değişkenlik nedeniyle bunlara bağlı olamaz. Gelecekte Android Common Kernel'e genel bir güvenli yığın uygulaması eklenirse, yükseltme cihazlarıyla çakışmaları önlemek için farklı bir ABI kullanması gerekir.

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

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

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

Bir 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 aynı anda bir yığının değiştirilmesine izin verir. Aşağıdaki adımlarda, ION_FLAG_MY_FLAG tek bayrağını destekleyen, my_heap adlı eski olmayan bir ION yığınının geçişine yönelik önerilen bir iş akışı gösterilmektedir.

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

  • my_heap davranışı, ION_FLAG_MY_FLAG bayrağının devre dışı olduğu ION yığınının davranışıyla tam olarak eşleşir.
  • my_heap_special davranışı, ION_FLAG_MY_FLAG bayrağı etkinken ION yığınının davranışıyla tam olarak eşleşir.

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

Adım 3: my_heap tahsis yapan istemciler için, makefile dosyalarını libdmabufheap bağlanacak şekilde değiştirin. İstemci başlatma sırasında, bir BufferAllocator nesnesi oluşturun ve <ION heap name/mask, flag> birleşimini eşdeğer DMA-BUF yığın adlarıyla eşlemek için MapNameToIonHeap() API'sini kullanın.

Örneğin:

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

MapNameToIonHeap() API'sini ad ve bayrak parametreleriyle kullanmak yerine, ION yığın adı parametresini boş olarak ayarlayarak <ION heap mask, flag> öğesinden eşdeğer DMA-BUF yığın adlarına eşleme oluşturabilirsiniz.

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

Tahsis türü libion libdmabufheap
ION_FLAG_MY_FLAG işaretiyle my_heap tahsis ayarlanmadı ion_alloc_fd(ionfd, size, 0, ION_HEAP_MY_HEAP, 0, &fd) allocator->Alloc("my_heap", size
ION_FLAG_MY_FLAG bayrağı ayarlanmış olarak my_heap tahsis ion_alloc_fd(ionfd, size, 0, ION_HEAP_MY_HEAP, ION_FLAG_MY_FLAG, &fd) allocator->Alloc("my_heap_special", size)

Bu noktada, istemci işlevseldir ancak DMA-BUF yığınını açmak için gerekli sepolicy izinlerine sahip olmadığından hala ION yığınından tahsis yapmaktadır.

Adım 5: İstemcinin yeni DMA-BUF yığınlarına erişmesi için gerekli olan sepolicy izinlerini oluşturun. İstemci artık yeni DMA-BUF yığınından tahsis yapmak için tam donanıma sahiptir.

Adım 6: Logcat'i inceleyerek tahsislerin yeni DMA-BUF yığınından gerçekleştiğini doğrulayın.

Adım 7: Çekirdekteki ION yığını my_heap devre dışı bırakın. İstemci kodunun yükseltme aygıtlarını (çekirdekleri yalnızca ION yığınlarını destekleyebilen) desteklemesi gerekmiyorsa, MapNameToIonHeap() çağrılarını da kaldırabilirsiniz.