Переход от ION к кучам DMA-BUF

В Android 12 GKI 2.0 заменяет распределитель ION кучами DMA-BUF по следующим причинам:

  • Безопасность. Поскольку каждая куча DMA-BUF представляет собой отдельное символьное устройство, доступ к каждой куче можно контролировать отдельно с помощью sepolicy. В ION это было невозможно, поскольку для выделения из любой кучи требовался только доступ к устройству /dev/ion .
  • Стабильность ABI. В отличие от ION, интерфейс IOCTL платформы кучи DMA-BUF является стабильным ABI, поскольку он поддерживается в исходном ядре Linux.
  • Стандартизация: структура кучи DMA-BUF предлагает четко определенный UAPI. ION позволял использовать собственные флаги и идентификаторы кучи, что препятствовало разработке общей среды тестирования, поскольку реализация ION на каждом устройстве могла вести себя по-разному.

Ветка android12-5.10 Android Common Kernel отключила CONFIG_ION 1 марта 2021 года .

Фон

Ниже приводится краткое сравнение куч ION и DMA-BUF.

Сходства между структурой куч ION и DMA-BUF

  • Платформы кучи ION и DMA-BUF являются экспортерами DMA-BUF на основе кучи.
  • Они оба позволяют каждой куче определять свой собственный распределитель и операции DMA-BUF.
  • Производительность распределения аналогична, поскольку для распределения обеим схемам требуется один IOCTL.

Различия между структурой куч ION и DMA-BUF

ИОННЫЕ кучи Кучи DMA-BUF
Все распределения ION выполняются с помощью /dev/ion . Каждая куча DMA-BUF представляет собой символьное устройство, которое присутствует в /dev/dma_heap/<heap_name> .
ION поддерживает частные флаги кучи. Кучи DMA-BUF не поддерживают частные флаги кучи. Вместо этого каждый тип распределения выполняется из отдельной кучи. Например, кэшированные и некэшированные варианты системной кучи представляют собой отдельные кучи, расположенные в /dev/dma_heap/system и /dev/dma_heap/system_uncached .
Для выделения необходимо указать идентификатор/маску и флаги кучи. Имя кучи используется для распределения.

В следующих разделах перечислены компоненты, работающие с ION, и описано, как переключить их на структуру кучи DMA-BUF.

Драйверы ядра перехода из ION в кучу DMA-BUF

Драйверы ядра, реализующие кучи ION

Кучи ION и DMA-BUF позволяют каждой куче реализовывать свои собственные распределители и операции DMA-BUF. Таким образом, вы можете переключиться с реализации кучи ION на реализацию кучи DMA-BUF, используя другой набор API для регистрации кучи. В этой таблице показаны API-интерфейсы регистрации кучи ION и их эквивалентные API-интерфейсы кучи DMA-BUF.

ИОННЫЕ кучи Кучи 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);

Кучи DMA-BUF не поддерживают частные флаги кучи. Поэтому каждый вариант кучи необходимо регистрировать индивидуально с помощью API dma_heap_add() . Чтобы облегчить совместное использование кода, рекомендуется зарегистрировать все варианты одной и той же кучи в одном драйвере. В этом примере dma-buf: system_heap показана реализация кэшированного и некэшированного вариантов системной кучи.

Используйте этот пример шаблона dma-buf: heaps: для создания кучи DMA-BUF с нуля.

Драйверы ядра, непосредственно выделяющие ресурсы из кучи ION

Платформа кучи DMA-BUF также предлагает интерфейс распределения для клиентов внутри ядра. Вместо указания маски кучи и флагов для выбора типа распределения интерфейс, предлагаемый кучами DMA-BUF, принимает на вход имя кучи.

Ниже показаны API выделения ION в ядре и его эквивалентные API выделения кучи DMA-BUF. Драйверы ядра могут использовать API dma_heap_find() для запроса существования кучи. API возвращает указатель на экземпляр структуры dma_heap , который затем можно передать в качестве аргумента API dma_heap_buffer_alloc() .

ИОННЫЕ кучи Кучи 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)

Драйверы ядра, использующие DMA-BUF

Никаких изменений не требуется для драйверов, которые импортируют только DMA-BUF, поскольку буфер, выделенный из кучи ION, ведет себя точно так же, как буфер, выделенный из эквивалентной кучи DMA-BUF.

