Przejście z ION na sterty DMA-BUF

W Androidzie 12 GKI 2.0 zastępuje alokator ION za pomocą stosów DMA-BUF z tych powodów:

  • Bezpieczeństwo: każda sterta DMA-BUF jest oddzielnym urządzeniem znakowym, więc dostępem do każdej z nich można oddzielnie kontrolować za pomocą sepolicy. Nie było to możliwe w przypadku ION, ponieważ alokacja z dowolnego stosu wymagała tylko dostępu do urządzenia /dev/ion.
  • Stabilność ABI: w odróżnieniu od ION interfejs IOCTL w ramach platformy stosu DMA-BUF jest stabilny pod względem ABI, ponieważ jest utrzymywany w górnym jądrze systemu Linux.
  • Standaryzacja: framework stosów DMA-BUF oferuje dobrze zdefiniowane UAPI. Usługa ION zezwalała na niestandardowe flagi i identyfikatory sterty, które uniemożliwiały opracowanie wspólnej platformy testowania, ponieważ implementacja ION w każdym urządzeniu mogła działać inaczej.

Gałąź android12-5.10 jądra Android Common Kernel została wyłączona CONFIG_ION 1 marca 2021 r..

Tło

Poniżej znajdziesz krótkie porównanie stosów ION i DMA-BUF.

Podobieństwa między strukturą stertów ION i DMA-BUF

  • Zarówno platformy sterty ION, jak i DMA-BUF są eksporterami DMA-BUF opartymi na stercie.
  • Oba te mechanizmy pozwalają każdej stercie zdefiniować własne funkcje alokacji i DMA-BUF.
  • Wydajność przydzielania jest podobna, ponieważ oba schematy wymagają wykonania pojedynczego polecenia IOCTL.

Różnice między frameworkami ION i DMA-BUF

sterty ION Kopie zapasowe DMA-BUF
Wszystkie alokacje ION są wykonywane za pomocą /dev/ion. Każdy stos DMA-BUF to urządzenie znakowe obecne w miejscu /dev/dma_heap/<heap_name>.
ION obsługuje flagi prywatne stosu. Kopie zapasowe DMA-BUF nie obsługują flag prywatności kopii zapasowej. Każdy rodzaj alokacji jest zamiast tego wykonywany z użyciem innego stosu. Na przykład wersje stosu systemowego z pamięci podręcznej i bez pamięci podręcznej to osobne stosy znajdujące się w miejscach /dev/dma_heap/system/dev/dma_heap/system_uncached.
Na potrzeby alokacji musisz podać identyfikator lub maskę sterty oraz flagi. Nazwa sterty służy do alokacji.

W następnych sekcjach wymieniono komponenty, które korzystają z ION, oraz opisano, jak przełączyć je na ramkę stosu DMA-BUF.

Przejście z sterowników jądra z ION na stosy DMA-BUF

Sterowniki jądra implementujące stosy ION

Zarówno stosy ION, jak i DMA-BUF umożliwiają każdemu stosowi implementowanie własnych alokacji i operacji DMA-BUF. Możesz więc przełączyć się z implementacji stosu ION na implementację stosu DMA-BUF, używając innego zestawu interfejsów API do zarejestrowania stosu. Ta tabela przedstawia interfejsy API rejestracji stosu ION i ich odpowiedniki w interfejsach API stosu DMA-BUF.

sterty ION Kopie zapasowe DMA-BUF
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);

Stosy DMA-BUF nie obsługują prywatnych flag sterty. Dlatego każdy wariant stosu musi być rejestrowany osobno za pomocą interfejsu API dma_heap_add(). Aby ułatwić udostępnianie kodu, zalecamy rejestrowanie wszystkich wersji tej samej sterty w tym samym sterowniku. Ten przykład dma-buf: system_heap pokazuje implementację wersji pamięci podręcznej i niezapisanej pamięci podręcznej systemu.

Aby utworzyć stos DMA-BUF od podstaw, użyj tego dma-buf: heaps: example template.

Sterowniki jądra przydzielają bezpośrednio z kupy ION.

Framework stosów DMA-BUF udostępnia też interfejs alokacji dla klientów w rdzeniu. Zamiast określać maskę stosu i flagi służące do wyboru typu alokacji, interfejs oferowany przez sterty DMA-BUF jako dane wejściowe przyjmuje nazwę stosu.

Poniżej przedstawiono interfejs API alokacji ION w rdzeniu i odpowiednie interfejsy API alokacji pamięci DMA-BUF. Sterowniki jądra mogą używać interfejsu API dma_heap_find(), aby wysyłać zapytania o istnienie sterty. Interfejs API zwraca wskaźnik do instancji struktury dma_heap, którą można następnie przekazać jako argument do interfejsu API dma_heap_buffer_alloc().

Stosy ION Kopie zapasowe DMA-BUF
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)

Sterowniki jądra korzystające z DMA-BUF

