在 Android 12 中实现 DMABUF 和 GPU 内存记帐

本页面介绍了 Android 12 中引入的各种内存统计改进。

sysfs 中的 DMA-BUF 统计信息

在 Android 11 和 Android 12 中,无法在用户构建中挂载debugfs 。所以在 Android 12 的/sys/kernel/dmabuf/buffers目录下的sysfs中添加了 DMA-BUF 统计信息。

小路描述
/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 被弃用,取而代之的是DMA-BUF 堆框架,它是上游 linux 内核的一部分。

在 Android 11 中跟踪以下全局 ION 统计数据:

  • 每个 ION 堆导出的 DMA-BUF 的总大小
  • 每个 ION 堆存储的未使用的预分配内存的总大小

Android 11 中没有可用于公开每个 ION 堆统计信息的接口。

下表比较了在 Android 12 中使用 DMA-BUF 堆框架的设备的 ION 统计接口与对应的接口。

Android 11 或在 Android 12 中启动并支持 ION 的设备在 Android 12 中使用 DMA-BUF 堆启动的设备
每堆 ION 统计信息没有DMA-BUF sysfs stats解析
导出的 DMA-BUF 的总大小/sys/kernel/ion/total_heap_size_kb
(不包括非 ION 导出器导出的 DMA-BUF 的大小)
从 DMA-BUF sysfs stats 解析
(包括所有导出的 DMA-BUF 的大小)。
堆池化的总内存/sys/kernel/ion/total_pool_size_kb /sys/kernel/dma_heap/total_pool_size_kb

提高丢失的 RAM 计算精度

以前丢失的 Ram 计算如下:

final 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

因此,在计算丢失的 RAM 时,内存映射到 GPU 地址空间的 DMA-BUF 被减去了两次。 Android 12 实施了一种解决方案来计算映射到 GPU 地址空间的 DMA-BUF 的大小,这意味着它在 Lost RAM 计算中只占了一次

解决方案详情如下:

  • 当使用 PID 0 调用 Memtrack HAL API getMemory()时,必须报告 MemtrackType::GL 和 MemtrackRecord::FLAG_SMAPS_UNACCOUNTED 的全局总 GPU 专用内存。
  • 使用PID 0为除GL以外的MemtrackType调用 getMemory() 时,不得失败。它必须改为返回 0。
  • Android 12 中添加的GPU 内存跟踪点/eBPF解决方案占 GPU 内存总量。从总 GPU 内存中减去总 GPU 私有内存可提供映射到 GPU 地址空间的 DMA-BUF 的大小。然后,该值可用于通过正确考虑 GPU 内存使用情况来提高 Lost RAM 计算的准确性。
  • 在大多数 Memtrack HAL 实现中,私有 GPU 内存包含在totalPss中,因此必须在将其从lostRAM中删除之前对其进行重复数据删除。

下一节将详细介绍实施的解决方案。

从丢失的 RAM 中删除 Memtrack 可变性

由于 Memtrack HAL 实施可能因合作伙伴而异,因此 HAL 的totalPSS中包含的 GPU 内存并不总是一致的。为了消除lostRAM的可变性, MemtrackType::GRAPHICSMemtrackType::GL中占的内存在lostRAM计算期间从totalPss中移除。

MemtrackType::GRAPHICS内存从totalPss中移除,替换为ActivityManagerService.java中的lostRAM计算中的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.java中的lostRAM计算中替换为私有 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 缓冲内存都包含在kernelUsed + totalPss中,从lostRAM中删除。这消除了丢失 RAM 计算的重复计数和 Memtrack 可变性。

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

验证

VTS 测试强制执行在 Android 12 中启动且具有 Linux 内核版本 5.4 或更高版本的设备支持getGpuDeviceInfo() API 的规则。

新的 Memtrack HAL API getGpuDeviceInfo()必须返回有关正在使用的 GPU 设备的信息。

这提供了更好的内存统计和对 DMA 缓冲区和 GPU 内存使用情况的可见性。实施 memtrack AIDL HAL 以更好地丢失 RAM 和内存记帐。此功能不依赖于 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 驱动程序中集成跟踪点,并实现 AIDL memtrack HAL getMemory() API 以在使用 PID 0 调用 MemtrackType::GL 和 MemtrackRecord:: 时正确返回全局总 GPU 专用内存: FLAG_SMAPS_UNACCOUNTED。