Rustで作るLinuxデバイスドライバ
だれでもできるシリーズとして、Rustでカーネルモジュールを実装しながら学んできましたが(役に立たないキャラクタデバイスドライバなど)、そろそろ実際に使える機能を実装したいころですよね!
今回は、筆者が実装したネットワークPHYドライバが、Rustで実装された初めてのデバイスドライバとしてLinuxカーネルに採用された話を紹介します。
誤解:LinuxカーネルがRustをサポート
「LinuxカーネルがRustをサポートした」というニュースを見て、Rustのコードがどんどん採用されていると誤解している方もいるようです。このニュースは、「LinuxカーネルをRustでも書けるようになりましたが、実際に何かを実装するかどうかは未定」という意味です。Linuxカーネルは、メモリマネージメント、ネットワーク、暗号など、数多くのサブシステムで構成されており、それぞれのメンテナが、コードの採否を判断します。これまで、Rustで実装された様々なカーネルモジュールが提案されてきましたが、採用されたものはありませんでした。Rustのコードはマージしないと言っているサブシステムメンテナもいます。
LinuxカーネルはC言語で実装しても、新しい機能の提案の大半が拒絶される世界です。採用されることを目指すのであれば、Rustで実装する場合も、機能の選択が大切です。長老に、消えていった思い出(採用されなかった機能)について聞きましょう。
ネットワークPHYデバイスドライバとは
ネットワークPHYデバイスは、通信に使われるハードウェアの1つの機能です。イーサネットネットのネットワークカードの多くは、下の図のようにコントローラとPHYのチップから構成されます。PHYは、ケーブル側のアナログ信号コントローラ側のデジタル信号を変換します。
筆者のコンピュータは、Realtek社のネットワークカードを搭載していますが、このカードに搭載されたコントローラチップ用のr8169デバイスドライバがロードされると、自動的にPHYチップ用のrealtekデバイスドライバもロードされて、2つのカーネルモジュールが使われます。
ネットワークPHYデバイスドライバは、車載イーサネットスイッチなど小型ネットワークスイッチハードウェアでも利用されています。
Rust API:bindings vs. abstractions
Rustのデバイスドライバに取り組むことで、意識の高いプログラマであることをアピールしてきましたが、結局のところ、Rustのデバイスドライバは、C言語のデバイスドライバと同様、C言語用のカーネルAPIを呼び出します。
Rustは、C言語の関数を呼び出す機能をサポートしており、この機能を使うAPIはバインディングAPIと呼ばれます。例えば、カーネルのMutexのバインディングAPIを使ったコードは以下のようになります。
bindings::mutex_lock(mutex_object);
// fiddle around with shared data
bindings::mutex_unlock(mutex_object);
Rustが保証するメモリ安全性などの利点がないし、C言語でよさそうですね!
Mutexを解放する関数を呼び忘れてもコンパイルは成功!Mutex共有データが別に管理されているので、Mutexをロックしなくても共有データにアクセスできる!
一方、抽象化 APIは、バインディングAPIを内部で使って、Rustが保証する安全性を実現するAPIを提供します。例えば、Mutexの抽象化APIは、コンパイル時に、共有データへの排他的なアクセスを強制するAPIになっています。
{
let mut shared_data = mutex.lock();
// fiddle around with shared data
}
// the mutex is automatically released here
Cが実現できない安全性を求めてRustがサポートされており、安全な抽象化APIだけで実装されたデバイスドライバのみを採用する方針となっています。
現在、カーネルは、ロックなど一部の基本的なAPIの抽象化はサポートしていますが、サブシステムの抽象化APIは何も実装されていません。もし、カーネルへの採用を目指してカーネルモジュールを実装するのであれば、抽象化APIも実装する必要があります。筆者は、ネットワークPHYデバイスのサブシステム(PHYLIB)の抽象化 APIと、その抽象化APIを使ったASIX社のPHYチップ(AX88796B)用のドライバを実装しました。
デバイスドライバは、Rustがちょっと好きぐらいの気持ちがあれば実装できますが、抽象化APIの実装では、Rustへの揺るがぬ信仰心が求められます。写経など、Rustへの信仰を深めるところから始めましょう。
まとめ
カーネルモジュールがロードできただけで興奮していた、だれでもできるシリーズですが、ついに、Rustで実際に使えるデバイスドライバを実装することができました。
Rustで実装された初のデバイスドライバが採用されたわけですが、まだまだ、LinuxカーネルやRust自体の機能追加が必要です。例えば、Rustがカーネルが多用しているCのヘッダファイルのinline関数を直接扱えるようにならないと、性能が求められるデバイスドライバの実装は難しいと思います。
NTT研究所では、低レイヤの開発に興味がある仲間を募集中です。連絡お待ちしています。