5月14日、Collabora が「Building Tyr in Rust: CSF architecture and booting the MCU」と題した記事を公開した。
Mali GPUといえば、Android端末の多くで採用されているモバイル向けGPUとして知られているが、長らくプロプライエタリドライバーに依存していた。しかし近年、オープンソース化の流れが加速しており、Collaboraが開発するRust製ドライバー「Tyr」が大きな注目を集めている。特に新世代のMali CSF(Command Stream Frontend)アーキテクチャでは、従来のJob Managerアーキテクチャから大幅に変更され、ハードウェアレベルでのスケジューリング機能を導入した革新的な設計となっている。
ハードウェアスケジューリングを実現するCSFアーキテクチャ
Mali CSFアーキテクチャの最も革新的な要素は、Arm Cortex-M7ベースのマイクロコントローラーユニット(MCU)を中核とする設計だ。このMCUはホストCPUとGPU実行エンジンの間に位置し、Armが提供する専用ファームウェアを実行する。
従来のMali Job Managerアーキテクチャでは、すべてのスケジューリングがホストCPU側のドライバーで行われていた。対してCSFでは、MCUがハードウェアレベルでのスケジューリング機能を提供する。ただし、これは完全に自動化されたスケジューリングではない。KMD(kernel-mode driver)は依然として依存関係の追跡、VM・アドレス空間の設定、ユーザー空間への完了通知などの重要な役割を担っている。
この変更により、GPUワークロードの並列実行効率が大幅に向上し、特に複数のアプリケーションが同時にGPUリソースを使用する現代のモバイル環境において、レスポンス性能の改善が期待される。
2層構造によるスケジューリング設計
CSFアーキテクチャでは、スケジューリング単位としてCSG(Command Stream Group)とCS(Command Stream)の階層構造を採用している。これは現代のGPUワークロードの複雑さに対応するための設計だ。
VulkanアプリケーションがVkQueueを作成する際、panvk(Vulkan UMD)は単一のキューではなく、用途別に分けられた複数のキューを持つグループを作成する:
struct drm_panthor_queue_create qc[] = {
[PANVK_SUBQUEUE_VERTEX_TILER] = {
.priority = 1,
.ringbuf_size = 64 * 1024,
},
[PANVK_SUBQUEUE_FRAGMENT] = {
.priority = 1,
.ringbuf_size = 64 * 1024,
},
[PANVK_SUBQUEUE_COMPUTE] = {
.priority = 1,
.ringbuf_size = 64 * 1024,
},
};
この設計により、以下の2層スケジューリングが実現される:
- Panthor/Tyr Group → firmware CSG slot:グループレベルでの優先度制御
- Panthor/Tyr Queue → firmware CS slot inside that CSG:グループ内キューの調整
MCUブートプロセス:CSF機能の前提条件
すべてのCSF機能を利用する前に、TyrはMCUファームウェアを起動する必要がある。このプロセスは以下の段階を経る:
- ファームウェアバイナリの解析:コードとデータセクションを特定
- メモリ割り当て:セクション用のバッキングメモリを確保
- アドレスマッピング:MCUが期待するアドレスにセクションをマップ
- MCU起動:デバイスの電源投入とエントリーポイントからの実行開始
MCUが起動すると、KMDはファームウェア可視階層を発見できる。この階層のルートはGlobalインターフェースであり、そこからCSGインターフェースの数、各CSGが公開する内容、各CSG配下のCSインターフェースの数を把握する。
CPU-MCU間通信:共有メモリによる制御
CPUとMCUの通信は、ファームウェアインターフェースが定義する共有メモリ領域を通じて行われる。各インターフェースは3つの役割別領域に分かれている:
- Control領域:ファームウェアが提供する発見状態(バージョン、機能ビット、アドレス情報など)
- Input領域:ホストが書き込み可能な要求・設定値の領域
- Output領域:ファームウェアが書き込む確認応答・ステータス・進行情報の領域
通常のワークフローは以下の通りだ:
- KMDがControlを読み取りインターフェースを発見
- KMDがInputフィールドとキューリングバッファを書き込み
- KMDがドアベルを鳴らす
- MCUが要求を処理
- MCUがOutputフィールドを書き込み
- CPUが反応すべき時にMCUが割り込みを発生
エンドツーエンドのワーク投入プロセス
実際のGPUワークはDRM_IOCTL_PANTHOR_GROUP_SUBMITを通じて投入される:
struct drm_panthor_queue_submit {
__u32 queue_index;
__u32 stream_size;
__u64 stream_addr;
__u32 latest_flush;
__u32 pad;
struct drm_panthor_obj_array syncs;
};
完全な投入プロセスは以下の段階を経る:
- UMDがGPU可視メモリにコマンドストリームを構築
- UMDがグループとそのキューの一つに対してGROUP_SUBMITを呼び出し
- KMDが依存関係をチェックし、投入を完了追跡と関連付け
- キューがワークを受け入れ可能になると、KMDがユーザー提供コマンドストリームにジャンプする固定命令セットを追加
- KMDがドアベルを鳴らしてMCUに新しいワークを通知
- ファームウェアがリングバッファを処理し、ワークの進行に応じて出力状態を更新
- KMDが結果のイベントまたは割り込みを処理し、完了をユーザー空間に通知
この分担により、KMDが依存関係・分離・Linux同期オブジェクトを所有し、ファームウェアが優先度に基づく常駐グループのスケジューリングとコマンドストリームプロトコルの低レベル処理を担当する構造が実現されている。
Rust実装の意義とモバイルGPUエコシステムへの影響
TyrがRustで実装されている点も見逃せない。従来のGPUドライバーはC/C++で書かれることが多かったが、Rustのメモリセーフティ機能により、ドライバーレベルでのメモリ破壊やセキュリティホールのリスクを大幅に軽減できる。特に、カーネル空間で動作するドライバーにおいて、この安全性の向上は重要な意味を持つ。
また、Mali GPUのオープンソースドライバー開発は、モバイルGPUエコシステムの多様化において重要な意味を持つ。これまでArmのプロプライエタリドライバーに依存していたデバイスメーカーやOSベンダーにとって、代替選択肢の存在は技術的自由度の向上につながる。Mesa 3Dプロジェクトの一部として開発が進められていることも、長期的な保守性とコミュニティサポートの観点から心強い要素だ。
詳細はBuilding Tyr in Rust: CSF architecture and booting the MCUを参照していただきたい。