W Androidzie 12 GKI 2.0 zastępuje alokator ION za pomocą stosów DMA-BUF z tych 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 dowolnego stosu wymagała tylko dostępu do urządzenia
/dev/ion
. - Stabilność ABI: w odróżnieniu od ION interfejs IOCTL w ramach platformy sterowania sterowania zasobami 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
jądra Android Common Kernel została wyłączona CONFIG_ION
1 marca 2021 r..
Tło
Poniżej znajduje się krótkie porównanie stosów ION i DMA-BUF.
Podobieństwa między architekturą stosó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ą do przydzielenia jednego polecenia IOCTL.
Różnice między stertą ION a stertą 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 prywatne flagi stosu. | Kopie zapasowe DMA-BUF nie obsługują flag prywatności kopii zapasowej. Każdy inny rodzaj alokacji jest natomiast 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 .
|
Do alokacji musisz podać identyfikator lub maskę sterty oraz flagi. | Nazwa stosu jest używana 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);
|
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.
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ę i flagi stosu, aby wybrać typ alokacji, interfejs oferowany przez stosy DMA-BUF przyjmuje jako dane wejściowe nazwę stosu.
Poniżej przedstawiono interfejs API alokacji ION w rdzeniu i odpowiadające mu interfejsy API alokacji pamięci 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 przypisany do stosu ION zachowuje się dokładnie tak samo jak bufor przypisany do odpowiednich buforów 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
) i zamykanie (/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óra ma być używana do alokacji.
- Zastąp interfejs API
ion_alloc_fd()
, który przyjmuje argumenty maska stosu i flaga, interfejsem APIBufferAllocator::Alloc()
, który przyjmuje zamiast tego nazwę stosu.
Tabela ilustruje te zmiany, pokazując, jak libion
i libdmabufheap
wpływają na przydzielanie niewykorzystanego miejsca w systemowym stosie pamięci.
Typ przydziału | libion | libdmabufheap |
---|---|---|
Alokacja z bufora systemowego w pamięci podręcznej | 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 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 ustawienia
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 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 otrzymanych od partnerów zidentyfikowaliśmy 2 kategorie stosów dostawców, do których należy uzyskać dostęp z kodu frameworku:
- stosy oparte na stosie systemowym z optymalizacjami wydajności dla danego 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.
- W komponencie
gki_defconfig
funkcjaCONFIG_DMABUF_HEAPS_SYSTEM
jest wyłączona, aby był on modułem 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 dotyczą również niewykorzystanego wariantu stosu systemowego, chociaż jego istnienie nie jest wymagane w przypadku urządzeń o pełnej spójności operacji wejścia/wyjścia.
stosy do przydzielenia z zabezpieczonej pamięci;
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 dokonywać przydziałów.
- 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 nie w ramach 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 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 podręcznej C2 DMA-BUF.
Przykładowy przepływ danych w strukturze ION
Aby płynnie przejść z kupy ION na kupę DMA-BUF, libdmabufheap
umożliwia przełączanie jednej kupy naraz. Poniższe kroki pokazują sugerowany proces przejścia na niestarszą pamięć podręczną ION o nazwie my_heap
, która obsługuje jeden flagę, ION_FLAG_MY_FLAG
.
Krok 1. Utwórz odpowiedniki stosu ION w ramach interfejsu DMA-BUF. W tym przykładzie, ponieważ stos ION my_heap
obsługuje flagę ION_FLAG_MY_FLAG
, rejestrujemy 2 stosy 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 zbiorem 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 przydzielają z poziomu my_heap
, zmodyfikuj ich pliki make, aby zawierały link do libdmabufheap
. Podczas inicjowania klienta utwórz instancję obiektu BufferAllocator
i za pomocą interfejsu API MapNameToIonHeap()
mapuj kombinację <ION heap name/mask, flag>
na równe nazwy stosu DMA-BUF.
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 odpowiadające nazwy stosu DMA-BUF, ustawiając parametr nazwy stosu ION na pusty.
Krok 4. Zastąp wywołania ion_alloc_fd()
wywołaniem 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 przez klienta do uzyskania dostępu do nowych stosów DMA-BUF. Klient jest teraz w pełni gotowy do przydzielenia pamięci z nowej puli DMA-BUF.
Krok 6. Sprawdź, czy alokacje są wykonywane z nowej puli DMA-BUF, analizując 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 obsługują tylko stosy ION), możesz też usunąć wywołania MapNameToIonHeap()
.