Przejście ze stosów ION na DMA-BUF

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

  • Bezpieczeństwo: Ponieważ każda sterta DMA-BUF jest oddzielnym urządzeniem znakowym, dostęp do każdej sterty może być kontrolowany oddzielnie 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 przeciwieństwie do ION, interfejs IOCTL struktury stert DMA-BUF gwarantuje stabilność ABI, ponieważ jest utrzymywany w jądrze Linuksa.
  • Standaryzacja: Struktura stert DMA-BUF oferuje dobrze zdefiniowany interfejs UAPI. ION pozwolił na niestandardowe flagi i identyfikatory sterty, które uniemożliwiały opracowanie wspólnej platformy testowej, ponieważ implementacja ION na każdym urządzeniu mogła zachowywać się inaczej.

Oddział android12-5.10 wspólnego jądra systemu Android wyłączył CONFIG_ION 1 marca 2021 r .

Tło

Poniżej znajduje się krótkie porównanie stert ION i DMA-BUF.

Podobieństwa pomiędzy frameworkiem stert ION i DMA-BUF

  • Struktury stert ION i DMA-BUF są eksporterami DMA-BUF opartymi na stertach.
  • Obydwa pozwalają każdemu stertowi zdefiniować własny alokator i operacje DMA-BUF.
  • Wydajność alokacji jest podobna, ponieważ oba schematy wymagają jednego IOCTL do alokacji.

Różnice pomiędzy frameworkiem stert ION i DMA-BUF

stosy jonów Sterty DMA-BUF
Wszystkie alokacje ION są wykonywane za pomocą /dev/ion . Każda sterta DMA-BUF jest urządzeniem znakowym obecnym w /dev/dma_heap/<heap_name> .
ION obsługuje prywatne flagi sterty. Sterty DMA-BUF nie obsługują prywatnych flag sterty. Zamiast tego każdy inny rodzaj alokacji jest wykonywany z innej sterty. Na przykład buforowane i niebuforowane warianty sterty systemowej to oddzielne sterty zlokalizowane w /dev/dma_heap/system i /dev/dma_heap/system_uncached .
Aby dokonać alokacji, należy określić identyfikator sterty/maskę i flagi. Do alokacji używana jest nazwa sterty.

W poniższych sekcjach wymieniono komponenty obsługujące ION i opisano, jak je przełączyć na platformę stert DMA-BUF.

Przejście sterowników jądra ze stert ION na DMA-BUF

Sterowniki jądra implementujące sterty ION

Zarówno sterty ION, jak i DMA-BUF umożliwiają każdej stercie implementację własnych alokatorów i operacji DMA-BUF. Można więc przełączyć się z implementacji sterty ION na implementację sterty DMA-BUF, używając innego zestawu interfejsów API do rejestracji sterty. W tej tabeli przedstawiono interfejsy API rejestracji sterty ION i odpowiadające im interfejsy API sterty DMA-BUF.

stosy jonów Sterty 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);

Sterty DMA-BUF nie obsługują prywatnych flag sterty. Zatem każdy wariant sterty musi zostać zarejestrowany indywidualnie przy użyciu funkcji API dma_heap_add() . Aby ułatwić współdzielenie kodu, zaleca się zarejestrowanie wszystkich wariantów tej samej sterty w tym samym sterowniku. Ten przykład dma-buf: system_heap pokazuje implementację buforowanych i niebuforowanych wariantów sterty systemowej.

Użyj tego dma-buf: heaps: przykładowy szablon , aby utworzyć stertę DMA-BUF od zera.

Sterowniki jądra przydzielane bezpośrednio ze stert ION

Struktura stert DMA-BUF oferuje również interfejs alokacji dla klientów wbudowanych w jądro. Zamiast określać maskę sterty i flagi w celu wybrania typu alokacji, interfejs oferowany przez sterty DMA-BUF przyjmuje jako dane wejściowe nazwę sterty.

Poniżej przedstawiono wbudowany w jądro interfejs API alokacji sterty ION i odpowiadające mu interfejsy API alokacji sterty DMA-BUF. Sterowniki jądra mogą używać funkcji API dma_heap_find() do sprawdzania istnienia sterty. Funkcja API zwraca wskaźnik do instancji struktury dma_heap , która może być następnie przekazana jako argument do funkcji API dma_heap_buffer_alloc() .

stosy jonów Sterty 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

