Implementazione del conteggio della memoria DMABUF e GPU in Android 12

Questa pagina descrive i vari miglioramenti della contabilizzazione della memoria introdotti in Android 12.

Statistiche DMA-BUF in sysfs

In Android 11 e Android 12, debugfs non può essere montato nelle build utente. Pertanto, le statistiche DMA-BUF sono state aggiunte a sysfs nella directory /sys/kernel/dmabuf/buffers in Android 12.

Percorso Descrizione
/sys/kernel/dmabuf/buffers La directory /sys/kernel/dmabuf/buffers contiene uno snapshot dello stato interno di ogni DMA-BUF. /sys/kernel/dmabuf/buffers/<inode_number> contiene le statistiche per il DMA-BUF con il numero inode univoco <inode_number>.
/sys/kernel/dmabuf/buffers/<inode_number>/exporter_name Questo file di sola lettura contiene il nome dell'esportatore DMA-BUF.
/sys/kernel/dmabuf/buffers/<inode_number>/size Questo file di sola lettura specifica le dimensioni del DMA-BUF in byte.

L'API libdmabufinfo analizza le statistiche sysfs DMA-BUF per esporre le statistiche per esportatore e per buffer.

Tieni presente che i driver del kernel che esportano DMA-BUF devono impostare correttamente il campo exp_name di struct dma_buf_export_info sul nome dell'esportatore prima di richiamare l'API dma_buf_export() per creare un DMA-BUF. Questo è necessario per libdmabufinfo e per lo strumento dmabuf_dump per derivare statistiche per esportatore che vengono poi esposte nel report bug.

Lo strumento dmabuf_dump è stato modificato per restituire queste informazioni con un nuovo argomento, -b.

Statistiche per il framework degli heap DMA-BUF

ION in GKI 2.0 verrà ritirato a favore del framework DMA-BUF heaps, che fa parte del kernel Linux upstream.

In Android 11 vengono monitorate le seguenti statistiche globali ION:

  • Dimensione totale dei DMA-BUF esportati da ogni heap ION
  • Dimensione totale della memoria preallocata inutilizzata archiviata da ogni heap ION

In Android 11 non è disponibile alcuna interfaccia per esporre le statistiche dell'heap per ION.

La seguente tabella confronta le interfacce delle statistiche ION con le corrispondenti per i dispositivi che utilizzano il framework heap DMA-BUF in Android 12.

Android 11 o dispositivi lanciati con il supporto di ION in Android 12 Dispositivi lanciati con heap DMA-BUF in Android 12
Statistiche ION per heap Nessuno Analizzato da statistiche sysfs DMA-BUF
Dimensione totale dei buffer DMA esportati /sys/kernel/ion/total_heap_size_kb
(non include le dimensioni dei buffer DMA esportati da esportatori non ION)
Analizzato dalle statistiche sysfs DMA-BUF
(include le dimensioni di tutti i DMA-BUF esportati).
Memoria totale raggruppata per heap /sys/kernel/ion/total_pool_size_kb /sys/kernel/dma_heap/total_pool_size_kb

Migliorare l'accuratezza del calcolo della RAM persa

In precedenza, il calcolo della RAM persa veniva eseguito nel seguente modo:

final long lostRAM = memInfo.getTotalSizeKb() - (totalPss - totalSwapPss)

- memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb()

- kernelUsed - memInfo.getZramTotalSizeKb();

Il componente totalPss includeva l'utilizzo della memoria GPU (restituito dall'interfaccia getMemory() di Memtrack HAL). Il componente kernelUsed includeva l'utilizzo totale della memoria DMA-BUF. Tuttavia, per i dispositivi Android, la memoria GPU proveniva da:

  • Allocazioni dirette effettuate dal driver GPU utilizzando l'allocatore di pagine fisico
  • DMA-BUFs mappati nello spazio di indirizzi della GPU

