Android 12에서 DMABUF 및 GPU 메모리 계산 구현

이 페이지에서는 Android 12에서 도입된 다양한 메모리 계산 개선사항을 설명합니다.

sysfs의 DMA-BUF 통계

Android 11과 Android 12에서는 debugfs를 사용자 빌드에 마운트할 수 없습니다. 따라서 DMA-BUF 통계가 Android 12의 /sys/kernel/dmabuf/buffers 디렉터리에 있는 sysfs에 추가되었습니다.

경로 설명
/sys/kernel/dmabuf/buffers /sys/kernel/dmabuf/buffers 디렉터리에는 모든 DMA-BUF의 내부 상태 스냅샷이 포함되어 있습니다. /sys/kernel/dmabuf/buffers/<inode_number>에는 고유한 inode 번호 <inode_number>가 있는 DMA-BUF 통계가 포함되어 있습니다.
/sys/kernel/dmabuf/buffers/<inode_number>/exporter_name 이 읽기 전용 파일에는 DMA-BUF 내보내기의 이름이 포함되어 있습니다.
/sys/kernel/dmabuf/buffers/<inode_number>/size 이 읽기 전용 파일은 DMA-BUF 크기를 바이트 단위로 지정합니다.

libdmabufinfo API는 DMA-BUF sysfs 통계를 파싱하여 내보내기별, 버퍼별 통계를 노출합니다.

DMA-BUF를 내보내는 커널 드라이버는 dma_buf_export() API를 호출하여 DMA-BUF를 만들기 전에 struct dma_buf_export_infoexp_name 필드를 내보내기 이름으로 올바르게 설정해야 합니다. 이는 libdmabufinfodmabuf_dump 도구가 버그 신고에 노출될 내보내기별 통계를 추출하는 데 필요합니다.

dmabuf_dump 도구는 이 정보를 새 인수 -b와 함께 출력하도록 수정되었습니다.

DMA-BUF 힙 프레임워크 통계

GKI 2.0의 ION이 지원 중단되고 업스트림 Linux 커널의 일부인 DMA-BUF 힙 프레임워크로 대체됩니다.

다음 전역 ION 통계는 Android 11에서 추적됩니다.

  • 모든 ION 힙에서 내보낸 DMA-BUF의 총 크기
  • 모든 ION 힙에서 저장한 사용되지 않은 사전 할당 메모리의 총 크기

Android 11에는 ION별 힙 통계를 노출하는 데 사용할 수 있는 인터페이스가 없습니다.

다음 표는 ION 통계 인터페이스를 Android 12에서 DMA-BUF 힙 프레임워크를 사용하는 기기의 ION 통계 인터페이스와 비교합니다.

Android 11 또는 Android 12에서 ION 지원이 적용되어 출시되는 기기 Android 12에서 DMA-BUF 힙이 적용되어 출시되는 기기
힙당 ION 통계 없음 DMA-BUF sysfs 통계에서 파싱됨
내보낸 DMA-BUF의 총 크기 /sys/kernel/ion/total_heap_size_kb
(ION이 아닌 내보내기에서 내보낸 DMA-BUF 크기는 포함되지 않음)
DMA-BUF sysfs 통계에서 파싱됨
(내보낸 모든 DMA-BUF의 크기 포함)
힙에서 풀링된 총 메모리 /sys/kernel/ion/total_pool_size_kb /sys/kernel/dma_heap/total_pool_size_kb

손실된 RAM 계산 정확도 개선

이전에는 손실된 RAM을 다음과 같이 계산했습니다.

최종 long lostRAM = memInfo.getTotalSizeKb() - (totalPss - totalSwapPss)

- memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb()

- kernelUsed - memInfo.getZramTotalSizeKb();

totalPss 구성요소에는 GPU 메모리 사용량(Memtrack HAL의 getMemory() 인터페이스에서 반환)이 포함되었습니다. kernelUsed 구성요소에는 총 DMA-BUF 메모리 사용량이 포함되었습니다. 그러나 Android 기기의 경우 GPU 메모리 출처는 다음과 같습니다.

  • 실제 페이지 할당자를 사용하여 GPU 드라이버에서 직접 할당
  • GPU 주소 공간에 매핑된 DMA-BUF

따라서 GPU 주소 공간에 메모리 매핑된 DMA-BUF는 손실된 RAM을 계산할 때 두 번 뺐습니다. Android 12는 GPU 주소 공간에 매핑된 DMA-BUF의 크기를 계산하는 솔루션을 구현합니다. 즉, 손실된 RAM 계산에서 한 번만 고려됩니다.