Żadne zmiany nie są wymagane w przypadku sterowników importujących tylko DMA-BUF, ponieważ bufor przydzielony ze sterty ION zachowuje się dokładnie tak samo, jak bufor przydzielony z równoważnej sterty DMA-BUF.

Przejście klientów przestrzeni użytkownika ION na sterty DMA-BUF

Aby ułatwić przejście klientom ION w przestrzeni użytkownika, dostępna jest biblioteka abstrakcyjna o nazwie libdmabufheap . libdmabufheap obsługuje alokację na stertach DMA-BUF i ION. Najpierw sprawdza, czy istnieje sterta DMA-BUF o określonej nazwie, a jeśli nie, powraca do równoważnej sterty ION, jeśli taka istnieje.

Klienci powinni zainicjować obiekt BufferAllocator podczas inicjalizacji, zamiast otwierać /dev/ion using ion_open() . Dzieje się tak, ponieważ deskryptory plików utworzone przez otwarcie /dev/ion i /dev/dma_heap/<heap_name> są zarządzane wewnętrznie przez obiekt BufferAllocator .

Aby przełączyć się z libion ​​na libdmabufheap , zmodyfikuj zachowanie klientów w następujący sposób:

  • Śledź nazwę sterty, która ma być używana do alokacji, zamiast identyfikatora/maski nagłówka i flagi sterty.
  • Zastąp funkcję API ion_alloc_fd() , która pobiera maskę sterty i argument flagi, funkcją API BufferAllocator::Alloc() , która zamiast tego pobiera nazwę sterty.

Ta tabela ilustruje te zmiany, pokazując, jak libion ​​i libdmabufheap dokonują alokacji sterty systemowej niezapisanej w pamięci podręcznej.

Rodzaj alokacji libion biblioteka libdmabufheap
Alokacja w pamięci podręcznej ze sterty systemowej ion_alloc_fd(ionfd, size, 0, ION_HEAP_SYSTEM, ION_FLAG_CACHED, &fd) allocator->Alloc("system", size)
Alokacja w pamięci podręcznej ze sterty systemowej ion_alloc_fd(ionfd, size, 0, ION_HEAP_SYSTEM, 0, &fd) allocator->Alloc("system-uncached", size)

Wariant sterty systemowej bez pamięci podręcznej oczekuje na zatwierdzenie, ale jest już częścią gałęzi android12-5.10 .

Aby wspierać aktualizację urządzeń, interfejs API MapNameToIonHeap() umożliwia mapowanie nazwy sterty na parametry sterty ION (nazwa/maska ​​i flagi sterty), aby umożliwić tym interfejsom również korzystanie z alokacji opartych na nazwach. Oto przykład alokacji opartej na nazwie .

Dostępna jest dokumentacja każdego interfejsu API udostępnianego przez libdmabufheap . Biblioteka udostępnia również plik nagłówkowy do użytku przez klientów C.

Referencyjna implementacja Graloc

Implementacja gralloc Hikey960 wykorzystuje libdmabufheap , więc możesz jej używać jako implementacji referencyjnej .

Wymagane dodatki ueventd

W przypadku nowych utworzonych stert DMA-BUF specyficznych dla urządzenia dodaj nowy wpis do pliku ueventd.rc urządzenia. Ten przykład konfiguracji ueventd do obsługi stert DMA-BUF pokazuje, jak zostało to zrobione dla sterty systemowej DMA-BUF.

Wymagane dodatki sepolicy

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

Dostęp do stosów dostawców z kodu frameworka

Aby zapewnić zgodność z Treble, kod frameworka może alokować tylko z wcześniej zatwierdzonych kategorii stert dostawców.

Na podstawie opinii otrzymanych od partnerów Google zidentyfikowało dwie kategorie stert dostawców, do których należy uzyskać dostęp z poziomu kodu frameworka:

  1. Sterty oparte na stercie systemowej z optymalizacjami wydajności specyficznymi dla urządzenia lub SoC.
  2. Sterty do przydzielenia z pamięci chronionej.

Sterty oparte na stercie systemowej z optymalizacjami wydajności specyficznymi dla urządzenia lub SoC

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

  • CONFIG_DMABUF_HEAPS_SYSTEM jest wyłączony w gki_defconfig , aby umożliwić mu bycie modułem dostawcy.
  • Testy zgodności VTS zapewniają, że sterta istnieje w /dev/dma_heap/system . Testy sprawdzają również, czy można przydzielić stertę i czy zwrócony deskryptor pliku ( fd ) może zostać zmapowany w pamięci (mmapped) z przestrzeni użytkownika.

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

