Przejście z użytkowania stosu ION na stos DMA-BUF (tylko jądro 5.4)

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/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)

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

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/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, interfejsem BufferAllocator::Alloc() API, który zamiast tego przyjmuje nazwę sterty.

W tabeli przedstawiono te zmiany, pokazując, jak libionlibdmabufheap 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:

  1. Stosy oparte na stosie systemowym z optymalizacjami wydajności specyficznymi dla urządzenia lub układu SoC.
  2. 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 w gki_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_heapmy_heap_special DMA-BUF. W tym momencie sterty są widoczne jako /dev/dma_heap/my_heap/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().