IONからDMA-BUFヒープへの移行

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)

struct dma_heap *dma_heap_find(const char *name)

struct dma_buf *struct dma_buf *dma_heap_buffer_alloc(struct dma_heap *heap, size_t len, unsigned int fd_flags, unsigned int heap_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に置き換えます。

この表は、 libionlibdmabufheapがキャッシュされていないシステムヒープ割り当てをどのように行うかを示すことにより、これらの変更を示しています。

割り当ての種類リビオン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つのカテゴリを特定しました。

  1. デバイスまたはSoC固有のパフォーマンス最適化を備えたシステムヒープに基づくヒープ。
  2. 保護されたメモリから割り当てるヒープ。

デバイスまたはSoC固有のパフォーマンス最適化を備えたシステムヒープに基づくヒープ

このユースケースをサポートするために、デフォルトのDMA-BUFヒープシステムのヒープ実装をオーバーライドできます。

  • CONFIG_DMABUF_HEAPS_SYSTEMgki_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()呼び出しを削除することもできます。