Envoi de données réseau avec des anneaux nets

Les pilotes clients NetAdapterCx envoient des données réseau lorsque l’infrastructure appelle sa fonction de rappel EvtPacketQueueAdvance pour une file d’attente de transmission. Pendant ce rappel, les pilotes clients publient les mémoires tampons de l’anneau de fragments de la file d’attente vers le matériel, puis vident les paquets et fragments terminés vers le système d’exploitation.

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

L’animation suivante montre comment un pilote client pour une simple interface réseau PCI carte (NIC) effectue des opérations de post-émission et de drainage pour une file d’attente de transmission (Tx).

Animation montrant les opérations de poteau et de drainage d’anneau net pour la transmission (Tx) pour une interface réseau PCI carte.

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.

Envoi de données dans l’ordre

Voici une séquence de post-drain typique pour un pilote dont l’appareil transmet des données dans l’ordre, comme une simple carte réseau PCI.

  1. Appelez NetTxQueueGetRingCollection pour récupérer la structure de collection d’anneau de la file d’attente de transmission. 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’anneau de paquets de la file d’attente de transmission.
  2. Publier des données sur le matériel :
    1. Allouez une variable UINT32 pour l’index de paquets et définissez-le sur NextIndex de l’anneau de paquets, qui est le début de la post-sous-section de l’anneau.
    2. Effectuez les opérations suivantes dans une boucle :
      1. Obtenez un paquet en appelant NetRingGetPacketAtIndex avec l’index de paquets.
      2. Vérifiez si ce paquet doit être ignoré. S’il doit être ignoré, passez à l’étape 6 de cette boucle. Si ce n’est pas le cas, continuez.
      3. Obtenez les fragments de ce paquet. Récupérez l’anneau de fragments de la file d’attente de transmission à partir de la collection d’anneau, récupérez le début des fragments du paquet à partir du membre FragmentIndex du paquet, puis récupérez la fin des fragments du paquet en appelant NetRingIncrementIndex avec fragmentCount du paquet.
      4. Effectuez les opérations suivantes dans une boucle :
        1. Appelez NetRingGetFragmentAtIndex pour obtenir un fragment.
        2. Traduire le descripteur NET_FRAGMENT en descripteur de fragment matériel associé.
        3. Faites avancer l’index de fragment en appelant NetRingIncrementIndex.
      5. Mettez à jour l’index Suivant de l’anneau de fragments pour qu’il corresponde à l’index actuel de l’itérateur de fragments, ce qui indique que la publication sur le matériel est terminée.
      6. Faites avancer l’index de paquets en appelant NetRingIncrementIndex.
    3. Mettez à jour nextIndex de l’anneau de paquets vers l’index de paquets pour finaliser la publication des paquets sur le matériel.
  3. Vider les paquets de transmission terminés vers le système d’exploitation :
    1. Définissez l’index de paquet sur BeginIndex de l’anneau de paquets, qui est le début de la sous-section drain de l’anneau.
    2. Effectuez les opérations suivantes dans une boucle :
      1. Obtenez un paquet en appelant NetRingGetPacketAtIndex avec l’index de paquets.
      2. Vérifiez si la transmission du paquet est terminée. Si ce n’est pas le cas, sortez de la boucle.
      3. Faites avancer l’index de paquets en appelant NetRingIncrementIndex.
    3. Mettez à jour BeginIndex de l’anneau de paquets vers l’index de paquets pour finaliser la publication des paquets sur le matériel.

Ces étapes peuvent ressembler à ceci dans le code. Notez que les détails spécifiques au matériel, tels que la façon de publier des descripteurs sur du matériel ou le vidage d’une transaction post-transaction réussie, sont laissés de côté pour des motifs de clarté.

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

Envoi de données dans le désordre

Pour les pilotes dont les périphériques peuvent terminer les transmissions dans le désordre, la principale différence par rapport aux appareils en ordre réside dans qui alloue les mémoires tampons de transmission et la façon dont le pilote gère le test d’achèvement de la transmission. Pour les transmissions dans l’ordre avec une carte réseau PCI compatible DMA, le système d’exploitation alloue, attache et possède en fin de compte les mémoires tampons de fragments. Ensuite, dans l’ordre, le pilote client peut tester l’indicateur de propriété matérielle correspondant de chaque fragment pendant EvtPacketQueueAdvance.

Contrairement à ce modèle, considérez une carte réseau usb classique. Dans ce cas, la pile USB possède les mémoires tampons pour la transmission et ces mémoires tampons peuvent se trouver ailleurs dans la mémoire système. La pile USB indique les achèvements au pilote client dans le désordre. Le pilote client doit donc enregistrer l’achèvement d’un paquet status séparément pendant sa routine de rappel d’achèvement. Pour ce faire, le pilote client peut utiliser le champ Scratch du paquet ou utiliser une autre méthode telle que le stockage d’informations dans son espace de contexte de file d’attente. Ensuite, dans l’appel à EvtPacketQueueAdvance, le pilote client vérifie ces informations pour les tests de saisie semi-automatique de paquets.