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) | |
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ą APIBufferAllocator::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:
- Sterty oparte na stercie systemowej z optymalizacjami wydajności specyficznymi dla urządzenia lub SoC.
- 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 wgki_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()
.