В 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 возвращает указатель на экземпляр 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()
, который принимает маску кучи и аргумент флага, на 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
Добавьте разрешения 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 Common Kernel будет добавлена общая реализация безопасной кучи, она должна использовать другой 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 для новых куч DMA-BUF my_heap
и my_heap_special
. На этом этапе кучи видны как /dev/dma_heap/my_heap
и /dev/dma_heap/my_heap_special
с предполагаемыми разрешениями.
Шаг 3: Для клиентов, которые выделяют из my_heap
, измените их makefiles для ссылки на libdmabufheap
. Во время инициализации клиента создайте экземпляр объекта BufferAllocator
и используйте API MapNameToIonHeap()
для сопоставления комбинации <ION heap name/mask, flag>
с эквивалентными именами heap 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()
.