Share via


Empfangen von Netzwerkdaten mit Netzringen

NetAdapterCx-Clienttreiber empfangen Netzwerkdaten, wenn das Framework ihre EvtPacketQueueAdvance-Rückruffunktion für eine Empfangswarteschlange aufruft. Während dieses Rückrufs zeigen Clienttreiber Empfangen an, indem sie empfangene Fragmente und Pakete an das Betriebssystem übertragen und dann neue Puffer auf hardwareseitig bereitstellen.

Übersicht über Empfangsvorgänge (Rx) nach ablaufen und abfließen

Die folgende Animation veranschaulicht, wie ein Clienttreiber für eine einfache PCI-Netzwerkschnittstelle Karte (NIC) Post- und Drain-Vorgänge für eine Empfangswarteschlange (Rx) ausführt. Fragmentpuffer in diesem Beispielszenario werden vom Betriebssystem zugeordnet und an den Fragmentring angefügt. In diesem Beispiel wird eine 1:1-Beziehung zwischen der Hardware-Empfangswarteschlange und der Empfangswarteschlange des Betriebssystems vorausgesetzt.

Animation zur Veranschaulichung von Netzring-Post- und Drain-Vorgängen für den Empfang für eine PCI-Netzwerkschnittstelle Karte.

In dieser Animation werden die Pakete, die dem Clienttreiber gehören, hellblau und dunkelblau hervorgehoben, und Fragmente im Besitz des Clienttreibers werden gelb und orange hervorgehoben. Die helleren Farben stellen den Unterabschnitt des Abflusses der Elemente dar, die dem Treiber gehören, während die dunkleren Farben den Post-Unterabschnitt der Elemente darstellen, die der Treiber besitzt.

Empfangen von Daten in der Reihenfolge

Hier ist eine typische Sequenz für einen Treiber, der Daten in der Reihenfolge mit einem Fragment pro Paket empfängt.

  1. Rufen Sie NetRxQueueGetRingCollection auf, um die Ringsammlungsstruktur der Empfangswarteschlange abzurufen. Sie können dies im Kontextbereich der Warteschlange speichern, um Aufrufe aus dem Treiber zu reduzieren. Verwenden Sie die Ringauflistung, um den Iterator für den Fragmentring und den Paketring der Empfangswarteschlange abzurufen.
  2. Geben Sie die empfangenen Daten an das Betriebssystem an, indem Sie die Netzringe entladen:
    1. Ordnen Sie UINT32-Variablen zu, um den aktuellen Index des Fragmentrings und den aktuellen Index des Paketrings zu verfolgen. Legen Sie diese Variablen auf den BeginIndex ihrer jeweiligen Netzringe fest, der der Anfang des Abflussunterabschnitts des Rings ist. Ordnen Sie eine UINT32-Variable für das Ende des Abflussabschnitts des Fragmentrings zu, indem Sie sie auf NextIndex des Fragmentrings festlegen.
    2. Führen Sie die folgenden Schritte in einer Schleife aus:
      1. Überprüfen Sie, ob das Fragment von der Hardware empfangen wurde. Andernfalls brechen Sie die Schleife aus.
      2. Rufen Sie NetRingGetFragmentAtIndex auf, um ein Fragment abzurufen.
      3. Geben Sie die Informationen des Fragments ein, z. B. validLength, basierend auf dem entsprechenden Hardwaredeskriptor.
      4. Rufen Sie ein Paket für dieses Fragment ab, indem Sie NetRingGetPacketAtIndex aufrufen.
      5. Binden Sie das Fragment an das Paket, indem Sie den FragmentIndex des Pakets auf den aktuellen Index des Fragments im Fragmentring festlegen und die Anzahl der Fragmente entsprechend festlegen (in diesem Beispiel ist sie auf 1 festgelegt).
      6. Geben Sie optional alle anderen Paketinformationen ein, z. B. Prüfsummeninformationen.
      7. Erhöhen Sie den Fragmentindex, indem Sie NetRingIncrementIndex aufrufen.
      8. Erhöhen Sie den Paketindex, indem Sie NetRingIncrementIndex aufrufen.
    3. Aktualisieren Sie beginIndex des Fragmentrings auf die aktuelle Fragmentindexvariable, und aktualisieren Sie BeginIndex des Paketrings auf den aktuellen Paketindex, um die empfangenen Pakete und deren Fragmente für das Betriebssystem abzuschließen.
  3. Bereitstellen von Fragmentpuffern auf Hardware für die nächsten Empfänge:
    1. Legen Sie den aktuellen Fragmentindex auf nextIndex des Fragmentrings fest, der der Anfang des Post-Unterabschnitts des Rings ist. Legen Sie den Endindex des Fragments auf endIndex des Fragmentrings fest.
    2. Führen Sie die folgenden Schritte in einer Schleife aus:
      1. Stellen Sie die Informationen des Fragments an den entsprechenden Hardwaredeskriptor bereit.
      2. Erhöhen Sie den Fragmentindex, indem Sie NetRingIncrementIndex aufrufen.
    3. Aktualisieren Sie nextIndex des Fragmentrings auf die aktuelle Fragmentindexvariable, um die Veröffentlichung von Fragmenten auf Hardware abzuschließen.

Diese Schritte können im Code wie folgt aussehen:

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;
}

Empfangen von Daten in einer nicht ordnungsgemäßen Reihenfolge

Im Gegensatz zu einer Tx-Warteschlange empfangen Clienttreiber in der Regel keine Daten außerhalb der Reihenfolge, wenn sie über eine Empfangswarteschlange des Betriebssystems pro Hardware-Empfangswarteschlange verfügen. Dies ist unabhängig vom Typ der NIC des Clienttreibers. Unabhängig davon, ob das Gerät PCI-basiert ist und das Betriebssystem die Empfangspuffer zuordnet und besitzt, oder ob das Gerät USB-basiert und der USB-Stapel die Empfangspuffer besitzt, initialisiert der Clienttreiber ein Paket für jedes empfangene Fragment und gibt es dem Betriebssystem an. Die Reihenfolge ist in diesem Fall nicht wichtig.

Wenn Ihre Hardware mehr als eine Empfangswarteschlange des Betriebssystems pro Hardware-Empfangswarteschlange unterstützt, müssen Sie den Zugriff auf die Empfangspuffer synchronisieren. Der Umfang dieser Vorgehensweise liegt außerhalb dieses Themas und hängt vom Entwurf Ihrer Hardware ab.