このページでは、Android 12 で導入されたさまざまなメモリ アカウンティングの改善点について説明します。
sysfs の DMA-BUF 統計情報
Android 11 と Android 12 では、ユーザービルドに debugfs
をマウントできません。Android 12 では、/sys/kernel/dmabuf/buffers
ディレクトリの sysfs
に DMA-BUF 統計情報が追加されています。
パス | 説明 |
---|---|
/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_info
の exp_name
のフィールドに正しいエクスポータの名前を設定する必要があります。これは、libdmabufinfo
と dmabuf_dump
ツールがエクスポータごとの統計情報を取得するために必要です。この統計情報はバグレポートで公開されます。
dmabuf_dump ツールは、新しい引数 -b
でこの情報を出力するように変更されています。
DMA-BUF ヒープ フレームワークの統計情報
アップストリームの Linux カーネルの一部である DMA-BUF ヒープ フレームワークを優先して、GKI 2.0 での ION のサポートが終了します。
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 の損失の計算は次のように行われていました。
final long lostRAM
= memInfo.getTotalSizeKb(
) - (totalPss
- totalSwapPss
)
- memInfo.getFreeSizeKb()
- memInfo.getCachedSizeKb()
- kernelUsed
- memInfo.getZramTotalSizeKb()
totalPss
コンポーネントには、Memtrack HAL の getMemory() インターフェースによって返される GPU メモリの使用量が含まれていました。また、kernelUsed
コンポーネントには、DMA-BUF の合計メモリ使用量が含まれていました。しかし、Android デバイスの場合、GPU メモリは次のソースから取得されます。
- 物理ページ アロケータを使用した GPU ドライバによる直接割り当て
- GPU アドレス空間にマッピングされた DMA-BUF
そのため、RAM の損失を計算する際に、GPU アドレス空間にメモリマップされた DMA-BUF が 2 回減算されていました。Android 12 では、GPU アドレス空間にマッピングされた DMA-BUF のサイズを計算するソリューションが実装されているため、このサイズは RAM の損失の計算時に 1 回だけ考慮されます。
ソリューションの詳細は次のとおりです。
- Memtrack HAL API の
getMemory()
は、PID 0 で呼び出された場合、MemtrackType::GL と MemtrackRecord::FLAG_SMAPS_UNACCOUNTED の全体の GPU プライベート メモリの合計をレポートします。 GL
以外のMemtrackType
に対して getMemory() をPID
0
で呼び出した場合は失敗せず、代わりに 0 が返されます。- Android 12 で追加された GPU メモリ トレースポイント / eBPF ソリューションでは、GPU メモリの合計が考慮されます。合計 GPU メモリから GPU プライベート メモリの合計を差し引くと、GPU アドレス空間にマッピングされた DMA-BUF のサイズがわかります。この値を使用して、GPU メモリの使用量を適切に考慮し、RAM の損失に関する計算の精度を高めることができます。
- プライベート GPU メモリは、ほとんどの Memtrack HAL 実装で
totalPss
に含まれているため、lostRAM
から削除する前に重複除去する必要があります。
実装されたソリューションについては、次のセクションで詳しく説明します。
RAM の損失から Memtrack のばらつきを取り除く
Memtrack HAL 実装はパートナーごとに異なるため、HAL の totalPSS
に含まれる GPU メモリは一貫しているとは限りません。lostRAM
からばらつきを取り除くため、MemtrackType::GRAPHICS
と MemtrackType::GL
で考慮されたメモリは、lostRAM
の計算中に totalPss
から削除されます。
以下に示すように、ActivityManagerService.java における lostRAM
の計算では、totalPss
から MemtrackType::GRAPHICS
メモリが削除され、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;
以下に示すように、ActivityManagerService.java における lostRAM
の計算では、totalPss
から MemtrackType::GL
メモリが削除され、プライベート 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
から削除されます。こうすることで、カウントの重複と Memtrack のばらつきを RAM の損失の計算から排除できます。
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 ドライバに統合したうえで、MemtrackType::GL と MemtrackRecord::FLAG_SMAPS_UNACCOUNTED に対して PID 0 で呼び出されたときに全体の GPU プライベート メモリの合計を正しく返すように AIDL memtrack HAL getMemory()
API を実装します。