Переход от 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() , который принимает маску кучи и аргумент флага, на API 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 .

Для поддержки обновления устройств API MapNameToIonHeap() позволяет сопоставлять имя кучи с параметрами кучи ION (имя/маска и флаги кучи), чтобы позволить этим интерфейсам также использовать распределения на основе имен. Вот пример распределения по имени .

Доступна документация для каждого 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() .