Share via


Senden von Netzwerkdaten mit Netzringen

NetAdapterCx-Clienttreiber senden Netzwerkdaten, wenn das Framework ihre EvtPacketQueueAdvance-Rückruffunktion für eine Übertragungswarteschlange aufruft. Während dieses Rückrufs stellen Clienttreiber Puffer aus dem Fragmentring der Warteschlange auf Hardware bereit und führen dann abgeschlossene Pakete und Fragmente wieder an das Betriebssystem aus.

Übersicht über Übertragungsvorgänge (Tx) nach und ablaufen

Die folgende Animation veranschaulicht, wie ein Clienttreiber für eine einfache PCI-Netzwerkschnittstelle Karte (NIC) Post- und Drain-Vorgänge für eine Übertragungswarteschlange (Tx) ausführt.

Animation mit Netzring-Post- und Entleerungsvorgängen für die Übertragung (Tx) 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.

Senden von Daten in der reihenfolge

Hier ist eine typische Post- und Drain-Sequenz für einen Treiber, dessen Gerät Daten in der richtigen Reihenfolge überträgt, z. B. eine einfache PCI-NIC.

  1. Rufen Sie NetTxQueueGetRingCollection auf, um die Ringsammlungsstruktur der Übertragungswarteschlange abzurufen. Sie können dies im Kontextbereich der Warteschlange speichern, um Aufrufe aus dem Treiber zu reduzieren. Verwenden Sie die Ringsammlung, um den Paketring der Übertragungswarteschlange abzurufen.
  2. Bereitstellen von Daten auf Hardware:
    1. Ordnen Sie eine UINT32-Variable für den Paketindex zu, und legen Sie sie auf nextIndex des Paketrings fest, der den Anfang des Post-Unterabschnitts des Rings darstellt.
    2. Führen Sie die folgenden Schritte in einer Schleife aus:
      1. Rufen Sie ein Paket ab, indem Sie NetRingGetPacketAtIndex mit dem Paketindex aufrufen.
      2. Überprüfen Sie, ob dieses Paket ignoriert werden soll. Wenn sie ignoriert werden soll, fahren Sie mit Schritt 6 dieser Schleife fort. Andernfalls fahren Sie fort.
      3. Rufen Sie die Fragmente dieses Pakets ab. Rufen Sie den Fragmentring der Übertragungswarteschlange aus der Ringsammlung ab, rufen Sie den Anfang der Paketfragmente aus dem FragmentIndex-Member des Pakets ab, und rufen Sie dann das Ende der Fragmente des Pakets ab, indem Sie NetRingIncrementIndex mit dem FragmentCount des Pakets aufrufen.
      4. Führen Sie die folgenden Schritte in einer Schleife aus:
        1. Rufen Sie NetRingGetFragmentAtIndex auf, um ein Fragment abzurufen.
        2. Übersetzen Sie den NET_FRAGMENT-Deskriptor in den zugeordneten Hardwarefragmentdeskriptor.
        3. Erhöhen Sie den Fragmentindex, indem Sie NetRingIncrementIndex aufrufen.
      5. Aktualisieren Sie den Next-Index des Fragmentrings, um dem aktuellen Index des Fragmentiterators zu entsprechen, der angibt, dass die Bereitstellung an die Hardware abgeschlossen ist.
      6. Erhöhen Sie den Paketindex, indem Sie NetRingIncrementIndex aufrufen.
    3. Aktualisieren Sie den NextIndex des Paketrings auf den Paketindex, um die Bereitstellung von Paketen an die Hardware abzuschließen.
  3. Entleeren Sie abgeschlossene Übertragungspakete an das Betriebssystem:
    1. Legen Sie den Paketindex auf den BeginIndex des Paketrings fest, der den Anfang des Unterabschnitts "Drain" des Rings darstellt.
    2. Führen Sie die folgenden Schritte in einer Schleife aus:
      1. Rufen Sie ein Paket ab, indem Sie NetRingGetPacketAtIndex mit dem Paketindex aufrufen.
      2. Überprüfen Sie, ob die Übertragung des Pakets abgeschlossen ist. Falls nicht, brechen Sie die Schleife aus.
      3. Erhöhen Sie den Paketindex, indem Sie NetRingIncrementIndex aufrufen.
    3. Aktualisieren Sie beginIndex des Paketrings auf den Paketindex, um die Bereitstellung von Paketen an die Hardware abzuschließen.

