В Android 12 GKI 2.0 заменяет распределитель ION на кучи DMA-BUF по следующим причинам:
- Безопасность. Поскольку каждая куча DMA-BUF является отдельным символьным устройством, доступ к ней можно контролировать отдельно с помощью sepolicy. В ION это было невозможно, поскольку для выделения памяти из любой кучи требовался доступ только к устройству
/dev/ion. - Стабильность ABI. В отличие от ION, интерфейс IOCTL фреймворка куч DMA-BUF стабилен на уровне ABI, поскольку поддерживается в основном ядре Linux.
- Стандартизация. Фреймворк DMA-BUF heaps предлагает четко определенный UAPI. ION позволял использовать специальные флаги и идентификаторы кучи, что мешало разработке общей платформы тестирования, поскольку реализация ION на каждом устройстве могла работать по-разному.
Ветка android12-5.10 общего ядра Android была отключена 1 марта 2021 г..CONFIG_ION
Фон
Ниже приведено краткое сравнение куч ION и DMA-BUF.
Сходства между фреймворками ION и DMA-BUF heaps
- Фреймворки ION и DMA-BUF – это экспортеры DMA-BUF на основе кучи.
- Оба позволяют каждой куче определять свой собственный распределитель и операции DMA-BUF.
- Эффективность распределения ресурсов схожа, поскольку обе схемы требуют одного вызова IOCTL для распределения.
Различия между фреймворками куч ION и DMA-BUF
| Кучи ION | Кучи 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 и их эквиваленты для кучи DMA-BUF.
| Кучи ION | Кучи 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 с нуля, используйте пример шаблона dma-buf: heaps.
Драйверы ядра, напрямую выделяющие память из куч ION
Фреймворк куч DMA-BUF также предлагает интерфейс распределения для клиентов в ядре. Вместо того чтобы указывать маску кучи и флаги для выбора типа выделения, интерфейс, предлагаемый кучами DMA-BUF, принимает в качестве входных данных имя кучи.
Ниже показан API выделения ION в ядре и его эквивалентные API выделения кучи DMA-BUF. Драйверы ядра могут использовать API dma_heap_find(), чтобы проверить, существует ли куча. API возвращает указатель на экземпляр struct dma_heap, который затем можно передать в качестве аргумента в API dma_heap_buffer_alloc().
| Кучи ION | Кучи DMA-BUF |
|---|---|
struct dma_buf *ion_alloc(size_t len, unsigned int heap_id_mask, unsigned int 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(), который принимает маску кучи и аргумент флага, на APIBufferAllocator::Alloc(), который принимает название кучи.
В таблице ниже показано, как libion и libdmabufheap выполняют выделение системной кучи без кеширования.
| Тип распределения | libion | 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
Добавьте разрешения sepolicy, чтобы клиент пользовательского пространства мог получить доступ к новому буферу DMA-BUF. В примере добавления необходимых разрешений показаны разрешения sepolicy, созданные для различных клиентов, чтобы они могли получить доступ к системной куче DMA-BUF.
Как получить доступ к кучам поставщика из кода фреймворка
Чтобы обеспечить соответствие требованиям Treble, код фреймворка может выделять память только из предварительно одобренных категорий куч поставщиков.
На основе отзывов партнеров Google выделил две категории куч поставщиков, к которым необходимо обращаться из кода фреймворка:
- Кучи, основанные на системной куче с оптимизацией производительности для устройства или 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 проверяют, можно ли выделять из них память.
- Компоненты фреймворка получают доступ к этим кучам, чтобы они могли использовать их через Codec2 HAL/непривязанные HAL в том же процессе. Однако общие функции фреймворка Android не могут зависеть от них из-за различий в деталях реализации. Если в будущем в общий код ядра Android будет добавлена реализация защищенной кучи, она должна использовать другой ABI, чтобы избежать конфликтов с обновляемыми устройствами.
Распределитель кодека 2 для куч DMA-BUF
В AOSP доступен распределитель codec2 для интерфейса куч DMA-BUF.
Интерфейс хранилища компонентов, позволяющий задавать параметры кучи из 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, измените их файлы makefile, чтобы они ссылались на 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(), используя подходящее название кучи.
| Тип распределения | libion | 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().