W przypadku sterowników, które importują tylko obiekty DMA-BUF, nie są wymagane żadne zmiany, ponieważ bufor przypisany ze sterty ION działa dokładnie tak samo jak bufor przypisany z odpowiedniej sterty DMA-BUF.

Przejście z klientów ION w przestrzeni użytkownika do stosów DMA-BUF

Aby ułatwić przejście klientom ION w przestrzeni użytkownika, udostępniono bibliotekę abstrakcji libdmabufheap. libdmabufheap obsługuje alokację w strukturach DMA-BUF i ION. Najpierw sprawdza, czy istnieje stos DMA-BUF o określonej nazwie. Jeśli nie, przechodzi do równoważnego stosu ION (jeśli istnieje).

Klienci powinni zainicjować obiekt BufferAllocator podczas inicjalizacji zamiast otwierać /dev/ion using ion_open(). Dzieje się tak, ponieważ opisami plików utworzonymi przez otwieranie /dev/ion/dev/dma_heap/<heap_name> zarządza wewnętrznie obiekt BufferAllocator.

Aby przejść z poziomu libion na poziom libdmabufheap, zmień zachowanie klientów w ten sposób:

  • Zamiast identyfikatora głowy/maski i flagi stosu śledź nazwę stosu, której używasz do alokacji.
  • Zastąp interfejs API ion_alloc_fd(), który wykorzystuje maskę stosu i argument flagi, interfejsem API BufferAllocator::Alloc(), który przyjmuje nazwę stosu.

Tabela ilustruje te zmiany, pokazując, jak libionlibdmabufheap wpływają na przydzielanie niewykorzystanego miejsca w systemowym stosie pamięci.

Typ przydziału libion libdmabufheap
Przydzielenie z bufora pamięci podręcznej z bufora systemowego ion_alloc_fd(ionfd, size, 0, ION_HEAP_SYSTEM, ION_FLAG_CACHED, &fd) allocator->Alloc("system", size)
Niebuforowana alokacja z stosu systemowego ion_alloc_fd(ionfd, size, 0, ION_HEAP_SYSTEM, 0, &fd) allocator->Alloc("system-uncached", size)

Niezcacheowany wariant stosu systemowego czeka na zatwierdzenie w głównym gałęzi, ale jest już częścią gałęzi android12-5.10.

Aby obsługiwać uaktualnianie urządzeń, interfejs MapNameToIonHeap() API umożliwia mapowanie nazwy sterty na parametry sterty ION (nazwa sterty lub maska i flagi), co umożliwia korzystanie z alokacji na podstawie nazw w tych interfejsach. Oto przykład przydziału na podstawie nazwy.

Dostępna jest dokumentacja każdego interfejsu API udostępnianego przez libdmabufheap. Biblioteka udostępnia też plik nagłówka do użytku przez klienty C.

Referencyjna implementacja Gralloc

Implementacja gralloc w Hikey960 korzysta z libdmabufheap, więc możesz ją wykorzystać jako implementację referencyjną.

Wymagane dodane elementy

W przypadku nowych stert DMA-BUF utworzonych na potrzeby konkretnego urządzenia dodaj nowy wpis do pliku ueventd.rc urządzenia. Ten przykład konfiguracji obsługi stosu DMA-BUF pokazuje, jak to zrobić w przypadku stosu systemowego DMA-BUF.

Wymagane dodatki do zasad bezpieczeństwa

Dodaj uprawnienia sepolicy, aby umożliwić klientowi w przestrzeni użytkownika dostęp do nowego stosu DMA-BUF. Ten przykład dodawania wymaganych uprawnień pokazuje uprawnienia sepolicy utworzone dla różnych klientów w celu dostępu do stosu systemu DMA-BUF.

Dostęp do stert dostawców z poziomu kodu platformy

Aby zapewnić zgodność z Treble, kod frameworku może przydzielać zasoby tylko z zaaprobowanych kategorii stosów dostawcy.

Na podstawie opinii partnerów zidentyfikowaliśmy 2 kategorie stert dostawców, do których należy uzyskać dostęp za pomocą kodu platformy:

  1. stosy oparte na stosie systemowym z optymalizacjami wydajności dla konkretnego urządzenia lub układu SoC;
  2. Stosy do przydzielenia z chronionej pamięci.

stosy oparte na stosie systemowym z optymalizacjami wydajności dla konkretnego urządzenia lub układu SoC;

Aby obsługiwać ten przypadek użycia, można zastąpić implementację stosu domyślnego systemu stosu DMA-BUF.

  • Funkcja CONFIG_DMABUF_HEAPS_SYSTEM została wyłączona w gki_defconfig, aby mogła pełnić funkcję modułu dostawcy.
  • Testy zgodności VTS sprawdzają, czy stos istnieje w miejscu /dev/dma_heap/system. Testy sprawdzają też, czy można przydzielić pamięć z kopca i czy zwracany deskryptor pliku (fd) może być mapowany w pamięci z przestrzeni użytkownika.

