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 aMemtrackType
other thanGL
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 fromlostRAM
.
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.