Implementación de contabilidad de memoria de GPU y DMABUF en Android 12

Esta página describe las diversas mejoras en la contabilidad de la 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 usuarios. Por lo tanto, se agregaron estadísticas DMA-BUF a sysfs en el directorio /sys/kernel/dmabuf/buffers en Android 12.

Sendero 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 de DMA-BUF con el número de inodo exclusivo <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 de DMA-BUF sysfs para exponer estadísticas por exportador y por búfer.

Tenga en cuenta que los controladores del kernel que exportan DMA-BUF deben establecer correctamente el campo exp_name de struct dma_buf_export_info 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 bugreport.

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

Estadísticas para el marco de pilas DMA-BUF

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

Las siguientes estadísticas globales de ION se rastrean en Android 11:

  • 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 una interfaz disponible para exponer estadísticas de montón por ION en Android 11.

En la siguiente tabla, se comparan 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 compatibilidad con ION en Android 12 Dispositivos que se inician con pilas DMA-BUF en Android 12
Estadísticas de ION por pila Ninguna Analizado a partir de las estadísticas de DMA-BUF sysfs
Tamaño total de DMA-BUF exportados /sys/kernel/ion/total_heap_size_kb
(No incluye el tamaño de DMA-BUF exportados por exportadores que no son ION)
Analizado a partir de las estadísticas de DMA-BUF sysfs
(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

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

Anteriormente, el cálculo de Ram perdido se hacía de la siguiente manera:

RAM perdida larga final = lostRAM memInfo.getTotalSizeKb( ) - ( totalPss - totalSwapPss )

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

- kernelUsed - memInfo.getZramTotalSizeKb() ;

El componente totalPss incluía el uso de memoria 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 GPU provino de lo siguiente:

  • Asignaciones directas realizadas por el controlador de GPU utilizando el asignador de página físico
  • DMA-BUF asignados al espacio de direcciones de la GPU

Por lo tanto, los DMA-BUF que se mapearon en la memoria en el espacio de direcciones de la GPU se restaron dos veces cuando se calculó la pérdida de RAM. 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 solo se tiene en cuenta una vez en el cálculo de RAM perdida.

Los detalles de la solución son los siguientes:

  • La API getMemory() de HAL de Memtrack 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 que no sea GL no debe fallar. En su lugar, debe devolver 0.
  • La solución eBPF/punto de seguimiento de la memoria de la GPU 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 usar para mejorar la precisión de los cálculos de RAM perdida al contabilizar correctamente el uso de la memoria de la GPU.
  • La memoria GPU privada está incluida en totalPss en la mayoría de las implementaciones de Memtrack HAL y, por lo tanto, debe desduplicarse antes de eliminarla de lostRAM .

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

Eliminación de la variabilidad de Memtrack de la memoria RAM perdida

Dado que las implementaciones de HAL de Memtrack pueden variar entre socios, la memoria 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 de lostRAM .

La memoria MemtrackType::GRAPHICS se elimina de totalPss y se reemplaza con la memoria totalExportedDmabuf en el cálculo de 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 de GPU privada ( gpuPrivateUsage ) en el cálculo de 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;

El cálculo actualizado de RAM perdida

Tanto la memoria de GPU privada total como la memoria de búfer de DMA exportada total están contenidas en kernelUsed + totalPss que se elimina de lostRAM . Esto elimina tanto el conteo doble como la variabilidad de Memtrack del cálculo de RAM perdido.

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 un kernel de Linux versión 5.4 o superior son compatibles con la API getGpuDeviceInfo() .

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

Esto proporciona una mejor contabilidad de la memoria y visibilidad del búfer de DMA y el uso de la memoria de la GPU. Implemente el 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 todas las HAL de HIDL se conviertan a AIDL en versiones futuras.

Se agregaron las siguientes API 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 asegurarse de que su versión funcione según lo previsto, integre los puntos de seguimiento en los controladores de su GPU e implemente la API getMemory() de AIDL memtrack HAL para devolver correctamente la memoria privada de GPU total global cuando se llame con PID 0 para MemtrackType::GL y MemtrackRecord:: FLAG_SMAPS_UNACCOUNTED.