Переведите клиентов пользовательского пространства ION в кучи DMA-BUF.

Чтобы облегчить переход для клиентов ION в пользовательском пространстве, доступна библиотека абстракций под названием libdmabufheap . libdmabufheap поддерживает распределение в кучах DMA-BUF и кучах ION. Сначала он проверяет, существует ли куча DMA-BUF с указанным именем, а если нет, возвращается к эквивалентной куче ION, если таковая существует.

Клиенты должны инициализировать объект BufferAllocator во время своей инициализации вместо открытия /dev/ion using ion_open() . Это связано с тем, что файловые дескрипторы, созданные путем открытия /dev/ion и /dev/dma_heap/<heap_name> управляются внутри объекта BufferAllocator .

Чтобы переключиться с libion ​​на libdmabufheap , измените поведение клиентов следующим образом:

  • Следите за именем кучи, которое будет использоваться для выделения, вместо идентификатора/маски головы и флага кучи.
  • Замените API ion_alloc_fd() , который принимает маску кучи и аргумент флага, на BufferAllocator::Alloc() , который вместо этого принимает имя кучи.

Эта таблица иллюстрирует эти изменения, показывая, как libion ​​и libdmabufheap распределяют некэшированную системную кучу.

Тип распределения либион libdmabufheap
Кэшированное выделение из системной кучи ion_alloc_fd(ionfd, size, 0, ION_HEAP_SYSTEM, ION_FLAG_CACHED, &fd) allocator->Alloc("system", size)
Некэшированное выделение из системной кучи ion_alloc_fd(ionfd, size, 0, ION_HEAP_SYSTEM, 0, &fd) allocator->Alloc("system-uncached", size)

Вариант некэшированной системной кучи ожидает одобрения, но уже является частью ветки android12-5.10 .

To support upgrading devices, the MapNameToIonHeap() API allows mapping a heap name to ION heap parameters (heap name or mask and flags) to let those interfaces use name-based allocations. Вот пример распределения по имени .

Доступна документация для каждого API, предоставляемого libdmabufheap . Библиотека также предоставляет файл заголовка для использования клиентами C.

Эталонная реализация Gralloc

Реализация gralloc Hikey960 использует libdmabufheap , поэтому вы можете использовать ее в качестве эталонной реализации .

Необходимые дополнения ueventd

Для любых новых созданных куч DMA-BUF для конкретного устройства добавьте новую запись в файл ueventd.rc устройства. В этом примере настройки ueventd для поддержки кучи DMA-BUF показано, как это делается для системной кучи DMA-BUF.

Необходимые дополнения к политике безопасности

Добавьте разрешения sepolicy, чтобы позволить клиенту пользовательского пространства получить доступ к новой куче DMA-BUF. В этом примере добавления необходимых разрешений показаны разрешения sepolicy, созданные для различных клиентов для доступа к системной куче DMA-BUF.

Доступ к куче поставщиков из кода платформы

Чтобы обеспечить соответствие требованиям Treble, код платформы может выделять ресурсы только из предварительно утвержденных категорий поставщиков.

Основываясь на отзывах, полученных от партнеров, Google определил две категории поставщиков, к которым необходимо получить доступ из кода платформы:

  1. Кучи, основанные на системной куче с оптимизацией производительности для конкретного устройства или SoC.
  2. Кучи для выделения из защищенной памяти.

Кучи на основе системной кучи с оптимизацией производительности для конкретного устройства или SoC.

Для поддержки этого варианта использования можно переопределить реализацию кучи стандартной системы кучи DMA-BUF.

  • CONFIG_DMABUF_HEAPS_SYSTEM отключен в gki_defconfig , чтобы это был модуль поставщика.
  • Тесты на соответствие VTS гарантируют, что куча существует в /dev/dma_heap/system . Тесты также проверяют, что куча может быть выделена и что возвращенный дескриптор файла ( fd ) может быть отображен в память (mmapped) из пользовательского пространства.

Предыдущие пункты также верны для некэшируемого варианта системной кучи, хотя ее существование не является обязательным для полностью когерентных устройств ввода-вывода.

Кучи для выделения из защищенной памяти

