Solaris 8 のソフトウェア開発 (追補)

エントリポイントおよびサービスルーチン

GLD ルーチンで使用される引数

macinfo

gld_mac_info(9S) 構造体に対するポインタ。

macaddr

有効な MAC アドレスが格納されたキャラクタ配列の先頭に対するポインタ。この配列は、gld_mac_info(9S) 構造体の gldm_addrlen 要素で、ドライバによって指定された長さになります。

multicastaddr

マルチキャスト、グループ、または機能アドレスが格納されたキャラクタ配列の先頭に対するポインタ。この配列は、gld_mac_info(9S) 構造体の gldm_addrlen 要素で、ドライバによって指定された長さになります。

multiflag

マルチキャストアドレスの受け付けを可能にするか不可能にするかを示すフラグ。この引数は、GLD_MULTI_ENABLE または GLD_MULTI_DISABLE として指定します。

promiscflag

有効になっているプロミスキュアスモードのタイプを示すフラグ。この引数は、GLD_MAC_PROMISC_PHYSGLD_MAC_PROMISC_MULTI、または GLD_MAC_PROMISC_NONE として指定します。

mp

gld_ioctl() は、実行する ioctl が格納されている STREAMS メッセージブロックへのポインタとして mp を使用します。gld_send() は、送信するパケットが格納されている STREAMS メッセージブロックへのポインタとして mp を使用します。gld_recv() は、受信パケットが格納されているメッセージブロックへのポインタとして mp を使用します。

stats

統計カウンタの現在値が入る、gld_stats(9S) 構造体に対するポインタ。

q

ioctl への応答で使用される、queue(9S) 構造体に対するポインタ。

dip

デバイスの dev_info 構造体に対するポインタ。

name

デバイスのインタフェース名

エントリポイント

これらのエントリポイントは、GLD とのインタフェースとして設計されたデバイス固有のネットワークドライバによって実装される必要があります。

gld(7D) で記述されているように、デバイス固有のドライバと GLD モジュール間の通信に関するメインデータ構造は、gld_mac_info(9S) 構造体です。この構造体の一部の要素は、ここで説明するエントリポイントへの関数ポインタです。デバイス固有のドライバは、attach(9E) ルーチンで、これらの関数ポインタを初期化してから gld_register() を呼び出さなければなりません。


int prefix_reset(gld_mac_info_t * macinfo);

gldm_reset() は、ハードウェアを初期状態にリセットします。


int prefix_start(gld_mac_info_t * macinfo);

gldm_start() により、デバイスは割り込みを発生させ、受信データパケットを GLD に配信する目的で、ドライバが gld_recv() を呼び出せるようにします。


int prefix_stop(gld_mac_info_t * macinfo);

gldm_stop() は、デバイスが割り込みを発生させることを不可にし、データパケットを GLD に配信する目的でドライバが gld_recv() を呼び出すことを止めさせます。GLD は gldm_stop() ルーチンによって、デバイスがこれ以上割り込みをかけなくなることを確実に保証しなければなりません。この関数は常に、GLD_SUCCESS を返します。


int prefix_set_mac_addr(gld_mac_info_t * macinfo, unsigned char * macaddr);

gldm_set_mac_addr() は、ハードウェアがデータの受信に使用する物理アドレスを設定します。この関数は、デバイスを渡された MAC アドレス macaddr にプログラムしなければなりません。現在利用できるリソースが不足していて要求を満たすことができない場合は、GLD_NORESOURCES を返します。要求された関数がサポートされていない場合は、GLD_NOTSUPPORTED を返します。


int prefix_set_multicast(gld_mac_info_t * macinfo, unsigned char * multicastaddr,
    int multiflag);

