Implementa el registro de memoria de DMABUF y GPU en Android 12

En esta página, se describen las diversas mejoras en la contabilización de la memoria que se introdujeron en Android 12.

Estadísticas de DMA-BUF en sysfs

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

Ruta 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 del DMA-BUF con el número de i-nodo único <inode_number>.
/sys/kernel/dmabuf/buffers/<inode_number>/exporter_name Este archivo de solo lectura contiene el nombre del exportador de 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 de libdmabufinfo analiza las estadísticas de sysfs de DMA-BUF para exponer estadísticas por exportador y por búfer.

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

Se modificó la herramienta dmabuf_dump para que genere esta información con un argumento nuevo, -b.

Estadísticas del framework de montones de DMA-BUF

ION en GKI 2.0 se está dando de baja en favor del framework de montones de DMA-BUF, que forma parte del kernel de Linux upstream.

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

  • Tamaño total de los DMA-BUF exportados por cada montón de ION
  • Tamaño total de la memoria preasignada sin usar que almacena cada montón de ION

No hay una interfaz disponible para exponer las 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 los dispositivos que usan el framework de montón DMA-BUF en Android 12.

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

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

Anteriormente, el cálculo de la RAM perdida se realizaba de la siguiente manera:

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

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

- kernelUsed - memInfo.getZramTotalSizeKb();

El componente totalPss incluía el uso de memoria de la GPU (que devolvía la interfaz getMemory() del HAL de Memtrack). El componente kernelUsed incluía el uso total de la memoria DMA-BUF. Sin embargo, en el caso de los dispositivos Android, la memoria de la GPU provino de lo siguiente:

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

Por lo tanto, los DMA-BUF que se asignaron a la memoria en el 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 tienen en cuenta solo una vez en el cálculo de RAM perdida.

Los detalles de la solución son los siguientes:

  • La API de HAL de Memtrack getMemory(), cuando se llama con el PID 0, debe informar la memoria total global privada de la GPU 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 de puntos de seguimiento/eBPF de memoria de GPU agregada en Android 12 tiene en cuenta la memoria total de la GPU. Si se resta la memoria privada total de la GPU de la memoria total de la GPU, se obtiene 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, ya que se tiene en cuenta correctamente el uso de la memoria de la GPU.
  • La memoria privada de la GPU se incluye en totalPss en la mayoría de las implementaciones del HAL de Memtrack y, por lo tanto, se debe quitar la duplicación antes de quitarla de lostRAM.

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

Cómo quitar la variabilidad de Memtrack de la RAM perdida

Dado que las implementaciones de HAL de Memtrack pueden variar entre los socios, la memoria de GPU incluida en totalPSS de la HAL no siempre es coherente. Para quitar la variabilidad de lostRAM, la memoria que se tiene en cuenta en MemtrackType::GRAPHICS y MemtrackType::GL se quita de totalPss durante el cálculo de lostRAM.

La memoria MemtrackType::GRAPHICS se quita de totalPss y se reemplaza por la memoria totalExportedDmabuf en el cálculo de lostRAM en ActivityManagerService.java, como se muestra en el siguiente código:

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 quita de totalPss y se reemplaza por la memoria de GPU privada (gpuPrivateUsage) en el cálculo de lostRAM en ActivityManagerService.java, como se muestra en el siguiente código:

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;

Se actualizó el cálculo de la RAM perdida

Tanto la memoria privada total de la GPU como la memoria total del búfer de DMA exportado se incluyen en kernelUsed + totalPss, que se quita de lostRAM. Esto elimina el doble recuento y la variabilidad de Memtrack del 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 lanzan en Android 12 con una versión del kernel de Linux 5.4 o posterior admiten la API de getGpuDeviceInfo().

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

Esto proporciona una mejor contabilización de la memoria y visibilidad del uso de la memoria de GPU y del búfer de DMA. Implementa la HAL de AIDL de memtrack para mejorar el registro de la RAM perdida y la memoria. Esta función no depende de los servicios de Google.

Implementación

Esta función depende de la HAL de Memtrack de AIDL, y las instrucciones para implementarla en Android 12 se incluyen en el código como comentarios. Se planea convertir todos los HAL de HIDL a AIDL en versiones futuras.

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