Powyższe punkty odnoszą się również do wariantu sterty systemowej bez pamięci podręcznej, chociaż jego istnienie nie jest obowiązkowe w przypadku w pełni spójnych urządzeń we/wy.

stosy do przydzielenia z pamięci chronionej,

Implementacje bezpiecznej sterty muszą być zależne od dostawcy, ponieważ Common Jądro Androida nie obsługuje ogólnej implementacji bezpiecznej sterty.

  • Zarejestruj implementacje swojego dostawcy jako /dev/dma_heap/system-secure<vendor-suffix>.
  • Implementacje stosu są opcjonalne.
  • Jeśli sterty istnieją, testy VTS sprawdzają, czy można z nich zrobić alokacje.
  • Komponenty frameworku mają dostęp do tych stosów, aby umożliwić ich użycie za pomocą interfejsu Codec2 HAL lub interfejsów HAL w ramach tego samego procesu, ale bez użycia Bindera. Ogólne funkcje Android Framework nie mogą jednak być od nich zależne ze względu na zmienność szczegółów ich implementacji. Jeśli w przyszłości do wspólnego jądra systemu Android zostanie dodana ogólna implementacja bezpiecznej stosu, będzie musiała używać innego interfejsu ABI, aby uniknąć konfliktów z uaktualnianiem urządzeń.

Przydzielacz 2 kodery dla stosów DMA-BUF

W AOSP jest dostępny algorytm alokacji codec2 dla interfejsu DMA-BUF heaps.

Interfejs magazynu komponentów, który umożliwia określenie parametrów sterty z C2 HAL, jest dostępny za pomocą algorytmu przydzielającego stertę C2 DMA-BUF.

Przykładowy przepływ danych w przypadku stosu ION

Aby płynnie przejść z kupy ION na kupę DMA-BUF, libdmabufheap umożliwia przełączanie po jednej kupie naraz. Poniższe kroki przedstawiają sugerowany przepływ pracy dotyczący przejścia na starszą stertę ION o nazwie my_heap, która obsługuje 1 flagę, ION_FLAG_MY_FLAG.

Krok 1. Utwórz odpowiedniki stosu ION w ramach interfejsu DMA-BUF. W tym przykładzie sterta ION my_heap obsługuje flagę ION_FLAG_MY_FLAG, dlatego rejestrujemy 2 sterty DMA-BUF:

  • Zachowanie my_heap dokładnie odpowiada działaniu stosu ION z wyłączoną flagą ION_FLAG_MY_FLAG.
  • Zachowanie funkcji my_heap_special jest dokładnie takie samo jak działanie stosu ION z włączoną flagą ION_FLAG_MY_FLAG.

Krok 2. Utwórz nieoczekiwane zmiany dla nowych stert DMA-BUF my_heap i my_heap_special. W tym momencie stosy są widoczne jako /dev/dma_heap/my_heap/dev/dma_heap/my_heap_special z odpowiednimi uprawnieniami.

Krok 3. W przypadku klientów, którzy przypisują z poziomu my_heap, zmodyfikuj ich pliki make, aby zawierały link do libdmabufheap. Podczas inicjowania klienta utwórz instancję obiektu BufferAllocator i użyj interfejsu API MapNameToIonHeap(), aby zmapować kombinację <ION heap name/mask, flag> na odpowiednie nazwy sterty DMA-BUF.

Na przykład:

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

Zamiast używać interfejsu API MapNameToIonHeap() z parametrami name i flag, możesz utworzyć mapowanie z <ION heap mask, flag> na równe sobie nazwy stosu DMA-BUF, ustawiając parametr nazwy stosu ION na pusty.

Krok 4. Zastąp wywołania ion_alloc_fd() wywołaniami BufferAllocator::Alloc(), używając odpowiedniej nazwy stosu.

Typ przydziału Libion libdmabufheap
Przydział z my_heap z nieustawioną flagą ION_FLAG_MY_FLAG ion_alloc_fd(ionfd, size, 0, ION_HEAP_MY_HEAP, 0, &fd) allocator->Alloc("my_heap", size)
Przydział z my_heap z ustawioną 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)

W tym momencie klient działa, ale nadal przydziela pamięć z kupy ION, ponieważ nie ma wymaganych uprawnień sepolicy do otwierania kupy DMA-BUF.

Krok 5. Utwórz uprawnienia sepolicy wymagane, aby klient miał dostęp do nowych stertów DMA-BUF. Klient jest w pełni przygotowany do przydzielania zadań z nowej sterty DMA-BUF.

Krok 6. Sprawdź, czy przydziały są realizowane z nowej sterty DMA-BUF, sprawdzając tag logcat.

Krok 7. Wyłącz stos ION my_heap w rdzeniu. Jeśli kod klienta nie musi obsługiwać urządzeń z aktualizacją (których jądra mogą obsługiwać tylko stosy ION), możesz też usunąć wywołania MapNameToIonHeap().