Cambios en la ABI de ION

Los dispositivos que envían el kernel 4.14 y versiones posteriores se ven afectados por una refactorización importante del módulo del kernel de ION, al que muchas implementaciones de la capa de abstracción de hardware (HAL) del asignador de memoria gráfica (gralloc) del proveedor llaman para asignar búferes de memoria compartida. En esta página, se proporciona orientación para migrar el código heredado del proveedor a la nueva versión de ION y se analizan posibles interrupciones futuras de la interfaz binaria de la aplicación (ABI).

Acerca de ION

ION es parte del árbol de etapa de pruebas del kernel upstream del trabajo en curso. Durante la etapa de preparación, es posible que la ABI de espacio de usuario a kernel de ION se rompa entre las versiones principales del kernel. Si bien las interrupciones de ABI de ION no afectan directamente a las apps comunes ni a los dispositivos que ya se lanzaron, es posible que los proveedores que migren a nuevas versiones de kernel principales encuentren cambios que afecten el código del proveedor que llame a ION. Además, es posible que se produzcan interrupciones futuras de la ABI a medida que el equipo de sistemas de Android trabaje con upstream para sacar ION del árbol de preparación.

Cambios en android-4.14

El kernel 4.12 refactorizó en gran medida el código del kernel de ION, lo que permitió limpiar y quitar partes de ION que se superponían con otros frameworks de kernel. Como resultado, muchos ioctls de ION heredados ya no son relevantes y se quitaron.

Eliminación de clientes y controladores de ION

Antes del kernel 4.12, al abrir /dev/ion se asignaba un cliente ION. El ioctl ION_IOC_ALLOC asignó un búfer nuevo y lo devolvió al espacio de usuario como un control de ION (un número entero opaco que solo tiene sentido para el cliente de ION que lo asignó). Para asignar búferes al espacio de usuario o compartirlos con otros procesos, los controladores de ION se volvieron a exportar como fds de dma-buf con el ioctl ION_IOC_SHARE.

En el kernel 4.12, la ioctl ION_IOC_ALLOC genera directamente dma-buf fds. Se quitó el estado de control de ION intermedio, junto con todos los ioctls que consumen o producen controles de ION. Debido a que los fds de dma-buf no están vinculados a clientes ION específicos, ya no se necesita el ioctl ION_IOC_SHARE, y se quitó toda la infraestructura del cliente ION.

Se agregaron ioctls de coherencia de caché.

Antes del kernel 4.12, ION proporcionaba un ioctl ION_IOC_SYNC para sincronizar el descriptor de archivos con la memoria. Este ioctl estaba mal documentado y era inflexible. Como resultado, muchos proveedores implementaron ioctls personalizadas para realizar el mantenimiento de la caché.

El kernel 4.12 reemplazó a ION_IOC_SYNC por el DMA_BUF_IOCTL_SYNC ioctl definido en linux/dma-buf.h. Llama a DMA_BUF_IOCTL_SYNC al comienzo y al final de cada acceso a la CPU, con marcas que especifiquen si estos accesos son de lectura o escritura. Aunque DMA_BUF_IOCTL_SYNC es más detallado que ION_IOC_SYNC, le brinda al espacio de usuario más control sobre las operaciones de mantenimiento de la caché subyacentes.

DMA_BUF_IOCTL_SYNC forma parte de la ABI estable del kernel y se puede usar con todos los fds de dma-buf, independientemente de si ION los asignó o no.

Cómo migrar el código del proveedor a Android 4.12 y versiones posteriores

En el caso de los clientes de espacio de usuario, el equipo de sistemas de Android recomienda usar libion en lugar de llamadas ioctl() de código abierto. A partir de Android 9, libion detecta automáticamente la ABI de ION durante el tiempo de ejecución y, luego, intenta enmascarar cualquier diferencia entre los kernels. Sin embargo, las funciones de libion que producían o consumían controladores ion_user_handle_t ya no funcionan después del kernel 4.12. Puedes reemplazar estas funciones por las siguientes operaciones equivalentes en los fds de dma-buf, que funcionan en todas las versiones del kernel hasta la fecha.

Llamada heredada de ion_user_handle_t Llamada equivalente a fd de dma-buf
ion_alloc(ion_fd, …, &buf_handle) ion_alloc_fd(ion_fd, ..., &buf_fd)
ion_share(ion_fd, buf_handle, &buf_fd) N/A (esta llamada no se necesita con dma-buf fds)
ion_map(ion_fd, buf_handle, ...) mmap(buf_fd, ...)
ion_free(ion_fd, buf_handle) close(buf_fd)
ion_import(ion_fd, buf_fd, &buf_handle) N/A (esta llamada no es necesaria con los fds de dma-buf)
ion_sync_fd(ion_fd, buf_fd)
If (ion_is_legacy(ion_fd))
    ion_sync_fd(ion_fd, buf_fd);
else
    ioctl(buf_fd, DMA_BUF_IOCTL_SYNC, ...);

En el caso de los clientes dentro del kernel, como ION ya no exporta ninguna API orientada al kernel, los controladores que antes usaban la API del kernel de ION dentro del kernel con ion_import_dma_buf_fd() deben convertirse para usar la API de dma-buf dentro del kernel con dma_buf_get().

Próximos cortes de ABI de ION

Antes de que ION se pueda mover del árbol de preparación, es posible que las versiones futuras del kernel deban volver a romper la ABI de ION. El equipo de sistemas de Android no espera que estos cambios afecten a los dispositivos que se lancen con la próxima versión de Android, pero es posible que estos cambios afecten a los dispositivos que se lancen con versiones posteriores de Android.

Por ejemplo, la comunidad upstream propuso dividir el único nodo /dev/ion en varios nodos por montón (por ejemplo, /dev/ion/heap0) para permitir que los dispositivos apliquen diferentes políticas de SELinux a cada montón. Si este cambio se implementa en una versión futura del kernel, se romperá la ABI de ION.