ネット リングを使用したネットワーク データの受信
NetAdapterCx クライアント ドライバーは、フレームワークが受信キューの EvtPacketQueueAdvance コールバック関数を呼び出すときに、ネットワーク データを受信します。 6 このコールバック中に、クライアント ドライバーは、受信したフラグメントとパケットを OS にドレインして受信を示し、新しいバッファーをハードウェアに投稿します。
受信 (Rx) ポストおよびドレイン動作の概要
次のアニメーションは、単純な PCI ネットワーク インターフェイス カード (NIC) のクライアント ドライバーが、受信 (Rx) キューのポスト操作とドレイン操作を実行する方法を示しています。 このシナリオ例のフラグメント バッファーは、OS によってフラグメント リングに割り当てられ、アタッチされます。 この例では、ハードウェア受信キューと OS 受信キューの間に 1 対 1 の関係があることを前提としています。
このアニメーションでは、クライアント ドライバーが所有するパケットが水色と濃い青色で強調表示され、クライアント ドライバーが所有するフラグメントが黄色とオレンジ色で強調表示されます。 明るい色は、ドライバーが所有する要素の drain サブセクションを表し、暗い色は、ドライバーが所有する要素の post サブセクションを表します。
データを順番に受信する
以下で、パケットごとに 1 つのフラグメントを使用して、データを順番に受信するドライバーの一般的なシーケンスを示します。
- NetRxQueueGetRingCollection を呼び出して、受信キューのリング コレクション構造を取得します。 これをキューのコンテキスト空間に格納して、ドライバーからの呼び出しを減らすことができます。 リング コレクションを使用して、受信キューのフラグメント リングとパケット リングのドレイン反復子を取得します。
- 受信したデータを OS に知らせるには、ネット リングをドレインします。
- フラグメント リングの現在のインデックスとパケット リングの現在のインデックスを追跡するための UINT32 変数を割り当てます。 これらの変数を、それぞれのネット リングの BeginIndex (リングのドレイン サブセクションの始点) に設定します。 フラグメント リングの NextIndex に設定して、フラグメント リングのドレイン セクションの末尾に UINT32 変数を割り当てます。
- ループで次の操作を行います。
- フラグメントがハードウェアによって受信されたかどうかを確認します。 そうでない場合は、ループから抜け出します。
- フラグメントを取得するには、NetRingGetFragmentAtIndex を呼び出します。
- 一致するハードウェア記述子に基づいて、フラグメントの情報 (ValidLength など) を入力します。
- NetRingGetPacketAtIndex を呼び出して、このフラグメントのパケットを取得します。
- パケットの FragmentIndex をフラグメント リング内のフラグメントの現在のインデックスに設定し、フラグメントの数を適切に設定して、フラグメントをパケットにバインドします (この例では、1 に設定されています)。
- 必要に応じて、チェックサム情報などのその他のパケット情報を入力します。
- NetRingIncrementIndex を呼び出して、フラグメント インデックスを進めます。
- NetRingIncrementIndex を呼び出して、パケット インデックスを進めます。
- フラグメント リングの BeginIndex を現在のフラグメント インデックス変数に更新し、パケット リングの BeginIndex を現在のパケット インデックスに更新して、受信したパケットとそのフラグメントを OS に示すファイナライズを行います。
- 次の受信のためにフラグメントバッファをハードウェアにポストします。
- 現在のフラグメント インデックスを、フラグメント リングの NextIndex (リングの post サブセクションの先頭) に設定します。 フラグメント終了インデックスをフラグメント リングの EndIndex に設定します。
- ループで次の操作を行います。
- フラグメントの情報を、一致するハードウェア記述子にポストします。
- NetRingIncrementIndex を呼び出して、フラグメント インデックスを進めます。
- 現在のフラグメント インデックスを、フラグメント リングの NextIndex (リングの post サブセクションの先頭) に設定します
これらの手順は、コードでは次のようになります。
void
MyEvtRxQueueAdvance(
NETPACKETQUEUE RxQueue
)
{
//
// Retrieve the receive queue's ring collection and net rings.
// This example stores the Rx queue's ring collection in its queue context space.
//
PMY_RX_QUEUE_CONTEXT rxQueueContext = MyGetRxQueueContext(RxQueue);
NET_RING_COLLECTION const * ringCollection = rxQueueContext->RingCollection;
NET_RING * packetRing = ringCollection->Rings[NET_RING_TYPE_PACKET];
NET_RING * fragmentRing = ringCollection->Rings[NET_RING_TYPE_FRAGMENT];
UINT32 currentPacketIndex = 0;
UINT32 currentFragmentIndex = 0;
UINT32 fragmentEndIndex = 0;
//
// Indicate receives by draining the rings
//
currentPacketIndex = packetRing->BeginIndex;
currentFragmentIndex = fragmentRing->BeginIndex;
fragmentEndIndex = fragmentRing->NextIndex;
while(currentFragmentIndex != fragmentEndIndex)
{
// Test for fragment reception. Break if fragment has not been received.
...
//
NET_FRAGMENT * fragment = NetRingGetFragmentAtIndex(fragmentRing, currentFragmentIndex);
fragment->ValidLength = ... ;
NET_PACKET * packet = NetRingGetPacketAtIndex(packetRing, currentPacketIndex);
packet->FragmentIndex = currentFragmentIndex;
packet->FragmentCount = 1;
if(rxQueueContext->IsChecksumExtensionEnabled)
{
// Fill in checksum info
...
//
}
currentFragmentIndex = NetRingIncrementIndex(fragmentRing, currentFragmentIndex);
currentPacketIndex = NetRingIncrementIndex(packetRing, currentPacketIndex);
}
fragmentRing->BeginIndex = currentFragmentIndex;
packetRing->BeginIndex = currentPacketIndex;
//
// Post fragment buffers to hardware
//
currentFragmentIndex = fragmentRing->NextIndex;
fragmentEndIndex = fragmentRing->EndIndex;
while(currentFragmentIndex != fragmentEndIndex)
{
// Post fragment information to hardware descriptor
...
//
currentFragmentIndex = NetRingIncrementIndex(fragmentRing, currentFragmentIndex);
}
fragmentRing->NextIndex = currentFragmentIndex;
}
順不同でデータを受信する
Tx キューとは異なり、ハードウェア受信キューごとに 1 つの OS 受信キューがある場合、クライアント ドライバーは通常、データを順不同で受信しません。 これは、クライアント ドライバーの NIC の種類とは関係ありません。 デバイスが PCI ベースで、OS が受信バッファーを割り当てて所有しているか、デバイスが USB ベースで USB スタックが受信バッファーを所有しているかにかかわらず、クライアント ドライバーは受信したフラグメントごとにパケットを初期化し、OS に示します。 この場合、順序は重要ではありません。
ハードウェアが 1 つのハードウェア受信キューに複数の OS 受信キューをサポートしている場合は、受信バッファーへのアクセスを同期する必要があります。 その範囲はこのトピックの範囲外であり、ハードウェアの設計によって異なります。