Invio di dati di rete con anelli di rete

I driver client NetAdapterCx inviano dati di rete quando il framework richiama la funzione di callback EvtPacketQueueAdvance per una coda di trasmissione. Durante questo callback, i driver client pubblicano i buffer dall'anello di frammento della coda all'hardware, quindi svuotano i pacchetti e i frammenti completati al sistema operativo.

Panoramica dell'operazione di trasmissione (Tx)

L'animazione seguente illustra come un driver client per una semplice scheda di interfaccia di rete PCI (NIC) esegue operazioni di post e svuotamento per una coda di trasmissione (Tx).

Animazione che mostra le operazioni di post e svuotamento dell'anello netto per la trasmissione (Tx) per una scheda di interfaccia di rete PCI.

In questa animazione i pacchetti di proprietà del driver client sono evidenziati in blu chiaro e blu scuro e frammenti di proprietà del driver client sono evidenziati in giallo e arancione. I colori più leggeri rappresentano la sottosezione di scarico degli elementi proprietari del driver, mentre i colori più scuri rappresentano la sottosezione post degli elementi proprietari del driver.

Invio di dati in ordine

Ecco una tipica sequenza di post e svuotamento per un driver il cui dispositivo trasmette i dati in ordine, ad esempio una semplice scheda di interfaccia di interfaccia di rete PCI.

  1. Chiamare NetTxQueueGetRingCollection per recuperare la struttura della raccolta dell'anello della coda di trasmissione. È possibile archiviare questo oggetto nello spazio di contesto della coda per ridurre le chiamate al di fuori del driver. Usare la raccolta anello per recuperare l'anello del pacchetto della coda di trasmissione.
  2. Pubblicare i dati nell'hardware:
    1. Allocare una variabile UINT32 per l'indice dei pacchetti e impostarla sull'anello di pacchetto NextIndex, ovvero l'inizio della sottosezione post dell'anello.
    2. Eseguire le operazioni seguenti in un ciclo:
      1. Ottenere un pacchetto chiamando NetRingGetPacketAtIndex con l'indice dei pacchetti.
      2. Verificare se questo pacchetto deve essere ignorato. Se deve essere ignorato, passare al passaggio 6 di questo ciclo. In caso contrario, continuare.
      3. Ottenere i frammenti di questo pacchetto. Recuperare l'anello di frammento della coda di trasmissione dall'insieme anello, recuperare l'inizio dei frammenti del pacchetto dal membro FragmentIndex del pacchetto, quindi recuperare la fine dei frammenti del pacchetto chiamando NetRingIncrementIndex con Il frammento del pacchetto.
      4. Eseguire le operazioni seguenti in un ciclo:
        1. Chiamare NetRingGetFragmentAtIndex per ottenere un frammento.
        2. Tradurre il descrittore di NET_FRAGMENT nel descrittore del frammento hardware associato.
        3. Avanzare l'indice del frammento chiamando NetRingIncrementIndex.
      5. Aggiornare l'indice Next dell'anello del frammento in modo che corrisponda all'indice corrente del frammento, che indica che la registrazione all'hardware è stata completata.
      6. Avanzare l'indice dei pacchetti chiamando NetRingIncrementIndex.
    3. Aggiornare l'anello di pacchetto NextIndex all'indice dei pacchetti per finalizzare i pacchetti in hardware.
  3. Svuotare i pacchetti di trasmissione completati al sistema operativo:
    1. Impostare l'indice del pacchetto sull'anello BeginIndex del pacchetto, ovvero l'inizio della sottosezione di scarico dell'anello.
    2. Eseguire le operazioni seguenti in un ciclo:
      1. Ottenere un pacchetto chiamando NetRingGetPacketAtIndex con l'indice dei pacchetti.
      2. Verificare se il pacchetto ha completato la trasmissione. In caso contrario, interrompere il ciclo.
      3. Avanzare l'indice dei pacchetti chiamando NetRingIncrementIndex.
    3. Aggiornare l'anello di pacchetto BeginIndex all'indice dei pacchetti per finalizzare i pacchetti di registrazione all'hardware.

Questi passaggi potrebbero essere simili al seguente nel codice. Si noti che i dettagli specifici dell'hardware, ad esempio come pubblicare i descrittori nell'hardware o scaricare una transazione post riuscita, vengono lasciati per chiarezza.

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

Invio di dati non in ordine

Per i driver i cui dispositivi potrebbero completare le trasmissioni in ordine, la differenza primaria dei dispositivi in ordine si trova in chi alloca i buffer di trasmissione e come il driver gestisce il test per il completamento della trasmissione. Per le trasmissioni in ordine con una scheda di interfaccia di rete PCI compatibile con DMA, il sistema operativo in genere alloca, allega e in definitiva possiede i buffer di frammento. Quindi, in ordine, il driver client può testare il flag di proprietà hardware corrispondente di ogni frammento durante EvtPacketQueueAdvance.

Al contrario di questo modello, prendere in considerazione una tipica scheda di interfaccia di interfaccia di rete basata su USB. In questa situazione, lo stack USB possiede i buffer di memoria per la trasmissione e tali buffer potrebbero trovarsi altrove nella memoria di sistema. Lo stack USB indica i completamento al driver client non in ordine, quindi il driver client deve registrare separatamente lo stato di completamento di un pacchetto durante la routine di callback di completamento. A tale scopo, il driver client può usare il campo Scratch del pacchetto oppure può usare un altro metodo come l'archiviazione di informazioni nello spazio di contesto della coda. Quindi, nella chiamata a EvtPacketQueueAdvance, il driver client controlla queste informazioni per i test di completamento dei pacchetti.