Implemente la contabilidad de memoria DMABUF y GPU en Android 12

Esta página describe las diversas mejoras en la contabilidad de memoria introducidas en Android 12.

Estadísticas DMA-BUF en sysfs

En Android 11 y Android 12, debugfs no se pueden montar en compilaciones de usuario. Por lo tanto, las estadísticas DMA-BUF se agregaron a sysfs en el directorio /sys/kernel/dmabuf/buffers en Android 12.

Camino Descripción
/sys/kernel/dmabuf/buffers El directorio /sys/kernel/dmabuf/buffers contiene una instantánea del estado interno de cada DMA-BUF. /sys/kernel/dmabuf/buffers/<inode_number> contiene las estadísticas para DMA-BUF con el número de inodo único <inode_number> .
/sys/kernel/dmabuf/buffers/<inode_number>/exporter_name Este archivo de solo lectura contiene el nombre del exportador DMA-BUF.
/sys/kernel/dmabuf/buffers/<inode_number>/size Este archivo de solo lectura especifica el tamaño del DMA-BUF en bytes.

La API libdmabufinfo analiza las estadísticas sysfs DMA-BUF para exponer estadísticas por exportador y por búfer.

Tenga en cuenta que los controladores del kernel que exportan DMA-BUF deben configurar el campo exp_name de struct dma_buf_export_info correctamente en el nombre del exportador antes de invocar la API dma_buf_export() para crear un DMA-BUF. Esto es necesario para que libdmabufinfo y la herramienta dmabuf_dump obtengan estadísticas por exportador que luego se exponen en el informe de errores.

La herramienta dmabuf_dump se ha modificado para generar esta información con un nuevo argumento, -b .

Estadísticas para el marco de montones DMA-BUF

ION en GKI 2.0 está en desuso en favor del marco de almacenamiento dinámico DMA-BUF , que forma parte del kernel de Linux ascendente.

En Android 11 se realiza un seguimiento de las siguientes estadísticas globales de ION:

  • Tamaño total de DMA-BUF exportados por cada montón de ION
  • Tamaño total de la memoria preasignada no utilizada almacenada por cada montón de ION

No hay ninguna interfaz disponible para exponer estadísticas de montón por ION en Android 11.

La siguiente tabla compara las interfaces de estadísticas de ION con sus contrapartes para dispositivos que usan el marco de almacenamiento dinámico DMA-BUF en Android 12.

Android 11 o dispositivos que se inician con soporte ION en Android 12 Dispositivos que se lanzan con montones DMA-BUF en Android 12
Estadísticas de ION por montón Ninguno Analizado a partir de estadísticas de sysfs DMA-BUF
Tamaño total de DMA-BUF exportados /sys/kernel/ion/total_heap_size_kb
(No incluye el tamaño de los DMA-BUF exportados por exportadores que no pertenecen a ION)
Analizado a partir de estadísticas de sysfs DMA-BUF
(incluye el tamaño de todos los DMA-BUF exportados).
Memoria total agrupada por montones /sys/kernel/ion/total_pool_size_kb /sys/kernel/dma_heap/total_pool_size_kb

Mejorar la precisión del cálculo de la RAM perdida

Anteriormente el cálculo de la RAM perdida se hacía de la siguiente manera:

lostRAM hace mucho tiempo = memInfo.getTotalSizeKb( ) - ( totalPss - totalSwapPss )

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

- kernelUsed - memInfo.getZramTotalSizeKb() ;

El componente totalPss incluía el uso de memoria de la GPU (devuelto por la interfaz getMemory() de Memtrack HAL). El componente kernelUsed incluía el uso total de memoria DMA-BUF. Sin embargo, para los dispositivos Android, la memoria de la GPU proviene de lo siguiente:

  • Asignaciones directas realizadas por el controlador de GPU mediante el asignador de páginas físicas
  • DMA-BUF asignados al espacio de direcciones de GPU

Por lo tanto, los DMA-BUF que estaban asignados en memoria al espacio de direcciones de la GPU se restaron dos veces cuando se calculó la RAM perdida. Android 12 implementa una solución para calcular el tamaño de los DMA-BUF asignados al espacio de direcciones de la GPU, lo que significa que se contabiliza solo una vez en el cálculo de la RAM perdida.

Los detalles de la solución son los siguientes:

  • La API getMemory() de Memtrack HAL cuando se llama con PID 0 debe informar la memoria privada de GPU total global, para MemtrackType::GL y MemtrackRecord::FLAG_SMAPS_UNACCOUNTED.
  • getMemory() cuando se llama con PID 0 para un MemtrackType distinto de GL no debe fallar. En su lugar, debe devolver 0.
  • La solución de punto de seguimiento de memoria GPU/eBPF agregada en Android 12 representa la memoria total de la GPU. Restar la memoria privada total de la GPU de la memoria total de la GPU proporciona el tamaño de los DMA-BUF asignados al espacio de direcciones de la GPU. Luego, el valor se puede utilizar para mejorar la precisión de los cálculos de RAM perdida al tener en cuenta correctamente el uso de memoria de la GPU.
  • La memoria privada de la GPU está incluida en totalPss en la mayoría de las implementaciones de Memtrack HAL y, por lo tanto, se debe deduplicar antes de eliminarla de lostRAM .

La solución implementada se detalla en la siguiente sección.

Eliminar la variabilidad de Memtrack de la RAM perdida

Dado que las implementaciones de Memtrack HAL pueden variar entre socios, la memoria de GPU incluida en totalPSS de HAL no siempre es consistente. Para eliminar la variabilidad de lostRAM , la memoria contabilizada en MemtrackType::GRAPHICS y MemtrackType::GL se elimina de totalPss durante el cálculo lostRAM .

La memoria MemtrackType::GRAPHICS se elimina de totalPss y se reemplaza con la memoria totalExportedDmabuf en el cálculo lostRAM en ActivityManagerService.java como se muestra a continuación:

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 se elimina de totalPss y se reemplaza con la memoria privada de GPU ( gpuPrivateUsage ) en el cálculo lostRAM en ActivityManagerService.java como se muestra a continuación:

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;

Cálculo de RAM perdida actualizado

Tanto la memoria privada total de la GPU como la memoria buffer DMA total exportada están contenidas en kernelUsed + totalPss , que se elimina de lostRAM . Esto elimina tanto el doble conteo como la variabilidad de Memtrack por el cálculo de RAM perdida.

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

Validación

Las pruebas de VTS aplican la regla de que los dispositivos que se inician en Android 12 con una versión del kernel de Linux 5.4 o superior admiten la API getGpuDeviceInfo() .

Una nueva API HAL de Memtrack getGpuDeviceInfo() debe devolver información sobre el dispositivo GPU en uso.

Esto proporciona una mejor contabilidad de la memoria y visibilidad del uso de la memoria del búfer DMA y de la GPU. Implemente memtrack AIDL HAL para mejorar la pérdida de RAM y la contabilidad de la memoria. Esta función no depende de los servicios de Google.

Implementación

Esta función depende de AIDL Memtrack HAL y las instrucciones para implementarla en Android 12 se incluyen en el código como comentarios.

Está previsto que todos los HIDL HAL se conviertan a AIDL en futuras versiones.

Las siguientes API se han agregado 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();

Para garantizar que su versión funcione según lo previsto, integre los puntos de seguimiento en los controladores de su GPU e implemente la API AIDL memtrack HAL getMemory() para devolver correctamente la memoria privada de GPU total global cuando se llama con PID 0 para MemtrackType::GL y MemtrackRecord:: FLAG_SMAPS_UNACCOUNTED.