Pertanto, i DMA-BUF mappati nella memoria GPU sono stati sottratti due volte durante il calcolo della RAM persa. Android 12 implementa una soluzione per calcolare le dimensioni dei DMA-BUF mappati nello spazio di indirizzi della GPU, il che significa che vengono conteggiati una sola volta nel calcolo della RAM persa.

I dettagli della soluzione sono i seguenti:

  • L'API Memtrack HAL getMemory() quando viene chiamata con PID 0 deve segnalare la memoria privata totale della GPU globale per MemtrackType::GL e MemtrackRecord::FLAG_SMAPS_UNACCOUNTED.
  • getMemory() chiamato con PID 0 per un MemtrackType diverso da GL non deve non riuscire. Deve invece restituire 0.
  • La soluzione GPU memory tracepoint/eBPF aggiunta in Android 12 tiene conto della memoria GPU totale. Sottraendo la memoria privata totale della GPU dalla memoria GPU totale si ottiene la dimensione dei DMA-BUF mappati nello spazio degli indirizzi della GPU. Il valore può quindi essere utilizzato per migliorare l'accuratezza dei calcoli della RAM persa tenendo conto correttamente dell'utilizzo della memoria GPU.
  • La memoria GPU privata è inclusa in totalPss nella maggior parte delle implementazioni HAL Memtrack e pertanto deve essere deduplicata prima di essere rimossa da lostRAM.

La soluzione implementata è descritta in dettaglio nella sezione successiva.

Rimuovere la variabilità di Memtrack dalla RAM persa

Poiché le implementazioni di Memtrack HAL possono variare a seconda dei partner, la memoria GPU inclusa in totalPSS da HAL non è sempre coerente. Per rimuovere la variabilità da lostRAM, la memoria contabilizzata in MemtrackType::GRAPHICS e MemtrackType::GL viene rimossa da totalPss durante il calcolo di lostRAM.

La memoria MemtrackType::GRAPHICS viene rimossa da totalPss e sostituita con la memoria totalExportedDmabuf nel calcolo lostRAM in ActivityManagerService.java come mostrato di seguito:

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;

La memoria MemtrackType::GL viene rimossa da totalPss e sostituita con la memoria GPU privata (gpuPrivateUsage) nel calcolo di lostRAM in ActivityManagerService.java come mostrato di seguito:

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;

Calcolo della RAM persa aggiornato

Sia la memoria GPU privata totale che la memoria buffer DMA totale esportata sono contenute in kernelUsed + totalPss, che viene rimosso da lostRAM. In questo modo vengono eliminati sia il doppio conteggio sia la variabilità di Memtrack dal calcolo della RAM persa.

final long lostRAM = memInfo.getTotalSizeKb() - (totalPss - totalSwapPss)
- memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb()
- kernelUsed - memInfo.getZramTotalSizeKb();

Convalida

I test VTS applicano la regola secondo cui i dispositivi lanciati in Android 12 con una versione del kernel Linux 5.4 o successive supportano l'API getGpuDeviceInfo().

Una nuova API HAL Memtrack getGpuDeviceInfo() deve restituire informazioni sul dispositivo GPU in uso.

In questo modo, si ottiene una migliore contabilità della memoria e visibilità sull'utilizzo del buffer DMA e della memoria GPU. Implementa l'HAL AIDL memtrack per una migliore contabilità della RAM e della memoria perse. Questa funzionalità non dipende dai servizi Google.

Implementazione

Questa funzionalità dipende dall'HAL AIDL Memtrack e le istruzioni per implementarla in Android 12 sono incluse nel codice come commenti.

È previsto che tutte le HAL HIDL vengano convertite in AIDL nelle release future.

Le seguenti API sono state aggiunte a 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();

Per assicurarti che la tua versione funzioni come previsto, integra i punti di tracciamento nei driver della GPU e implementa l'API AIDL memtrack HAL getMemory() per restituire correttamente la memoria privata totale globale della GPU quando viene chiamata con PID 0 per MemtrackType::GL e MemtrackRecord::FLAG_SMAPS_UNACCOUNTED.