gldm_set_multicast() は、特定のマルチキャストアドレスのデバイスレベルでの受け付けを可能または不可能にします。3 番目の引数 multiflagGLD_MULTI_ENABLE に設定されている場合、この関数は 2 番目の引数によって示されたマルチキャストアドレスを持つパケットを受信するよう、インタフェースを設定します。multiflagGLD_MULTI_DISABLE に設定されている場合、ドライバは指定されたマルチキャストアドレスの受け付けを不可にすることが許可されます。

この関数は、GLD がマルチキャスト、グループ、または機能アドレスの受け付けを可能または不可能に設定するたびに呼び出されます。GLD は、デバイスがどのような方法でマルチキャストをサポートするのか、どのような方法でこの関数を呼び出して特定のマルチキャストアドレスを有効または無効にするのかについて、何も想定を行いません。デバイスによっては、ハッシュアルゴリズムとビットマスクを使用して、マルチキャストアドレスの集合を有効にするものもあります。この手順は認められており、GLD が余分なパケットをフィルタリングして除外します。1 つのアドレスを無効にするとデバイスレベルで複数のアドレスが無効になる可能性がある場合、GLD が有効にしているアドレスを無効にしてしまうことがないように、必要な情報を保存するのはデバイスドライバ側の役目です。

gldm_set_multicast() は、すでに有効になっている特定のマルチキャストアドレスを有効にするために呼び出されることはなく、また現在有効になっていないアドレスを無効にするために呼び出されることもありません。GLD は同じマルチキャストアドレスに対する複数の要求を追跡し、特定のマルチキャストアドレスを有効にすることを求める最初の要求、または無効にすることを求める最後の要求に限り、ドライバのエントリポイントを呼び出します。そのとき、利用できるリソースが不足していて要求を満たせない場合は、GLD は GLD_NORESOURCES を返します。要求された関数がサポートされていない場合は、GLD_NOTSUPPORTED を返します。


int prefix_set_promiscuous(gld_mac_info_t * macinfo, int promiscflag);

gldm_set_promiscuous() は、プロミスキュアスモードを有効または無効にします。この関数は、GLD がメディア上のすべてのパケットの受信、またはメディア上のすべてのマルチキャストパケットの受信を可能または不可能に設定するたびに呼び出されます。2 番目の引数 promiscflagGLD_MAC_PROMISC_PHYS の値に設定されている場合は、この関数は物理レベルのプロミスキュアスモードを有効にし、その結果、メディア上のすべてのパケットが受信されます。promiscflagGLD_MAC_PROMISC_MULTI に設定されている場合は、すべてのマルチキャストパケットの受信が可能になります。promiscflagGLD_MAC_PROMISC_NONE に設定されている場合は、プロミスキュアスモードが不可になります。

プロミスキュアスマルチキャストモードの要求の場合、マルチキャスト専用プロミスキュアスモードを備えていないデバイスのドライバは、デバイスを物理プロミスキュアスモードに設定して、すべてのマルチキャストパケットが受信されるようにしなければなりません。その場合、ルーチンは GLD_SUCCESS を返さねばなりません。GLD ソフトウェアが余分なパケットをフィルタリングして除外します。そのとき、利用できるリソースが不足していて要求を満たせない場合、GLD_NORESOURCES を返します。要求された関数がサポートされていない場合は、GLD_NOTSUPPORTED を返します。

上位互換性を維持するために、gldm_set_promiscuous() ルーチンは、promiscflag として認識できないすべての値を GLD_MAC_PROMISC_PHYS のように扱う必要があります。


int prefix_send(gld_mac_info_t * macinfo, mblk_t * mp);

gldm_send() は、デバイスへのパケットを送信待ちのキューに入れます。このルーチンには、送信されるべきパケットが入った STREAMS メッセージが渡されます。メッセージには複数のメッセージブロックが含まれることがあり、送信されるパケット全体にアクセスするために、送信ルーチンはそのメッセージ内のすべてのメッセージブロックを通らなければなりません。連結内の長さがゼロのメッセージ継続ブロックを認識してスキップするように、ドライバを準備する必要があります。ドライバでは、パケットが許容される最大パケットサイズを超えていないかどうかをチェックし、必要であれば、許容される最小パケットサイズになるまでパケットにパディングをしなければなりません。送信ルーチンがパケットを正常に送信した場合、またはキューに格納した場合は、GLD_SUCCESS を返します。

