Ricezione di dati di rete con anelli di rete

I driver client NetAdapterCx ricevono dati di rete quando il framework richiama la funzione di callback EvtPacketQueueAdvance per una coda di ricezione. Durante questo callback, i driver client indicano che riceve svuotando frammenti e pacchetti ricevuti nel sistema operativo, quindi pubblicando nuovi buffer nell'hardware.

Panoramica delle operazioni di ricezione (Rx) post e svuotamento

L'animazione seguente illustra come un driver client per una semplice scheda di interfaccia di rete PCI esegue operazioni di post-svuotamento per una coda di ricezione (Rx). I buffer di frammenti in questo scenario di esempio vengono allocati e collegati all'anello del frammento dal sistema operativo. Questo esempio presuppone una relazione uno-a-uno tra la coda di ricezione hardware e la coda di ricezione del sistema operativo.

Animazione che illustra le operazioni di post e svuotamento dell'anello di rete per la ricezione di 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 i frammenti di proprietà del driver client sono evidenziati in giallo e arancione. I colori più chiari rappresentano la sottosezione di svuotamento degli elementi di proprietà del driver, mentre i colori più scuri rappresentano la sottosezione post degli elementi di proprietà del driver.

Ricezione dei dati in ordine

Ecco una sequenza tipica per un driver che riceve i dati in ordine, con un frammento per pacchetto.

  1. Chiamare NetRxQueueGetRingCollection per recuperare la struttura della raccolta circolare della coda di ricezione. È possibile archiviare questo valore nello spazio di contesto della coda per ridurre le chiamate al driver. Usare la raccolta circolare per recuperare l'iteratore di scarico per l'anello di frammento e l'anello di pacchetto della coda di ricezione.
  2. Indicare i dati ricevuti al sistema operativo svuotando gli anelli di rete:
    1. Allocare le variabili UINT32 per tenere traccia dell'indice corrente dell'anello del frammento e dell'indice corrente dell'anello di pacchetti. Impostare queste variabili sul BeginIndex dei rispettivi anelli netti, ovvero l'inizio della sottosezione di scarico dell'anello. Allocare una variabile UINT32 per la fine della sezione di svuotamento dell'anello del frammento impostandola su NextIndex dell'anello del frammento.
    2. Eseguire le operazioni seguenti in un ciclo:
      1. Controllare se il frammento è stato ricevuto dall'hardware. In caso contrario, interrompere il ciclo.
      2. Chiamare NetRingGetFragmentAtIndex per ottenere un frammento.
      3. Inserire le informazioni del frammento, ad esempio il relativo Valore ValidLength, in base al descrittore hardware corrispondente.
      4. Ottenere un pacchetto per questo frammento chiamando NetRingGetPacketAtIndex.
      5. Associare il frammento al pacchetto impostando FragmentIndex del pacchetto sull'indice corrente del frammento nell'anello del frammento e impostando il numero di frammenti in modo appropriato (in questo esempio è impostato su 1).
      6. Facoltativamente, compilare eventuali altre informazioni sui pacchetti, ad esempio le informazioni sul checksum.
      7. Avanzare l'indice del frammento chiamando NetRingIncrementIndex.
      8. Avanzare l'indice dei pacchetti chiamando NetRingIncrementIndex.
    3. Aggiornare beginIndex dell'anello del frammento alla variabile di indice del frammento corrente e aggiornare beginIndex dell'anello di pacchetto all'indice di pacchetto corrente per finalizzare l'indicazione dei pacchetti ricevuti e dei relativi frammenti nel sistema operativo.
  3. Post fragment buffers to hardware for the next receives( Post fragment buffers to hardware for the next receives:
    1. Impostare l'indice del frammento corrente sull'indice NextIndex dell'anello del frammento, ovvero l'inizio della sottosezione post dell'anello. Impostare l'indice finale del frammento sul valore EndIndex dell'anello del frammento.
    2. Eseguire le operazioni seguenti in un ciclo:
      1. Pubblicare le informazioni del frammento nel descrittore hardware corrispondente.
      2. Avanzare l'indice del frammento chiamando NetRingIncrementIndex.
    3. Aggiornare nextIndex dell'anello del frammento alla variabile di indice del frammento corrente per finalizzare la registrazione dei frammenti nell'hardware.

Questi passaggi potrebbero essere simili al seguente nel codice:

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

Ricezione di dati non in ordine

A differenza di una coda Tx , i driver client in genere non ricevono dati in ordine se hanno una coda di ricezione del sistema operativo per ogni coda di ricezione hardware. Indipendentemente dal tipo di scheda di interfaccia di rete del driver client. Se il dispositivo è basato su PCI e il sistema operativo alloca e possiede i buffer di ricezione o se il dispositivo è basato su USB e lo stack USB possiede i buffer di ricezione, il driver client inizializza un pacchetto per ogni frammento ricevuto e lo indica al sistema operativo. L'ordine non è importante in questo caso.

Se l'hardware supporta più code di ricezione del sistema operativo per ogni coda di ricezione hardware, è necessario sincronizzare l'accesso ai buffer di ricezione. L'ambito di questa operazione è esterno a questo argomento e dipende dalla progettazione dell'hardware.