在 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,並改用上游 Linux 核心隨附的 DMA-BUF 堆積架構

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 統計資料剖析
已匯出的 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 計算方式如下:

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 大小,這表示系統在計算遺失 RAM 時,只會將其計入一次

解決方案詳細資料如下:

  • 使用 PID 0 呼叫時,Memtrack HAL API getMemory() 必須回報 GPU 私有記憶體總量,適用於 MemtrackType::GL 和 MemtrackRecord::FLAG_SMAPS_UNACCOUNTED。
  • 針對 GL 以外的 MemtrackType,使用 PID 0 呼叫 getMemory() 時,不得發生失敗。而是必須傳回 0。
  • Android 12 中新增的 GPU 記憶體追蹤點/eBPF解決方案涵蓋 GPU 記憶體總量。從 GPU 記憶體總量中減去 GPU 私人記憶體總量,即可取得對應至 GPU 位址空間的 DMA-BUF 大小。接著,您可以使用這個值來正確計算 GPU 記憶體用量,進而提升 Lost RAM 計算的準確度。
  • 私人 GPU 記憶體已包含在 totalPss 大部分的 Memtrack HAL 實作中,因此在將其從 lostRAM 移除之前,必須先刪除重複項目。

實作解決方案的詳細資訊請見下一節。

從遺失的 RAM 中移除 Memtrack 變數

由於合作夥伴的 Memtrack HAL 實作方式可能不同,因此 HAL 從 totalPSS 擷取的 GPU 記憶體不一定一致。如要從 lostRAM 移除可變性,MemtrackType::GRAPHICSMemtrackType::GL 中所佔的記憶體會在 lostRAM 計算期間從 totalPss 移除。

系統會從 totalPss 中移除 MemtrackType::GRAPHICS 記憶體,並替換為 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.javalostRAM 計算中,以私人 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 計算結果

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 中使用 PID 0 時,正確傳回全域的 GPU 私有記憶體總量。