ION에서 DMA-BUF 힙으로 전환

Android 12에서 GKI 2.0은 다음과 같은 이유로 ION 할당자를 DMA-BUF 힙으로 대체합니다.

  • 보안: 각 DMA-BUF 힙은 별도의 문자 장치이기 때문에 각 힙에 대한 액세스는 sepolicy를 통해 개별적으로 제어할 수 있습니다. 모든 힙에서 할당하려면 /dev/ion 장치에 대한 액세스만 필요하기 때문에 ION에서는 불가능했습니다.
  • ABI 안정성: ION과 달리 DMA-BUF 힙 프레임워크의 IOCTL 인터페이스는 업스트림 Linux 커널에서 유지 관리되기 때문에 ABI 안정성이 보장됩니다.
  • 표준화: DMA-BUF 힙 프레임워크는 잘 정의된 UAPI를 제공합니다. ION은 각 장치의 ION 구현이 다르게 작동할 수 있기 때문에 공통 테스트 프레임워크 개발을 방해하는 사용자 지정 플래그 및 힙 ID를 허용했습니다.

Android 공통 커널의 android12-5.10 분기는 2021년 3월 1일CONFIG_ION 을 비활성화했습니다.

배경

다음은 ION 힙과 DMA-BUF 힙을 간단히 비교한 것입니다.

ION과 DMA-BUF 힙 프레임워크 간의 유사점

  • ION 및 DMA-BUF 힙 프레임워크는 모두 힙 기반 DMA-BUF 내보내기입니다.
  • 둘 다 각 힙이 자체 할당자와 DMA-BUF 작업을 정의하도록 합니다.
  • 두 체계 모두 할당을 위해 단일 IOCTL이 필요하기 때문에 할당 성능은 비슷합니다.

ION과 DMA-BUF 힙 프레임워크의 차이점

ION 힙 DMA-BUF 힙
모든 ION 할당은 /dev/ion 으로 수행됩니다. 각 DMA-BUF 힙은 /dev/dma_heap/<heap_name> 에 있는 문자 장치입니다.
ION은 힙 개인 플래그를 지원합니다. DMA-BUF 힙은 힙 개인 플래그를 지원하지 않습니다. 대신 각각의 다른 종류의 할당이 다른 힙에서 수행됩니다. 예를 들어 캐시된 시스템 힙 변형과 캐시되지 않은 시스템 힙 변형은 /dev/dma_heap/system/dev/dma_heap/system_uncached 에 있는 별도의 힙입니다.
할당을 위해 힙 ID/마스크 및 플래그를 지정해야 합니다. 힙 이름은 할당에 사용됩니다.

다음 섹션에서는 ION을 처리하는 구성 요소를 나열하고 DMA-BUF 힙 프레임워크로 전환하는 방법을 설명합니다.

ION에서 DMA-BUF 힙으로 커널 드라이버 전환

ION 힙을 구현하는 커널 드라이버

ION 및 DMA-BUF 힙 모두 각 힙이 자체 할당자와 DMA-BUF 작업을 구현할 수 있도록 합니다. 따라서 다른 API 세트를 사용하여 힙을 등록함으로써 ION 힙 구현에서 DMA-BUF 힙 구현으로 전환할 수 있습니다. 이 표는 ION 힙 등록 API 및 이에 상응하는 DMA-BUF 힙 API를 보여줍니다.

ION 힙 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);

DMA-BUF 힙은 힙 개인 플래그를 지원하지 않습니다. 따라서 힙의 각 변형은 dma_heap_add() API를 사용하여 개별적으로 등록해야 합니다. 코드 공유를 용이하게 하려면 동일한 드라이버 내에서 동일한 힙의 모든 변형을 등록하는 것이 좋습니다. 이 dma-buf: system_heap 예제는 시스템 힙의 캐시된 변형과 캐시되지 않은 변형의 구현을 보여줍니다.

dma-buf: heaps: example 템플릿 을 사용하여 처음부터 DMA-BUF 힙을 만듭니다.

ION 힙에서 직접 할당하는 커널 드라이버

DMA-BUF 힙 프레임워크는 커널 내 클라이언트에 대한 할당 인터페이스 도 제공합니다. 할당 유형을 선택하기 위해 힙 마스크와 플래그를 지정하는 대신 DMA-BUF 힙에서 제공하는 인터페이스는 힙 이름을 입력으로 사용합니다.

다음은 커널 내 ION 할당 API 및 이에 상응하는 DMA-BUF 힙 할당 API를 보여줍니다. 커널 드라이버는 dma_heap_find() API를 사용하여 힙의 존재를 쿼리할 수 있습니다. API는 struct dma_heap 인스턴스에 대한 포인터를 반환하며, 이는 dma_heap_buffer_alloc() API에 인수로 전달할 수 있습니다.

ION 힙 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)

DMA-BUF를 사용하는 커널 드라이버

ION 힙에서 할당된 버퍼는 동등한 DMA-BUF 힙에서 할당된 버퍼와 정확히 동일하게 동작하기 때문에 DMA-BUF만 가져오는 드라이버에는 변경할 필요가 없습니다.

