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 a sepolicy. Isso não era possível com o ION porque a alocação de qualquer heap exigia apenas acesso ao dispositivo
/dev/ion
. - Estabilidade da ABI: ao contrário do ION, a interface IOCTL do framework de heaps DMA-BUF é estável porque é mantida no kernel do Linux upstream.
- Padronização: o framework de heaps DMA-BUF oferece uma UAPI bem definida. O ION permitia flags personalizadas e IDs de heap que impediam o desenvolvimento de uma estrutura de teste comum, porque a implementação do ION em cada dispositivo podia se comportar de maneira diferente.
A ramificação android12-5.10
do kernel comum do Android desativou
CONFIG_ION
em 1º de março de 2021.
Contexto
Confira a seguir uma breve comparação entre os heaps ION e DMA-BUF.
Semelhanças entre os frameworks de heaps ION e DMA-BUF
- Os frameworks de heaps ION e DMA-BUF são exportadores DMA-BUF baseados em heap.
- Ambos permitem que cada heap defina seu próprio alocador e operações DMA-BUF.
- A performance de alocação é semelhante porque os dois esquemas precisam de um único IOCTL para alocação.
Diferenças entre os frameworks de heaps ION e DMA-BUF
Heaps ION | Heaps DMA-BUF |
---|---|
Todas as alocações de ION são feitas com /dev/ion .
|
Cada heap DMA-BUF é um dispositivo de caractere presente em /dev/dma_heap/<heap_name> .
|
O ION é compatível com flags particulares de heap. | Heaps DMA-BUF não são compatíveis com flags particulares de heap. Cada tipo diferente de alocação é feito de um heap diferente. Por exemplo, as variantes de heap do sistema em cache e sem cache são heaps separados localizados em /dev/dma_heap/system e /dev/dma_heap/system_uncached .
|
O ID/máscara e as flags de heap 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 fazer a troca para o framework de heaps DMA-BUF.
Fazer a transição dos drivers de kernel do ION para heaps DMA-BUF
Drivers do kernel que implementam heaps ION
Os heaps ION e DMA-BUF permitem que cada heap implemente os próprios alocadores e operações DMA-BUF. Assim, é possível 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 do ION e as APIs de heap DMA-BUF equivalentes.
Heaps ION | Heaps 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 são compatíveis com flags particulares de heap. Portanto, cada variante do heap
precisa ser registrada individualmente usando a API
dma_heap_add()
. Para facilitar o compartilhamento de código, recomendamos registrar todas as variantes do mesmo heap no mesmo driver.
Este exemplo de dma-buf: system_heap
mostra a implementação das variantes em cache e sem cache do heap
do sistema.
Use este modelo de exemplo: heaps: dma-buf para criar um heap DMA-BUF do zero.
Drivers de kernel que alocam 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 e as flags de heap para selecionar o tipo de alocação, a interface oferecida pelos heaps DMA-BUF usa um nome de heap como entrada.
A seguir, mostramos a API de alocação ION no kernel e as 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 transmitida como um argumento para a
API dma_heap_buffer_alloc()
.
Heaps ION | Heaps DMA-BUF |
---|---|
struct dma_buf *ion_alloc(size_t len, unsigned int heap_id_mask, unsigned int flags)
|
|
Drivers de kernel que usam DMA-BUFs
Nenhuma mudança é 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.
Fazer a transição dos clientes do espaço do usuário do ION para heaps DMA-BUF
Para facilitar a transição para clientes do espaço do usuário do ION, uma biblioteca de abstração chamada
libdmabufheap
está disponível. O libdmabufheap
oferece suporte à alocação em heaps DMA-BUF e ION. Primeiro, ele verifica se existe um heap DMA-BUF com o nome especificado e, se não houver, volta para um heap ION equivalente, se houver.
Os clientes precisam inicializar um objeto
BufferAllocator
durante a inicialização em vez de abrir /dev/ion using
ion_open()
. Isso ocorre porque os descritores de arquivos criados ao abrir
/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 para usar na alocação, em vez do ID/máscara do cabeçalho e da flag do heap.
- Substitua a API
ion_alloc_fd()
, que usa uma máscara de heap e um argumento de flag, pela APIBufferAllocator::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 não armazenada em cache.
Tipo de alocação | libion | 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 não armazenada em cache
está aguardando aprovação upstream, mas já faz parte da ramificação android12-5.10
.
Para oferecer suporte à atualização de dispositivos, a API MapNameToIonHeap()
permite mapear um nome de heap para parâmetros de heap ION (nome ou máscara e flags) para que essas interfaces usem alocações baseadas em nomes. Confira um exemplo de alocação baseada em nome.
A documentação de todas as APIs expostas pelo
libdmabufheap
está disponível. A
biblioteca
também expõe um arquivo de cabeçalho para uso por clientes C.
Implementação de referência do Gralloc
A implementação do gralloc do Hikey960 usa libdmabufheap
, então você pode usá-la como uma implementação de referência.
Adições necessárias ao ueventd
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 do ueventd para oferecer suporte a heaps DMA-BUF demonstra como isso é feito para o heap do sistema DMA-BUF.
Adições necessárias à sepolicy
Adicione permissões sepolicy para permitir que um cliente do espaço do usuário acesse um novo heap DMA-BUF. Este exemplo de adicionar permissões necessárias mostra as permissões sepolicy criadas para vários clientes acessarem o heap do sistema DMA-BUF.
Acessar heaps do fornecedor no código do framework
Para garantir a conformidade com o Treble, o código do framework só pode alocar de categorias pré-aprovadas de heaps de fornecedores.
Com base no feedback recebido dos parceiros, o Google identificou duas categorias de heaps de fornecedores que precisam ser acessadas pelo código do framework:
- Heaps baseados no heap do sistema com otimizações de desempenho específicas do dispositivo ou do SoC.
- 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 oferecer suporte a esse caso de uso, a implementação de heap do sistema padrão de heap DMA-BUF pode ser substituída.
- O
CONFIG_DMABUF_HEAPS_SYSTEM
é desativado nogki_defconfig
para que ele seja um módulo do 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 (mmapped) do espaço do usuário.
Os pontos anteriores também são válidos para a variante não armazenada em cache do heap do sistema, embora a existência dela não seja obrigatória para dispositivos totalmente coerentes com E/S.
Heaps para alocar da memória protegida
As implementações de heap seguro precisam ser específicas do fornecedor, já que o kernel comum do Android não oferece suporte a uma implementação de heap seguro genérica.
- 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 vão garantir que as alocações possam ser feitas deles.
- Os componentes do framework têm acesso a esses heaps para que possam ativar o uso de heaps pelas HALs Codec2/não vinculadas e HALs do mesmo processo. No entanto, os recursos genéricos do framework Android não podem depender deles devido à variabilidade nos detalhes de implementação. Se uma implementação genérica de heap seguro for adicionada ao kernel comum do Android no futuro, ela precisará usar uma ABI diferente para evitar conflitos com a atualização de dispositivos.
Alocador do Codec 2 para heaps DMA-BUF
Um alocador codec2 para a interface DMA-BUF heaps está disponível no AOSP.
A interface do repositório de componentes que permite especificar parâmetros de heap da HAL C2 está disponível com o alocador de heap DMA-BUF C2.
Exemplo de fluxo de transição para um heap do ION
Para facilitar a transição de ION para heaps DMA-BUF, o libdmabufheap
permite
trocar 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 uma
flag, ION_FLAG_MY_FLAG
.
Etapa 1:crie equivalentes do heap ION na estrutura DMA-BUF. Neste exemplo, como o heap ION my_heap
é compatível com uma flag ION_FLAG_MY_FLAG
, registramos dois heaps DMA-BUF:
- O comportamento de
my_heap
corresponde exatamente ao comportamento do heap ION com a flagION_FLAG_MY_FLAG
desativada. - O comportamento de
my_heap_special
corresponde exatamente ao comportamento do heap ION com a flagION_FLAG_MY_FLAG
ativada.
Etapa 2:crie as mudanças do ueventd para os novos heaps my_heap
e my_heap_special
DMA-BUF. Neste ponto, os heaps ficam 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 os makefiles
para vincular a libdmabufheap
. Durante a inicialização do cliente, crie uma instância de 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.
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 de nome e flag, crie o mapeamento de <ION heap mask, flag>
para nomes equivalentes de heap DMA-BUF 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 | libion | libdmabufheap |
---|---|---|
Alocação de my_heap com a flag
ION_FLAG_MY_FLAG não definida
|
ion_alloc_fd(ionfd, size, 0, ION_HEAP_MY_HEAP, 0, &fd)
|
allocator->Alloc("my_heap", size)
|
Alocação de my_heap com a flag ION_FLAG_MY_FLAG definida
|
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 sepolicy necessárias para abrir o heap DMA-BUF.
Etapa 5:crie as permissões sepolicy necessárias para que o cliente acesse os novos heaps DMA-BUF. O cliente agora está totalmente equipado para alocar do novo heap DMA-BUF.
Etapa 6:verifique se as alocações estão ocorrendo no novo heap DMA-BUF examinando o logcat.
Etapa 7:desative o heap ION my_heap
no kernel. Se o código do cliente
não precisar oferecer suporte à atualização de dispositivos (cujos kernels podem oferecer suporte apenas a heaps
ION), você também poderá remover as invocações de MapNameToIonHeap()
.