Ion ABI の変更

カーネル 4.14 以上を搭載したデバイスでは、Ion カーネル モジュールが大幅にリファクタリングされています。多くのベンダーで、共有メモリバッファを割り当てる際に呼び出すグラフィックス メモリ アロケータ(gralloc)のハードウェア抽象化レイヤ(HAL)での実装に同モジュールが利用されており、リファクタリングによる影響がおよびます。この記事では、従来のベンダーコードを Ion の新しいバージョンに移行する方法を紹介し、将来起こる可能性があるアプリケーション バイナリ インターフェース(ABI)の互換性の問題について説明します。

Ion について

Ion は、アップストリーム カーネルで開発中のステージング ツリーの一部です。ステージングに際し、カーネルのメジャー バージョンが異なると、ユーザー空間とカーネルの間に位置する ABI に互換性の問題が生じる可能性があります。Ion ABI で問題が生じたとしても、通常のアプリやリリース済みのデバイスに直接の影響が生じることはありません。しかし、ベンダーがカーネルを新しいメジャー バージョンに移行する際には、変更された内容が Ion を呼び出すベンダーコードに影響する可能性があります。また、Android システムチームが Ion をステージング ツリーから移行してアップストリームで扱う際にも、ABI の互換性の問題が起こる可能性があります。

android-4.14 における変更

カーネル 4.12 では、Ion カーネルコードが大幅にリファクタリングされ、他のカーネル フレームワークと重複していた部分のクリーンアップと削除が行われました。その結果、従来の Ion ioctl の多くが不要となり、削除されました。

Ion client と Ion handle の削除

カーネル 4.12 より前のバージョンでは、/dev/ion を open することで Ion client が割り当てられていました。IOC_ION_ALLOC ioctl により新しいバッファが割り当てられ、それを Ion handle(opaque 型の整数型。割り当てられる Ion client にのみ有意な値)としてユーザー空間に返します。バッファをユーザー空間にマッピングする、もしくは他のプロセスと共有する際には、IOC_ION_SHARE ioctl により、Ion handle が dma-buf fd として再エクスポートされていました。

カーネル 4.12 では、IOC_ION_ALLOC ioctl により dma-buf fd が直接出力されます。中間処理として用意されていた Ion handle は、Ion handle の使用や生成を行うすべての ioctl と合わせて削除されました。dma-buf fd は特定の Ion client には結び付けられていないため、IOC_ION_SHARE ioctl が不要になり、Ion client のインフラストラクチャもすべて削除されています。

cache-coherency ioctl の追加

カーネル 4.12 より前のバージョンでは、ファイル ディスクリプタをメモリと同期させる ION_IOC_SYNC ioctl が提供されていました。ただ、この ioctl についてはドキュメントが十分に用意されておらず、柔軟性に欠けていたことから、多くのベンダーがキャッシュ メンテナンスを行うためのカスタム ioctl を実装していました。

カーネル 4.12 では、ION_IOC_SYNClinux/dma-buf.h で定義されている DMA_BUF_IOCTL_SYNC ioctl に置き換えられました。DMA_BUF_IOCTL_SYNC は CPU へのアクセスの開始と終了のたびに呼び出され、アクセスが読み取り、書き込み、両方のどれにあたるかがフラグを使って示されます。DMA_BUF_IOCTL_SYNCION_IOC_SYNC よりも複雑ですが、背後で動作するキャッシュ メンテナンス オペレーションについて、ユーザー空間でより詳細な操作が行えるようになります。

DMA_BUF_IOCTL_SYNC はカーネルの安定版 ABI であり、Ion による割り当ての有無にかかわらず、すべての dma-buf fd で使用できます。

android-4.12 以上へのベンダーコードの移行

Android システムチームは、ユーザー空間の client について、オープン コーディングで ioctl() を呼び出すのではなく、libion を使用するように強く推奨しています。Android 9 以降の libion では、実行時に Ion ABI が自動的に検出され、カーネル間の相違が顕在化しないような処理が行われます。しかし、カーネル 4.12 以降からは、ion_user_handle_t handle を生成または使用する libion 関数は機能しなくなりました。同等の操作は、次の dma-buf fd 関数によって置き換えられており、すべてのカーネル バージョンで同関数が利用できます。

従来の ion_user_handle_t 呼び出し 同等の dma-buf fd 呼び出し
ion_alloc(ion_fd, …, &buf_handle) ion_alloc_fd(ion_fd, ..., &buf_fd)
ion_share(ion_fd, buf_handle, &buf_fd) N/A(dma-buf fd ではこの呼び出しは不要です)
ion_map(ion_fd, buf_handle, ...) mmap(buf_fd, ...)
ion_free(ion_fd, buf_handle) close(buf_fd)
ion_import(ion_fd, buf_fd, &buf_handle) N/A(dma-buf fd ではこの呼び出しは不要です)
ion_sync_fd(ion_fd, buf_fd) If (ion_is_legacy(ion_fd))

ion_sync_fd(ion_fd, buf_fd);

else

ioctl(buf_fd, DMA_BUF_IOCTL_SYNC, ...);

カーネル内の client については、カーネル向け API のエクスポートが Ion では行われなくなったことから、ion_import_dma_buf_fd() でカーネル内 Ion カーネル API を使用していたドライバを、dma_buf_get()カーネル内 dma-buf API を使用するように変換する必要があります。

将来的な Ion ABI の互換性の問題

Ion がステージング ツリーに移行するまでに、今後リリースされるカーネルで Ion ABI の互換性の問題が再び生じる可能性があります。Android システムチームでは、こうした変更が Android の次期バージョンで動作するデバイスに影響するとは想定していません。ただし、次期バージョンより後の Android においては、デバイスに影響をおよぼす可能性があります。

たとえば、アップストリームのコミュニティでは、単一の /dev/ion ノードをヒープごとの複数のノードに分割する(例: /dev/ion/heap0)ことが提案されており、この場合、デバイスでヒープごとに異なる SELinux ポリシーが適用できるようになります。このような変更が将来のカーネル リリースで実装されると、Ion ABI に互換性の問題が生じる恐れがあります。