Webでの公開にあたりhtml化を含め一部の表記は見直していますが、記事の内容は当時のままです。対応する内容であり、最新のNetBSDバージョンにおいては対応しない部分も多々含まれます。元の文章が学術論文であるとはいえ、正直言って訳の質としては雑誌記事として掲載できるレベルには達していなかったという反省部分もありますが、そもそもがデバイスドライバを実装できるレベルの読者を対象にした内容ですし、このまま埋めておくのももったいないので今回適当に修正およびhtml化して上げることにしました。移植性の高いカーネルを実装する際の課題のうちの一つに、意味的に類似してはいるが多くの場合機種依存となる実装を持つ操作に対して、適切な抽象化を作り出すことが挙げられる。これは、PCIバスのような一般的なアーキテクチャ上の特徴を共有する現代の計算機においては特に重要である。本論文は、NetBSD/alphaとNetBSD/i386カーネルにおいて、機種に依存しないDMAマッピングの抽象化が必要である理由、そのような抽象化のための設計考察、そしてこの抽象化の実装について解説する。序文NetBSDは、高い移植性を持つ現代的なUNIXライクオペレーティングシステムであり、現在9つのプロセッサアーキテクチャを含む18のプラットホームで動作している。PCIバスを共有している。PCIデバイスのためのデバイスドライバを共有するためには、バス空間の詳細を隠す抽象化を考案する必要がある。ここで隠されなければならない詳細は、2つの階層に分類することができる。一つはバス上のデバイスに対するCPUのアクセス(bus_space)であり、もう一つはホストメモリに対するデバイスのアクセス(bus_dma)である。ここでは、我々は後者を議論する。bus_spaceは、それ自体が複雑な内容であり、本論文で扱う範囲を超えている。DMAという視点において、核となるデバイスドライバーから隠されるべき詳細として2つの主要な階層がある。第1の階層(ホスト詳細)は、システムメモリの物理的マッピング(とそのようなマッピングの結果として使用されるDMA機構)とキャッシュに対する処置といった問題を扱う。第2の階層(バス詳細)は、デバイスが接続されるバスに固有な仕様や制約(例えばDMA転送やアドレス範囲の制約)に関連した問題を扱う。R.ThorpeはMRJテクノロジー・ソリューション社の従業員である。この作業はNASA契約NAS2-14303によって資金助成されている。IIを含む、386クラス及びその上位のプロセッサのすべてを指すのに用いられる。上で実例として挙げたプラットホームでは、DMAの実行において少なくとも3つの異なる機構が用いられる。第1はi386プラットホームによって使われる。Get=見えるものをそのまま得る)」として説明される。デバイスがDMA転送を実行する際に用いるアドレスは、ホストCPUが当該のメモリ位置にアクセスするのに使うものと同じアドレスである。DMA第2の機構はAlphaによって使われるが、これは第1の機構に非常に類似している。ホストCPUが当該のメモリー位置にアクセスするために使うアドレスは、DMAのためにデバイスバス上にダイレクトマップされたホストメモリのベースアドレスからのオフセット値である。ダイレクトマップDMA第3の機構はスキャッタギャザマップDMAであり、DMAアドレスからホストメモリの物理アドレスへの変換を行うMMUを使用する。この機構もAlphaにおいて用いられる。PCIデバイスにおいてサポートされる32ビットアドレス空間よりもはるかに大きなアドレス空間をサポートする必要があるためである。スキャッタ・ギャザ・マップDMA上記の第2および第3のDMA機構は、AlphaではDMAウィンドウを使用することにより同じように扱われる。DMAウィンドウを少なくとも2つ備えている。各々のウィンドウは、ダイレクトマップDMAもしくはスキャッタギャザマップDMAのために設定される。これらのウィンドウは、実行されるDMA転送のタイプ、バスタイプおよびアクセスされるホストメモリの物理アドレスの範囲により選択される。PCI以外のバスにもあてはまる。ホストシステムのキャッシュの管理方法も、DMAの実行を行うデバイスにとって重要である。いくつかのシステムでは、キャッシュコヒレントDMA[訳注1]が可能である。そのようなシステムの上では、キャッシュは多くの場合ライトスルー(すなわちストアデータはキャッシュとホストメモリの両方に書き込まれる)であるか、ダーティーなキャッシュラインが存在するメモリへのアクセスを検出することができる特殊なスヌープロジック(アクセス検出機構)を備えている(それによりキャッシュは自動的にフラッシュされる)。他のいくつかのシステムでは、キャッシュコヒレントDMAが不可能である。これらのシステムでは、メモリからデバイスへのDMA転送の前のデータキャッシュのフラッシュをソフトウェアが明示的に行う必要があり、デバイスからメモリへのDMAの前にも転送により内容が古くなるキャッシュラインの無効化についても同様にソフトウェアが明示的に行う必要がある。[訳注1]DMA転送においてキャッシュとホストメモリとの間の一貫性(coherency)が保たれることが保証されていること。詳細を隠すことに加えて、複数のバスに接続されるデバイスにとってはできるだけ多くのデバイスドライバコードを共有することが望ましい。アダプタシリーズが挙げられる。デバイスの検出や割り込みの初期化など各バスに固有の部分はいくらか存在するが、これらの一連のデバイスを動作させるための大部分のコードはそれぞれのバスのデバイスにおいても同一である。アダプタシリーズは、バスマスタと呼ばれるものの典型例である。転送においてすべてのバスハンドシェイクとホストメモリアクセスを自身で実行する。転送においてそれ以外の第三者を必要としない。アドレスをバスのアドレスに送出し、バスのフェッチまたはストア操作を実行し、アドレスをインクリメントし......といった動作を転送が終了するまで行う。デバイスはバスアドレスの信号線を使うので、デバイスがアクセスすることができるホストの物理アドレスの範囲はそのアドレスのビット数によって制限される。32ビットアーキテクチャ(例えばi386)の物理アドレス空間全体にアクセスすることができるだろう。24ビットのアドレスしか持たない。16Mバイトしかないということを意味する。bouncing)[訳注2]として知られているテクニックである。この手法では、デバイスがアクセス可能な物理アドレス範囲に位置する別のメモリ領域が必要になる。これはバウンスバッファと呼ばれているものである。DMA操作が開始される。CPUがデータをバウンスバッファからコピーする。実装は簡単であるが、DMAバウンスはアドレス制限問題を解決するための最適手段とは言えない。DMAアドレスに変換するのにスキャッタギャザマップDMAを使用することができる。この解決策は、データのコピーが不要となるため性能も良い場合が多く、かつメモリ使用量の観点においても有利である。DMAバウンスの詳細な情報を記述することは望ましくない。DMA機構に関与しない、一貫性のあるインターフェースを提供する抽象化が必要である。[訳注2]「bouncing」を強いて日本語にすれば「中継」となるが、ここではDMAにおける用語としてそのまま「バウンス」という訳語を使う。設計考察ホストおよびバスの詳細を隠すことは実際に非常に簡単である。DMA機構の扱いは取るに足らない。マシン依存部のコード層において保持される状態を用いれば、スキャッタギャザマップDMAを扱うことも非常に容易である。キャッシュの存在とそれに対する処置についても、4つの一組の「同期(sync)」操作で扱うのが簡単である。キャッシュを適切に処理することができれば、DMAバウンスもそれをDMAコヒレントでないキャッシュと同様捉えれば概念的には簡単である。残念なことに、これらの操作は個別に実行するのが非常に簡単であるにもかかわらず、従来のカーネルはこれらの操作に十分な抽象化されたインターフェースを提供していない。これは、従来のカーネルにおけるデバイスドライバは明示的にそれぞれのケースを扱わなければならないことを意味する。これらの操作に対するインターフェースに加えて、包括的DMAフレームワークは、データバッファ構造とDMA-safeメモリ操作についても考慮する必要がある。3つの大きく異なる構造体を持つ。第1は、仮想空間における単純な線形バッファである。これはたとえばファイルシステムバッファキャッシュを実装するのに用いられるデータ領域や、汎用カーネルメモリーアロケータによって割り当てられる種々のバッファである。プロセス間通信やネットワーキングを実装するコードに使われる。それらの構造(チェーンで1つに連結される小さなバッファ群)はメモリ断片化を減らし、パケットヘッダを付加することを容易にする。この構造体は、カーネルアドレス空間または特定のプロセスのアドレス空間にソフトウェアスキャッタギャザを実装している。システムコールにより使用される。デバイスドライバは複数の単純なリニアバッファの集合として2つのより複雑なバッファ構造を扱うことも可能であるが、これはソースコードメンテナンスの観点から望ましくない。エラー処理の観点から、これらのデータバッファ構造を扱うコードは、複雑なものになる可能性がある。カーネルアドレス空間にマップされたメモリに対するDMA転送という要求に加え、ユーザープロセスにおいてもプロセスアドレス空間にマップされたメモリ領域にデバイスが直接DMA転送を行う手段を提供できるよう最適化されたI/Oインターフェースを実装することも現代的なOSでは一般的である。この機能はキャラクタデバイスのI/Oに対してはユーザーバッファをカーネルアドレス空間に二重にマッピングすることで部分的に提供されているが、このインターフェースは十分に一般的とは言えず、またカーネルリソースも消費する。構造にいくぶん関連がある。いくつかのアプリケーションにおいては、リニアバッファのような別のデータ構造を使うほうが望ましいだろう。これを実装するために、DMAマッピングのフレームワークは、各プロセスの仮想メモリの構造体に対するアクセス手段を備えていなければならない。いずれのアドレス空間にもマップされないバッファに対するDMA転送もおそらく必要であろう。わかりやすい例としては、スクリーンキャプチャデバイスである。多くの場合、ビデオ画像をキャプチャするこれらのデバイスは、キャプチャした画像データを記憶するために物理的に連続した巨大なメモリ領域を必要とする。いくつかのアーキテクチャでは、仮想アドレス空間のマッピングは重い処理となる。アプリケーションは、デバイスに大きいバッファを提供してデバイスが連続的にバッファを更新するのを許可し、ある一定の期間のみバッファの一部の範囲のマップだけを要求するかもしれない。バッファ全体が仮想アドレス空間にマップされる必要はないので、DMAフレームワークはDMA転送において未加工(raw)[訳注3]の、マップされないバッファを使うためのインターフェースを提供する必要がある。[訳注3]ここでは「raw」という単語に対して「未加工の」という訳語を当てることにする。包括的DMAフレームワークは、いくつかのメモリ操作機能についても提供する必要がある。これらのうち最も明らかなものは、DMA-safeメモリを割り当てる(そして解放する)手段である。用語「DMA-safe」は、メモリが持つ一連の属性を述べる手段である。DMA-safeメモリはバスの制約条件においてもそのアドレスが指定可能でなければならない。DMA-safeメモリは前述の条件下で、かつ呼び出し元によって指定される物理的セグメントの数を超えないように割り当てられなければならない[注3]。カーネルがDMA-safeメモリにアクセスするために、このメモリをカーネル仮想記憶空間にマップする手段が必要である。これは、1つの例外はあるがかなり簡単な操作である。キャッシュコヒレントDMAを備えていないプラットホームでは、キャッシュフラッシュは、非常に高価である。メモリの仮想空間へのマップをキャッシュ無効と指定することや、キャッシュ無効のダイレクトマップアドレスセグメントを通して物理メモリをアクセスすることが可能な場合もある。これらの状況に対応するために、このメモリの使用者が高価なデータキャッシュフラッシュを避けたいということを示すヒントをメモリマッピング関数に対して提供することができる。プロセスアドレス空間に対する最適化されたI/Oを容易にするため、プロセスにDMA-safeメモリ領域をマップする手段を提供することが必要である。エントリーポイントを経由して行う方法である。したがって、DMAマップのフレームワークは、VMシステムのデバイスページャ[注4]とやりとりするための手段を持たなければならない。完全なDMAフレームワークの設計においては、これらすべての要求事項について考慮をする必要がある。可能であればフレームワークは意味的に同じとなる操作や概念を統合するかもしれないが、いずれにせよこれらの問題の全部に対処しなければならない。次の節では、そのフレームワークによって提供されるインターフェースを解説する。[注3]この表現はやや正確ではない。実際の制約条件はメモリがマップするであろうDMAセグメントの数である。これは通常割り当てられたメモリを構成する物理メモリのセグメント数と一致する。[注4]デバイスページャは、プロセスのアドレス空間にメモリマッピングデバイスのためのサポートを提供する。して一般に参照される。DMA特有のデータ型、そして、13の関数コールから成る。インターフェースと共有する。2つのカテゴリーに分けられる。マクロとして実装される場合もある。[注5]この名前はインターフェースを外部参照するためのインクルードファイルの名前に由来する。DMAのために使われるデバイスのバスアドレスを記述し、システム上存在する最大のバスアドレスを指定できるだけの十分な大きさを持つ必要がある。これはバスアドレス範囲のサイズを記述する。この不透明(opaque)型[訳注4]は、機種依存部のコードによってバスのオートコンフィギュレーション機構に渡される。バス階層はそれを順番に下位層のデバイスドライバへ渡していく。このタグは、インターフェースにおけるすべての関数への1番目の引数である。この型は、対外的にアクセス可能な2つのメンバを持つ構造体である。