Transição de ION para DMA-BUF Heaps

No Android 12, o GKI 2.0 substitui o alocador ION por heaps DMA-BUF pelos seguintes motivos:

  • Segurança: Como cada heap DMA-BUF é um dispositivo de caractere separado, o acesso a cada heap pode ser controlado separadamente com sepolicy. Isso não era possível com o ION porque a alocação de qualquer heap exigia apenas acesso ao /dev/ion .
  • Estabilidade da ABI: Ao contrário do ION, a interface IOCTL da estrutura de heaps DMA-BUF é garantida para ser ABI estável porque é mantida no kernel Linux upstream.
  • Padronização: A estrutura de heaps DMA-BUF oferece uma UAPI bem definida. O ION permitia sinalizadores personalizados e IDs de heap que impediam o desenvolvimento de uma estrutura de teste comum porque a implementação do ION de cada dispositivo poderia se comportar de maneira diferente.

A ramificação android12-5.10 do Android Common Kernel desativou CONFIG_ION em 1º de março de 2021 .

Fundo

O seguinte é uma breve comparação entre heaps ION e DMA-BUF.

Semelhanças entre a estrutura de heaps ION e DMA-BUF

  • As estruturas de heaps ION e DMA-BUF são exportadores de DMA-BUF baseados em heap.
  • Ambos permitem que cada heap defina seu próprio alocador e operações DMA-BUF.
  • O desempenho de alocação é semelhante porque ambos os esquemas precisam de um único IOCTL para alocação.

Diferenças entre a estrutura de heaps ION e DMA-BUF

pilhas de íons Pilhas de DMA-BUF
Todas as alocações de ION são feitas com /dev/ion . Cada heap DMA-BUF é um dispositivo de caractere que está presente em /dev/dma_heap/<heap_name> .
ION suporta sinalizadores privados de heap. Heaps DMA-BUF não suportam sinalizadores privados de heap. Cada tipo diferente de alocação é feito de um heap diferente. Por exemplo, as variantes de heap do sistema com e sem cache são heaps separados localizados em /dev/dma_heap/system e /dev/dma_heap/system_uncached .
Heap ID/máscara e sinalizadores precisam ser especificados para alocação. O nome do heap é usado para alocação.

As seções a seguir listam os componentes que lidam com o ION e descrevem como alterná-los para a estrutura de heaps DMA-BUF.

Transição de drivers de kernel de heaps ION para DMA-BUF

Drivers de kernel implementando heaps ION

Os heaps ION e DMA-BUF permitem que cada heap implemente seus próprios alocadores e operações DMA-BUF. Assim, você pode alternar de uma implementação de heap ION para uma implementação de heap DMA-BUF usando um conjunto diferente de APIs para registrar o heap. Esta tabela mostra as APIs de registro de heap ION e suas APIs de heap DMA-BUF equivalentes.

pilhas de íons Pilhas de DMA-BUF
void ion_device_add_heap(struct ion_heap *heap) struct dma_heap *dma_heap_add(const struct dma_heap_export_info *exp_info);
void ion_device_remove_heap(struct ion_heap *heap) void dma_heap_put(struct dma_heap *heap);

Heaps DMA-BUF não suportam sinalizadores privados de heap. Portanto, cada variante do heap deve ser registrada individualmente usando a API dma_heap_add() . Para facilitar o compartilhamento de código, é recomendável registrar todas as variantes do mesmo heap dentro do mesmo driver. Este exemplo de dma-buf: system_heap mostra a implementação das variantes em cache e não em cache do heap do sistema.

Use este dma-buf: heaps: modelo de exemplo para criar um heap DMA-BUF do zero.

Drivers de kernel alocando diretamente de heaps ION

A estrutura de heaps DMA-BUF também oferece uma interface de alocação para clientes no kernel. Em vez de especificar a máscara de heap e os sinalizadores para selecionar o tipo de alocação, a interface oferecida pelos heaps DMA-BUF recebe um nome de heap como entrada.

Veja a seguir a API de alocação de ION no kernel e suas APIs de alocação de heap DMA-BUF equivalentes. Os drivers do kernel podem usar a API dma_heap_find() para consultar a existência de um heap. A API retorna um ponteiro para uma instância de struct dma_heap , que pode ser passado como um argumento para a API dma_heap_buffer_alloc() .

pilhas de íons Pilhas de DMA-BUF
struct dma_buf *ion_alloc(size_t len, unsigned int heap_id_mask, unsigned int flags)

struct dma_heap *dma_heap_find(const char *name)

struct dma_buf *struct dma_buf *dma_heap_buffer_alloc(struct dma_heap *heap, size_t len, unsigned int fd_flags, unsigned int heap_flags)