送信ルーチンは、送信パケットをすぐに受け付けることができない場合、GLD_NORESOURCES を返します。その場合、GLD は後で再試行を行います。gldm_send()GLD_NORESOURCES を返した場合、ドライバはリソースが利用できるようになった時点で、gld_sched() を呼び出さなければなりません。この gld_sched() への呼び出しは、ドライバが以前送信用のキューに入れることができなかったパケットを再試行するように、GLD に通知します (ドライバの gldm_stop() ルーチンが呼び出されても、後に gldm_send() ルーチンから再び GLD_NORESOURCES を返すまで、ドライバはこの義務を免除されます。ただし、gld_sched() を余分に呼び出しても、誤った動作になることはありません)。

ドライバの送信ルーチンが GLD_SUCCESS を返し、ドライバとハードウェアでそのメッセージがもう不要になったときに、メッセージを解放するのはドライバの役目です。送信ルーチンがデバイスにメッセージをコピーした場合、または専用バッファにコピーした場合は、コピー後に送信ルーチンでメッセージを解放できます。ハードウェアが DMA を使用して、メッセージデータブロックから直接データを読み取る場合は、ハードウェアによるデータの読み取りが完了するまで、ドライバはメッセージを解放してはなりません。この場合、ドライバは割り込みルーチンでメッセージを解放するか、または将来の送信動作の開始時にバッファ再請求動作でメッセージを解放するのが一般的です。送信ルーチンが GLD_SUCCESS 以外のものを返した場合、ドライバはメッセージを解放してはなりません。ネットワークまたはリンクパートナーとの間に物理接続が無いときに gldm_send() が呼び出された場合は、GLD_NOLINK を返します。


int prefix_intr(gld_mac_info_t * macinfo);

gldm_intr() は、デバイスに割り込みがかけられている可能性がある場合に呼び出されます。割り込みを他のデバイスと共有する可能性があるので、ドライバはデバイスの状態を調べ、実際に割り込みが発生したかどうかを判別しなければなりません。ドライバが制御しているデバイスで割り込みが起きなかった場合、このルーチンは DDI_INTR_UNCLAIMED を返さなければなりません。それ以外の場合は、割り込みに対処し、DDI_INTR_CLAIMED を返さなければなりません。パケットの正常な受信によって割り込みが発生した場合、このルーチンは受信パケットを M_DATA タイプの STREAMS メッセージに格納し、メッセージを gld_recv() に渡す必要があります。

gld_recv() は、着信パケットをアップストリーム方向に、ネットワークプロトコルスタックの該当する次の層に渡します。gld_recv() を呼び出す前に、STREAMS メッセージの b_rptr および b_wptr メンバーを正しく設定しておくことが重要です。

ドライバは、gld_recv() の呼び出し時に、相互排他 (mutex) ロックまたは他のロックを保持していないようにしなければなりません。特に、送信スレッドが使用する可能性のあるロックは、gld_recv() の呼び出し時に保持されていてはなりません。場合によっては、gld_recv() を呼び出す割り込みスレッドが、発信パケットの送信を含めた処理を実行してしまい、その結果、ドライバの gldm_send() ルーチンが呼び出されてしまうことがあるためです。gldm_intr() ルーチンが gld_recv() を呼び出すときに保持していた相互排他ロックを、gldm_send() ルーチンが取得しようとした場合、相互排他エントリが繰り返されることになり、パニックに陥る可能性があります。gld_recv() を呼び合いドライバが保持する相互排他ロックを他のドライバのエントリポイントが取得しようとした場合、デッドロックに陥る可能性があります。