Реализации безопасной кучи должны зависеть от поставщика, поскольку общее ядро ​​Android не поддерживает общую реализацию безопасной кучи.

  • Зарегистрируйте свои реализации для конкретного поставщика как /dev/dma_heap/system-secure<vendor-suffix> .
  • Эти реализации кучи не являются обязательными.
  • Если кучи существуют, тесты VTS гарантируют, что из них можно выполнить выделение.
  • Компонентам платформы предоставляется доступ к этим кучам, чтобы они могли использовать их через HAL Codec2/несвязанные HAL одного и того же процесса. Однако общие функции платформы Android не могут зависеть от них из-за различий в деталях их реализации. Если в будущем в общее ядро ​​Android будет добавлена ​​универсальная реализация безопасной кучи, она должна будет использовать другой ABI, чтобы избежать конфликтов с обновлением устройств.

Распределитель кодека 2 для кучи DMA-BUF

Распределитель codec2 для интерфейса кучи DMA-BUF доступен в AOSP.

Интерфейс хранилища компонентов, который позволяет указывать параметры кучи из C2 HAL, доступен с помощью распределителя кучи C2 DMA-BUF.

Пример переходного процесса для ION-кучи

Чтобы сгладить переход от кучи ION к куче DMA-BUF, libdmabufheap позволяет переключать одну кучу за раз. Следующие шаги демонстрируют предлагаемый рабочий процесс для перехода к неустаревшей куче ION с именем my_heap , которая поддерживает один флаг, ION_FLAG_MY_FLAG .

Шаг 1. Создайте эквиваленты кучи ION в рамках DMA-BUF. В этом примере, поскольку куча ION my_heap поддерживает флаг ION_FLAG_MY_FLAG , мы регистрируем две кучи DMA-BUF:

  • Поведение my_heap точно соответствует поведению кучи ION с отключенным флагом ION_FLAG_MY_FLAG .
  • Поведение my_heap_special точно соответствует поведению кучи ION с включенным флагом ION_FLAG_MY_FLAG .

Шаг 2. Создайте изменения ueventd для новых куч my_heap и my_heap_special DMA-BUF. На этом этапе кучи видны как /dev/dma_heap/my_heap и /dev/dma_heap/my_heap_special с заданными разрешениями.

Шаг 3. Для клиентов, которые выделяют ресурсы из my_heap , измените их make-файлы, чтобы они ссылались на libdmabufheap . Во время инициализации клиента создайте экземпляр объекта BufferAllocator и используйте API MapNameToIonHeap() для сопоставления комбинации <ION heap name/mask, flag> с эквивалентными именами кучи DMA-BUF.

Например:

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

Вместо использования API MapNameToIonHeap() с параметрами name и flag вы можете создать сопоставление <ION heap mask, flag> с эквивалентными именами кучи DMA-BUF, установив для параметра имени кучи ION пустое значение.

Шаг 4. Замените вызовы ion_alloc_fd() на BufferAllocator::Alloc() используя соответствующее имя кучи.

Тип распределения либион libdmabufheap
Выделение из my_heap со снятым флагом ION_FLAG_MY_FLAG ion_alloc_fd(ionfd, size, 0, ION_HEAP_MY_HEAP, 0, &fd) allocator->Alloc("my_heap", size)
Выделение из my_heap с установленным флагом ION_FLAG_MY_FLAG ion_alloc_fd(ionfd, size, 0, ION_HEAP_MY_HEAP, ION_FLAG_MY_FLAG, &fd) allocator->Alloc("my_heap_special", size)

На этом этапе клиент работоспособен, но все еще выделяет ресурсы из кучи ION, поскольку у него нет необходимых разрешений sepolicy для открытия кучи DMA-BUF.

Шаг 5. Создайте разрешения sepolicy, необходимые клиенту для доступа к новым кучам DMA-BUF. Теперь клиент полностью готов к выделению ресурсов из новой кучи DMA-BUF.

Шаг 6. Убедитесь, что выделение происходит из новой кучи DMA-BUF, проверив logcat .

Шаг 7. Отключите ION-кучу my_heap в ядре. Если клиентскому коду не требуется поддерживать обновление устройств (ядра которых могут поддерживать только кучи ION), вы также можете удалить вызовы MapNameToIonHeap() .