Przejście z użytkowania stosu ION na stos DMA-BUF (tylko jądro 5.4)

W Androidzie 12 GKI 2.0 zastępuje alokator ION sterami DMA-BUF z następujących powodów:

  • Bezpieczeństwo: ponieważ każda pula DMA-BUF jest oddzielnym urządzeniem znakowym, dostęp do każdej z nich można kontrolować osobno za pomocą sepolicy. Nie było to możliwe w przypadku ION, ponieważ alokacja z dowolnej sterty wymagała jedynie 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. ION zezwalał na niestandardowe flagi i identyfikatory stosu, co uniemożliwiało opracowanie wspólnego frameworku testowego, ponieważ implementacja ION na każdym urządzeniu mogła się różnić.

Gałąź android12-5.10 systemu Android Common jądra systemu 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

  • Platformy ION i DMA-BUF to platformy eksportujące DMA-BUF oparte na stosie.
  • 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 stosu jest używana do alokacji.

W kolejnych sekcjach wymieniono komponenty obsługujące ION oraz omówiono, jak przenieść je na platformę stert 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 przejść z implementacji stosu ION na implementację sterty DMA-BUF, rejestrując stertę za pomocą innego zestawu interfejsów API. 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);

Kopie zapasowe DMA-BUF nie obsługują flag prywatności kopii zapasowej. Dlatego każdy wariant stosu musi być rejestrowany osobno za pomocą interfejsu API dma_heap_add(). Aby ułatwić udostępnianie kodu, zalecamy zarejestrowanie wszystkich wersji tego samego stosu w ramach tego samego sterownika. Ten przykład dma-buf: system_heap pokazuje implementację wersji pamięci podręcznej i niezapisanej pamięci podręcznej systemu.

Użyj tego szablonu dma-buf: heaps: przykładowego, aby utworzyć od podstaw stertę DMA-BUF.

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 jądrze i odpowiadające mu interfejsy API alokacji DMA-BUF. Sterowniki jądra mogą używać interfejsu API dma_heap_find() do sprawdzania istnienia stosu. 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().

sterty 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 bufory DMA-BUF, nie trzeba wprowadzać żadnych zmian, ponieważ bufor przydzielony z puli ION zachowuje się dokładnie tak samo jak bufor przydzielony z odpowiedniej puli DMA-BUF.

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

Aby ułatwić przejście klientom w przestrzeni użytkownika ION, dostępna jest biblioteka abstrakcji o nazwie 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).

Klienty powinny zainicjować obiekt BufferAllocator podczas inicjowania zamiast otwierania /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.

Ta tabela ilustruje te zmiany, pokazując, jak libion i libdmabufheap wykonują alokację sterty systemu bez pamięci podręcznej.

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 ze sterty systemu 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 API MapNameToIonHeap() umożliwia zmapowanie nazwy stosu na parametry stosu ION (nazwa stosu lub maska i flagi), aby te interfejsy mogły używać alokacji na podstawie nazwy. 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życia przez klientów 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 sepolicy

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 uzyskania dostępu do stosu systemu DMA-BUF.

Dostęp do stosów dostawcy z poziomu kodu frameworku

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.

  • Aby CONFIG_DMABUF_HEAPS_SYSTEM był modułem dostawcy, wyłącz go w gki_defconfig.
  • Testy zgodności VTS pozwalają sprawdzić, czy sterta istnieje pod adresem /dev/dma_heap/system. Testy sprawdzają też, czy stos może zostać przydzielonych z niego i czy zwracany deskryptor pliku (fd) może być mapowany na pamięć (mmapped) 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 bezpiecznego stosu muszą być specyficzne dla dostawcy, ponieważ wspólny jądro Androida nie obsługuje ogólnej implementacji bezpiecznego stosu.

  • Zarejestruj implementacje specyficzne dla dostawcy jako /dev/dma_heap/system-secure<vendor-suffix>.
  • Implementacje stosu są opcjonalne.
  • Jeśli stosy istnieją, testy VTS sprawdzają, czy można z nich dokonać alokacji.
  • Komponenty platformy mają dostęp do tych stosów, dzięki czemu mogą korzystać z nich za pomocą interfejsu HAL Codec2/niepowiązane interfejsy HAL korzystające z tego samego procesu. 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 jądra Android Common Kernel zostanie dodana ogólna bezpieczna sterta, musi ona używać innego ABI, aby uniknąć konfliktów z urządzeniami w trakcie aktualizacji.

Przydzielacz 2 kodery dla stosów DMA-BUF

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

Interfejs repozytorium komponentów, który umożliwia określenie parametrów stosu z poziomu interfejsu C2 HAL, jest dostępny w ramach modułu alokacji pamięci C2 DMA-BUF.

Przykładowy przepływ danych w przypadku stosu ION

Aby płynnie przejść ze sterty ION na sterty DMA-BUF, libdmabufheap umożliwia przełączanie 1 sterty naraz. Te kroki pokazują sugerowany sposób przenoszenia niestandardowego stosu ION o nazwie my_heap, który obsługuje jeden flagę, ION_FLAG_MY_FLAG.

Krok 1. Utwórz odpowiedniki sterty ION w ramach DMA-BUF. W tym przykładzie, ponieważ pamięć alokowana z wykorzystaniem ION my_heap obsługuje flagę ION_FLAG_MY_FLAG, rejestrujemy 2 pamięci alokowane z wykorzystaniem DMA-BUF:

  • Zachowanie my_heap dokładnie odpowiada działaniu stosu ION z wyłączoną flagą ION_FLAG_MY_FLAG.
  • Zachowanie my_heap_special jest identyczne ze sposobem działania pamięci ION z włączoną flagą ION_FLAG_MY_FLAG.

Krok 2. Wprowadź zmiany w plikach my_heapmy_heap_special DMA-BUF. 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 przeprowadzają alokację z my_heap, zmodyfikuj pliki Makic, aby zawierały link do libdmabufheap. Podczas inicjowania klienta utwórz instancję obiektu BufferAllocator i za pomocą interfejsu API MapNameToIonHeap() przemapuj kombinację <ION heap name/mask, flag> na równe nazwy stosu 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 tej chwili klient działa, ale nadal przydziela stertę ION, ponieważ nie ma wymaganych uprawnień sepolicy potrzebnych do otwarcia sterty 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 alokacje są wykonywane z nowej puli DMA-BUF, analizując logcat.

Krok 7. Wyłącz stertę ION my_heap w jądrze. 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().