割り込みコードは、あらゆるエラーに関する統計カウンタを増分しなければなりません。このエラーには、受信データ用バッファの割り当てエラーをはじめ、CRC エラーやフレーミングエラーなどのハードウェア固有のエラーが含まれます。


int prefix_get_stats(gld_mac_info_t * macinfo, struct gld_stats * stats);

gldm_get_stats() は、ハードウェア、ドライバ専用カウンタ、またはその両方から統計情報を収集し、stats で指し示された gld_stats(9S) 構造体を更新します。このルーチンは、統計要求を受けた時に GLD によって呼び出され、GLD が統計要求に対する応答を作成する前に、ドライバからデバイスに依存する統計情報を得るためのメカニズムを提供します。定義されている統計カウンタの詳細は、gld_stats(9S) および gld(7D) のマニュアルページを参照してください。


int prefix_ioctl(gld_mac_info_t * macinfo, queue_t * q, mblk_t * mp);

gldm_ioctl() は、すべてのデバイス固有の ioctl コマンドを実装します。ドライバが ioctl 関数を全く実装しない場合、この要素は NULL として指定できます。メッセージブロックを ioctl 応答メッセージに変換し、GLD_SUCCESS を呼び出す前に greply(9F) 関数を呼び出すのは、ドライバの役目です。この関数は常に GLD_SUCCESS を返すべきです。ドライバに報告させる必要のあるエラーは、greply(9F) に渡すメッセージで返させる必要があります。gldm_ioctl 要素が NULL として指定されている場合、GLD は M_IOCNAK タイプのメッセージを EINVAL というエラーとともに返します。

戻り値

これまでに説明した戻り値および制限事項のほかに、一部の GLD エントリポイント関数は次の値を返すことができます。

GLD_BADARG

関数が不適切な引数、たとえば、不良マルチキャストアドレス、不良 MAC アドレス、不良パケット、不良パケット長などを検出した場合

GLD_FAILURE

ハードウェア障害の場合

GLD_SUCCESS

成功した場合

サービスルーチン


gld_mac_info_t * gld_mac_alloc(dev_info_t * dip);

gld_mac_alloc() は、新しい gld_mac_info(9S) 構造体を割り当て、その構造体に対するポインタを返します。この構造体の GLD 専用の要素のなかには、gld_mac_alloc() を返す前に初期化されるものがありますが、他の要素はすべてゼロに初期化されます。デバイスドライバは、mac_info 構造体へのポインタを gld_register() へ渡す前に、gld_mac_info(9S) に記述されているように、一部の構造体メンバーを初期化する必要があります。


void gld_mac_free(gld_mac_info_t * macinfo);

gld_mac_free() は、以前に gld_mac_alloc() によって割り当てられていた gld_mac_info(9S) 構造体を解放します。


int gld_register(dev_info_t * dip, char * name, gld_mac_info_t * macinfo);

gld_register() は、デバイスドライバの attach(9E) ルーチンから呼び出され、GLD ベースのデバイスドライバと GLD フレームワークを結びつけるために使用されます。デバイスドライバの attach(9E) ルーチンは、gld_register() を呼び出す前に、gld_mac_alloc() を最初に使用して gld_mac_info(9S) 構造体を割り当て、その構造体の要素のいくつかを初期化しなければなりません。詳細は、gld_mac_info(9S) のマニュアルページを参照してください。gld_register() が正常に呼び出されると、次の動作が発生します。

gld_register() に渡すデバイスインタフェース名は、ファイルシステムに存在しているドライバモジュール名と完全に一致しなければなりません。

ドライバの attach(9E) ルーチンは、gld_register() が正常に完了した場合に DDI_SUCCESS を返す必要があります。gld_register()DDI_SUCCESS を返さなかった場合、attach(9E) ルーチンは gld_register() を呼び出す前に割り当てたすべてのリソースの割り当てを解除し、その後 DDI_FAILURE を返さなければなりません。