Diese Schritte können im Code wie folgt aussehen. Beachten Sie, dass hardwarespezifische Details, z. B. das Posten von Deskriptoren auf der Hardware oder das Leeren einer erfolgreichen nach der Transaktion, aus Gründen der Übersichtlichkeit ausgelassen werden.

void
MyEvtTxQueueAdvance(
    NETPACKETQUEUE TxQueue
)
{
    //
    // Retrieve the transmit queue's ring collection and packet ring. 
    // This example stores the Tx queue's ring collection in its queue context space.
    //
    PMY_TX_QUEUE_CONTEXT txQueueContext = MyGetTxQueueContext(TxQueue);
    NET_RING_COLLECTION const * ringCollection = txQueueContext->RingCollection;
    NET_RING * packetRing = ringCollection->Rings[NET_RING_TYPE_PACKET];
    UINT32 currentPacketIndex = 0;

    //
    // Post data to hardware
    //      
    currentPacketIndex = packetRing->NextIndex;
    while(currentPacketIndex != packetRing->EndIndex)
    {
        NET_PACKET * packet = NetRingGetPacketAtIndex(packetRing, currentPacketIndex);        
        if(!packet->Ignore)
        {
            NET_RING * fragmentRing = ringCollection->Rings[NET_RING_TYPE_FRAGMENT];
            UINT32 currentFragmentIndex = packet->FragmentIndex;
            UINT32 fragmentEndIndex = NetRingIncrementIndex(fragmentRing, currentFragmentIndex + packet->FragmentCount - 1);
            
            for(txQueueContext->PacketTransmitControlBlocks[packetIndex]->numTxDescriptors = 0; 
                currentFragmentIndex != fragmentEndIndex; 
                txQueueContext->PacketTransmitControlBlocks[packetIndex]->numTxDescriptors++)
            {
                NET_FRAGMENT * fragment = NetRingGetFragmentAtIndex(fragmentRing, currentFragmentIndex);

                // Post fragment descriptor to hardware
                ...
                //

                currentFragmentIndex = NetRingIncrementIndex(fragmentRing, currentFragmentIndex);
            }

            //
            // Update the fragment ring's Next index to indicate that posting is complete and prepare for draining
            //
            fragmentRing->NextIndex = currentFragmentIndex;
        }
        currentPacketIndex = NetRingIncrementIndex(packetRing, currentPacketIndex);
    }
    packetRing->NextIndex = currentPacketIndex;

    //
    // Drain packets if completed
    //
    currentPacketIndex = packetRing->BeginIndex;
    while(currentPacketIndex != packetRing->NextIndex)
    {        
        NET_PACKET * packet = NetRingGetPacketAtIndex(packetRing, currentPacketIndex); 
        
        // Test packet for transmit completion by checking hardware ownership flags in the packet's last fragment
        // Break if transmit is not complete
        ...
        //
        
        currentPacketIndex = NetRingIncrementIndex(packetRing, currentPacketIndex);
    }
    packetRing->BeginIndex = currentPacketIndex;
}

Senden von Daten in nicht ordnungsgemäßer Reihenfolge

Bei Treibern, deren Geräte übertragungen möglicherweise in einer nicht ordnungsgemäßen Reihenfolge durchführen, besteht der Hauptunterschied zu den In-Order-Geräten darin, wer die Übertragungspuffer zuordnet und wie der Treiber den Test für den Übertragungsabschluss verarbeitet. Bei der Auftragsübertragung mit einer DMA-fähigen PCI-NIC weist das Betriebssystem die Fragmentpuffer in der Regel zu, fügt sie an und besitzt sie letztendlich. Anschließend kann der Clienttreiber das entsprechende Hardwarebesitzflag jedes Fragments während evtPacketQueueAdvance testen.

Im Gegensatz zu diesem Modell sollten Sie eine typische USB-basierte NIC in Betracht ziehen. In diesem Fall besitzt der USB-Stapel die Speicherpuffer für die Übertragung, und diese Puffer befinden sich möglicherweise an anderer Stelle im Systemspeicher. Der USB-Stapel zeigt vervollständigte Vervollständigungen für den Clienttreiber in nicht ordnungsgemäßer Reihenfolge an, sodass der Clienttreiber die Vervollständigung eines Pakets status während seiner Abschlussrückrufroutine separat aufzeichnen muss. Dazu kann der Clienttreiber entweder das Feld Scratch des Pakets oder eine andere Methode verwenden, z. B. das Speichern von Informationen in seinem Warteschlangenkontextbereich. Anschließend überprüft der Clienttreiber im Aufruf von EvtPacketQueueAdvance diese Informationen auf Paketvervollständigungstests.