Réception de données réseau avec des anneaux nets

Les pilotes clients NetAdapterCx reçoivent des données réseau lorsque l’infrastructure appelle sa fonction de rappel EvtPacketQueueAdvance pour une file d’attente de réception. Pendant ce rappel, les pilotes clients indiquent les réceptions en drainant les fragments et paquets reçus vers le système d’exploitation, puis en postant de nouvelles mémoires tampons sur le matériel.

Vue d’ensemble de l’opération post-vidange (Rx)

L’animation suivante montre comment un pilote client pour une simple interface réseau PCI carte (NIC) effectue des opérations de post-drainage pour une file d’attente de réception (Rx). Dans cet exemple de scénario, les mémoires tampons de fragments sont allouées et attachées à l’anneau de fragments par le système d’exploitation. Cet exemple suppose une relation un-à-un entre la file d’attente de réception matérielle et la file d’attente de réception du système d’exploitation.

Animation illustrant les opérations de poteau et de drainage de l’anneau net pour la réception d’une carte d’interface réseau PCI.

Dans cette animation, les paquets appartenant au pilote client sont mis en surbrillance en bleu clair et bleu foncé, et les fragments appartenant au pilote client sont mis en surbrillance en jaune et orange. Les couleurs plus claires représentent la sous-section drain des éléments que le pilote possède, tandis que les couleurs plus foncées représentent la sous-section post des éléments que le pilote possède.

Réception des données dans l’ordre

Voici une séquence classique pour un pilote qui reçoit des données dans l’ordre, avec un fragment par paquet.

  1. Appelez NetRxQueueGetRingCollection pour récupérer la structure de collection d’anneau de la file d’attente de réception. Vous pouvez le stocker dans l’espace de contexte de la file d’attente pour réduire les appels du pilote. Utilisez la collection d’anneau pour récupérer l’itérateur de drain pour l’anneau de fragments et l’anneau de paquets de la file d’attente de réception.
  2. Indiquez les données reçues au système d’exploitation en drainant les anneaux nets :
    1. Allouez des variables UINT32 pour le suivi de l’index actuel de l’anneau de fragments et de l’index actuel de l’anneau de paquets. Définissez ces variables sur BeginIndex de leurs anneaux nets respectifs, qui est le début de la sous-section drain de l’anneau. Allouez une variable UINT32 pour la fin de la section de drain de l’anneau de fragments en la définissant sur NextIndex de l’anneau de fragments.
    2. Effectuez les opérations suivantes dans une boucle :
      1. Vérifiez si le fragment a été reçu par le matériel. Si ce n’est pas le cas, sortez de la boucle.
      2. Appelez NetRingGetFragmentAtIndex pour obtenir un fragment.
      3. Renseignez les informations du fragment, telles que son ValidLength, en fonction de son descripteur matériel correspondant.
      4. Obtenez un paquet pour ce fragment en appelant NetRingGetPacketAtIndex.
      5. Liez le fragment au paquet en définissant FragmentIndex du paquet sur l’index actuel du fragment dans l’anneau de fragments et en définissant le nombre de fragments de manière appropriée (dans cet exemple, il est défini sur 1).
      6. Si vous le souhaitez, renseignez toutes les autres informations sur les paquets, telles que les informations de somme de contrôle.
      7. Faites avancer l’index de fragment en appelant NetRingIncrementIndex.
      8. Faites avancer l’index de paquets en appelant NetRingIncrementIndex.
    3. Mettez à jour beginIndex de l’anneau de fragments vers la variable d’index fragment actuelle et mettez à jour BeginIndex de l’anneau de paquets vers l’index de paquets actuel pour finaliser l’indication des paquets reçus et de leurs fragments au système d’exploitation.
  3. Postez les mémoires tampons de fragment sur le matériel pour les prochaines réceptions :
    1. Définissez l’index de fragment actuel sur NextIndex de l’anneau de fragments, qui est le début de la sous-section post de l’anneau. Définissez l’index de fin de fragment sur EndIndex de l’anneau de fragments.
    2. Effectuez les opérations suivantes dans une boucle :
      1. Publiez les informations du fragment dans le descripteur matériel correspondant.
      2. Faites avancer l’index de fragment en appelant NetRingIncrementIndex.
    3. Mettez à jour nextIndex de l’anneau de fragments vers la variable d’index de fragments actuel pour finaliser la publication de fragments sur le matériel.

Ces étapes peuvent ressembler à ceci dans le code :

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

Réception de données dans le désordre

Contrairement à une file d’attente Tx , les pilotes clients ne reçoivent généralement pas de données en désordre s’ils ont une file d’attente de réception du système d’exploitation par file d’attente de réception matérielle. Ceci quel que soit le type de carte réseau du pilote client. Que l’appareil soit basé sur PCI et que le système d’exploitation alloue et possède les mémoires tampons de réception, ou que l’appareil soit basé sur USB et que la pile USB possède les mémoires tampons de réception, le pilote client initialise un paquet pour chaque fragment reçu et l’indique au système d’exploitation. L’ordre n’est pas important dans ce cas.

Si votre matériel prend en charge plusieurs files d’attente de réception de système d’exploitation par file d’attente de réception matérielle, vous devez synchroniser l’accès aux mémoires tampons de réception. L’étendue de cette opération est en dehors de cette rubrique et dépend de la conception de votre matériel.