Netwerkgegevens verzenden met net-ringen

NetAdapterCx-clientstuurprogramma's verzenden netwerkgegevens wanneer het framework hun EvtPacketQueueAdvance callback-functie voor een verzendwachtrij aanroept. Tijdens deze callback posten clientstuurprogramma's buffers van de fragmentring van de wachtrij door naar hardware en voeren vervolgens afgehandelde pakketten en fragmenten terug naar het besturingssysteem.

Overzicht van verzending (Tx) post- en afvoerbewerking

In de volgende animatie ziet u hoe een clientstuurprogramma voor een eenvoudige PCI-netwerkinterfacekaart (NIC) post- en afvoerbewerkingen uitvoert voor een verzendwachtrij (Tx).

Animatie van netring- en afvoerbewerkingen voor overdracht (Tx) voor een PCI-netwerkinterfacekaart.

In deze animatie worden de pakketten die eigendom zijn van het clientstuurprogramma gemarkeerd in lichtblauw en donkerblauw, en fragmenten die eigendom zijn van het clientstuurprogramma zijn gemarkeerd in geel en oranje. De lichtere kleuren vertegenwoordigen de afvoer subsectie van de elementen waarvan de bestuurder eigenaar is, terwijl de donkerdere kleuren de post subsectie van de elementen vertegenwoordigen waarvan de bestuurder eigenaar is.

Gegevens in volgorde verzenden

Hier volgt een typische post- en afvoervolgorde voor een stuurprogramma waarvan het apparaat gegevens op volgorde verzendt, zoals een eenvoudige PCI-NIC.

  1. Roep NetTxQueueGetRingCollection aan om de ringverzamelingstructuur van de verzendwachtrij op te halen. U kunt dit opslaan in de contextruimte van de wachtrij om het aantal oproepen vanuit de stuurprogramma te verminderen. Gebruik de ringverzameling om de pakketring van de verzendwachtrij op te halen.
  2. Gegevens posten op hardware:
    1. Wijs een UINT32-variabele toe voor de pakketindex en stel deze in op de NextIndexvan de pakketring. Dit is het begin van de postsubsectie van de ring.
    2. Ga als volgt te werk in een lus:
      1. Haal een pakket op door NetRingGetPacketAtIndex aan te roepen met de pakketindex.
      2. Controleer of dit pakket moet worden genegeerd. Als dit moet worden genegeerd, gaat u verder met stap 6 van deze lus. Zo niet, ga dan verder.
      3. Haal de fragmenten van dit pakket op. Haal de fragmentring van de verzendwachtrij uit de verzamelring op, haal het begin van de fragmenten van het pakket op uit het FragmentIndex- lid van het pakket en haal vervolgens het einde van de fragmenten van het pakket op door NetRingIncrementIndex aan te roepen met de FragmentCount-van het pakket.
      4. Voer het volgende uit in een lus:
        1. Roep NetRingGetFragmentAtIndex aan om een fragment op te halen.
        2. Vertaal de NET_FRAGMENT descriptor naar de bijbehorende hardware-fragmentdescriptor.
        3. Verhoog de fragmentindex door NetRingIncrementIndexaan te roepen.
      5. Werk de volgende index van de fragmenteringsring bij zodat deze overeenkomt met de huidige indexvan de fragmentiterator, wat aangeeft dat het posten naar de hardware is voltooid.
      6. Vergroot de pakketindex door NetRingIncrementIndexaan te roepen.
    3. Werk de NextIndex van de pakketring bij naar de pakketindex om te voltooien met het posten van pakketten naar de hardware.
  3. Afvoer van verzonden pakketten naar het OS.
    1. Stel de pakketindex in op de BeginIndexvan de pakketring, wat het begin is van de afvoersubsectie van de ring.
    2. Ga als volgt te werk in een lus:
      1. Haal een pakket op door NetRingGetPacketAtIndex aan te roepen met de pakketindex.
      2. Controleer of het pakket is verzonden. Als dat niet het geval is, verlaat u de lus.
      3. Vergroot de pakketindex door NetRingIncrementIndexaan te roepen.
    3. Werk de BeginIndex- van de pakketring bij naar de pakketindex om het posten van pakketten naar hardware te voltooien.

Deze stappen kunnen er als volgt uitzien in code. Houd er rekening mee dat hardwarespecifieke details, zoals het plaatsen van descriptors op hardware of het afronden van een succesvolle posttransactie, voor duidelijkheid worden weggelaten.

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

Gegevens buiten de volgorde verzenden

Voor stuurprogramma's waarvan de verzendingen buiten de volgorde kunnen worden voltooid, ligt het belangrijkste verschil met apparaten in de volgorde waarin de overdrachtsbuffers worden toegewezen en hoe het stuurprogramma de test voor de voltooiing van de transmissie afhandelt. Voor verzendingen in volgorde met een PCI NIC die geschikt is voor DMA, wijst het besturingssysteem doorgaans de fragmentbuffers toe, koppelt deze en wordt uiteindelijk de eigenaar ervan. Vervolgens kan het clientstuurprogramma de bijbehorende vlag voor hardware-eigendom testen tijdens EvtPacketQueueAdvance.

In tegenstelling tot dit model kunt u een typische NIC op basis van USB overwegen. In deze situatie is de USB-stack eigenaar van de geheugenbuffers voor transmissie en deze buffers kunnen zich elders in het systeemgeheugen bevinden. De USB-stack geeft voltooiingen aan het clientstuurprogramma door in een verkeerde volgorde, dus het clientstuurprogramma moet de voltooiingsstatus van een pakket afzonderlijk vastleggen tijdens zijn callbackroutine voor voltooiing. Hiervoor kan het clientstuurprogramma het veld Scratch van het pakket gebruiken of een andere methode gebruiken, zoals het opslaan van gegevens in de contextruimte van de wachtrij. Vervolgens controleert het clientstuurprogramma in de aanroep naar EvtPacketQueueAdvancedeze informatie voor het testen van pakketvoltooiing.