W Androidzie 12 GKI 2.0 zastępuje alokator ION stertami DMA-BUF z tych powodów:
- Bezpieczeństwo: ponieważ każdy obszar pamięci DMA-BUF jest osobnym urządzeniem znakowym, dostęp do każdego obszaru pamięci można kontrolować oddzielnie za pomocą sepolicy. W przypadku ION nie było to możliwe, ponieważ przydzielanie pamięci z dowolnego stogu wymagało tylko dostępu do
/dev/ion
urządzenia. - Stabilność interfejsu ABI: w przeciwieństwie do ION interfejs IOCTL platformy stert DMA-BUF jest stabilny pod względem interfejsu ABI, ponieważ jest utrzymywany w górnej części jądra systemu Linux.
- Standaryzacja: struktura stert DMA-BUF oferuje dobrze zdefiniowany interfejs UAPI. ION umożliwiał stosowanie niestandardowych flag i identyfikatorów sterty, co uniemożliwiało opracowanie wspólnych ram testowych, ponieważ implementacja ION na każdym urządzeniu mogła działać inaczej.
Gałąź android12-5.10
wspólnego jądra Androida została wyłączonaCONFIG_ION
1 marca 2021 r..
Tło
Poniżej znajdziesz krótkie porównanie stert ION i DMA-BUF.
Podobieństwa między strukturą stert ION i DMA-BUF
- Platformy ION i DMA-BUF to eksportery DMA-BUF oparte na stertach.
- Oba te interfejsy umożliwiają zdefiniowanie własnego alokatora i operacji DMA-BUF dla każdego stogu.
- Wydajność alokacji jest podobna, ponieważ oba schematy wymagają pojedynczego wywołania IOCTL do alokacji.
Różnice między platformami ION i DMA-BUF
Sterty ION | Stosy DMA-BUF |
---|---|
Wszystkie przydziały ION są realizowane za pomocą /dev/ion .
|
Każdy stos DMA-BUF jest urządzeniem znakowym, które znajduje się w /dev/dma_heap/<heap_name> .
|
ION obsługuje prywatne flagi sterty. | Stosy DMA-BUF nie obsługują prywatnych flag stosu. Każdy rodzaj alokacji jest wykonywany z innego stogu. Na przykład warianty sterty systemowej z pamięci podręcznej i bez niej to oddzielne sterty znajdujące się pod adresami /dev/dma_heap/system i /dev/dma_heap/system_uncached .
|
Na potrzeby alokacji należy określić identyfikator/maskę sterty i flagi. | Nazwa sterty jest używana do alokacji. |
W kolejnych sekcjach wymieniamy komponenty, które obsługują ION, i opisujemy, jak przełączyć je na framework stert DMA-BUF.
Przenoszenie sterowników jądra z pamięci ION do stert DMA-BUF
Sterowniki jądra implementujące sterty ION
Zarówno sterty ION, jak i DMA-BUF umożliwiają każdej stercie implementowanie własnych alokatorów i operacji DMA-BUF. Możesz więc przełączyć się z implementacji sterty ION na implementację sterty DMA-BUF, używając innego zestawu interfejsów API do rejestrowania sterty. W tej tabeli znajdziesz interfejsy API rejestracji sterty ION i odpowiadające im interfejsy API sterty DMA-BUF.
Sterty ION | Stosy 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);
|
Stosy DMA-BUF nie obsługują prywatnych flag stosu. Dlatego każdą odmianę sterty należy zarejestrować osobno za pomocą interfejsu dma_heap_add()
. Aby ułatwić udostępnianie kodu, zalecamy zarejestrowanie wszystkich wariantów tego samego stogu w ramach tego samego sterownika.
Ten przykład dma-buf: system_heap pokazuje implementację wariantów z pamięcią podręczną i bez niej w przypadku sterty systemowej.
Użyj tego szablonu dma-buf: heaps: example template, aby utworzyć od podstaw stertę DMA-BUF.
Sterowniki jądra bezpośrednio przydzielające pamięć ze stert ION
Struktura stert DMA-BUF udostępnia też interfejs alokacji dla klientów w jądrze. Zamiast określać maskę sterty i flagi, aby wybrać typ alokacji, interfejs oferowany przez sterty DMA-BUF przyjmuje jako dane wejściowe nazwę sterty.
Poniżej przedstawiamy interfejs API przydzielania pamięci ION w jądrze i jego odpowiedniki w postaci interfejsów API przydzielania sterty DMA-BUF. Sterowniki jądra mogą używać interfejsu dma_heap_find()
API do sprawdzania, czy sterta istnieje. Interfejs API zwraca wskaźnik do instancji struct dma_heap, który można następnie przekazać jako argument do interfejsu API dma_heap_buffer_alloc()
.
Sterty ION | Stosy 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 są wymagane żadne zmiany, ponieważ bufor przydzielony z pamięci ION działa dokładnie tak samo jak bufor przydzielony z równoważnej pamięci DMA-BUF.
Przeniesienie klientów przestrzeni użytkownika ION do stert DMA-BUF
Aby ułatwić przejście klientom ION w przestrzeni użytkownika, dostępna jest biblioteka abstrakcji o nazwie libdmabufheap
. libdmabufheap
obsługuje alokację w pamięciach DMA-BUF i ION. Najpierw sprawdza, czy istnieje sterta DMA-BUF o określonej nazwie. Jeśli nie, przełącza się na równoważną stertę ION (jeśli taka istnieje).
Klienci powinni inicjować obiekt a
BufferAllocator
podczas inicjowania zamiast otwierać /dev/ion using
ion_open()
. Jest to spowodowane tym, że deskryptory plików utworzone przez otwieranie /dev/ion
i /dev/dma_heap/<heap_name>
są zarządzane wewnętrznie przez obiekt BufferAllocator
.
Aby przejść z libion
na libdmabufheap
, zmień zachowanie klientów w ten sposób:
- Śledź nazwę sterty, która ma być używana do alokacji, zamiast identyfikatora/maski głowicy i flagi sterty.
- Zastąp interfejs
ion_alloc_fd()
API, który przyjmuje maskę sterty i argument flagi, interfejsemBufferAllocator::Alloc()
API, który zamiast tego przyjmuje nazwę sterty.
W tabeli przedstawiono te zmiany, pokazując, jak libion
i libdmabufheap
przydzielają pamięć na stercie systemu bez użycia pamięci podręcznej.
Rodzaj przydziału | libion | libdmabufheap |
---|---|---|
Pamięć podręczna przydzielona ze sterty systemowej | ion_alloc_fd(ionfd, size, 0, ION_HEAP_SYSTEM, ION_FLAG_CACHED, &fd)
|
allocator->Alloc("system", size)
|
Niebuforowane przydzielanie z systemowej sterty | 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 w górę, ale jest już częścią gałęzi android12-5.10
.
Aby obsługiwać uaktualnianie urządzeń, interfejs MapNameToIonHeap()
API umożliwia mapowanie nazwy sterty na parametry sterty ION (nazwa sterty lub maska i flagi), dzięki czemu interfejsy te mogą używać alokacji opartych na nazwach. Oto przykład przydziału opartego na nazwie.
Dostępna jest dokumentacja każdego interfejsu API udostępnianego przezlibdmabufheap
. Biblioteka udostępnia też plik nagłówkowy do użytku przez klientów C.
Referencyjna implementacja Gralloc
Implementacja gralloc na urządzeniu Hikey960 korzysta z libdmabufheap
, więc możesz jej używać jako implementacji referencyjnej.
Wymagane dodatki do ueventd
W przypadku każdego nowo utworzonego sterty DMA-BUF specyficznej 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 to zrobić w przypadku systemowej sterty DMA-BUF.
Wymagane dodatki do sepolicy
Dodaj uprawnienia sepolicy, aby umożliwić klientowi przestrzeni użytkownika dostęp do nowego obszaru pamięci DMA-BUF. Ten przykład dodawania wymaganych uprawnień pokazuje uprawnienia sepolicy utworzone dla różnych klientów w celu uzyskania dostępu do systemowego stogu DMA-BUF.
Dostęp do stosów dostawców z kodu platformy
Aby zapewnić zgodność z Treble, kod platformy może przydzielać pamięć tylko z wcześniej zatwierdzonych kategorii stert dostawcy.
Na podstawie opinii otrzymanych od partnerów Google zidentyfikował 2 kategorie stert dostawców, do których kod platformy musi mieć dostęp:
- Stosy oparte na stosie systemowym z optymalizacjami wydajności specyficznymi dla urządzenia lub układu SoC.
- Stosy, z których można przydzielać pamięć chronioną.
Stosy oparte na stosie systemowym z optymalizacjami wydajności dostosowanymi do urządzenia lub układu SoC
Aby obsługiwać ten przypadek użycia, można zastąpić implementację sterty domyślnego systemu sterty DMA-BUF.
CONFIG_DMABUF_HEAPS_SYSTEM
jest wyłączony wgki_defconfig
, aby mógł być modułem dostawcy.- Testy zgodności VTS zapewniają, że sterta istnieje w lokalizacji
/dev/dma_heap/system
. Testy sprawdzają też, czy można przydzielić pamięć sterty i czy zwrócony deskryptor pliku (fd
) może być mapowany w pamięci (mmapped) z przestrzeni użytkownika.
Powyższe punkty dotyczą też wariantu sterty systemowej bez pamięci podręcznej, choć jego istnienie nie jest obowiązkowe w przypadku urządzeń w pełni spójnych pod względem wejścia/wyjścia.
Stosy do przydzielania z pamięci chronionej
Implementacje bezpiecznego stogu muszą być specyficzne dla dostawcy, ponieważ wspólny kernel Androida nie obsługuje ogólnej implementacji bezpiecznego stogu.
- Zarejestruj implementacje specyficzne dla dostawcy jako
/dev/dma_heap/system-secure<vendor-suffix>
. - Te implementacje sterty są opcjonalne.
- Jeśli sterty istnieją, testy VTS sprawdzają, czy można z nich przydzielać pamięć.
- Komponenty platformy mają dostęp do tych stert, dzięki czemu mogą włączać ich użycie za pomocą interfejsu HAL Codec2 lub interfejsów HAL w tym samym procesie, które nie korzystają z mechanizmu Binder. Ogólne funkcje platformy Android nie mogą jednak od nich zależeć ze względu na różnice w szczegółach implementacji. Jeśli w przyszłości do wspólnego jądra Androida zostanie dodana ogólna implementacja bezpiecznego stogu, musi ona używać innego interfejsu ABI, aby uniknąć konfliktów z urządzeniami, które są uaktualniane.
Alokator Codec 2 dla stert DMA-BUF
W AOSP dostępny jest alokator codec2 dla interfejsu stert DMA-BUF.
Interfejs magazynu komponentów, który umożliwia określanie parametrów sterty z poziomu C2 HAL, jest dostępny w przypadku alokatora sterty C2 DMA-BUF.
Przykładowy przepływ przejścia dla sterty ION
Aby ułatwić przejście z pamięci ION na pamięć DMA-BUF, libdmabufheap
umożliwia przełączanie po jednej pamięci naraz. Poniższe kroki przedstawiają sugerowany przepływ pracy podczas przekształcania starszego obszaru pamięci ION o nazwie my_heap
, który obsługuje 1 flagę, ION_FLAG_MY_FLAG
.
Krok 1. Utwórz odpowiedniki sterty ION w ramach DMA-BUF. W tym przykładzie, ponieważ sterta ION my_heap
obsługuje flagę ION_FLAG_MY_FLAG
, rejestrujemy 2 sterty DMA-BUF:
my_heap
zachowuje się dokładnie tak samo jak sterta ION z wyłączoną flagąION_FLAG_MY_FLAG
.my_heap_special
zachowuje się dokładnie tak samo jak sterta ION z włączoną flagąION_FLAG_MY_FLAG
.
Krok 2. Utwórz zmiany ueventd dla nowych stert my_heap
i my_heap_special
DMA-BUF. W tym momencie sterty 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ą zasoby z my_heap
, zmodyfikuj pliki makefile, aby były połączone z libdmabufheap
. Podczas inicjowania klienta utwórz obiekt BufferAllocator
i użyj interfejsu MapNameToIonHeap()
API, aby zmapować kombinację <ION heap name/mask, flag>
na równoważne nazwy stert 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 MapNameToIonHeap()
API z parametrami nazwy i flagi możesz 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. Zastąp wywołania ion_alloc_fd()
wywołaniami
BufferAllocator::Alloc()
, używając odpowiedniej nazwy sterty.
Typ przydziału | libion | libdmabufheap |
---|---|---|
Alokacja z my_heap z flagą ION_FLAG_MY_FLAG ustawioną na „nie”
|
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 przydziela pamięć z pamięci ION, ponieważ nie ma wymaganych uprawnień sepolicy do otwarcia pamięci DMA-BUF.
Krok 5. Utwórz uprawnienia sepolicy wymagane, aby klient miał dostęp do nowych obszarów pamięci DMA-BUF. Klient może teraz w pełni przydzielać pamięć z nowego stogu DMA-BUF.
Krok 6. Sprawdź, czy alokacje są przeprowadzane z nowego stogu DMA-BUF, analizując logcat.
Krok 7. Wyłącz stertę ION my_heap
w jądrze. Jeśli kod klienta nie musi obsługiwać uaktualniania urządzeń (których jądra mogą obsługiwać tylko sterty ION), możesz też usunąć wywołania MapNameToIonHeap()
.