Implement DMABUF and GPU memory accounting in Android 12

This page describes the various memory accounting improvements introduced in Android 12.

DMA-BUF statistics in sysfs

In Android 11 and Android 12, debugfs can’t be mounted in User builds. So DMA-BUF statistics has been added to sysfs in the /sys/kernel/dmabuf/buffers directory in Android 12.

Path Description
/sys/kernel/dmabuf/buffers The /sys/kernel/dmabuf/buffers directory contains a snapshot of the internal state of every DMA-BUF. /sys/kernel/dmabuf/buffers/<inode_number> contains the statistics for the DMA-BUF with the unique inode number <inode_number>.
/sys/kernel/dmabuf/buffers/<inode_number>/exporter_name This read-only file contains the name of the DMA-BUF exporter.
/sys/kernel/dmabuf/buffers/<inode_number>/size This read-only file specifies the size of the DMA-BUF in bytes.

The libdmabufinfo API parses the DMA-BUF sysfs stats to expose per-exporter and per-buffer statistics.

Please note that kernel drivers who export DMA-BUFs must set the exp_name field of struct dma_buf_export_info correctly to the exporter name before invoking the dma_buf_export() API to create a DMA-BUF. This is required for libdmabufinfo and the dmabuf_dump tool to derive per-exporter statistics which are then exposed in bugreport.

The dmabuf_dump tool has been modified to output this information with a new argument, -b.

Statistics for the DMA-BUF heaps framework

ION in GKI 2.0 is being deprecated in favor of the DMA-BUF heaps framework, which is part of the upstream linux kernel.

The following global ION statistics are tracked in Android 11:

  • Total size of DMA-BUFs exported by every ION heap
  • Total size of unused pre-allocated memory stored by every ION heap

There is no interface available to expose per-ION heap statistics in Android 11.

The following table compares the ION statistics interfaces with their counterparts for devices that use the DMA-BUF heap framework in Android 12.

Android 11 or Devices launching with ION support in Android 12 Devices launching with DMA-BUF heaps in Android 12
Per-heap ION statistics None Parsed from DMA-BUF sysfs stats
Total size of DMA-BUFs exported /sys/kernel/ion/total_heap_size_kb
(Doesn’t include the size of DMA-BUFs exported by non-ION exporters)
Parsed from DMA-BUF sysfs stats
(includes the size of all DMA-BUFs exported).
Total memory pooled by heaps /sys/kernel/ion/total_pool_size_kb /sys/kernel/dma_heap/total_pool_size_kb

Improve the lost RAM calculation accuracy

Previously the lost RAM calculation was done as follows:

final long lostRAM = memInfo.getTotalSizeKb() - (totalPss - totalSwapPss)

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

- kernelUsed - memInfo.getZramTotalSizeKb();

The totalPss component included the GPU memory usage (returned by the Memtrack HAL’s getMemory() interface). The kernelUsed component included the total DMA-BUF memory usage. However, for Android devices, the GPU memory came from the following:

  • Direct allocations made by the GPU driver using physical page allocator
  • DMA-BUFs mapped into GPU address space

Therefore, DMA-BUFs that were memory-mapped into the GPU address space were subtracted twice when lost RAM was calculated. Android 12 implements a solution to calculate the size of DMA-BUFs mapped into the GPU address space, which means that it’s accounted for only once in the Lost RAM calculation.

The details of the solution are as follows:

  • The Memtrack HAL API getMemory() when called with PID 0 must report the global total GPU-private memory, for MemtrackType::GL and MemtrackRecord::FLAG_SMAPS_UNACCOUNTED.
  • getMemory() when called with PID 0 for a MemtrackType other than GL must not fail. It must instead return 0.
  • The GPU memory tracepoint/eBPF solution added in Android 12 accounts for total GPU memory. Subtracting the total GPU private memory from the total GPU memory provides the size of DMA-BUFs mapped into the GPU address space. The value can then be used to improve the accuracy of Lost RAM calculations by correctly accounting for the GPU memory usage.
  • The private GPU memory is included in totalPss in most Memtrack HAL implementations and therefore must be deduplicated before removing it from lostRAM.

The implemented solution is detailed in the next section.

Remove Memtrack variability from lost RAM

Since Memtrack HAL implementations can vary across partners, the GPU memory included in totalPSS from the HAL isn't always consistent. To remove the variability from lostRAM, the memory accounted for in MemtrackType::GRAPHICS and MemtrackType::GL is removed from totalPss during the lostRAM calculation.

MemtrackType::GRAPHICS memory is removed from totalPss and replaced with the totalExportedDmabuf memory in the lostRAM calculation in ActivityManagerService.java as shown below:

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 memory is removed from totalPss and replaced with the private GPU memory (gpuPrivateUsage) in lostRAM calculation in ActivityManagerService.java as shown below:

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;

Updated lost RAM calculation

Both the total private GPU memory and the total exported DMA buffer memory are contained in kernelUsed + totalPss which is removed from lostRAM. This eliminates both double-counting and Memtrack variability from lost RAM calculation.

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

Validation

VTS tests enforce the rule that devices launching in Android 12 with a Linux kernel version 5.4 or higher support the getGpuDeviceInfo() API.

A new Memtrack HAL API getGpuDeviceInfo() must return information about the GPU device in use.

This provides better memory accounting and visibility into DMA buffer and GPU memory usage. Implement the memtrack AIDL HAL for better lost RAM and memory accounting. This feature isn't dependent on Google services.

Implementation

This feature depends on the AIDL Memtrack HAL, and directions for implementing it in Android 12 are included in the code as comments.

All HIDL HALs are planned to be converted to AIDL in future releases.

The following APIs have been added to 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();

To ensure your version works as intended, integrate the tracepoints in your GPU drivers, and implement the AIDL memtrack HAL getMemory() API to correctly return the global total GPU-private memory when called with PID 0 for MemtrackType::GL and MemtrackRecord::FLAG_SMAPS_UNACCOUNTED.