En Android 12, GKI 2.0 reemplaza al asignador ION con montones de DMA-BUF por los siguientes motivos:
- Seguridad: Como cada montón de DMA-BUF es un dispositivo de caracteres independiente, el acceso a cada montón se puede controlar por separado con sepolicy. Esto no era posible con ION porque la asignación desde cualquier montón solo requería acceso al dispositivo
/dev/ion
. - Estabilidad de la ABI: A diferencia de ION, la interfaz IOCTL del marco de trabajo de montones de DMA-BUF es estable en la ABI porque se mantiene en el kernel de Linux upstream.
- Estandarización: El framework de montones de DMA-BUF ofrece una UAPI bien definida. ION permitía marcas personalizadas y IDs de montón que impedían desarrollar un marco de trabajo de pruebas común, ya que la implementación de ION de cada dispositivo podía comportarse de manera diferente.
La rama android12-5.10
del kernel común de Android inhabilitó CONFIG_ION
el 1 de marzo de 2021.
Información general
A continuación, se incluye una breve comparación entre las pilas de ION y DMA-BUF.
Similitudes entre los marcos de trabajo de ION y los montones de DMA-BUF
- Los frameworks de ION y de montones de DMA-BUF son exportadores de DMA-BUF basados en montones.
- Ambos permiten que cada montón defina su propio asignador y operaciones de DMA-BUF.
- El rendimiento de la asignación es similar porque ambos esquemas necesitan un solo IOCTL para la asignación.
Diferencias entre los frameworks de ION y DMA-BUF
Montones de ION | Montones de DMA-BUF |
---|---|
Todas las asignaciones de ION se realizan con /dev/ion .
|
Cada montón de DMA-BUF es un dispositivo de caracteres que está presente en /dev/dma_heap/<heap_name> .
|
ION admite marcas privadas de montón. | Los montones de DMA-BUF no admiten marcas privadas de montón. Cada tipo diferente de asignación se realiza desde un montón diferente. Por ejemplo, las variantes de montón del sistema almacenadas en caché y sin almacenar en caché son montones separados ubicados en /dev/dma_heap/system y /dev/dma_heap/system_uncached .
|
Se deben especificar la máscara o el ID del montón y las marcas para la asignación. | El nombre del heap se usa para la asignación. |
En las siguientes secciones, se enumeran los componentes que se encargan de ION y se describe cómo cambiarlos al framework de montones DMA-BUF.
Transición de los controladores de kernel de ION a montones de DMA-BUF
Controladores de kernel que implementan montones de ION
Tanto los montones de ION como los de DMA-BUF permiten que cada montón implemente sus propios asignadores y operaciones de DMA-BUF. Por lo tanto, puedes cambiar de una implementación de montón de ION a una implementación de montón de DMA-BUF usando un conjunto diferente de APIs para registrar el montón. En esta tabla, se muestran las APIs de registro de montón de ION y sus APIs de montón de DMA-BUF equivalentes.
Montones de ION | Montones 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);
|
Los montones de DMA-BUF no admiten marcas privadas de montón. Por lo tanto, cada variante del montón se debe registrar de forma individual con la API de dma_heap_add()
. Para facilitar el uso compartido del código, se recomienda registrar todas las variantes del mismo montón dentro del mismo controlador.
En este ejemplo de dma-buf: system_heap, se muestra la implementación de las variantes con caché y sin caché del heap del sistema.
Usa esta plantilla de ejemplo de montones de dma-buf para crear un montón de DMA-BUF desde cero.
Controladores de kernel que asignan directamente desde montones de ION
El framework de montones DMA-BUF también ofrece una interfaz de asignación para clientes en el kernel. En lugar de especificar la máscara y las marcas de montón para seleccionar el tipo de asignación, la interfaz que ofrecen los montones de DMA-BUF toma un nombre de montón como entrada.
A continuación, se muestra la API de asignación de ION en el kernel y sus APIs de asignación de montón de DMA-BUF equivalentes. Los controladores del kernel pueden usar la API de dma_heap_find()
para consultar la existencia de un heap. La API devuelve un puntero a una instancia de struct dma_heap, que luego se puede pasar como argumento a la API de dma_heap_buffer_alloc()
.
Montones de ION | Montones de DMA-BUF |
---|---|
struct dma_buf *ion_alloc(size_t len, unsigned int heap_id_mask, unsigned int flags)
|
|
Controladores de kernel que usan DMA-BUF
No se requieren cambios en los controladores que solo importan DMA-BUF, ya que un búfer asignado desde un montón de ION se comporta exactamente igual que un búfer asignado desde un montón de DMA-BUF equivalente.
Realiza la transición de los clientes de ION del espacio del usuario a montones de DMA-BUF
Para facilitar la transición a los clientes de ION del espacio del usuario, se encuentra disponible una biblioteca de abstracción llamada libdmabufheap
. libdmabufheap
admite la asignación en montones de DMA-BUF y montones de ION. Primero, verifica si existe un montón DMA-BUF con el nombre especificado y, si no es así, recurre a un montón ION equivalente, si existe.
Los clientes deben inicializar un objeto BufferAllocator
durante su inicialización en lugar de abrir /dev/ion using
ion_open()
. Esto se debe a que los descriptores de archivos creados al abrir /dev/ion
y /dev/dma_heap/<heap_name>
se administran internamente con el objeto BufferAllocator
.
Para cambiar de libion
a libdmabufheap
, modifica el comportamiento de los clientes de la siguiente manera:
- Realiza un seguimiento del nombre del montón para usarlo en la asignación, en lugar de la máscara o el ID del encabezado y la marca del montón.
- Reemplaza la API de
ion_alloc_fd()
, que toma una máscara de montón y un argumento de marca, por la API deBufferAllocator::Alloc()
, que toma un nombre de montón.
En esta tabla, se ilustran estos cambios mostrando cómo libion
y libdmabufheap
realizan una asignación de montón del sistema sin almacenar en caché.
Tipo de asignación | libion | libdmabufheap |
---|---|---|
Asignación almacenada en caché del montón del sistema | ion_alloc_fd(ionfd, size, 0, ION_HEAP_SYSTEM, ION_FLAG_CACHED, &fd)
|
allocator->Alloc("system", size)
|
Asignación sin caché del montón del sistema | ion_alloc_fd(ionfd, size, 0, ION_HEAP_SYSTEM, 0, &fd)
|
allocator->Alloc("system-uncached", size)
|
La variante del heap del sistema sin caché está pendiente de aprobación en el upstream, pero ya forma parte de la rama android12-5.10
.
Para admitir la actualización de dispositivos, la API de MapNameToIonHeap()
permite asignar un nombre de montón a los parámetros de montón de ION (nombre o máscara y marcas de montón) para permitir que esas interfaces usen asignaciones basadas en nombres. Este es un ejemplo de asignación basada en el nombre.
Está disponible la documentación de cada API expuesta por libdmabufheap
. La biblioteca también expone un archivo de encabezado para que lo usen los clientes de C.
Implementación de referencia de Gralloc
La implementación de gralloc de Hikey960 usa libdmabufheap
, por lo que puedes usarla como una implementación de referencia.
Adiciones obligatorias de ueventd
Para cualquier nuevo montón de DMA-BUF específico del dispositivo que se cree, agrega una entrada nueva al archivo ueventd.rc
del dispositivo.
En este ejemplo de configuración de ueventd para admitir montones de DMA-BUF, se muestra cómo se hace esto para el montón del sistema DMA-BUF.
Adiciones obligatorias a sepolicy
Agrega permisos de sepolicy para permitir que un cliente del espacio del usuario acceda a un nuevo heap de DMA-BUF. En este ejemplo de agregar permisos obligatorios, se muestran los permisos de sepolicy creados para que varios clientes accedan al heap del sistema DMA-BUF.
Cómo acceder a montones de proveedores desde el código del framework
Para garantizar el cumplimiento de Treble, el código del framework solo puede asignar memoria de categorías preaprobadas de montones del proveedor.
Según los comentarios que recibimos de los socios, Google identificó dos categorías de montones de proveedores a los que se debe acceder desde el código del framework:
- Son montones basados en el montón del sistema con optimizaciones de rendimiento específicas del SoC o del dispositivo.
- Son los montones que se asignarán desde la memoria protegida.
Montones basados en el montón del sistema con optimizaciones de rendimiento específicas del dispositivo o del SoC
Para admitir este caso de uso, se puede anular la implementación del montón del sistema de montón DMA-BUF predeterminado.
CONFIG_DMABUF_HEAPS_SYSTEM
está desactivado engki_defconfig
para que sea un módulo del proveedor.- Las pruebas de cumplimiento de VTS garantizan que el heap exista en
/dev/dma_heap/system
. Las pruebas también verifican que se pueda asignar memoria dinámica y que el descriptor de archivo devuelto (fd
) se pueda asignar a la memoria (mmapped) desde el espacio del usuario.
Los puntos anteriores también se aplican a la variante sin caché del heap del sistema, aunque su existencia no es obligatoria para los dispositivos completamente coherentes con la E/S.
Montones para asignar desde la memoria protegida
Las implementaciones de montón seguro deben ser específicas del proveedor, ya que el kernel común de Android no admite una implementación genérica de montón seguro.
- Registra tus implementaciones específicas del proveedor como
/dev/dma_heap/system-secure<vendor-suffix>
. - Estas implementaciones de montón son opcionales.
- Si existen los montones, las pruebas de VTS garantizan que se puedan realizar asignaciones desde ellos.
- Los componentes del framework tienen acceso a estos montículos para que puedan habilitar el uso de montículos a través de Codec2 HAL/HAL no vinculados y HAL del mismo proceso. Sin embargo, las funciones genéricas del framework de Android no pueden depender de ellas debido a la variabilidad en los detalles de implementación. Si en el futuro se agrega una implementación genérica de montón seguro al kernel común de Android, esta deberá usar una ABI diferente para evitar conflictos con la actualización de dispositivos.
Asignador de códec 2 para montones de DMA-BUF
En AOSP, está disponible un asignador de codec2 para la interfaz de montones de DMA-BUF.
La interfaz de almacenamiento de componentes que permite especificar parámetros de montón desde la HAL de C2 está disponible con el asignador de montón DMA-BUF de C2.
Flujo de transición de muestra para un montón de ION
Para facilitar la transición de ION a montones de DMA-BUF, libdmabufheap
permite cambiar un montón a la vez. En los siguientes pasos, se muestra un flujo de trabajo sugerido para la transición de un montón de ION no heredado llamado my_heap
que admite una marca, ION_FLAG_MY_FLAG
.
Paso 1: Crea equivalentes del montón de ION en el framework de DMA-BUF. En este ejemplo, dado que el montón de ION my_heap
admite una marca ION_FLAG_MY_FLAG
, registramos dos montones de DMA-BUF:
- El comportamiento de
my_heap
coincide exactamente con el comportamiento del montón de ION con la marcaION_FLAG_MY_FLAG
inhabilitada. - El comportamiento de
my_heap_special
coincide exactamente con el comportamiento del montón de ION con la marcaION_FLAG_MY_FLAG
habilitada.
Paso 2: Crea los cambios de ueventd para los nuevos montones de my_heap
y my_heap_special
DMA-BUF. En este punto, los montones son visibles como /dev/dma_heap/my_heap
y /dev/dma_heap/my_heap_special
, con los permisos previstos.
Paso 3: En el caso de los clientes que asignan desde my_heap
, modifica sus archivos makefile para vincularlos a libdmabufheap
. Durante la inicialización del cliente, crea una instancia de un objeto BufferAllocator
y usa la API de MapNameToIonHeap()
para asignar la combinación de <ION heap name/mask, flag>
a nombres de montón de DMA-BUF equivalentes.
Por ejemplo:
allocator->MapNameToIonHeap("my_heap_special" /* name of DMA-BUF heap */, "my_heap" /* name of the ION heap */, ION_FLAG_MY_FLAG /* ion flags */ )
En lugar de usar la API de MapNameToIonHeap()
con los parámetros de nombre y marca, puedes crear la asignación de <ION heap mask, flag>
a nombres de montón DMA-BUF equivalentes configurando el parámetro de nombre de montón de ION como vacío.
Paso 4: Reemplaza las invocaciones de ion_alloc_fd()
por BufferAllocator::Alloc()
con el nombre de heap adecuado.
Tipo de asignación | libion | libdmabufheap |
---|---|---|
Asignación de my_heap con la marca ION_FLAG_MY_FLAG sin configurar
|
ion_alloc_fd(ionfd, size, 0, ION_HEAP_MY_HEAP, 0, &fd)
|
allocator->Alloc("my_heap", size)
|
Asignación de my_heap con la marca ION_FLAG_MY_FLAG establecida
|
ion_alloc_fd(ionfd, size, 0, ION_HEAP_MY_HEAP,
ION_FLAG_MY_FLAG, &fd)
|
allocator->Alloc("my_heap_special", size)
|
En este punto, el cliente funciona, pero sigue asignando desde el montón de ION porque no tiene los permisos de sepolicy necesarios para abrir el montón de DMA-BUF.
Paso 5: Crea los permisos de sepolicy necesarios para que el cliente acceda a los nuevos montones de DMA-BUF. El cliente ahora está completamente equipado para asignar desde el nuevo heap DMA-BUF.
Paso 6: Verifica que las asignaciones se realicen desde el nuevo heap de DMA-BUF examinando logcat.
Paso 7: Inhabilita el heap de ION my_heap
en el kernel. Si el código del cliente no necesita admitir la actualización de dispositivos (cuyos kernels solo admiten ION heaps), también puedes quitar las invocaciones de MapNameToIonHeap()
.