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 i /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)
|
|
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
i /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 APIBufferAllocator::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:
- stosy oparte na stosie systemowym z optymalizacjami wydajności dla konkretnego urządzenia lub układu SoC;
- 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 wgki_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_heap
i my_heap_special
DMA-BUF. W tym momencie stosy są widoczne jako /dev/dma_heap/my_heap
i /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()
.