Living with Modules

事の発端は、去年6月にオタワで開催された「Kernel Summit」だった。Rusty Russellは、”Living with Modules”というタイトルのセッションをそこで行った。今回は前回に引き続いてカーネル2.6で新しくローダブル・モジュールの構造とインタフェイスが変えられた経緯について紹介する。

Rusty Russellは、ローダブル・モジュールの唯一の目的は、カーネルをブートしたときにはなかったハードウェアを加える事だけだという主張からセッションを始めた。しかし現実には、そのようにはなっていない。Rustyによれば、ローダブル・モジュールに関する現状(カーネル2.4と2.5の初期まで)の問題は、3つのタイプがある。

  • 実装上の問題 (implementation problems)
  • 初期化の問題 (initialization problems)
  • 削除に関する問題 (removal problems)

モジュール実装上の問題

実装上の問題はほとんどの場合、乏しいインタフェイスと、出来が悪いサブシステムから来ていて、これは修正されることができる問題である。Rustyは、特にinter_module呼び出しを、インターフェースが乏しく、また正しく使うのが不可能であると指摘した。(Keith Owensは、カーネル2.0から長い間modutilsとインタフェイスのメンテナンスを担当しているため、Rustyの矛先はその場にいないKeithに向けられていたようだった。)

モジュール・リンクは改善されることができた。Rustyは、リンク・コードをinsmodプログラムからカーネル内に移すことによって行った改善策を示した。その結果、実際にカーネル・コードの総量は落ちた。カーネルとユーザー空間の環境の違いに対するやり取りがないとき、多くの場合は(コードの移行は)思ったよりも簡単らしい。付加されたボーナスとして、彼は初期化コードを捨てること(ローダブル・モジュールのために全くこれまでされなかった事)を実装した。

Rustyが実装しなかったものの一つは、modversions(バイナリのモジュールが複数のカーネル・バージョンにロードされるのを許可するためにデータ構造を合わせること)であった。modversionsは見苦しく、カーネル開発者はあまり使わない機能であるが、Alan Coxその他の人が「カーネルが旧来のmodversionsをサポートしないならば、カスタマー・サポートは悪夢である」と指摘したため、無くならない可能性は強い。

モジュール初期化の問題

次にRustyはモジュール初期化の問題を指摘した。ブート時に動作では、/procインタフェイスへのアクセスを取り扱うことが準備できる前に、モジュールが/procインタフェイスを用意するのを見ることはよくある。この種の競合状態はユーザーには影響がないが、それは存在する。

モジュール初期化コードのエラー検査は不十分である。乏しいインタフェイスは、この部分で指摘される。初期化の間にエラーから元に戻ることは、ほとんど不可能である。/procで2つのファイルを作成するドライバの場合、ファイルの作成の間にエラーが発生したらば、最初のものは取り除かれなければならない。しかしユーザがすでに最初のファイルを開いたという可能性が存在するため、安全な解決策が無い。

いくつかの解決案が、初期化問題にはある。第一は、あらゆるカーネル登録インタフェイスを「reserv」と「use」の2つのフェーズに分けることである。「reserv」機能は必要とされた資源をモジュールに割り当てるが、インタフェイスはカーネルやユーザー空間に利用できるようにしない。「reserv」の全てが成功したとき、モジュールは問題なく資源を利用できるようにするために「use」機能を呼び出すようにすることである。

他の方法は、モジュール・ポインタを各登録インタフェイスに加えることである。登録された資源のどんな使用においても、モジュールで参照カウントを増加させる。その結果、早まった削除を防ぐ。たとえモジュール初期化が失敗するとしても、参照カウントがゼロに落ちるまでモジュールはアンロードされない。各モジュールの初期化コードを審査してこのような変更を行えば、モジュール・ロードは安全になる。

モジュールの削除問題

モジュール削除は、より難しい問題である。この問題は次の懸念事項を含んでいる。

  • モジュールの削除を強制する方法がない。
  • いくつかのモジュールは実際のところ、まったく取り外しできない。

モジュール削除操作はその戻り値が無効なため、失敗ステータスを返すことができない。また最大の問題は、モジュール削除をしようとする場合の、多くの競合状態である。それがいつ本当にシステムからモジュールを取り外すべきなのかを、確実に知ることは難しい。

削除問題を扱ういくつかの方法がある。一つ目は、ロックと参照カウントをモジュールに関連する操作ごとに全て加えることである。モジュール参照カウントを増加させようとするものすべては、操作の状態をチェックしなければならない。そして増加が失敗する(モジュールが削除されているならば起こりえる)ならば、モジュールを使用させない。この変更によれば、すべての参照カウントによるパフォーマンス・オーバーヘッドは顕著になるだろう。

第二のオプションは、2-ステージ・モジュール削除プロセスである。最初のステージ「cleanup」は全てのインタフェイスをモジュールへ移す。そして、モジュール参照カウントが増加しないことを保証する。「cleanup」ステップは失敗することがありえる、その場合にはモジュールはアンロードされない。成功したクリーンアップの後の「destroy」フェーズで、「それが安全であるならば」実際にシステムからモジュールを取り外す。

しかし「それが安全であるならば」は、簡単なもの以外にもある。モジュールの参照カウントがゼロに落ちるまで、カーネルは待たなければならない。少なくともモジュールはその参照カウントを減少させた後に「return」命令を実行しなければならない。このあたりの問題を回避するために、(かつて提供された)オリジナルのcleanup/destroyパッチは、システムのあらゆるプロセッサが一度スケジュールするのを待った。それは、プロセッサが関連づけられたモジュールのコードを実行していないことを保証した。少なくとも、プリエンプティブなカーネル・パッチが併合されるまでは、働いていた。プリエンプティブなカーネルで、本当に安全な唯一の方法は、システムのあらゆるプロセスが一度CPUを受け渡すまで、待つことである。

このシステムには利点がある。それは、安全に動作させる事ができる点である。モジュールの強制的なアンロードが可能になる。各モジュールは、自身の参照カウントを管理できる。しかしそれは複雑である。

第3のアプローチは、より単純である。単にモジュール削除が無いようにすることである。モジュール削除は任意選択機構(結局カーネル・コードをデバッグすることに最も役立つ)として残る。しかしデフォルトでは、モジュールは永遠にロードされたままである。「結局は、カーネル・モジュールはそんなに多くのスペースをとらず、メモリは安い」とRustyは言った。モジュール削除問題の最適化は、我々がもはや必要としない問題であるかもしれない。例えば古いPCMCIAドライバのような、残りのいくつかの問題がある。それらは、モジュールが削除されなければ、正しく後片付けをしない。しかし全体としては、このオプションの選択は簡単であり、そして多くの余計なコードを開発する必要が無くなる。

続きは...

このRustyのセッションの後、 LKML(Linux Kernel Mailing List)ではカーネル2.5への導入を目指した新しいモジュール・フォーマットに関する議論が本格的に行われるようになり、 Roman Zippelが積極的に実装をすすめて多くの提案をしている。そのうちいくつかは採用され、またRusty Russellが、modutilsに代わるmodule-init-toolsを開発しているのは前回紹介した通りである。Rusty Russellは、Linux Kernel Conferenceで近く来日予定なので、新しいmoduleフォーマットに関する疑問や意見がある方は、この機会にぶつけてみるとよいだろう。