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