В 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) | |
Драйверы ядра, использующие 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
распределяют некэшированную системную кучу.
Тип распределения | либион | 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 определил две категории поставщиков, к которым необходимо получить доступ из кода платформы:
- Кучи, основанные на системной куче с оптимизацией производительности для конкретного устройства или SoC.
- Кучи для выделения из защищенной памяти.
Кучи на основе системной кучи с оптимизацией производительности для конкретного устройства или 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()
.