Sterty do przydzielenia z pamięci chronionej

Implementacje bezpiecznej sterty muszą być specyficzne dla dostawcy, ponieważ wspólne jądro systemu Android nie obsługuje ogólnej implementacji bezpiecznej sterty.

  • Zarejestruj implementacje specyficzne dla dostawcy jako /dev/dma_heap/system-secure<vendor-suffix> .
  • Te implementacje sterty są opcjonalne.
  • Jeśli sterty istnieją, testy VTS zapewniają, że można z nich dokonać alokacji.
  • Komponenty frameworku mają dostęp do tych stert, dzięki czemu mogą umożliwić użycie stert poprzez warstwę HAL Codec2/niepowiązane warstwy HAL o tym samym procesie. Jednak ogólne funkcje platformy Android nie mogą 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 sterty, będzie ona musiała używać innego interfejsu ABI, aby uniknąć konfliktów podczas aktualizacji urządzeń.

Alokator kodeków 2 dla stert DMA-BUF

Alokator kodeków 2 dla interfejsu stert DMA-BUF jest dostępny w AOSP.

Interfejs magazynu komponentów, który umożliwia określenie parametrów sterty z warstwy C2 HAL, jest dostępny z alokatorem sterty C2 DMA-BUF.

Przykładowy przepływ przejścia dla sterty ION

Aby wygładzić przejście ze stert ION do DMA-BUF, libdmabufheap umożliwia przełączanie jednej sterty na raz. Poniższe kroki przedstawiają sugerowany przepływ pracy dotyczący przenoszenia niestarej sterty ION o nazwie my_heap , która obsługuje jedną flagę, ION_FLAG_MY_FLAG .

Krok 1: Utwórz odpowiedniki sterty ION w frameworku DMA-BUF. W tym przykładzie, ponieważ sterta ION my_heap obsługuje flagę ION_FLAG_MY_FLAG , rejestrujemy dwie sterty DMA-BUF:

  • Zachowanie my_heap dokładnie odpowiada zachowaniu sterty ION z wyłączoną flagą ION_FLAG_MY_FLAG .
  • Zachowanie my_heap_special dokładnie odpowiada zachowaniu sterty ION z włączoną flagą ION_FLAG_MY_FLAG .

Krok 2: Utwórz zmiany ueventd dla nowych stert DMA-BUF my_heap i my_heap_special . W tym momencie sterty są widoczne jako /dev/dma_heap/my_heap i /dev/dma_heap/my_heap_special , z zamierzonymi uprawnieniami.

Krok 3: W przypadku klientów, którzy alokują z my_heap , zmodyfikuj ich pliki makefile, aby łączyły się z libdmabufheap . Podczas inicjowania klienta utwórz instancję obiektu BufferAllocator i użyj funkcji API MapNameToIonHeap() , aby odwzorować kombinację <ION heap name/mask, flag> na równoważne 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ć funkcji API MapNameToIonHeap() z parametrami name i flag, można utworzyć mapowanie z <ION heap mask, flag> na równoważne nazwy sterty DMA-BUF, ustawiając parametr nazwy sterty ION na pusty.

Krok 4: Zamień wywołania ion_alloc_fd() na BufferAllocator::Alloc() używając odpowiedniej nazwy sterty.

Typ alokacji libion biblioteka libdmabufheap
Alokacja 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
Alokacja 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 dokonuje alokacji ze sterty ION, ponieważ nie ma wymaganych uprawnień sepolicy do otwierania sterty DMA-BUF.

Krok 5: Utwórz uprawnienia sepolicy wymagane dla klienta w celu uzyskania dostępu do nowych stert DMA-BUF. Klient jest teraz w pełni przygotowany do alokacji z nowej sterty DMA-BUF.

Krok 6: Sprawdź, czy alokacje zachodzą na nowej stercie DMA-BUF, sprawdzając logcat .

Krok 7: Wyłącz stertę ION my_heap w jądrze. Jeśli kod klienta nie musi obsługiwać aktualizacji urządzeń (których jądra mogą obsługiwać tylko sterty ION), możesz także usunąć wywołania MapNameToIonHeap() .