在 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。