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