솔루션 세부정보는 다음과 같습니다.

  • PID 0으로 호출될 때 Memtrack HAL API getMemory()는 MemtrackType::GL 및 MemtrackRecord::FLAG_SMAPS_UNACCOUNTED의 전역 총 GPU 비공개 메모리를 보고해야 합니다.
  • GL이 아닌 MemtrackTypePID 0으로 호출될 때 getMemory()는 실패해서는 안 됩니다. 대신 0을 반환해야 합니다.
  • Android 12에서 추가된 GPU 메모리 tracepoint/eBPF 솔루션은 총 GPU 메모리를 고려합니다. 총 GPU 메모리에서 총 GPU 비공개 메모리를 빼면 GPU 주소 공간에 매핑된 DMA-BUF 크기가 제공됩니다. 이 값은 GPU 메모리 사용량을 올바르게 고려하여 손실된 RAM 계산의 정확성을 개선하는 데 사용할 수 있습니다.
  • 비공개 GPU 메모리는 대부분의 Memtrack HAL 구현에서 totalPss에 포함되어 있으므로 lostRAM에서 삭제하기 전에 중복 삭제해야 합니다.

구현된 솔루션은 다음 섹션에서 자세히 설명합니다.

손실된 RAM에서 Memtrack 가변성 삭제

Memtrack HAL 구현은 파트너마다 다를 수 있으므로 HAL의 totalPSS에 포함된 GPU 메모리가 항상 일관된 것은 아닙니다. lostRAM에서 가변성을 삭제하기 위해 MemtrackType::GRAPHICSMemtrackType::GL에서 고려된 메모리는 lostRAM 계산 중에 totalPss에서 삭제됩니다.

MemtrackType::GRAPHICS 메모리는 totalPss에서 삭제되고 아래와 같이 ActivityManagerService.javalostRAM 계산에서 totalExportedDmabuf 메모리로 대체됩니다.

final long totalExportedDmabuf = Debug.getDmabufTotalExportedKb();

. . .

final long dmabufUnmapped = totalExportedDmabuf - dmabufMapped;

. . .

// Account unmapped dmabufs as part of the kernel memory allocations
kernelUsed += dmabufUnmapped;

// Replace Memtrack HAL reported Graphics category with mapped dmabufs
totalPss -= totalMemtrackGraphics;
totalPss += dmabufMapped;

MemtrackType::GL 메모리는 totalPss에서 삭제되고 아래와 같이 ActivityManagerService.javalostRAM 계산에서 비공개 GPU 메모리(gpuPrivateUsage)로 대체됩니다.

final long gpuUsage = Debug.getGpuTotalUsageKb();

. . .

final long gpuPrivateUsage = Debug.getGpuPrivateMemoryKb();

. . .

// Replace the Memtrack HAL-reported GL category with private GPU allocations.
// Count it as part of the kernel memory allocations.
totalPss -= totalMemtrackGl;
kernelUsed += gpuPrivateUsage;

업데이트된 손실 RAM 계산

총 비공개 GPU 메모리와 내보낸 총 DMA 버퍼 메모리는 모두 lostRAM에서 삭제된 kernelUsed + totalPss에 포함됩니다. 이렇게 하면 손실된 RAM 계산으로 인한 이중 계산과 Memtrack 가변성이 모두 제거됩니다.

final long lostRAM = memInfo.getTotalSizeKb() - (totalPss - totalSwapPss)
- memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb()
- kernelUsed - memInfo.getZramTotalSizeKb();

유효성 검사

VTS 테스트는 Linux 커널 버전 5.4 이상으로 Android 12에서 실행되는 기기가 getGpuDeviceInfo() API를 지원한다는 규칙을 적용합니다.

새 Memtrack HAL API getGpuDeviceInfo()는 사용 중인 GPU 기기에 관한 정보를 반환해야 합니다.

이를 통해 메모리 계산과 DMA 버퍼 및 GPU 메모리 사용량에 관한 가시성이 개선됩니다. 손실된 RAM 및 메모리 계산 개선을 위해 memtrack AIDL HAL을 구현합니다. 이 기능은 Google 서비스에 종속되지 않습니다.

구현

이 기능은 AIDL Memtrack HAL에 따라 달라지며 Android 12에서 구현하는 안내는 코드에 주석으로 포함되어 있습니다.

모든 HIDL HAL은 향후 출시에서 AIDL로 변환될 예정입니다.

다음 API가 core/java/android/os/Debug.java에 추가되었습니다.

   /**
     * Return total memory size in kilobytes for exported DMA-BUFs or -1 if
     * the DMA-BUF sysfs stats at /sys/kernel/dmabuf/buffers could not be read.
     *
     * @hide
     */
    public static native long getDmabufTotalExportedKb();

   /**
     * Return memory size in kilobytes allocated for DMA-BUF heap pools or -1 if
     * /sys/kernel/dma_heap/total_pools_kb could not be read.
     *
     * @hide
     */
    public static native long getDmabufHeapPoolsSizeKb();

버전이 의도 대로 작동하도록 하려면 GPU 드라이버에서 tracepoint를 통합하고 MemtrackType::GL 및 MemtrackRecord::FLAG_SMAPS_UNACCOUNTED의 PID 0으로 호출될 때 전역 총 GPU 비공개 메모리를 올바르게 반환하도록 AIDL memtrack HAL getMemory() API를 구현하세요.