Android 12 では、次の理由により、GKI 2.0 で ION アロケータが DMA-BUF ヒープに置き換えられています。
- セキュリティ: 各 DMA-BUF ヒープは個別のキャラクター デバイスであるため、各ヒープへのアクセスは sepolicy で個別に制御できます。ION の場合、ヒープからの割り当てには
/dev/ion
デバイスへのアクセスのみが必要だったため、これは不可能でした。 - ABI の安定性: ION とは異なり、DMA-BUF ヒープ フレームワークの IOCTL インターフェースは、アップストリームの Linux カーネルで維持されているため、ABI の安定性が保証されます。
- 標準化: DMA-BUF ヒープ フレームワークでは、明確に定義された UAPI が提供されています。ION ではカスタムフラグとヒープ ID が許可されていましたが、デバイスごとに ION 実装の動作が異なるため、一般的なテスト フレームワークを開発できませんでした。
Android 共通カーネルの android12-5.10
ブランチでは、2021 年 3 月 1 日をもって CONFIG_ION
が無効にされました。
背景
次のリストと表は、ION ヒープと DMA-BUF ヒープを簡単に比較したものです。
ION ヒープ フレームワークと DMA-BUF ヒープ フレームワークの類似点
- ION ヒープ フレームワークと DMA-BUF ヒープ フレームワークは、どちらもヒープベースの DMA-BUF エクスポータです。
- 両方のヒープ フレームワークで、各ヒープが独自のアロケータと DMA-BUF オペレーションを定義できます。
- どちらのスキームでも割り当てに対して 1 つの 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 にある別個のヒープです。
|
割り当てには、ヒープ ID / マスクとフラグを指定する必要があります。 | 割り当てにはヒープ名が使用されます。 |
次のセクションでは、ION を処理するコンポーネントと、それらを DMA-BUF ヒープ フレームワークに切り替える方法について説明します。
ION ヒープから DMA-BUF ヒープへのカーネル ドライバの移行
ION ヒープを実装するカーネル ドライバ
ION ヒープと DMA-BUF ヒープでは、各ヒープに独自のアロケータと DMA-BUF オペレーションを実装できます。したがって、ION ヒープ実装から DMA-BUF ヒープ実装に切り替えるには、別の API セットを使用してヒープを登録します。次の表に、ION ヒープ登録 API と、それに対応する DMA-BUF ヒープ API を示します。
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 ヒープは、ヒープ プライベート フラグをサポートしていません。そのため、ヒープの各バリアントは、dma_heap_add()
API を使用して個別に登録する必要があります。コード共有を容易にするために、同一ヒープのすべてのバリアントを同じドライバ内で登録することをおすすめします。dma-buf: system_heap の例は、システムヒープのキャッシュされたバリアントとキャッシュされていないバリアントの実装を示しています。
この dma-buf: heaps: サンプル テンプレートを使用して、DMA-BUF ヒープをゼロから作成します。
ION ヒープから直接割り当てるカーネル ドライバ
DMA-BUF ヒープ フレームワークには、カーネル内クライアント用の割り当てインターフェースも用意されています。DMA-BUF ヒープによって提供されるインターフェースは、ヒープのマスクとフラグを指定して割り当てのタイプを選択するのではなく、ヒープ名を入力として使用します。
カーネル内 ION 割り当て API と、それに対応する DMA-BUF ヒープ割り当て API を以下に示します。カーネル ドライバは、dma_heap_find()
API を使用してヒープの存在をクエリできます。API は struct dma_heap のインスタンスへのポインタを返します。このポインタは引数として dma_heap_buffer_alloc()
API に渡すことができます。
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 ヒープ(存在する場合)にフォールバックします。
クライアントは初期化時に、/dev/ion using
ion_open()
を開く代わりに BufferAllocator
オブジェクトを初期化する必要があります。これは、/dev/ion
と /dev/dma_heap/<heap_name>
を開くことで作成されるファイル記述子が、BufferAllocator
オブジェクトによって内部的に管理されるためです。
libion
から libdmabufheap
に切り替えるには、クライアントの動作を次のように変更します。
- ヒープ ID / マスクとヒープフラグの代わりに、割り当てに使用するヒープ名を追跡します。
- ヒープマスクとフラグ引数を取る
ion_alloc_fd()
API を、ヒープ名を取るBufferAllocator::Alloc()
API に置き換えます。
次の表では、上記の変更を説明するために、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
ブランチの一部になっています。
デバイスのアップグレードをサポートするため、MapNameToIonHeap()
API では、ヒープ名を ION ヒープ パラメータ(ヒープ名、ヒープマスク、ヒープフラグ)にマッピングすることで、これらのインターフェースが名前ベースの割り当てを使用できるようになっています。名前ベースの割り当ての例をご覧ください。
libdmabufheap
によって公開されるすべての API のドキュメントを参照できます。また、このライブラリでは、C クライアントが使用するヘッダー ファイルも公開されています。
Gralloc のリファレンス実装
Hikey960 の gralloc 実装では libdmabufheap
が使用されているため、リファレンス実装として使用できます。
ueventd への必要な追加
新規にデバイス固有の DMA-BUF ヒープを作成した場合は、デバイスの ueventd.rc
ファイルに新しいエントリを追加します。この DMA-BUF ヒープをサポートするように ueventd をセットアップする場合の例では、DMA-BUF システムヒープに対してこれを行う方法を示しています。
必要な sepolicy の追加
ユーザー空間クライアントが新しい DMA-BUF ヒープにアクセスできるように、sepolicy 権限を追加します。 必要な権限の追加の例は、さまざまなクライアントが DMA-BUF システムヒープにアクセスできるようにするために作成された sepolicy 権限を示しています。
フレームワーク コードからベンダーヒープへのアクセス
Treble に準拠するため、フレームワーク コードはベンダーヒープの事前承認されたカテゴリからのみ割り当てることができます。
パートナーから寄せられたフィードバックに基づいて、フレームワーク コードからアクセスする必要があるベンダーヒープの 2 つのカテゴリが特定されました。
- デバイスまたは SoC 固有のパフォーマンス最適化を伴うシステムヒープに基づくヒープ。
- 保護されたメモリから割り当てるヒープ。
デバイスまたは SoC 固有のパフォーマンス最適化を伴うシステムヒープに基づくヒープ
このユースケースをサポートするために、デフォルトの DMA-BUF ヒープシステムのヒープ実装をオーバーライドできます。
gki_defconfig
のCONFIG_DMABUF_HEAPS_SYSTEM
をオフにして、ベンダー モジュールとして扱われるようにします。- VTS 準拠テストでは、ヒープが
/dev/dma_heap/system
に存在することを確認します。また、このヒープが割り当て可能であることと、返されたファイル記述子(fd
)がユーザー空間からメモリマップ(mmap)可能であることも確認します。
上記のポイントは、システムヒープのキャッシュされていないバリアントでも同様ですが、完全に IO に一貫性のあるデバイスでは必須ではありません。
保護されたメモリから割り当てるヒープ
Android 共通カーネルは汎用のセキュアヒープ実装をサポートしていないため、セキュアヒープ実装はベンダー固有のものにする必要があります。
- ベンダー固有の実装を
/dev/dma_heap/system-secure<vendor-suffix>
として登録します。 - これらのヒープ実装は省略可能です。
- ヒープが存在する場合、VTS テストで、このヒープから割り当てができることを確認します。
- フレームワーク コンポーネントには、Codec2 HAL またはバインダ化されていない同じプロセス HAL を介してヒープ使用を有効にできるように、これらのヒープへのアクセス権が付与されます。 ただし、実装の詳細のばらつきにより、一般的な Android フレームワーク機能に依存することはできません。将来的に、汎用のセキュアヒープ実装を Android 共通カーネルに追加する場合は、デバイスのアップグレードとの競合を避けるために別の ABI を使用する必要があります。
DMA-BUF ヒープ用の Codec2 アロケータ
AOSP では、DMA-BUF ヒープ インターフェース用の Codec2 アロケータが提供されています。
C2 HAL からヒープ パラメータを指定できるようにするコンポーネント ストア インターフェースは、C2 DMA-BUF ヒープ アロケータで使用できます。
ION ヒープの移行フローの例
ION ヒープから DMA-BUF ヒープへの移行をスムーズにするために、libdmabufheap
ではヒープを 1 つずつ切り替えることができます。次の手順では、ION_FLAG_MY_FLAG
という 1 つのフラグをサポートする、my_heap
という非レガシー ION ヒープを移行するための推奨ワークフローを示しています。
ステップ 1: DMA-BUF フレームワークで、ION ヒープと同等のヒープを作成します。この例では、ION ヒープ my_heap
はフラグ ION_FLAG_MY_FLAG
をサポートしているため、次の 2 つの DMA-BUF ヒープを登録します。
my_heap
の動作は、フラグION_FLAG_MY_FLAG
を無効にした場合の ION ヒープの動作と完全に一致します。my_heap_special
の動作は、フラグION_FLAG_MY_FLAG
を有効にした場合の ION ヒープの動作と完全に一致します。
ステップ 2: 新しい my_heap
および my_heap_special
DMA-BUF ヒープの uevent 変更を作成します。この時点で、ヒープは想定どおりの権限で /dev/dma_heap/my_heap
と /dev/dma_heap/my_heap_special
として表示されます。
ステップ 3: my_heap
から割り当てるクライアントの場合、libdmabufheap
にリンクするように makefile を変更します。クライアントの初期化中に、BufferAllocator
オブジェクトをインスタンス化し、MapNameToIonHeap()
API を使用して <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 */ )
名前パラメータとフラグ パラメータを指定して MapNameToIonHeap()
API を使用する代わりに、ION ヒープの名前パラメータを空に設定することで、<ION heap mask, flag>
から同等の DMA-BUF ヒープ名へのマッピングを作成できます。
ステップ 4: 適切なヒープ名を使用して、ion_alloc_fd()
の呼び出しを BufferAllocator::Alloc()
に置き換えます。
割り当てタイプ | libion | libdmabufheap |
---|---|---|
フラグ ION_FLAG_MY_FLAG が未設定の my_heap からの割り当て
|
ion_alloc_fd(ionfd, size, 0, ION_HEAP_MY_HEAP, 0, &fd)
|
allocator->Alloc("my_heap", size
|
フラグ ION_FLAG_MY_FLAG を設定した my_heap からの割り当て
|
ion_alloc_fd(ionfd, size, 0, ION_HEAP_MY_HEAP, ION_FLAG_MY_FLAG, &fd)
|
allocator->Alloc("my_heap_special", size)
|
この時点で、クライアントは機能しますが、DMA-BUF ヒープを開くために必要な sepolicy 権限がないため、引き続き ION ヒープから割り当てています。
ステップ 5: クライアントが新しい DMA-BUF ヒープにアクセスするために必要な sepolicy 権限を作成します。クライアントは、新しい DMA-BUF ヒープから割り当てる準備ができています。
ステップ 6: logcat を調べて、割り当てが新しい DMA-BUF ヒープから発生していることを確認します。
ステップ 7: カーネルで ION ヒープ my_heap
を無効にします。クライアント コードがデバイスのアップグレードをサポートする必要がない場合(カーネルが ION ヒープのみをサポートしている場合に必要)、MapNameToIonHeap()
呼び出しを削除することもできます。