Android12でのDMABUFとGPUメモリアカウンティングの実装

このページでは、Android12で導入されたさまざまなメモリアカウンティングの改善について説明します。

sysfsのDMA-BUF統計

Android11およびAndroid12では、 debugfsをユーザービルドにマウントすることはできません。そのため、Android12の/sys/kernel/dmabuf/buffersディレクトリのsysfsにDMA-BUF統計が追加されました。

説明
/sys/kernel/dmabuf/buffers /sys/kernel/dmabuf/buffersディレクトリには、すべてのDMA-BUFの内部状態のスナップショットが含まれています。 /sys/kernel/dmabuf/buffers/<inode_number>には、一意のiノード番号<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フィールドをエクスポーター名に正しく設定する必要があることに注意してください。これは、 libdmabufinfoおよびdmabuf_dumpツールがエクスポーターごとの統計を取得するために必要であり、バグレポートで公開されます。

dmabuf_dumpツールは、この情報を新しい引数-bで出力するように変更されました。

DMA-BUFヒープフレームワークの統計

GKI 2.0のIONは廃止され、アップストリームLinuxカーネルの一部であるDMA-BUFヒープフレームワークが採用されています。

次のグローバルION統計は、Android11で追跡されます。

  • すべてのIONヒープによってエクスポートされたDMA-BUFの合計サイズ
  • すべてのIONヒープによって格納された未使用の事前割り当てメモリの合計サイズ

Android11でIONごとのヒープ統計を公開するために使用できるインターフェースはありません。

次の表は、Android12でDMA-BUFヒープフレームワークを使用するデバイスのION統計インターフェイスと対応するインターフェイスを比較しています。

Android11またはAndroid12でIONをサポートして起動するデバイスAndroid12でDMA-BUFヒープを使用して起動するデバイス
ヒープごとのION統計なしDMA-BUF sysfsstatsから解析
エクスポートされたDMA-BUFの合計サイズ/sys/kernel/ion/total_heap_size_kb
(非IONエクスポーターによってエクスポートされたDMA-BUFのサイズは含まれません)
DMA-BUF sysfsstatsから解析
(エクスポートされたすべての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

したがって、GPUアドレス空間にメモリマップされたDMA-BUFは、失われたRAMが計算されたときに2回減算されました。 Android 12は、GPUアドレス空間にマップされたDMA-BUFのサイズを計算するソリューションを実装しています。つまり、LostRAMの計算で1回だけ考慮されます。

ソリューションの詳細は次のとおりです。

  • Memtrack HAL API getMemory()をPID 0で呼び出すと、MemtrackType :: GLおよびMemtrackRecord :: FLAG_SMAPS_UNACCOUNTEDについて、GPUプライベートメモリのグローバル合計を報告する必要があります。
  • GL以外のPIDに対して0MemtrackTypeれた場合、getMemory()は失敗してはなりません。代わりに0を返す必要があります。
  • Android12で追加されたGPUメモリトレースポイント/ eBPFソリューションは、GPUメモリの合計を占めます。合計GPUメモリから合計GPUプライベートメモリを差し引くと、GPUアドレス空間にマップされたDMA-BUFのサイズが得られます。この値を使用して、GPUメモリ使用量を正しく計算することにより、失われたRAMの計算の精度を向上させることができます。
  • プライベートGPUメモリはほとんどのMemtrackHAL実装のtotalPssに含まれているため、 lostRAMから削除する前に重複排除する必要があります。

実装されたソリューションについては、次のセクションで詳しく説明します。

失われたRAMからMemtrackの変動性を取り除く

Memtrack HALの実装はパートナーによって異なる可能性があるため、HALからのtotalPSSに含まれるGPUメモリは常に一貫しているとは限りません。 lostRAMから変動性を取り除くために、 MemtrackType::GRAPHICSおよびMemtrackType::GLで説明されているメモリは、 lostRAM 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.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の計算

プライベート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以降を搭載したAndroid12で起動するデバイスがgetGpuDeviceInfo() APIをサポートするというルールを適用します。

新しいMemtrackHAL API getGpuDeviceInfo()は、使用中のGPUデバイスに関する情報を返す必要があります。

これにより、メモリアカウンティングが向上し、DMAバッファとGPUメモリの使用状況が可視化されます。 memtrack AIDL HALを実装して、失われたRAMとメモリのアカウンティングを改善します。この機能はGoogleサービスに依存していません。

実装

この機能はAIDLMemtrack HALに依存しており、Android12に実装するための指示がコメントとしてコードに含まれています。

すべてのHIDLHALは、将来のリリースで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 ::のPID0で呼び出されたときにグローバル合計GPUプライベートメモリを正しく返すようにします。 FLAG_SMAPS_UNACCOUNTED。