ネット リングを使用したネットワーク データの受信

NetAdapterCx クライアント ドライバーは、フレームワークが受信キューの EvtPacketQueueAdvance コールバック関数を呼び出すときに、ネットワーク データを受信します。 6 このコールバック中に、クライアント ドライバーは、受信したフラグメントとパケットを OS にドレインして受信を示し、新しいバッファーをハードウェアに投稿します。

受信 (Rx) ポストおよびドレイン動作の概要

次のアニメーションは、単純な PCI ネットワーク インターフェイス カード (NIC) のクライアント ドライバーが、受信 (Rx) キューのポスト操作とドレイン操作を実行する方法を示しています。 このシナリオ例のフラグメント バッファーは、OS によってフラグメント リングに割り当てられ、アタッチされます。 この例では、ハードウェア受信キューと OS 受信キューの間に 1 対 1 の関係があることを前提としています。

Animation illustrating net ring post and drain operations for receive for a PCI network interface card.

このアニメーションでは、クライアント ドライバーが所有するパケットが水色と濃い青色で強調表示され、クライアント ドライバーが所有するフラグメントが黄色とオレンジ色で強調表示されます。 明るい色は、ドライバーが所有する要素の drain サブセクションを表し、暗い色は、ドライバーが所有する要素の post サブセクションを表します。

データを順番に受信する

以下で、パケットごとに 1 つのフラグメントを使用して、データを順番に受信するドライバーの一般的なシーケンスを示します。

  1. NetRxQueueGetRingCollection を呼び出して、受信キューのリング コレクション構造を取得します。 これをキューのコンテキスト空間に格納して、ドライバーからの呼び出しを減らすことができます。 リング コレクションを使用して、受信キューのフラグメント リングとパケット リングのドレイン反復子を取得します。
  2. 受信したデータを OS に知らせるには、ネット リングをドレインします。
    1. フラグメント リングの現在のインデックスとパケット リングの現在のインデックスを追跡するための UINT32 変数を割り当てます。 これらの変数を、それぞれのネット リングの BeginIndex (リングのドレイン サブセクションの始点) に設定します。 フラグメント リングの NextIndex に設定して、フラグメント リングのドレイン セクションの末尾に UINT32 変数を割り当てます。
    2. ループで次の操作を行います。
      1. フラグメントがハードウェアによって受信されたかどうかを確認します。 そうでない場合は、ループから抜け出します。
      2. フラグメントを取得するには、NetRingGetFragmentAtIndex を呼び出します。
      3. 一致するハードウェア記述子に基づいて、フラグメントの情報 (ValidLength など) を入力します。
      4. NetRingGetPacketAtIndex を呼び出して、このフラグメントのパケットを取得します。
      5. パケットの FragmentIndex をフラグメント リング内のフラグメントの現在のインデックスに設定し、フラグメントの数を適切に設定して、フラグメントをパケットにバインドします (この例では、1 に設定されています)。
      6. 必要に応じて、チェックサム情報などのその他のパケット情報を入力します。
      7. NetRingIncrementIndex を呼び出して、フラグメント インデックスを進めます。
      8. NetRingIncrementIndex を呼び出して、パケット インデックスを進めます。
    3. フラグメント リングの BeginIndex を現在のフラグメント インデックス変数に更新し、パケット リングの BeginIndex を現在のパケット インデックスに更新して、受信したパケットとそのフラグメントを OS に示すファイナライズを行います。
  3. 次の受信のためにフラグメントバッファをハードウェアにポストします。
    1. 現在のフラグメント インデックスを、フラグメント リングの NextIndex (リングの post サブセクションの先頭) に設定します。 フラグメント終了インデックスをフラグメント リングの EndIndex に設定します。
    2. ループで次の操作を行います。
      1. フラグメントの情報を、一致するハードウェア記述子にポストします。
      2. NetRingIncrementIndex を呼び出して、フラグメント インデックスを進めます。
    3. 現在のフラグメント インデックスを、フラグメント リングの 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 受信キューをサポートしている場合は、受信バッファーへのアクセスを同期する必要があります。 その範囲はこのトピックの範囲外であり、ハードウェアの設計によって異なります。