しばらく Linux kernel の動きを追えていなかったので、手元の torvalds/linux.git を最新化して master を眺め直した。この記事では、その中でも特に気になった Rust in Linux の動きを整理する。
確認した時点の master は以下。
4549871118cf Linux 7.1-rc7
つまり、この記事は Linux 7.1-rc7 時点の話である。rc7 なので、現在の master そのものはリリース直前の修正フェーズに近い。一方で、7.1 の merge window で入った Rust 関連の変更を見ると、Rust in Linux は「実験的に置いてあるもの」から「各サブシステムで使うための基盤を地道に整えているもの」へ進んでいる印象を受けた。
7.1 サイクルの Rust 関連 merge
Rust 関連の大きな merge は以下。
26ff969926a0 Merge tag 'rust-7.1'
この merge では 79 commits が入っている。内容を大きく分けると、次のようなものだった。
- Rust toolchain 要件の更新
- Kbuild / bindgen 周りの整理
- Rust kernel crate の API 拡充
- DMA、DRM/GPU、workqueue、hrtimer など driver 向け API の拡充
- Rust Binder 周辺の調整
- rust-analyzer 向け生成スクリプトの改善
派手な「Rust 製 driver が一気に増えた」という話ではないが、kernel 内で Rust を使うための土台が広がっている。
最低 Rust バージョンが 1.85.0 に
まず目立つのは、最低 Rust バージョンの更新だ。
7.1 サイクルでは、Rust の minimum supported version が 1.85.0 に、bindgen の最低バージョンが 0.71.1 に上がった。
これは単なるバージョン上げではなく、今後は Debian Stable の Rust バージョンを基準にしていく、という方針に沿ったものと説明されている。kernel 開発では「新しすぎる compiler に依存しすぎない」ことが重要なので、ディストリビューションで現実的に入手できる toolchain を基準にする、という判断だと思う。
この更新により、古い Rust / bindgen 向けの workaround が削除され、Rust 側のコードもいくつか簡素化されている。
例えばコミットログには次のような変更が並んでいる。
rust: bump `bindgen` minimum supported version to 0.71.1
rust: kbuild: remove `feature(...)`s that are now stable
rust: alloc: simplify with `NonNull::add()` now that it is stable
rust: remove `RUSTC_HAS_COERCE_POINTEE` and simplify code
Rust in Linux は、言語機能の進化と kernel 側の保守性のバランスを取りながら進んでいる。
CONFIG_RUST_INLINE_HELPERS
もうひとつ面白いのが、実験的な CONFIG_RUST_INLINE_HELPERS だ。
これは C 側の helper を Rust 側に inline するための Kconfig option で、説明を見る限り、Rust と C の境界にある小さな helper 呼び出しの overhead を減らす狙いがある。
ざっくり言うと、LTO のように全体を大きく最適化するのではなく、Rust helper 周辺に限定して llvm-link を使い、局所的に最適化する仕組みのようだ。null block driver の workload で 2% 程度の改善が見られた、という説明もある。
Rust in Linux は安全性の話として語られがちだが、kernel に入る以上、性能面の詰めも避けられない。この option は、その現実的な調整の一例に見える。
kernel crate の API が広がっている
7.1 の Rust 更新で特に重要に見えるのは、rust/kernel 以下の API 拡充だ。
目についたものを挙げると、次のような変更がある。
rust: add `const_assert!` macro
rust: sizes: add SizeConstants trait for device address space constants
rust: dma: add generalized container for types other than slices
rust: drm: gem: shmem: Add DRM shmem helper abstraction
rust: gpu: Add GPU buddy allocator bindings
rust: io: introduce `write_reg` and `LocatedRegister`
rust: time: make ClockSource unsafe trait
rust/time: Add Delta::from_nanos()
このあたりは、Rust で kernel module を書くための「便利関数」以上の意味がある。DMA、DRM/GPU、MMIO register access、timekeeping などは、実際の driver を書こうとすると必ず必要になる領域だからだ。
const_assert!
const_assert! は、compile-time assertion のための macro だ。
既存の static_assert! より柔軟で、generic parameter を含む関数や impl の中でも使える。merge message では、次のような例が示されている。
fn f<const N: usize>() {
const_assert!(N > 1);
}
kernel code では「この条件が崩れたら build 時に止めたい」という場面が多い。C 側にも BUILD_BUG_ON() などの文化があるが、それに対応する Rust 側の表現が整理されてきている。
SizeConstants
SizeConstants trait も地味に便利そうだ。
これは SZ_1M のようなサイズ定数を、u32 や u64 などの型付きで扱えるようにするもの。たとえば device address space の幅が 32-bit か 64-bit かで適切な型を使いたい場面がある。
merge message では、次のような例が挙げられている。
let gpu_heap = 14 * u64::SZ_1M;
let mmio_window = u32::SZ_16M;
kernel driver では cast が積み重なると、review も debugging もつらくなる。型を使って意図を表せるのは Rust らしい改善だと思う。
DMA / DRM / GPU 周りが目立つ
今回の差分で個人的に一番「実用段階に近づいている」と感じたのは、DMA、DRM、GPU 周りの更新だ。
例えば Rust 側には次のようなファイルが大きく更新・追加されている。
rust/kernel/dma.rs
rust/kernel/drm/gem/shmem.rs
rust/kernel/gpu/buddy.rs
rust/kernel/io/register.rs
rust/kernel/interop/list.rs
GPU driver や DRM driver を Rust で書くには、単に Rust syntax が kernel で使えるだけでは足りない。DMA buffer、GEM object、register access、既存 C 構造体との連携など、C の kernel subsystem と自然に接続できる API が必要になる。
今回の変更は、その周辺をかなり意識しているように見える。
Rust Binder も続いている
Android Binder の Rust 実装周辺も動いている。
差分には以下のようなものがある。
drivers/android/binder/rust_binder_main.rs
drivers/android/binder/transaction.rs
drivers/android/binder/thread.rs
rust_binder: introduce TransactionInfo
Rust Binder は、Rust in Linux の中でも実際の subsystem に近い場所で進んでいる取り組みのひとつだ。今回も大きな新機能というより、実装を前に進めるための整理や補助的な変更が中心に見える。
「Rust で全部書き直す」話ではない
Rust in Linux を見るときに注意したいのは、これは C を一気に Rust へ置き換える話ではない、という点だ。
現時点で起きているのは、むしろ次のようなことだと思う。
- C kernel と共存する Rust 用基盤を作る
- driver author が必要とする API を少しずつ増やす
- Rust 側でも kernel らしい build-time check や ownership model を整える
- C helper との境界を性能面でも詰める
- rust-analyzer など開発体験も改善する
つまり、Rust in Linux は「言語を入れた」という段階を越えて、「kernel の流儀に合う形で Rust を使えるようにする」段階にいる。
まとめ
Linux 7.1-rc7 時点の Rust in Linux を見ると、目立つポイントは以下だ。
- 最低 Rust バージョンが 1.85.0 に上がった
-
bindgenの最低バージョンが 0.71.1 に上がった - 古い toolchain 向け workaround が減り、コードが整理された
-
CONFIG_RUST_INLINE_HELPERSで C helper 呼び出しの最適化が試されている -
const_assert!やSizeConstantsなど、kernel crate の基本 API が増えた - DMA、DRM/GPU、MMIO register access、workqueue、hrtimer など driver 向け API が拡充された
- Rust Binder も継続して前進している
Rust in Linux は、まだ「誰でも普通に Rust で kernel driver を書ける」という段階ではないと思う。しかし、7.1 サイクルの変更を見る限り、基盤は確実に厚くなっている。
特に DMA、DRM/GPU、register access まわりが伸びているのは、今後 Rust 製 driver が増えていくうえで重要な兆候に見える。派手なニュースではないが、kernel 開発としてはこういう地道な API 整備こそが本筋なのだと思う。
確認に使ったコマンド
git log -1 --oneline --decorate
git show -s --format=fuller 26ff969926a0
git log --oneline --no-merges v7.0..v7.1-rc7 -- rust
git diff --stat v7.0..v7.1-rc7 -- rust Documentation/rust drivers/android/binder