ナビゲーションリンクをスキップ | |
印刷ビューの終了 | |
デバイスドライバの記述 Oracle Solaris 11.1 Information Library (日本語) |
パート I Oracle Solaris プラットフォーム用デバイスドライバの設計
2. Oracle Solaris カーネルとデバイスツリー
22. ドライバのコンパイル、ロード、パッケージ化、およびテスト
23. デバイスドライバのデバッグ、テスト、およびチューニング
デバイスドライバは、サービスのコンシューマとしてもプロバイダとしても、Oracle Solaris OS と互換性がある必要があります。このセクションでは、デバイスドライバを設計する上で考慮する点として、次の項目について説明します。
Oracle Solaris DDI/DKI インタフェースは、ドライバの移植性のために提供されています。DDI/DKI を使用すると、開発者はハードウェアやプラットフォームの違いについて心配することなく標準的な方法でドライバコードを作成できます。このセクションでは、DDI/DKI インタフェースのさまざまな局面について説明します。
DDI インタフェースを使用すると、ドライバは持続的な一意の ID をデバイスに割り当てることができます。デバイス ID は、デバイスを識別したり、検出したりするために使用できます。この ID は、デバイスの名前や番号 (dev_t) には依存していません。アプリケーションでは、libdevid(3LIB) で定義された関数を使用すると、ドライバが登録したデバイス ID を読み取り、操作できます。
デバイスまたはデバイスドライバの属性は、プロパティーによって指定されます。プロパティーは名前-値ペアです。名前は、対応付けられた値を使ってプロパティーを識別する文字列です。プロパティーは、自己識別デバイスの FCode またはハードウェア構成ファイル (driver.conf(4) のマニュアルページを参照) で定義することも、ドライバ自身が ddi_prop_update(9F) ファミリのルーチンを使用して定義することもできます。
DDI/DKI では、デバイス割り込み処理の次の局面に対応します。
システムへのデバイス割り込みの登録
デバイス割り込みの削除
割り込みハンドラへの割り込みのディスパッチ
デバイス割り込みのソースは、interrupt と呼ばれるプロパティーに含まれています。このプロパティーは、自己識別デバイスの PROM、ハードウェア構成ファイル、または x86 プラットフォームでのブートシステムによって提供されます。
一部の DDI 機構には、コールバック機構が備わっています。DDI 関数には、条件が満たされたときにコールバックをスケジュールするための機構が備わっています。コールバック関数は、次の一般的な条件に対して使用できます。
転送が完了した
資源が利用できるようになった
タイムアウト期間が経過した
コールバック関数は、割り込みハンドラなどのエントリポイントに多少似ています。コールバックを許可する DDI 関数は、コールバック関数が特定のタスクを実行するものとみなします。DMA ルーチンの場合、コールバック関数は、障害が発生した場合にコールバック関数を再スケジュールする必要があるかどうかを示す値を返します。
コールバック関数は、単独の割り込みスレッドとして実行されます。コールバックは、通常のマルチスレッド問題をすべて処理する必要があります。
注 - ドライバは、デバイスを切り離す前に、スケジュールされたすべてのコールバック関数を取り消す必要があります。
状態構造体を割り当てる際にデバイスドライバの作成者を支援するために、DDI/DKI ではソフトウェア状態管理ルーチン (soft-state ルーチン) と呼ばれる一連のメモリー管理ルーチンを提供します。これらのルーチンは、指定されたサイズのメモリー項目の動的な割り当て、取得、および破棄を行い、リスト管理の詳細を非表示にします。インスタンス番号は、目的のメモリー項目を特定するために使われます。この番号は通常、システムによって割り当てられるインスタンス番号です。
ルーチンは、次のタスクに対して提供されます。
ドライバのソフト状態リストを初期化する
ドライバのソフト状態のインスタンスに領域を割り当てる
ドライバのソフト状態のインスタンスを指すポインタを取得する
ドライバのソフト状態のインスタンスのメモリーを解放する
ドライバのソフト状態リストの使用を終了する
これらのルーチンの使用方法の例については、「ロード可能なドライバインタフェース」を参照してください。
プログラム式入出力デバイスアクセスとは、ホストの CPU からデバイスレジスタやデバイスメモリーの読み書きを行う動作のことです。Oracle Solaris DDI には、カーネルによってデバイスのレジスタやメモリーをマッピングするためのインタフェースのほかに、ドライバからデバイスメモリーの読み書きを行うためのインタフェースも備わっています。これらのインタフェースを使用すると、デバイスとホストのエンディアンネスのあらゆる違いを自動的に管理したり、デバイスで設定されたメモリーとストアのシーケンス要件を適用したりすることによって、プラットフォームやバスに依存しないドライバを開発できます。
Oracle Solaris プラットフォームでは、DMA 対応デバイスをサポートするための、アーキテクチャーに依存しないハイレベルなモデルを定義しています。Oracle Solaris DDI は、プラットフォーム固有の詳細からドライバを遮蔽します。この概念を使えば、複数のプラットフォームやアーキテクチャー上で 1 つの共通のドライバを動作させることができます。
DDI/DKI には、階層化デバイスインタフェース (LDI) と呼ばれるインタフェースグループがあります。このインタフェースを使用すると、Oracle Solaris カーネルの内部からデバイスにアクセスできます。この機能を使用すると、開発者はカーネルデバイスの使用を監視するアプリケーションを作成できます。たとえば、prtconf(1M) と fuser(1M) の両方のコマンドで LDI を使用すると、システム管理者はデバイス使用のさまざまな局面を追跡できます。LDI については、第 14 章階層化ドライバインタフェース (LDI)で詳しく説明しています。
ドライバコンテキストとは、ドライバが現在動作している状況のことを意味します。コンテキストは、ドライバが実行できる操作を制限します。ドライバコンテキストは、呼び出される実行コードによって異なります。ドライバコードは、次の 4 つのコンテキストで実行されます。
ユーザーコンテキスト。同期方式でユーザースレッドによって呼び出された場合、ドライバのエントリポイントにユーザーコンテキストがあります。つまり、ユーザースレッドは、呼び出されたエントリポイントからシステムが復帰するのを待ちます。たとえば、ドライバの read(9E) エントリポイントが read(2) システムコールから呼び出された場合、そのエントリポイントにユーザーコンテキストがあります。この場合、ドライバはデータをユーザースレッドにコピーしたりユーザースレッドからコピーしたりするためにユーザー領域にアクセスできます。
カーネルコンテキスト。カーネルの一部から呼び出された場合、ドライバ関数はカーネルコンテキストで動作します。ブロックデバイスドライバでは、デバイスにページを書き込むために、strategy(9E) エントリポイントが pageout デーモンによって呼び出されることがあります。このページデーモンは現在のユーザースレッドとは関係がないため、この場合、strategy(9E) はカーネルコンテキストで動作します。
割り込みコンテキスト。割り込みコンテキストはカーネルコンテキストのより制限された形式です。割り込みコンテキストは、割り込みが処理された結果、呼び出されます。ドライバ割り込みルーチンは、関連付けられた割り込みレベルを使って割り込みコンテキストで動作します。コールバックルーチンも同様に割り込みコンテキストで動作します。詳細は、第 8 章割り込みハンドラを参照してください。
高レベルの割り込みコンテキスト。高レベルの割り込みコンテキストは、割り込みコンテキストの、より制限の厳しい形式です。ddi_intr_hilevel(9F) で割り込みが高レベルであることが示された場合、ドライバ割り込みハンドラは高レベルの割り込みコンテキストで動作します。詳細は、第 8 章割り込みハンドラを参照してください。
セクション 9F のマニュアルページには、各関数に使用できるコンテキストが記載されています。たとえば、カーネルコンテキストでは、ドライバは copyin(9F) を呼び出してはいけません。
データ破壊などの予期しないエラーが発生した場合を除き、デバイスドライバからは通常メッセージは出力されません。ドライバのエントリポイントは代わりにエラーコードを返して、アプリケーションがエラーの処理方法を決定できるようにします。cmn_err(9F) 関数を使用してメッセージをシステムログに書き出せば、そのあとでコンソールに表示できます。
cmn_err(9F) によって解釈される書式文字列指定子は printf(3C) 書式文字列指定子に似ていますが、ビットフィールドを出力する %b 書式が追加されています。この書式文字列の最初の文字には特別な意味を持たせることができます。cmn_err(9F) の呼び出しでは、出力される重要度レベルを示すメッセージレベル (level) も指定します。詳細は、cmn_err(9F) のマニュアルページを参照してください。
CE_PANIC レベルには、システムをクラッシュさせるという副作用があります。このレベルは、システムが、続行することによってより多くの問題が発生するような不安定な状態である場合にだけ使用されます。このレベルは、デバッグ時にシステムコアダンプを取るためにも使用できます。CE_PANIC は本稼働デバイスドライバでは使用しません。
デバイスドライバは、ドライバが作動させると宣言したすべての接続デバイスを同時に処理する準備ができている必要があります。ドライバが処理するデバイスの数には制限がありません。デバイスごとの情報をすべて動的に割り当てる必要があります。
void *kmem_alloc(size_t size, int flag);
標準的なカーネルメモリー割り当てルーチンは、kmem_alloc(9F) です。kmem_alloc() は C ライブラリルーチン malloc(3C) に似ていますが、flag 引数が追加されています。flag 引数には、要求されたサイズが使用できない場合に呼び出し側がブロックするかどうかを示す KM_SLEEP または KM_NOSLEEP を指定できます。KM_NOSLEEP が設定されていて、メモリーが使用できない場合、kmem_alloc(9F) は NULL を返します。
kmem_zalloc(9F) は kmem_alloc(9F) に似ていますが、割り当てられたメモリーの内容もクリアします。
注 - カーネルメモリーは限られた資源であり、ページング不可能なため、物理メモリーをめぐってユーザーアプリケーションやカーネルの残りの部分と競合します。ドライバが大量のカーネルメモリーを割り当てると、システム性能が低下する可能性があります。
void kmem_free(void *cp, size_t size);
kmem_alloc(9F) または kmem_zalloc(9F) によって割り当てられたメモリーは、kmem_free(9F) を使ってシステムに返されます。kmem_free() は C ライブラリルーチン free(3C) に似ていますが、size 引数が追加されています。ドライバは、あとで kmem_free(9F) を呼び出すために、割り当てられた各オブジェクトのサイズを追跡し記録する必要があります。
このマニュアルには、ホットプラグによる取り付けに関する情報は載っていません。このドキュメントに記載されたデバイスドライバ作成のための規則や提案に従えば、ドライバでホットプラグによる取り付けを扱えるようになります。特に、自動構成 (第 6 章ドライバの自動構成を参照) と detach(9E) の両方がドライバで正しく機能することを確認してください。また、電源管理を使用するドライバを設計している場合は、第 12 章電源管理に記載された情報に従うようにしてください。SCSI HBA ドライバでは、ホットプラグによる取り付け機能を利用するために、cb_ops 構造体をその dev_ops 構造体 (第 18 章SCSI ホストバスアダプタドライバを参照) に追加することが必要な場合があります。
以前の Oracle Solaris OS バージョンでは、DT_HOTPLUG プロパティーを組み込むためにホットプラグ対応ドライバが必要でしたが、このプロパティーは必要なくなりました。ただし、ドライバの作成者が適切と思えば、DT_HOTPLUG プロパティーを組み込んで使用しても構いません。