Envío de datos de red con anillos netos

Los controladores de cliente netAdapterCx envían datos de red cuando el marco invoca su función de devolución de llamada EvtPacketQueueAdvance para una cola de transmisión. Durante esta devolución de llamada, los controladores de cliente publican búferes del anillo de fragmentos de la cola al hardware y, a continuación, purgan los paquetes y fragmentos completados de nuevo en el sistema operativo.

Información general sobre la operación de post y purga de transmisión (Tx)

En la siguiente animación se muestra cómo un controlador de cliente para una tarjeta de interfaz de red PCI simple (NIC) realiza operaciones de post y purga para una cola de transmisión (Tx).

Animación en la que se muestran las operaciones de poste y purga de anillos netos para transmitir (Tx) para una tarjeta de interfaz de red PCI.

En esta animación, los paquetes propiedad del controlador cliente se resaltan en azul claro y azul oscuro, y los fragmentos propiedad del controlador cliente se resaltan en amarillo y naranja. Los colores más claros representan la subsección de purga de los elementos que posee el controlador, mientras que los colores más oscuros representan la subsección posterior de los elementos que posee el controlador.

Envío de datos en orden

Esta es una secuencia típica de post y purga para un controlador cuyo dispositivo transmite datos en orden, como una NIC PCI simple.

  1. Llame a NetTxQueueGetRingCollection para recuperar la estructura de colección de anillos de la cola de transmisión. Puede almacenarlo en el espacio de contexto de la cola para reducir las llamadas del controlador. Use la colección ring para recuperar el anillo de paquetes de la cola de transmisión.
  2. Publicar datos en hardware:
    1. Asigne una variable UINT32 para el índice de paquetes y establézcala en nextIndex del anillo de paquetes, que es el inicio de la subsección posterior del anillo.
    2. Realice lo siguiente en un bucle:
      1. Para obtener un paquete, llame a NetRingGetPacketAtIndex con el índice de paquetes.
      2. Compruebe si se debe omitir este paquete. Si se debe omitir, vaya al paso 6 de este bucle. Si no es así, continúe.
      3. Obtenga los fragmentos de este paquete. Recupere el anillo de fragmento de la cola de transmisión de la colección de anillos, recupere el principio de los fragmentos del paquete del miembro FragmentIndex del paquete y, a continuación, recupere el final de los fragmentos del paquete mediante una llamada a NetRingIncrementIndex con fragmentCount del paquete.
      4. Realice lo siguiente en un bucle:
        1. Llame a NetRingGetFragmentAtIndex para obtener un fragmento.
        2. Convierta el descriptor de NET_FRAGMENT en el descriptor de fragmento de hardware asociado.
        3. Avance el índice de fragmento llamando a NetRingIncrementIndex.
      5. Actualice el índice Next del anillo de fragmento para que coincida con el índice actual del iterador del fragmento, lo que indica que la publicación en el hardware está completa.
      6. Avance el índice de paquetes llamando a NetRingIncrementIndex.
    3. Actualice el NextIndex del anillo de paquetes al índice de paquetes para finalizar la publicación de paquetes en el hardware.
  3. Purgar los paquetes de transmisión completados al sistema operativo:
    1. Establezca el índice de paquetes en beginIndex del anillo de paquetes, que es el inicio de la subsección de purga del anillo.
    2. Realice lo siguiente en un bucle:
      1. Para obtener un paquete, llame a NetRingGetPacketAtIndex con el índice de paquetes.
      2. Compruebe si el paquete ha terminado de transmitirse. Si no lo tiene, interrumpa el bucle.
      3. Avance el índice de paquetes llamando a NetRingIncrementIndex.
    3. Actualice beginIndex del anillo de paquetes al índice de paquetes para finalizar la publicación de paquetes en el hardware.

Estos pasos podrían tener este aspecto en el código. Tenga en cuenta que los detalles específicos del hardware, como cómo publicar descriptores en hardware o vaciar una transacción posterior correcta, se dejan fuera para mayor claridad.

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

Envío de datos desordenados

En el caso de los controladores cuyos dispositivos podrían completar las transmisiones desordenados, la diferencia principal de los dispositivos en orden radica en quién asigna los búferes de transmisión y cómo controla el controlador la prueba para la finalización de la transmisión. Para las transmisiones en orden con una NIC PCI compatible con DMA, el sistema operativo normalmente asigna, adjunta y, en última instancia, posee los búferes de fragmentos. A continuación, en orden, el controlador cliente puede probar la marca de propiedad de hardware correspondiente de cada fragmento durante EvtPacketQueueAdvance.

A diferencia de este modelo, considere una NIC típica basada en USB. En esta situación, la pila USB posee los búferes de memoria para la transmisión y esos búferes pueden encontrarse en otro lugar de la memoria del sistema. La pila USB indica que las finalizaciones del controlador cliente no están ordenadas, por lo que el controlador cliente debe registrar el estado de finalización de un paquete por separado durante su rutina de devolución de llamada de finalización. Para ello, el controlador de cliente puede usar el campo Scratch del paquete o puede usar algún otro método, como almacenar información en su espacio de contexto de cola. A continuación, en la llamada a EvtPacketQueueAdvance, el controlador cliente comprueba esta información para las pruebas de finalización de paquetes.