int gld_unregister(gld_mac_info_t * macinfo);

gld_unregister() は、デバイスドライバの detach(9E) 関数によって呼び出され、成功した場合は次の作業を実行します。

gld_unregister()DDI_SUCCESS を返した場合、detach(9E) ルーチンは attach(9E) ルーチンで割り当てられたすべてのデータ構造体を割り当て解除し、gld_mac_free() を使用して、macinfo 構造を割り当て解除し、DDI_SUCCESS を返します。gld_unregister()DDI_SUCCESS を返さなかった場合、ドライバの detach(9E) ルーチンはデバイスを動作状態にしたまま、DDI_FAILURE を返さなければなりません。


void gld_recv(gld_mac_info_t * macinfo, mblk_t * mp);

gld_recv() は、ドライバの割り込みハンドラによって呼び出され、受信したパケットをアップストリームに渡します。ドライバは raw パケットを格納した STREAMS M_DATA メッセージを作成して渡さなければなりません。gld_recv() がパケットのコピーを受け取るべき STREAMS キュー (あれば) を判別し、必要に応じてコピーします。さらに、必要であれば DL_UNITDATA_IND メッセージをフォーマットして、データを該当するすべての Stream に渡します。

ドライバは、gld_recv() の呼び出し時に、相互排他 (mutex) ロックまたは他のロックを保持していないようにしなければなりません。特に、送信スレッドが使用するロックは、gld_recv() の呼び出し時には保持できません。場合によっては、gld_recv() を呼び出す割り込みスレッドが、発信パケットの送信を含めた処理を実行してしまい、その結果、ドライバの gldm_send() ルーチンが呼び出されることがあるためです。gldm_intr() ルーチンが gld_recv() を呼び出すときに保持していた相互排他ロックを、gldm_send() ルーチンが取得しようとした場合、相互排他エントリが繰り返されることになり、パニックになる可能性があります。gld_recv() を呼び合いドライバが保持する相互排他ロックを他のドライバのエントリポイントが取得しようとした場合、デッドロックに陥る可能性があります。


void gld_sched(gld_mac_info_t * macinfo);

gld_sched() は、据え置かれていた発信パケットを再スケジューリングするために、デバイスドライバによって呼び出されます。ドライバの gldm_send() ルーチンが GLD_NORESOURCES を返すたびに、ドライバは後で gld_sched() を呼び出して、以前送信できなかったパケットについて再試行するように、GLD フレームワークに通知しなければなりません。gld_sched() は、リソースが利用可能になった時点で、できるかぎり迅速に呼び出され、GLD がドライバの gldm_send() ルーチンに対する発信パケットの受け渡しを、タイミングよく再開できるようにしなければなりません (ドライバの gldm_stop() ルーチンが呼び出されても、gldm_send() ルーチンから再び GLD_NORESOURCES が返されるまで、ドライバはこの義務を免除されます。ただし、gld_sched() を余分に呼び出しても、誤った動作になることはありません)。


uint_t gld_intr(caddr_t);

gld_intr() は、GLD のメインの割り込みハンドラです。通常、デバイスドライバの ddi_add_intr(9F) 呼び出しで、割り込みルーチンとして指定します。割り込みハンドラに対する引数 (ddi_add_intr(9F) 呼び出しに int_handler_arg として指定) は、gld_mac_info(9S) 構造体へのポインタでなければなりません。gld_intr() は、該当する場合、デバイスドライバの gldm_intr() 関数を呼び出し、そのポインタを gld_mac_info(9S) 構造体に渡します。しかし、ドライバが上位レベルの割り込みを使用する場合は、独自の上位割り込みハンドラを提供し、その中からソフト割り込みを起動しなければなりません。この場合、gld_intr() は通常、ddi_add_softintr() 呼び出しにソフト割り込みハンドラとして指定されます。gld_intr() は、割り込みハンドラに適した値を返します。