このページでは、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_info
のexp_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
に対して0
でMemtrackType
れた場合、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.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テストは、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。