ION의 사용자 공간 클라이언트를 DMA-BUF 힙으로 전환

ION의 사용자 공간 클라이언트가 쉽게 전환할 수 있도록 libdmabufheap 이라는 추상화 라이브러리를 사용할 수 있습니다. libdmabufheap 은 DMA-BUF 힙 및 ION 힙에서 할당을 지원합니다. 먼저 지정된 이름의 DMA-BUF 힙이 존재하는지 확인하고 존재하지 않으면 동등한 ION 힙(있는 경우)으로 폴백합니다.

클라이언트는 /dev/ion using ion_open() 을 여는 대신 초기화 중에 BufferAllocator 객체를 초기화해야 합니다. /dev/ion/dev/dma_heap/<heap_name> 을 열어 생성된 파일 기술자는 내부적으로 BufferAllocator 개체에 의해 관리되기 때문입니다.

libdmabufheap 에서 libion 으로 전환하려면 다음과 같이 클라이언트의 동작을 수정하십시오.

  • 헤드 ID/마스크 및 힙 플래그 대신 할당에 사용할 힙 이름을 추적합니다.
  • 힙 마스크 및 플래그 인수를 사용하는 ion_alloc_fd ion_alloc_fd() ) API를 힙 이름을 대신 사용하는 BufferAllocator::Alloc() API로 교체합니다.

이 표는 libionlibdmabufheap 이 캐시되지 않은 시스템 힙 할당을 수행하는 방법을 보여줌으로써 이러한 변경 사항을 보여줍니다.

할당 유형 리비온 libdmabufheap
시스템 힙에서 캐시된 할당 ion_alloc_fd(ionfd, size, 0, ION_HEAP_SYSTEM, ION_FLAG_CACHED, &fd) allocator->Alloc("system", size)
시스템 힙에서 캐시되지 않은 할당 ion_alloc_fd(ionfd, size, 0, ION_HEAP_SYSTEM, 0, &fd) allocator->Alloc("system-uncached", size)

캐시되지 않은 시스템 힙 변형 은 업스트림 승인을 기다리고 있지만 이미 android12-5.10 분기의 일부입니다.

장치 업그레이드를 지원하기 위해 MapNameToIonHeap() API를 사용하면 힙 이름을 ION 힙 매개변수(힙 이름/마스크 및 플래그)에 매핑하여 해당 인터페이스에서 이름 기반 할당도 사용할 수 있습니다. 다음은 이름 기반 할당의 예 입니다.

libdmabufheap 에 의해 노출된 모든 API에 대한 문서를 사용할 수 있습니다. 라이브러리 는 또한 C 클라이언트에서 사용할 헤더 파일을 노출합니다.

참조 Granloc 구현

Hikey960 gralloc 구현은 libdmabufheap 을 사용하므로 참조 구현 으로 사용할 수 있습니다.

필수 이벤트 추가

새로 생성된 장치별 DMA-BUF 힙에 대해 장치의 ueventd.rc 파일에 새 항목을 추가합니다. DMA-BUF 힙을 지원하기 위한 이 설정 ueventd 예제 는 DMA-BUF 시스템 힙에 대해 이것이 어떻게 수행되는지 보여줍니다.

필수 sepolicy 추가

사용자 공간 클라이언트가 새 DMA-BUF 힙에 액세스할 수 있도록 sepolicy 권한을 추가합니다. 이 추가 필수 권한 예제는 DMA-BUF 시스템 힙에 액세스하기 위해 다양한 클라이언트에 대해 생성된 sepolicy 권한을 보여줍니다.

프레임워크 코드에서 공급업체 힙 액세스

Treble 준수를 보장하기 위해 프레임워크 코드는 사전 승인된 벤더 힙 범주에서만 할당할 수 있습니다.

파트너로부터 받은 피드백을 기반으로 Google은 프레임워크 코드에서 액세스해야 하는 두 가지 범주의 공급업체 힙을 식별했습니다.

  1. 장치 또는 SoC별 성능 최적화가 있는 시스템 힙을 기반으로 하는 힙입니다.
  2. 보호된 메모리에서 할당할 힙입니다.

장치 또는 SoC별 성능 최적화가 있는 시스템 힙 기반 힙

이 사용 사례를 지원하기 위해 기본 DMA-BUF 힙 시스템의 힙 구현을 재정의할 수 있습니다.

  • CONFIG_DMABUF_HEAPS_SYSTEM 은 공급업체 모듈이 될 수 있도록 gki_defconfig 에서 꺼져 있습니다.
  • VTS 준수 테스트는 힙이 /dev/dma_heap/system 에 있는지 확인합니다. 테스트는 또한 힙이 할당될 수 있고 반환된 파일 설명자( fd )가 사용자 공간에서 메모리 매핑(mmapped)될 수 있는지 확인합니다.

이전 사항은 시스템 힙의 캐시되지 않은 변형에도 해당되지만 완전한 IO 일관성 장치의 경우 반드시 존재해야 하는 것은 아닙니다.

