ナビゲーションリンクをスキップ | |
印刷ビューの終了 | |
デバイスドライバの記述 Oracle Solaris 11.1 Information Library (日本語) |
パート I Oracle Solaris プラットフォーム用デバイスドライバの設計
2. Oracle Solaris カーネルとデバイスツリー
Ethernet V2 および ISO 8802-3 (IEEE 802.3)
gldm_set_promiscuous() エントリポイント
22. ドライバのコンパイル、ロード、パッケージ化、およびテスト
23. デバイスドライバのデバッグ、テスト、およびチューニング
GLDv3 フレームワークは、MAC プラグインと、MAC ドライバサービスのルーチンおよび構造体に対する、関数呼び出しベースのインタフェースです。GLDv3 フレームワークは、必要な STREAMS エントリポイントを GLDv3 準拠ドライバに代わって実装し、DLPI との互換性を実現します。
このセクションの内容は次のとおりです。
GLDv3 は、MAC_PLUGIN_IDENT_ETHER タイプのプラグインに登録するドライバ用のドライバ API を定義します。
GLDv3 デバイスドライバは、次の手順を実行して MAC 層に登録する必要があります。
sys/mac_ether.h、sys/mac_provider.h の 2 つの MAC ヘッダーファイルをインクルードします。その他の MAC 関連ヘッダーファイルはドライバにインクルードしないでください。
mac_callbacks 構造体を生成します。
mac_init_ops() 関数をその _init() エントリポイントで呼び出します。
mac_alloc() 関数をその attach() エントリポイントで呼び出し、mac_register 構造体を割り当てます。
mac_register 構造体を生成し、mac_register() 関数をその attach() エントリポイントで呼び出します。
mac_unregister() 関数をその detach() エントリポイントで呼び出します。
mac_fini_ops() 関数をその _fini() エントリポイントで呼び出します。
misc/mac への依存性とリンクします。
# ld -N"misc/mac" xx.o -o xx
GLDv3 インタフェースには、MAC 層への登録中に通知されるドライバエントリポイントと、ドライバによって呼び出される MAC エントリポイントが含まれています。
void mac_init_ops(struct dev_ops *ops, const char *name);
GLDv3 デバイスドライバは mod_install(9F) を呼び出す前に、mac_init_ops(9F) 関数をその _init(9E) エントリポイントで呼び出す必要があります。
void mac_fini_ops(struct dev_ops *ops);
GLDv3 デバイスドライバは mod_remove(9F) を呼び出したあとに、mac_fini_ops(9F) 関数をその _fini(9E) エントリポイントで呼び出す必要があります。
例 19-1 mac_init_ops() および mac_fini_ops() 関数
int _init(void) { int rv; mac_init_ops(&xx_devops, "xx"); if ((rv = mod_install(&xx_modlinkage)) != DDI_SUCCESS) { mac_fini_ops(&xx_devops); } return (rv); } int _fini(void) { int rv; if ((rv = mod_remove(&xx_modlinkage)) == DDI_SUCCESS) { mac_fini_ops(&xx_devops); } return (rv); }
mac_register_t *mac_alloc(uint_t version);
mac_alloc(9F) 関数は新しい mac_register 構造体を割り当て、この構造体へのポインタを返します。新しい構造体を mac_register() に渡す前に構造体のメンバーを初期化してください。mac_alloc() が戻る前に、MAC 専用要素が MAC 層によって初期化されます。version の値は MAC_VERSION_V1 である必要があります。
void mac_free(mac_register_t *mregp);
mac_free(9F) 関数は、以前に mac_alloc() によって割り当てられた mac_register 構造体を解放します。
int mac_register(mac_register_t *mregp, mac_handle_t *mhp);
新しいインスタンスを MAC 層に登録するために、GLDv3 ドライバは mac_register(9F) 関数をその attach(9E) エントリポイントで呼び出す必要があります。mregp 引数は、mac_register 登録情報構造体へのポインタです。成功した場合、mhp 引数は新しい MAC インスタンスの MAC ハンドルへのポインタです。このハンドルは、mac_tx_update()、mac_link_update()、mac_rx() などのほかのルーチンで必要になります。
例 19-2 mac_alloc()、mac_register()、および mac_free() 関数と mac_register 構造体
int xx_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) { mac_register_t *macp; /* ... */ if ((macp = mac_alloc(MAC_VERSION)) == NULL) { xx_error(dip, "mac_alloc failed"); goto failed; } macp->m_type_ident = MAC_PLUGIN_IDENT_ETHER; macp->m_driver = xxp; macp->m_dip = dip; macp->m_src_addr = xxp->xx_curraddr; macp->m_callbacks = &xx_m_callbacks; macp->m_min_sdu = 0; macp->m_max_sdu = ETHERMTU; macp->m_margin = VLAN_TAGSZ; if (mac_register(macp, &xxp->xx_mh) == DDI_SUCCESS) { mac_free(macp); return (DDI_SUCCESS); } /* failed to register with MAC */ mac_free(macp); failed: /* ... */ }
int mac_unregister(mac_handle_t mh);
mac_unregister(9F) 関数は、以前に mac_register() によって登録された MAC インスタンスを登録解除します。mh 引数は、mac_register() によって割り当てられた MAC ハンドルです。mac_unregister() は detach(9E) エントリポイントから呼び出してください。
例 19-3 mac_unregister() 関数
int xx_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) { xx_t *xxp; /* driver soft state */ /* ... */ switch (cmd) { case DDI_DETACH: if (mac_unregister(xxp->xx_mh) != 0) { return (DDI_FAILURE); } /* ... */ }
このセクションで説明する構造体は、sys/mac_provider.h ヘッダーファイルで定義されます。sys/mac_ether.h および sys/mac_provider.h MAC ヘッダーファイルを GLDv3 ドライバにインクルードします。その他の MAC 関連ヘッダーファイルはインクルードしないでください。
mac_register(9S) データ構造体は、mac_alloc() によって割り当てられ、mac_register() に渡される MAC 登録情報構造体です。新しい構造体を mac_register() に渡す前に構造体のメンバーを初期化してください。mac_alloc() が戻る前に、MAC 専用要素が MAC 層によって初期化されます。構造体の m_version メンバーは MAC バージョンです。MAC バージョンを変更しないでください。構造体の m_type_ident メンバーは MAC タイプ識別子です。MAC タイプ識別子は MAC_PLUGIN_IDENT_ETHER に設定してください。mac_register 構造体の m_callbacks メンバーは、mac_callbacks 構造体のインスタンスへのポインタです。
mac_callbacks(9S) データ構造体は、デバイスドライバがそのエントリポイントを MAC 層に公開するために使用する構造体です。これらのエントリポイントは、ドライバを制御するために MAC 層によって使用されます。これらのエントリポイントは、アダプタの起動と停止、マルチキャストアドレスの管理、プロミスキュアス (promiscuous) モードの設定、アダプタの機能の照会、プロパティーの取得と設定などのタスクを実行するために使用されます。すべての必須および省略可能 GLDv3 エントリポイントの一覧については、表 19-1 を参照してください。mac_register 構造体の m_callbacks フィールドには、mac_callbacks 構造体へのポインタを指定します。
mac_callbacks 構造体の mc_callbacks メンバーは、次のフラグを組み合わせたビットマスクであり、ドライバで実装される省略可能エントリポイントを指定します。mac_callbacks 構造体のほかのメンバーは、ドライバの各エントリポイントへのポインタです。
mc_ioctl() エントリポイントが存在します。
mc_getcapab() エントリポイントが存在します。
mc_setprop() エントリポイントが存在します。
mc_getprop() エントリポイントが存在します。
mc_propinfo() エントリポイントが存在します。
すべてのプロパティーエントリポイントが存在します。MC_PROPERTIES を設定すると、MC_SETPROP、MC_GETPROP、および MC_PROPINFO の 3 つのフラグすべてを設定したことになります。
例 19-4 mac_callbacks 構造体
#define XX_M_CALLBACK_FLAGS \ (MC_IOCTL | MC_GETCAPAB | MC_PROPERTIES) static mac_callbacks_t xx_m_callbacks = { XX_M_CALLBACK_FLAGS, xx_m_getstat, /* mc_getstat() */ xx_m_start, /* mc_start() */ xx_m_stop, /* mc_stop() */ xx_m_promisc, /* mc_setpromisc() */ xx_m_multicst, /* mc_multicst() */ xx_m_unicst, /* mc_unicst() */ xx_m_tx, /* mc_tx() */ NULL, /* Reserved, do not use */ xx_m_ioctl, /* mc_ioctl() */ xx_m_getcapab, /* mc_getcapab() */ NULL, /* Reserved, do not use */ NULL, /* Reserved, do not use */ xx_m_setprop, /* mc_setprop() */ xx_m_getprop, /* mc_getprop() */ xx_m_propinfo /* mc_propinfo() */ };
GLDv3 が実装する機能機構では、GLDv3 ドライバでサポートされている機能をフレームワークで照会して有効化できます。機能を報告するには mc_getcapab(9E) エントリポイントを使用します。機能がドライバによってサポートされている場合は、機能固有のエントリポイントやフラグなど、その機能についての情報を mc_getcapab() を通して渡します。mc_getcapab() エントリポイントへのポインタを mac_callback 構造体で渡します。mac_callbacks 構造体の詳細は、「GLDv3 の MAC 登録データ構造体」を参照してください。
boolean_t mc_getcapab(void *driver_handle, mac_capab_t cap, void *cap_data);
cap 引数は、照会する機能のタイプを指定します。cap に指定できる値は MAC_CAPAB_HCKSUM (ハードウェアチェックサムオフロード)、MAC_CAPAB_LSO (ラージセグメントオフロード)、または MAC_CAPAB_RINGS です。機能データをフレームワークに返すには、cap_data 引数を使用します。
ドライバが cap の機能をサポートする場合、mc_getcapab() エントリポイントは B_TRUE を返す必要があります。ドライバが cap の機能をサポートしない場合、mc_getcapab() は B_FALSE を返す必要があります。
例 19-5 mc_getcapab() エントリポイント
static boolean_t xx_m_getcapab(void *arg, mac_capab_t cap, void *cap_data) { switch (cap) { case MAC_CAPAB_HCKSUM: { uint32_t *txflags = cap_data; *txflags = HCKSUM_INET_FULL_V4 | HCKSUM_IPHDRCKSUM; break; } case MAC_CAPAB_LSO: { /* ... */ break; } case MAC_CAPAB_RINGS: { /* ... */ break; } default: return (B_FALSE); } return (B_TRUE); }
次のセクションからは、サポートされている機能と、対応する機能ごとに返されるデータについて説明します。
送信ハードウェアリングと受信ハードウェアリングはどちらも DMA チャネルであり、デバイスドライバはこれらを公開できます。リングはリンググループに関連付けられます。受信リンググループには 1 つ以上の MAC アドレスが関連付けられますが、ある受信グループに関連付けられた MAC アドレスのいずれかに一致するネットワークトラフィックはすべて、そのグループのいずれかのリング経由で NIC から配信される必要があります。受信リンググループへのトラフィックの配信は、ハードウェアのレイヤー 2 分類によって可能になります。
受信リングとリンググループとのマッピングは動的または静的に行うことができます。動的リンググループでは、フレームワークからのリクエストに応じてグループ間でリングを移動できるため、グループのサイズを動的に縮小または拡大できます。一方、静的リンググループでは、リングは静的にグループに割り当てられ、その割り当てを変更することはできません。
受信グループに複数のリングが含まれている場合、NIC は、RSS (Receive Side Scaling) などのハッシュメカニズムを使ってそれらのリング間にトラフィックを分散させることで、複数の接続がそれぞれ異なるリングに割り当てられるようにする必要があります。
受信グループのどれか 1 つ (通常はインデックス 0 にある最初のグループ) をデフォルトグループとして指定する必要があります。この受信グループには次の特性が割り当てられます。
リングを少なくとも 1 つ持つべきです。
NIC のプライマリ MAC クライアントに割り当てられます。プライマリ MAC クライアントには NIC のプライマリ MAC アドレスが割り当てられ、通常は IP になります。
ネットワークから受信されたすべてのマルチキャストおよびブロードキャストトラフィックを受信するために使用する必要があります。
NIC がプロミスキュアスモードになっている場合、デフォルト以外の受信グループに割り当てられた MAC アドレスに一致しないトラフィックをすべて、これを使って受信する必要があります。
受信リングや受信リンググループのハードウェア実装について注意すべき点を次に示します。
複数の受信リングが実装されているがレイヤー 2 分類はサポートされていない場合、ハードウェアは、すべてのリングが属する単一の受信リンググループをフレームワークに公開すべきです。
レイヤー 2 ハードウェア分類は実装されているが RSS はサポートされていない場合、ハードウェアは、それぞれ 1 つのリングを含む受信グループを複数個登録すべきです。
レイヤー 2 ハードウェア分類と RSS の両方が実装されている場合、ハードウェアは、それぞれ 1 つ以上のリングを含む受信グループを複数個登録すべきです。
レイヤー 2 ハードウェア分類も RSS も実装されていない場合、ハードウェアは、リング機能を通知しないか、あるいは単一の擬似リングとリンググループを備えたリング機能を通知すべきであり、これらを使えば、アダプタに対してトラフィックの有無を動的にポーリングできます。
リングをフレームワークに登録する際には、フレームワークからドライバへの各種呼び出しで構成されるプロセスを実行します。次の手順はその登録プロセスを説明したものです。
フレームワークは、ドライバの MAC_CAPAB_RINGS 機能のクエリーを行うために、ドライバを呼び出します。呼び出しは、送信リング用と受信リング用にそれぞれ 1 回ずつ行われます。詳細は、「MAC_CAPAB_RINGS 機能」を参照してください。
フレームワークは、1 つ前の手順で得られた mr_rget(9E) および mr_gget(9E) エントリポイントを使って特定のリングまたはリンググループに関する情報を取得します。詳細は、mr_rget(9E) および mr_gget(9E) のマニュアルページを参照してください。
フレームワークがリングを使用する際は、1 つ前の手順で通知されたように、mgi_start(9E) エントリポイントでリンググループを開始したあと、mri_start(9E) エントリポイントを使ってリングを開始します。
これで、リングが mgi_stop(9E) および mri_stop(9E) エントリポイント経由で停止されるまで、トラフィックをリング経由で流すことができます。
フレームワークはハードウェアの送信リングと受信リングのサポートに関する情報を取得するため、cap 引数の MAC_CAPAB_RINGS を送信し、mac_capab_rings 構造体を指している cap_data フィールドに情報が返されることを想定します。
フレームワークは、mac_capab_rings(9S) 構造体を割り当て、mr_type メンバーを受信リングの場合は MAC_RING_TYPE_RX に、送信リングの場合は MAC_RING_TYPE_TX にそれぞれ設定します。すると、ドライバによって構造体 mac_capab_rings の残りのメンバーに情報が設定されます。
mac_capab_rings 構造体に定義されているフィールドは次のとおりです。
MAC_RINGS_VERSION_1 に設定する必要があります。
リングの数。
グループの数。
次の値が定義されています。
MAC_GROUP_TYPE_DYNAMIC – グループは動的です。
MAC_GROUP_TYPE_STATIC – グループは静的です。
詳細は、「リングとリンググループのレイヤー 2 分類」を参照してください。
リンググループの詳細情報を取得するドライバエントリポイント。詳細は、「mr_gget() エントリポイント」を参照してください。
リングの詳細情報を取得するドライバエントリポイント。詳細は、「mr_rget() エントリポイント」を参照してください。
リングをグループに追加するドライバエントリポイント。mr_gaddring(9E) を参照してください。
グループからリングを削除するドライバエントリポイント。mr_gremring(9E) を参照してください。
mr_gnum パラメータに示されたグループ数に対応する有効なグループインデックスごとに、mr_gget(9E) エントリポイントがフレームワークによって呼び出されます。詳細は、mr_gget(9E) を参照してください。mr_gget() を呼び出すと、ドライバによって mac_group_info 構造体にグループ情報が返されます。構造体自体はフレームワークによって事前に割り当てられ、構造体の設定はドライバによって行われます。
mac_group_info 構造体に定義されているフィールドは次のとおりです。
後続のグループエントリポイント呼び出し時にフレームワークによって使用される不透明なドライバグループハンドル。
グループ内のリング数。
グループフラグ MAC_GROUP_DEFAULT は、そのグループがデフォルトグループであることを示します。詳細は、「リングとリンググループのレイヤー 2 分類」を参照してください。
グループ開始エントリポイント。
グループ停止エントリポイント。
ユニキャスト MAC アドレス追加エントリポイント。
ユニキャスト MAC アドレス削除エントリポイント。
ハードウェアの VLAN フィルタリング、タグ付け、および VLAN タグのストリップを追加するエントリポイント。
ハードウェアの VLAN フィルタリング、タグ付け、および VLAN タグのストリップを削除するエントリポイント。
RX グループ MTU 設定エントリポイント
グループの SR-IOV 情報を取得するエントリポイント。詳細は、「リンググループと SR-IOV 」を参照してください。
詳細は、mac_group_info(9S) および mac_group_info(9E) を参照してください。
注 - mgi_addmac(9E) および mgi_remmac(9E) エントリポイントは、受信グループでのみ使用されます。デバイスドライバがリング機能をサポートしている場合は必ず、mc_unicst(9E) エントリポイントを NULL に設定する必要があります。
注 - mgi_addvlan() エントリポイントは次のアクションを実行します。
これは、NIC によって許可される必要のある、送信用および受信用の VLAN ID を定義します。つまり、構成されたリストに含まれていないタグ付きパケットはすべてドロップされます。
MAC_GROUP_VLAN_TRANSPARENT_ENABLE フラグが設定されていた場合、これは、その特定の VLAN ID のハードウェア VLAN タグ付けやストリップの有効化も行います。
MAC_CAPAB_RINGS 呼び出しで通知された mr_gnum と mr_rnum にそれぞれ示されたグループ数とリング数に対応する有効なグループインデックスおよびリングインデックスごとに、mr_rget(9E) エントリポイントがフレームワークによって呼び出されます。詳細は、mr_rget(9E) を参照してください。
mr_rget() 呼び出しが完了すると、ドライバによって mac_ring_info 構造体にリング情報が返されます。構造体はフレームワークによって事前に割り当てられ、構造体の設定はドライバによって行われます。
mac_ring_info 構造体に定義されているフィールドは次のとおりです。
後続のリングエントリポイント呼び出し時にフレームワークによって使用される不透明なドライバグループハンドル。
リング開始エントリポイント。
リング停止エントリポイント
リング統計エントリポイント。詳細は、「GLDv3 のネットワーク統計情報」を参照してください。
リング送信エントリポイント。詳細は、「送信データパス」を参照してください。
リングポーリングエントリポイント。詳細は、「受信データパス」 を参照してください。
このリングの割り込みに関連付けられた DDI 割り込みハンドル。
RX リングの割り込みを有効にします。詳細は、「受信データパス」を参照してください。
RX リングの割り込みを無効にします。詳細は、「受信データパス」 を参照してください。
詳細は、mac_group_info(9S) と mac_ring_info(9S) のマニュアルページを参照してください。
注 - mri_tx() を設定する必要があるのは送信リングの場合だけであり、mri_poll() を設定する必要があるのは受信リングの場合だけです。
注 - ドライバがリング機能を実装している場合は、mac_callbacks 構造体の mc_tx() エントリポイントを NULL に設定する必要があります。
SR-IOV に対応したデバイスドライバが、自身が SR-IOV に対応していることを MAC_CAPAB_RINGS 機能を使ってフレームワークに通知するには、mgi_getsriov_info(9E) グループエントリポイントを実装します。このエントリポイントの実装は、PF ドライバが担当します。
mgi_getsriov_info(9E) を呼び出すと、ドライバによって mac_sriov_info 構造体に SR-IOV 情報が返されます。構造体はフレームワークによって事前に割り当てられ、構造体の設定はドライバによって行われます。
PF (物理機能) ドライバインスタンスは、VF (仮想機能) と同じ数の送信および受信リンググループを登録します。PF ドライバによって通知されるこれらのリンググループは特殊なもので、VF の管理に使用されます。これらのリンググループ内をデータが通過することは一切ありません。これらは、ユニキャスト MAC アドレスの構成、MTU の設定、VLAN フィルタの追加、VLAN フィルタの削除、VLAN ハードウェアの削除、および VF の VLAN タグ付けやストリップの実行を行うために使用されます。
注 - VF ドライバは自身が参加する MAC マルチキャストグループをプログラミングします。PF ドライバはこれらのアドレスのプログラミングを制御しません。
PF ドライバによって設定される msi_vf_index 構造体メンバーには、リンググループに対応する VF インデックスが設定されます。これは、デバイスドライバが pci_plist_getvf(9F) 関数を呼び出す際に使用するのと同じインデックスです。
SR-IOV ドライバの詳細については、第 21 章SR-IOV ドライバを参照してください。
ハードウェアチェックサムオフロードのサポートについてのデータを取得するために、フレームワークは cap 引数で MAC_CAPAB_HCKSUM を送信します。「ハードウェアチェックサムオフロード機能情報」を参照してください。
ハードウェアによるチェックサム計算が有効なときに、チェックサムオフロードのメタデータを照会したり、パケット単位でのハードウェアによるチェックサム計算のメタデータを取得したりするには、mac_hcksum_get(9F) を使用します。「mac_hcksum_get() 関数のフラグ」を参照してください。
チェックサムオフロードのメタデータを設定するには、mac_hcksum_set(9F) を使用します。「mac_hcksum_set() 関数のフラグ」を参照してください。
詳細は、「ハードウェアによるチェックサム計算: ハードウェア」および「ハードウェアによるチェックサム計算: MAC 層」を参照してください。
MAC_CAPAB_HCKSUM 機能についての情報をフレームワークに渡すために、ドライバは uint32_t を指し示す cap_data で次のフラグの組み合わせを設定する必要があります。これらのフラグは、アウトバウンドパケットに対してドライバが実行できるハードウェアチェックサムオフロードのレベルを示します。
1 の補数による部分チェックサム機能
IPv4 パケット対象の 1 の補数による完全チェックサム機能
IPv6 パケット対象の 1 の補数による完全チェックサム機能
IPv4 ヘッダーのチェックサムオフロード機能
mac_hcksum_get() の flags 引数は次の値の組み合わせです。
このパケットの完全チェックサムを計算します。
完全チェックサムがハードウェアで検証済みであり、正確です。
mac_hcksum_get() に渡されるほかのパラメータに基づいて、1 の補数による部分チェックサムを計算します。HCK_PARTIALCKSUM は HCK_FULLCKSUM と相互排他です。
IP ヘッダーのチェックサムを計算します。
IP ヘッダーのチェックサムはハードウェアで検証済みであり、正確です。
mac_hcksum_set() の flags 引数は次の値の組み合わせです。
完全チェックサムが計算され、value 引数を介して渡されました。
完全チェックサムがハードウェアで検証済みであり、正確です。
部分チェックサムが計算され、value 引数を介して渡されました。HCK_PARTIALCKSUM は HCK_FULLCKSUM と相互排他です。
IP ヘッダーのチェックサムが計算され、value 引数を介して渡されました。
IP ヘッダーのチェックサムはハードウェアで検証済みであり、正確です。
ラージセグメント (送信) オフロードのサポートを照会するために、フレームワークは cap 引数で MAC_CAPAB_LSO を送信し、mac_capab_lso(9S) 構造体を指し示す cap_data に情報が返されることを想定します。フレームワークは mac_capab_lso 構造体を割り当て、この構造体へのポインタを cap_data に渡します。mac_capab_lso 構造体は lso_basic_tcp_ipv4(9S) 構造体と lso_flags メンバーで構成されます。ドライバインスタンスが TCP/IPv4 での LSO をサポートする場合、LSO_TX_BASIC_TCP_IPV4 フラグを lso_flags に設定し、デバイスインスタンスがサポートする最大ペイロードサイズを lso_basic_tcp_ipv4 構造体の lso_max メンバーに設定します。
パケット単位での LSO のメタデータを取得するには、mac_lso_get(9F) を使用します。このパケットに対して LSO が有効な場合、mac_lso_get() の flags 引数に HW_LSO フラグが設定されます。ラージセグメントのセグメンテーション中に使用される最大セグメントサイズ (MSS) は、mss 引数によって指示される場所を介して返されます。詳細は、「ラージセグメントオフロード」を参照してください。
データパスエントリポイントは、次のコンポーネントで構成されます。
ドライバによってエクスポートされ、パケット送信のために GLDv3 フレームワークによって呼び出されるコールバック。
転送フロー制御およびパケット受信のためにドライバによって呼び出される GLDv3 フレームワークのエントリポイント。
注 - ドライバがリング機能を実装している場合、そのドライバによって送信および受信されるデータはすべて、リング固有のエントリポイントを介して渡されます。
GLDv3 フレームワークがメッセージブロックをドライバに渡すために呼び出す送信エントリポイントのタイプは、ベースとなるドライバの MAC_CAPAB_RINGS サポートによって異なります。ドライバが MAC_CAPAB_RINGS 機能をサポートしている場合、フレームワークは mri_tx(9E) リングエントリポイントを呼び出します。それ以外の場合、フレームワークは mc_tx(9E) エントリポイントを呼び出します。
したがって、デバイスドライバは mc_tx() と mri_tx() のいずれかに送信エントリポイントへのポインタを指定する必要があります。詳細は、「GLDv3 の MAC 登録データ構造体」および「mr_rget() エントリポイント」を参照してください。
例 19-6 mc_tx() および mri_tx() エントリポイント
mblk_t * xx_m_tx(void *arg, mblk_t *mp) { xx_t *xxp = arg; mblk_t *nmp; mutex_enter(&xxp->xx_xmtlock); if (xxp->xx_flags & XX_SUSPENDED) { while ((nmp = mp) != NULL) { xxp->xx_carrier_errors++; mp = mp->b_next; freemsg(nmp); } mutex_exit(&xxp->xx_xmtlock); return (NULL); } while (mp != NULL) { nmp = mp->b_next; mp->b_next = NULL; if (!xx_send(xxp, mp)) { mp->b_next = nmp; break; } mp = nmp; } mutex_exit(&xxp->xx_xmtlock); return (mp); }
次のセクションでは、ハードウェアへのデータ転送に関連する事項について説明します。
ハードウェアリソース不足のためドライバがパケットを送信できない場合、ドライバは送信できなかったパケットのサブチェーンを返します。利用可能な記述子があとから増えたとき、ドライバは mac_tx_update(9F) または mac_tx_ring(9F) を呼び出してフレームワークに通知する必要があります。ドライバは、自身がリング機能を実装しているかどうかに応じていずれかの関数を呼び出します。
ドライバでハードウェアチェックサムのサポート (「ハードウェアチェックサムオフロード」を参照) を指定した場合、ドライバは次のタスクを実行する必要があります。
mac_hcksum_get(9F) を使用して、ハードウェアチェックサムのメタデータの全パケットを確認します。
必要なチェックサム計算を実行するようにハードウェアをプログラミングします。
ドライバで LSO 機能 (「ラージセグメント (送信) オフロード」を参照) を指定した場合、ドライバは mac_lso_get(9F) を使用して、パケットに対して LSO を実行する必要があるかどうかを照会する必要があります。
管理者が VLAN を構成した場合、アウトバウンドパケットが mc_tx() エントリポイントを介してドライバに渡される前に、MAC 層が必要な VLAN ヘッダーをアウトバウンドパケットに挿入します。ただし、ハードウェアが VLAN タグ付けをサポートしている場合、ハードウェアにタグ付けがオフロードされます。詳細は、「mr_gget() エントリポイント」を参照してください。
受信データパスは割り込み駆動とポーリング駆動のいずれかになります。
注: ドライバがリング機能をサポートしていない場合、ドライバの割り込みハンドラ内で mac_rx(9F) 関数を呼び出すことで、1 つ以上のパケットのチェーンをスタック上方の MAC 層に渡します。mac_rx() または mac_rx_ring() の呼び出し中は、相互排他ロックやその他のロックを保持しないでください。特に、mac_rx() または mac_rx_ring() の呼び出し中に転送スレッドによって取得される可能性があるロックを保持しないでください。
割り込みモードでは、NIC でパケットチェーンが受信され、それをドライバから取得可能な場合は常に、ドライバから上方のフレームワークにパケットチェーンが送信されます。パケットチェーンでは 1 つ以上の mblk_t が b_next を介して互いに連結されているため、パケット単位の処理のオーバーヘッドを減らすことができます。割り込みモードで受信されたパケットを上方のフレームワークに渡すには、mac_rx_ring() エントリポイントを呼び出します。
void mac_rx_ring(mac_handle_t mh, mac_ring_handle_t mrh, mblk_t *mp_chain, int64_tmr_gen_num)
mh_handle は、デバイスドライバが mac_register() 関数経由でのカーネルへの登録時に取得した MAC ハンドルに対応しています。mrh _handle は、mr_rget() 呼び出しの一部としてドライバに渡されたフレームワークリングハンドルです。mr_gen_num は、mri_start() エントリポイント経由で受信リングを開始する際にフレームワークによって指定された生成番号に設定する必要があります。ドライバから提供されたリング生成番号は、フレームワークに保持されているリング生成番号と比較されます。これらが一致しない場合、受信されたパケットはそのリングの古い割り当てからの古いパケットであるとみなされ、ドロップされます。
割り込み駆動のパス経由でパケットを受信できるだけでなく、フレームワークではポーリングベースのデータパスもサポートされています。ポーリングモードでは、スタック内で実行されているカーネルスレッドが、ポーリングエントリポイント経由でドライバからパケットを取得します。このため、スタックは、パケットを処理するタイミングやその優先度を効率的に制御しながら、システムに入ってくる割り込みの数を実際の負荷に基づいて減らすことができます。さらに、スタックはポーリングを使用することで、受信トラフィックに対して帯域幅制限をより効率的に適用できますが、これは特に、仮想化シナリオではクリティカルになります。ホストは、割り込みモードとポーリングモードを必要に応じて切り替えます。あるリングがポーリングモードになっている場合、ドライバはその受信リング経由で受信されたパケットを mac_rx_ring() 関数を使って配信すべきではありません。ポーリングモードでは割り込みが無効化されるため、このことが保証されます。代わりにフレームワークは、ドライバによって mac_ring_info 構造体の一部として公開された mri_poll() エントリポイントを呼び出します。詳細は、「mr_rget() エントリポイント」を参照してください。
デフォルトでは、開始後のリングは割り込みモードになっているはずです。リングは、割り込みモードになっているかぎり、受信したパケットをエントリポイント経由でチェーンの形式で渡すべきです。ホストは、リングをポーリングモードに切り替える際に、その割り込みを無効化するために、mac_ring_info 構造体経由で事前に公開された mac_intr 構造体を介してエントリポイントを呼び出します。
ドライバでハードウェアチェックサムのサポート (「ハードウェアチェックサムオフロード」を参照) を指定した場合、ドライバは mac_hcksum_set(9F) 関数を使用して、ハードウェアによるチェックサム計算のメタデータをパケットと関連付ける必要があります。
VLAN パケットは、そのタグとともに MAC 層に渡される必要があります。パケットから VLAN ヘッダーを取り除かないでください。ただし、ハードウェアが VLAN ストリップをサポートしており、かつフレームワークがハードウェアに VLAN タグのストリップをリクエストした場合、ハードウェアは VLAN タグをストリップすることでパフォーマンスを改善できます。詳細は、「mr_gget() エントリポイント」を参照してください。
ドライバは次の関数を呼び出して、ドライバの状態が変化したことをネットワークスタックに通知できます。
void mac_tx_update(mac_handle_t mh);
void mac_tx_ring_update(mac_handle_t mh, mac_ring_handle_t rh)
mac_tx_update(9F) または mac_tx_ring(9F) 関数は、より多くの TX 記述子が使用可能になったことをフレームワークに通知します。空でないパケットチェーンを mc_tx() または mri_tx() が返した場合、ドライバはリソースが利用可能になったらただちに mac_tx_update() または mac_tx_ring_update() を呼び出して、未送信として返送されたパケットの再送を試みるよう MAC 層に通知する必要があります。mc_tx() および mri_tx() エントリポイントの詳細は、「送信データパス」を参照してください。
void mac_link_update(mac_handle_t mh, link_state_t new_state);
mac_link_update(9F) 関数は、メディアリンクの状態が変化したことを MAC 層に通知します。new_state 引数は次のいずれかの値である必要があります。
メディアリンクは稼働しています。
メディアリンクはダウンしています。
メディアリンクの状態は不明です。
デバイスドライバは、管理対象のデバイスインスタンスに関する一連の統計情報を管理します。MAC 層は、ドライバの mc_getstat(9E) エントリポイントを介してこれらの統計情報を照会します。
int mc_getstat(void *driver_handle, uint_t stat, uint64_t *stat_value);
GLDv3 フレームワークは stat を使用して、照会する統計情報を指定します。ドライバは stat_value を使用して、stat で指定された統計情報の値を返します。統計情報の値が返された場合、mc_getstat() は 0 を返す必要があります。stat の統計情報がドライバでサポートされていない場合、mc_getstat() は ENOTSUP を返す必要があります。
GLDv3 でサポートされる統計情報は、汎用の MAC 統計情報と Ethernet 固有の統計情報を合わせたものです。サポートされる統計情報の完全な一覧については、mc_getstat(9E) のマニュアルページを参照してください。
例 19-7 mc_getstat() エントリポイント
int xx_m_getstat(void *arg, uint_t stat, uint64_t *val) { xx_t *xxp = arg; mutex_enter(&xxp->xx_xmtlock); if ((xxp->xx_flags & (XX_RUNNING|XX_SUSPENDED)) == XX_RUNNING) xx_reclaim(xxp); mutex_exit(&xxp->xx_xmtlock); switch (stat) { case MAC_STAT_MULTIRCV: *val = xxp->xx_multircv; break; /* ... */ case ETHER_STAT_MACRCV_ERRORS: *val = xxp->xx_macrcv_errors; break; /* ... */ default: return (ENOTSUP); } return (0); }
mri_stat() リングエントリポイントは、リング機能をサポートするすべてのデバイスドライバで実装する必要のある必須のリングエントリポイントです。このエントリポイントは、ハードウェア送信リングおよび受信リングのそれぞれに維持された統計のクエリーを行うために、フレームワークによって使用されます。
ハードウェア送信リングの場合、フレームワークは次の統計のクエリーを行います。
MAC_STAT_OERRORS
MAC_STAT_OBYTES
MAC_STAT_OPACKETS
ハードウェア受信リングの場合、フレームワークは次の統計のクエリーを行います。
MAC_STAT_IERRORS
MAC_STAT_RBYTES
MAC_STAT_IPACKETS
プロパティーの不変属性を返すには、mc_propinfo(9E) エントリポイントを使用します。この情報にはアクセス権、デフォルト値、および値の許容範囲が含まれています。この特定のドライバインスタンスのプロパティー値を設定するには、mc_setprop(9E) を使用します。プロパティーの現在の値を返すには、mc_getprop(9E)を使用します。
プロパティーとその型の完全な一覧については、mc_propinfo(9E) のマニュアルページを参照してください。
mc_propinfo() エントリポイントは、mac_prop_info_set_perm()、mac_prop_info_set_default()、および mac_prop_info_set_range() 関数を呼び出して、デフォルト値、アクセス権、値の許容範囲など、照会するプロパティーの特定の属性を関連付けます。
mac_prop_info_set_default_uint8(9F)、mac_prop_info_set_default_str(9F)、および mac_prop_info_set_default_link_flowctrl(9F) 関数は、デフォルト値を特定のプロパティーと関連付けます。mac_prop_info_set_range_uint32(9F) 関数は、特定のプロパティーについて値の許容範囲を関連付けます。
mac_prop_info_set_perm(9F) 関数はプロパティーのアクセス権を指定します。アクセス権は次のいずれかの値になります。
プロパティーは読み取り専用
プロパティーは書き込み専用
プロパティーは読み書き可能
mc_propinfo() エントリポイントで特定のプロパティーについて mac_prop_info_set_perm() を呼び出さない場合、GLDv3 フレームワークはそのプロパティーのアクセス権を読み書き可能 (MAC_PROP_PERM_RW) と想定します。
mc_propinfo(9E) のマニュアルページに列挙されたプロパティーに加えて、ドライバではそのドライバ専用のプロパティーも公開できます。ドライバでサポートするドライバ専用プロパティーを指定するには、mac_register 構造体の m_priv_props フィールドを使用します。フレームワークは MAC_PROP_PRIVATE プロパティー ID を mc_setprop()、mc_getprop()、または mc_propinfo() に渡します。詳細は、mc_propinfo (9E) のマニュアルページを参照してください。
次の表は、GLDv3 ネットワークデバイスドライバのフレームワークを構成するエントリポイント、その他の DDI 関数、およびデータ構造体の一覧です。
表 19-1 GLDv3 のインタフェース
|