Drivers de kernel que usam DMA-BUFs

Nenhuma alteração é necessária para drivers que importam apenas DMA-BUFs, porque um buffer alocado de um heap ION se comporta exatamente da mesma forma que um buffer alocado de um heap DMA-BUF equivalente.

Transição dos clientes de espaço do usuário de ION para heaps DMA-BUF

Para facilitar a transição para clientes de espaço de usuário do ION, uma biblioteca de abstração chamada libdmabufheap está disponível. libdmabufheap suporta alocação em heaps DMA-BUF e heaps ION. Ele primeiro verifica se existe um heap DMA-BUF com o nome especificado e, caso contrário, retorna para um heap ION equivalente, se existir.

Os clientes devem inicializar um objeto BufferAllocator durante sua inicialização em vez de abrir /dev/ion using ion_open() . Isso ocorre porque os descritores de arquivo criados abrindo /dev/ion e /dev/dma_heap/<heap_name> são gerenciados internamente pelo objeto BufferAllocator .

Para mudar de libion para libdmabufheap , modifique o comportamento dos clientes da seguinte forma:

  • Acompanhe o nome do heap a ser usado para alocação, em vez do ID/máscara do cabeçalho e do sinalizador de heap.
  • Substitua a API ion_alloc_fd() , que usa uma máscara de heap e um argumento de sinalizador, pela BufferAllocator::Alloc() , que usa um nome de heap.

Esta tabela ilustra essas mudanças mostrando como libion e libdmabufheap fazem uma alocação de heap do sistema sem cache.

Tipo de alocação libião libdmabufheap
Alocação em cache do heap do sistema ion_alloc_fd(ionfd, size, 0, ION_HEAP_SYSTEM, ION_FLAG_CACHED, &fd) allocator->Alloc("system", size)
Alocação sem cache do heap do sistema ion_alloc_fd(ionfd, size, 0, ION_HEAP_SYSTEM, 0, &fd) allocator->Alloc("system-uncached", size)

A variante de heap do sistema sem cache está aguardando aprovação upstream, mas já faz parte da ramificação android12-5.10 .

Para oferecer suporte a dispositivos de atualização, a API MapNameToIonHeap() permite mapear um nome de heap para parâmetros de heap ION (nome/máscara de heap e sinalizadores) para permitir que essas interfaces também usem alocações baseadas em nome. Aqui está um exemplo de alocação baseada em nome .

A documentação para cada API exposta pelo libdmabufheap está disponível. A biblioteca também expõe um arquivo de cabeçalho para uso por clientes C.

Implementação Gralloc de referência

A implementação do Hikey960 gralloc usa libdmabufheap , então você pode usá-lo como uma implementação de referência .

Adições ueventd necessárias

Para qualquer novo heap DMA-BUF específico do dispositivo criado, adicione uma nova entrada ao arquivo ueventd.rc do dispositivo. Este exemplo de configuração ueventd para dar suporte a heaps DMA-BUF demonstra como isso é feito para o heap do sistema DMA-BUF.

Adições de política de segurança obrigatórias

Adicione permissões de sepolicy para permitir que um cliente de espaço de usuário acesse um novo heap DMA-BUF. Este exemplo de adição de permissões obrigatórias mostra as permissões de sepolicy criadas para vários clientes acessarem o heap do sistema DMA-BUF.

Acessando heaps de fornecedores a partir do código da estrutura

Para garantir a conformidade com o Treble, o código da estrutura só pode alocar categorias pré-aprovadas de heaps de fornecedores.

Com base no feedback recebido dos parceiros, o Google identificou duas categorias de heaps de fornecedores que devem ser acessados ​​a partir do código da estrutura:

  1. Heaps baseados no heap do sistema com otimizações de desempenho específicas do dispositivo ou do SoC.
  2. Heaps para alocar da memória protegida.

Heaps baseados no heap do sistema com otimizações de desempenho específicas do dispositivo ou do SoC

Para dar suporte a esse caso de uso, a implementação de heap do sistema de heap DMA-BUF padrão pode ser substituída.

  • CONFIG_DMABUF_HEAPS_SYSTEM está desativado em gki_defconfig para permitir que seja um módulo de fornecedor.
  • Os testes de conformidade do VTS garantem que o heap exista em /dev/dma_heap/system . Os testes também verificam se o heap pode ser alocado e se o descritor de arquivo retornado ( fd ) pode ser mapeado na memória (mmapado) a partir do espaço do usuário.

Os pontos anteriores também são verdadeiros para a variante sem cache do heap do sistema, embora sua existência não seja obrigatória para dispositivos totalmente coerentes com E/S.

