Implementação de DMABUF e contabilidade de memória de GPU no Android 12

Esta página descreve as várias melhorias de contabilidade de memória introduzidas no Android 12.

Estatísticas de DMA-BUF no sysfs

No Android 11 e no Android 12, o debugfs não pode ser montado em builds do usuário. As estatísticas DMA-BUF foram adicionadas a sysfs no diretório /sys/kernel/dmabuf/buffers no Android 12.

Caminho Descrição
/sys/kernel/dmabuf/buffers O diretório /sys/kernel/dmabuf/buffers contém um snapshot do estado interno de cada DMA-BUF. /sys/kernel/dmabuf/buffers/<inode_number> contém as estatísticas da DMA-BUF com o número de inodo exclusivo <inode_number>.
/sys/kernel/dmabuf/buffers/<inode_number>/exporter_name Esse arquivo somente leitura contém o nome do exportador DMA-BUF.
/sys/kernel/dmabuf/buffers/<inode_number>/size Esse arquivo somente leitura especifica o tamanho do DMA-BUF em bytes.

A API libdmabufinfo analisou as estatísticas sysfs do DMA-BUF para expor estatísticas por exportador e por buffer.

Os drivers do kernel que exportam DMA-BUFs precisam definir o campo exp_name de struct dma_buf_export_info corretamente para o nome do exportador antes de invocar a API dma_buf_export() para criar um DMA-BUF. Isso é necessário para que libdmabufinfo e a ferramenta dmabuf_dump extraiam estatísticas por exportador, que são expostas em bugreport.

A ferramenta dmabuf_dump foi modificada para gerar essas informações com um novo argumento, -b.

Estatísticas do framework de heaps DMA-BUF

O ION no GKI 2.0 está sendo descontinuado em favor do framework de heaps DMA-BUF, que faz parte do kernel do Linux upstream.

As seguintes estatísticas globais de ION são rastreadas no Android 11:

  • Tamanho total de DMA-BUFs exportados por cada heap ION
  • Tamanho total da memória pré-alocada não utilizada armazenada por cada heap ION

Não há uma interface disponível para mostrar as estatísticas de heap por ION no Android 11.

A tabela a seguir compara as interfaces de estatísticas do ION com as contrapartes para dispositivos que usam o framework de heap DMA-BUF no Android 12.

Android 11 ou dispositivos lançados com suporte ao ION no Android 12 Dispositivos lançados com heaps DMA-BUF no Android 12
Estatísticas ION por heap Nenhum Analisado a partir das estatísticas do sysfs de DMA-BUF
Tamanho total de DMA-BUFs exportados /sys/kernel/ion/total_heap_size_kb
(não inclui o tamanho de DMA-BUFs exportados por exportadores não ION)
Analisado a partir das estatísticas do sysfs DMA-BUF
(inclui o tamanho de todos os DMA-BUFs exportados).
Memória total agrupada por heaps /sys/kernel/ion/total_pool_size_kb /sys/kernel/dma_heap/total_pool_size_kb

Melhorar a precisão do cálculo de RAM perdida

Anteriormente, o cálculo da RAM perdida era feito da seguinte maneira:

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

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

- kernelUsed - memInfo.getZramTotalSizeKb();

O componente totalPss incluiu o uso de memória da GPU (retornado pela interface getMemory() do HAL do Memtrack). O componente kernelUsed incluiu o uso total de memória DMA-BUF. No entanto, para dispositivos Android, a memória da GPU veio do seguinte:

  • Alocações diretas feitas pelo driver da GPU usando o alocador de página física
  • DMA-BUFs mapeados no espaço de endereço da GPU

Portanto, os DMA-BUFs que foram mapeados na memória para o espaço de endereço da GPU foram subtraídos duas vezes quando a RAM perdida foi calculada. O Android 12 implementa uma solução para calcular o tamanho de DMA-BUFs mapeados no espaço de endereço da GPU, o que significa que ele é contabilizado apenas uma vez no cálculo de RAM perdida.

Os detalhes da solução são os seguintes:

  • A API HAL do Memtrack getMemory() quando chamada com PID 0 precisa informar a memória total global privada da GPU para MemtrackType::GL e MemtrackRecord::FLAG_SMAPS_UNACCOUNTED.
  • getMemory() quando chamado com PID 0 para um MemtrackType diferente de GL não pode falhar. Ele precisa retornar 0.
  • A solução de tracepoint/eBPF de memória de GPU adicionada no Android 12 representa a memória total da GPU. Subtrair a memória privada total da GPU da memória total da GPU fornece o tamanho dos DMA-BUFs mapeados no espaço de endereço da GPU. O valor pode ser usado para melhorar a precisão dos cálculos de RAM perdida, considerando corretamente o uso da memória da GPU.
  • A memória privada da GPU é incluída em totalPss na maioria das implementações do HAL do Memtrack e, portanto, precisa ser desduplicada antes de ser removida de lostRAM.

A solução implementada é detalhada na próxima seção.

Remover a variabilidade do Memtrack da RAM perdida

Como as implementações do HAL do Memtrack podem variar entre os parceiros, a memória da GPU incluída em totalPSS do HAL nem sempre é consistente. Para remover a variabilidade de lostRAM, a memória contabilizada em MemtrackType::GRAPHICS e MemtrackType::GL é removida de totalPss durante o cálculo de lostRAM.

A memória MemtrackType::GRAPHICS é removida de totalPss e substituída pela memória totalExportedDmabuf no cálculo lostRAM em ActivityManagerService.java , conforme mostrado abaixo:

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;

A memória MemtrackType::GL é removida de totalPss e substituída pela memória privada da GPU (gpuPrivateUsage) no cálculo de lostRAM em ActivityManagerService.java , conforme mostrado abaixo:

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;

Atualização do cálculo de RAM perdida

A memória total de GPU privada e a memória total do buffer DMA exportado estão contidas em kernelUsed + totalPss, que é removido de lostRAM. Isso elimina a contagem dupla e a variabilidade do Memtrack do cálculo de RAM perdida.

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

Validação

Os testes do VTS aplicam a regra de que os dispositivos lançados no Android 12 com um kernel Linux versão 5.4 ou mais recente oferecem suporte à API getGpuDeviceInfo().

Uma nova API HAL do Memtrack getGpuDeviceInfo() precisa retornar informações sobre o dispositivo de GPU em uso.

Isso oferece uma melhor contabilidade de memória e visibilidade no buffer de DMA e no uso da memória da GPU. Implementamos o HAL de AIDL do memtrack para melhorar a contabilidade de RAM e memória perdida. Esse recurso não depende dos Serviços do Google.

Implementação

Esse recurso depende do HAL do AIDL Memtrack, e as instruções para implementá-lo no Android 12 estão incluídas no código como comentários.

Todas as HALs de HIDL serão convertidas em AIDL em versões futuras.

As APIs a seguir foram adicionadas 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 garantir que a versão funcione conforme o esperado, integre os pontos de rastreamento nos drivers da GPU e implemente a API getMemory() do HAL de memtrack do AIDL para retornar corretamente a memória total global privada da GPU quando chamada com PID 0 para MemtrackType::GL e MemtrackRecord::FLAG_SMAPS_UNACCOUNTED.