| ナビゲーションリンクをスキップ | |
| 印刷ビューの終了 | |
|
デバイスドライバの記述 Oracle Solaris 10 1/13 Information Library (日本語) |
パート I Oracle Solaris プラットフォーム用デバイスドライバの設計
2. Oracle Solaris カーネルとデバイスツリー
Ethernet V2 および ISO 8802-3 (IEEE 802.3)
gldm_set_promiscuous() エントリポイント
22. ドライバのコンパイル、ロード、パッケージ化、およびテスト
GLDv3 フレームワークは、MAC プラグインと、MAC ドライバサービスのルーチンおよび構造体に対する、関数呼び出しベースのインタフェースです。GLDv3 フレームワークは、必要な STREAMS エントリポイントを GLDv3 準拠ドライバに代わって実装し、DLPI との互換性を実現します。
このセクションの内容は次のとおりです。
GLDv3 は、MAC_PLUGIN_IDENT_ETHER タイプのプラグインに登録するドライバ用のドライバ API を定義します。
GLDv3 デバイスドライバは、次の手順を実行して MAC 層に登録する必要があります。
sys/mac.h、sys/mac_ether.h、および sys/mac_provider.h の 3 つの 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.h、sys/mac_ether.h、および sys/mac_provider.h の 3 つの 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 (ラージセグメントオフロード) です。機能データをフレームワークに返すには、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;
}
default:
return (B_FALSE);
}
return (B_TRUE);
}
次のセクションからは、サポートされている機能と、対応する機能ごとに返されるデータについて説明します。
ハードウェアチェックサムオフロードのサポートについてのデータを取得するために、フレームワークは 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 フレームワークは、転送エントリポイント mc_tx(9E) を使用して、メッセージブロックのチェーンをドライバに渡します。mc_tx() エントリポイントへのポインタは mac_callbacks 構造体で指定します。mac_callbacks 構造体の詳細は、「GLDv3 の MAC 登録データ構造体」を参照してください。
例 19-6 mc_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_hcksum_get(9F) を使用して、ハードウェアチェックサムのメタデータの全パケットを確認します。
必要なチェックサム計算を実行するようにハードウェアをプログラミングします。
ドライバで LSO 機能 (「ラージセグメント (送信) オフロード」を参照) を指定した場合、ドライバは mac_lso_get(9F) を使用して、パケットに対して LSO を実行する必要があるかどうかを照会する必要があります。
管理者が VLAN を構成した場合、アウトバウンドパケットが mc_tx() エントリポイントを介してドライバに渡される前に、MAC 層が必要な VLAN ヘッダーをアウトバウンドパケットに挿入します。
ドライバの割り込みハンドラで mac_rx(9F) 関数を呼び出して、1 つ以上のパケットのチェーンをスタック上位の MAC 層に渡します。mac_rx() の呼び出し中に mutex ロックまたはその他のロックを保持しないでください。特に、mac_rx() の呼び出し中に転送スレッドによって取得される可能性があるロックを保持しないでください。MAC 層に送信される必要があるパケットについては、mc_unicst(9E)を参照してください。
次のセクションでは、MAC 層へのデータ送信に関連する事項について説明します。
ドライバでハードウェアチェックサムのサポート (「ハードウェアチェックサムオフロード」を参照) を指定した場合、ドライバは mac_hcksum_set(9F) 関数を使用して、ハードウェアによるチェックサム計算のメタデータをパケットと関連付ける必要があります。
VLAN パケットは、そのタグとともに MAC 層に渡される必要があります。パケットから VLAN ヘッダーを取り除かないでください。
ドライバは次の関数を呼び出して、ドライバの状態が変化したことをネットワークスタックに通知できます。
void mac_tx_update(mac_handle_t mh);
mac_tx_update(9F) 関数は、利用可能な TX 記述子が増えたことをフレームワークに通知します。空でないパケットチェーンを mc_tx() が返した場合、ドライバはリソースが利用可能になったらただちに mac_tx_update() を呼び出して、未送信として返送されたパケットの再送を試みるよう MAC 層に通知する必要があります。mc_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);
}
プロパティーの不変属性を返すには、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 のインタフェース
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||