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

本頁面說明 Android 12 中推出的各種記憶體計算改善功能。

sysfs 中的 DMA-BUF 統計資料

在 Android 11 和 Android 12 中,debugfs 無法在使用者版本中掛載。因此,DMA-BUF 統計資料已新增至 Android 12 中 /sys/kernel/dmabuf/buffers 目錄的 sysfs

路徑 說明
/sys/kernel/dmabuf/buffers /sys/kernel/dmabuf/buffers 目錄包含每個 DMA-BUF 內部狀態的快照。/sys/kernel/dmabuf/buffers/<inode_number> 包含 DMA-BUF 的統計資料,其中包含專屬的 inode 編號 <inode_number>
/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 的核心驅動程式必須先將 struct dma_buf_export_infoexp_name 欄位正確設為匯出程式名稱,才能叫用 dma_buf_export() API 建立 DMA-BUF。這項功能是 libdmabufinfodmabuf_dump 工具用來擷取每個匯出工具的統計資料,並在 bugreport 中公開這些資料。

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 計算準確度

先前的記憶體損失計算方式如下:

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 專屬記憶體總量。
  • 針對 GL 以外的 MemtrackType,使用 PID 0 呼叫 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 的變化,系統會在 lostRAM 計算期間,從 totalPss 中移除 MemtrackType::GRAPHICSMemtrackType::GL 所占用的記憶體。

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;

更新的記憶體損失計算方式

kernelUsed + totalPss 包含私人 GPU 記憶體總量和匯出的 DMA 緩衝區記憶體總量,而 lostRAM 已從 kernelUsed + totalPss 中移除。這麼做可避免重複計算和 Memtrack 變化,導致計算出錯誤的 RAM 用量。

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,以便在使用 MemtrackType::GL 和 MemtrackRecord::FLAG_SMAPS_UNACCOUNTED 的 PID 0 呼叫時,正確傳回全球 GPU 私有記憶體總量。