Heaps para alocar da memória protegida

As implementações de heap seguras devem ser específicas do fornecedor, pois o Android Common Kernel não oferece suporte a uma implementação genérica de heap segura.

  • Registre suas implementações específicas do fornecedor como /dev/dma_heap/system-secure<vendor-suffix> .
  • Essas implementações de heap são opcionais.
  • Se os heaps existirem, os testes VTS garantem que as alocações possam ser feitas a partir deles.
  • Os componentes da estrutura são fornecidos com acesso a esses heaps para que eles possam habilitar o uso de heaps por meio de HALs do mesmo processo Codec2 HAL/não vinculados e do mesmo processo. No entanto, os recursos genéricos da estrutura do Android não podem depender deles devido à variabilidade em seus detalhes de implementação. Se uma implementação de heap segura genérica for adicionada ao Android Common Kernel no futuro, ela deverá usar uma ABI diferente para evitar conflitos com dispositivos de atualização.

Alocador de codec 2 para heaps DMA-BUF

Um alocador de codec2 para a interface de heaps DMA-BUF está disponível no AOSP.

A interface de armazenamento de componentes que permite que os parâmetros de heap sejam especificados a partir do C2 HAL está disponível com o alocador de heap C2 DMA-BUF.

Fluxo de transição de amostra para um heap ION

Para suavizar a transição de heaps ION para DMA-BUF, libdmabufheap permite alternar um heap por vez. As etapas a seguir demonstram um fluxo de trabalho sugerido para fazer a transição de um heap ION não legado chamado my_heap que oferece suporte a um sinalizador, ION_FLAG_MY_FLAG .

Etapa 1: Criar equivalentes do heap ION na estrutura DMA-BUF. Neste exemplo, como o heap ION my_heap suporta um sinalizador ION_FLAG_MY_FLAG , registramos dois heaps DMA-BUF:

  • O comportamento my_heap corresponde exatamente ao comportamento do heap ION com o sinalizador ION_FLAG_MY_FLAG desativado.
  • O comportamento my_heap_special corresponde exatamente ao comportamento do heap ION com o sinalizador ION_FLAG_MY_FLAG ativado.

Etapa 2: crie as alterações ueventd para os novos my_heap e my_heap_special DMA-BUF. Neste ponto, os heaps são visíveis como /dev/dma_heap/my_heap e /dev/dma_heap/my_heap_special , com as permissões pretendidas.

Etapa 3: Para clientes que alocam de my_heap , modifique seus makefiles para vincular a libdmabufheap . Durante a inicialização do cliente, instancie um objeto BufferAllocator e use a API MapNameToIonHeap() para mapear a combinação <ION heap name/mask, flag> para nomes de heap DMA-BUF equivalentes.

Por exemplo:

allocator->MapNameToIonHeap("my_heap_special" /* name of DMA-BUF heap */, "my_heap" /* name of the ION heap */, ION_FLAG_MY_FLAG /* ion flags */ )

Em vez de usar a API MapNameToIonHeap() com os parâmetros name e flag, você pode criar o mapeamento de <ION heap mask, flag> para nomes de heap DMA-BUF equivalentes definindo o parâmetro de nome de heap ION como vazio.

Etapa 4: Substitua as invocações de ion_alloc_fd() por BufferAllocator::Alloc() usando o nome de heap apropriado.

Tipo de alocação libião libdmabufheap
Alocação de my_heap com sinalizador ION_FLAG_MY_FLAG não definido ion_alloc_fd(ionfd, size, 0, ION_HEAP_MY_HEAP, 0, &fd) allocator->Alloc("my_heap", size
Alocação de my_heap com sinalizador ION_FLAG_MY_FLAG definido ion_alloc_fd(ionfd, size, 0, ION_HEAP_MY_HEAP, ION_FLAG_MY_FLAG, &fd) allocator->Alloc("my_heap_special", size)

Neste ponto, o cliente está funcional, mas ainda alocando do heap ION porque não tem as permissões de sepolicy necessárias para abrir o heap DMA-BUF.

Etapa 5: crie as permissões de sepolicy necessárias para que o cliente acesse os novos heaps DMA-BUF. O cliente está agora totalmente equipado para alocar a partir do novo heap DMA-BUF.

Etapa 6: verifique se as alocações estão acontecendo a partir do novo heap DMA-BUF examinando logcat .

Etapa 7: Desabilite o heap ION my_heap no kernel. Se o código do cliente não precisar oferecer suporte a dispositivos de atualização (cujos kernels podem suportar apenas heaps ION), você também poderá remover as invocações MapNameToIonHeap() .