在 Android 12 中實作 DMABUF 和 GPU 記憶體會計

本頁面介紹了 Android 12 中引入的各種記憶體核算改進。

sysfs 中的 DMA-BUF 統計資訊

在 Android 11 和 Android 12 中,無法在使用者版本中安裝debugfs 。因此在 Android 12 中,DMA-BUF 統計資料已新增至/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 已被棄用,取而代之的是DMA-BUF 堆框架,它是上游 Linux 核心的一部分。

Android 11 中追蹤以下全域 ION 統計資料:

  • 每個 ION 堆導出的 DMA-BUF 的總大小
  • 每個 ION 堆儲存的未使用預先分配記憶體的總大小

Android 11 中沒有可用於公開每個 ION 堆統計資料的介面。

下表將 ION 統計介面與 Android 12 中使用 DMA-BUF 堆疊框架的裝置的對應介面進行了比較。

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 計算如下:

最終長度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 的大小,這意味著它在遺失 RAM 運算中僅佔一次

解決方案詳情如下:

  • 當使用 PID 0 呼叫 Memtrack HAL API getMemory()時,必須報告 MemtrackType::GL 和 MemtrackRecord::FLAG_SMAPS_UNACCOUNTED 的全域 GPU 專用記憶體總量。
  • 當使用PID 0GL以外的MemtrackType呼叫 getMemory() 時,不得失敗。它必須返回 0。
  • Android 12 中新增的GPU 記憶體追蹤點/eBPF解決方案佔 GPU 總記憶體。從總 GPU 記憶體中減去總 GPU 私有記憶體即可得到對應到 GPU 位址空間的 DMA-BUF 的大小。然後,透過正確考慮 GPU 記憶體使用情況,該值可用於提高遺失 RAM 運算的準確性。
  • 在大多數 Memtrack HAL 實作中,私有 GPU 記憶體包含在totalPss中,因此必須先進行重複資料刪除,然後才能將其從lostRAM中刪除。

下一節將詳細介紹所實施的解決方案。

消除丟​​失 RAM 中的 Memtrack 可變性

由於 Memtrack HAL 實作可能因合作夥伴而異,因此 HAL 中的totalPSS中包含的 GPU 記憶體並不總是一致。為了消除lostRAM的可變性,在lostRAM計算期間, MemtrackType::GRAPHICSMemtrackType::GL中佔用的記憶體將從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.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 測試強制執行以下規則:在具有 Linux 核心版本 5.4 或更高版本的 Android 12 中啟動的裝置支援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。