보호된 메모리에서 할당할 힙

Android Common Kernel은 일반 보안 힙 구현을 지원하지 않으므로 보안 힙 구현은 공급업체에 따라 달라야 합니다.

  • 공급업체별 구현을 /dev/dma_heap/system-secure<vendor-suffix> 로 등록하십시오.
  • 이러한 힙 구현은 선택 사항입니다.
  • 힙이 존재하는 경우 VTS 테스트는 힙에서 할당이 이루어질 수 있는지 확인합니다.
  • 프레임워크 구성 요소는 이러한 힙에 대한 액세스 권한이 제공되므로 Codec2 HAL/바인더화되지 않은 동일 프로세스 HAL을 통해 힙을 사용할 수 있습니다. 그러나 일반 Android 프레임워크 기능은 구현 세부정보의 가변성으로 인해 종속될 수 없습니다. 향후 일반 보안 힙 구현이 Android 공통 커널에 추가되는 경우 기기 업그레이드와 충돌을 피하기 위해 다른 ABI를 사용해야 합니다.

DMA-BUF 힙용 코덱 2 할당자

DMA-BUF 힙 인터페이스에 대한 코덱2 할당자는 AOSP에서 사용할 수 있습니다.

C2 HAL에서 힙 매개변수를 지정할 수 있도록 하는 구성 요소 저장소 인터페이스는 C2 DMA-BUF 힙 할당자와 함께 사용할 수 있습니다.

ION 힙에 대한 샘플 전환 흐름

ION에서 DMA-BUF 힙으로 원활하게 전환하기 위해 libdmabufheap 을 사용하면 한 번에 하나의 힙을 전환할 수 있습니다. 다음 단계는 하나의 플래그 my_heap 를 지원하는 ION_FLAG_MY_FLAG 이라는 레거시가 아닌 ION 힙을 전환하기 위해 제안된 워크플로를 보여줍니다.

1단계: DMA-BUF 프레임워크에서 ION 힙에 해당하는 항목을 만듭니다. 이 예에서는 ION 힙 my_heapION_FLAG_MY_FLAG 플래그를 지원하기 때문에 두 개의 DMA-BUF 힙을 등록합니다.

  • my_heap 동작은 ION_FLAG_MY_FLAG 플래그가 비활성화된 ION 힙의 동작과 정확히 일치합니다.
  • my_heap_special 동작은 ION_FLAG_MY_FLAG 플래그가 활성화된 ION 힙의 동작과 정확히 일치합니다.

2단계:my_heapmy_heap_special DMA-BUF 힙에 대한 ueventd 변경 사항을 생성합니다. 이 시점에서 힙은 의도한 권한과 함께 /dev /dev/dma_heap/my_heap/dev/dma_heap/my_heap_special 로 표시됩니다.

3단계: my_heap 에서 할당하는 클라이언트의 경우 해당 makefile을 수정하여 libdmabufheap 에 연결합니다. 클라이언트 초기화 중에 BufferAllocator 개체를 인스턴스화하고 MapNameToIonHeap() API를 사용하여 <ION heap name/mask, flag> 조합을 동등한 DMA-BUF 힙 이름에 매핑합니다.

예를 들어:

allocator->MapNameToIonHeap("my_heap_special" /* name of DMA-BUF heap */, "my_heap" /* name of the ION heap */, ION_FLAG_MY_FLAG /* ion flags */ )

이름 및 플래그 매개변수와 함께 MapNameToIonHeap() API를 사용하는 대신 ION 힙 이름 매개변수를 공백으로 설정하여 <ION heap mask, flag> 에서 동등한 DMA-BUF 힙 이름 으로의 매핑을 생성할 수 있습니다.

4단계: 적절한 힙 이름을 사용하여 ion_alloc_fd() 호출을 BufferAllocator::Alloc() 로 교체합니다.

할당 유형 리비온 libdmabufheap
my_heap 플래그가 설정되지 않은 ION_FLAG_MY_FLAG 에서 할당 ion_alloc_fd(ionfd, size, 0, ION_HEAP_MY_HEAP, 0, &fd) allocator->Alloc("my_heap", size
my_heap 플래그가 설정된 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)

이 시점에서 클라이언트는 작동하지만 DMA-BUF 힙을 여는 데 필요한 sepolicy 권한이 없기 때문에 여전히 ION 힙에서 할당합니다.

5단계: 클라이언트가 새 DMA-BUF 힙에 액세스하는 데 필요한 sepolicy 권한을 만듭니다. 클라이언트는 이제 새 DMA-BUF 힙에서 할당할 수 있는 모든 준비가 되었습니다.

6단계: logcat 을 검사하여 새 DMA-BUF 힙에서 할당이 발생하는지 확인합니다.

7단계: 커널에서 ION 힙 my_heap 을 비활성화합니다. 클라이언트 코드가 장치 업그레이드를 지원할 필요가 없는 경우(커널이 ION 힙만 지원할 수 MapNameToIonHeap() 호출을 제거할 수도 있습니다.