Enviando dados de rede com anéis de rede

Os drivers de cliente NetAdapterCx enviam dados de rede quando a estrutura invoca sua função de retorno de chamada EvtPacketQueueAdvance para uma fila de transmissão. Durante esse retorno de chamada, os drivers de cliente postam buffers do anel de fragmento da fila para o hardware e, em seguida, esvaziam os pacotes e fragmentos concluídos de volta para o sistema operacional.

Visão geral da operação de transmissão (Tx) pós e drenagem

A animação a seguir ilustra como um driver de cliente para uma NIC (cartão interface de rede) PCI simples executa operações de postagem e drenagem para uma fila de transmissão (Tx).

Animação mostrando as operações net ring post e drain para transmissão (Tx) para uma interface de rede PCI cartão.

Nesta animação, os pacotes pertencentes ao driver cliente são realçados em azul claro e azul escuro, e os fragmentos pertencentes ao driver cliente são realçados em amarelo e laranja. As cores mais claras representam a subseção drain dos elementos que o driver possui, enquanto as cores mais escuras representam a subseção pós-seção dos elementos que o driver possui.

Enviando dados em ordem

Aqui está uma sequência de postagem e drenagem típica para um driver cujo dispositivo transmite dados em ordem, como uma NIC PCI simples.

  1. Chame NetTxQueueGetRingCollection para recuperar a estrutura de coleção de anéis da fila de transmissão. Você pode armazenar isso no espaço de contexto da fila para reduzir as chamadas do driver. Use a coleção de anéis para recuperar o anel de pacote da fila de transmissão.
  2. Postar dados em hardware:
    1. Aloque uma variável UINT32 para o índice de pacotes e defina-a como NextIndex do anel de pacote, que é o início da subseção pós-seção do anel.
    2. Faça o seguinte em um loop:
      1. Obtenha um pacote chamando NetRingGetPacketAtIndex com o índice de pacotes.
      2. Verifique se esse pacote deve ser ignorado. Se ele precisar ser ignorado, pule para a etapa 6 desse loop. Caso contrário, continue.
      3. Obtenha os fragmentos desse pacote. Recupere o anel de fragmento da fila de transmissão da coleção de anéis, recupere o início dos fragmentos do pacote do membro FragmentIndex do pacote e recupere o final dos fragmentos do pacote chamando NetRingIncrementIndex com FragmentCount do pacote.
      4. Faça o seguinte em um loop:
        1. Chame NetRingGetFragmentAtIndex para obter um fragmento.
        2. Traduza o descritor NET_FRAGMENT para o descritor de fragmento de hardware associado.
        3. Avance o índice de fragmento chamando NetRingIncrementIndex.
      5. Atualize o índice Next do anel de fragmento para corresponder ao Índice atual do iterador de fragmento, que indica que a postagem no hardware foi concluída.
      6. Avance o índice de pacotes chamando NetRingIncrementIndex.
    3. Atualize o NextIndex do anel de pacotes para o índice de pacotes para finalizar a postagem de pacotes no hardware.
  3. Esvaziar pacotes de transmissão concluídos para o sistema operacional:
    1. Defina o índice de pacotes como BeginIndex do anel de pacote, que é o início da subseção drain do anel.
    2. Faça o seguinte em um loop:
      1. Obtenha um pacote chamando NetRingGetPacketAtIndex com o índice de pacotes.
      2. Verifique se o pacote terminou a transmissão. Se não tiver, saia do loop.
      3. Avance o índice de pacotes chamando NetRingIncrementIndex.
    3. Atualize BeginIndex do anel de pacotes para o índice de pacotes para finalizar a postagem de pacotes no hardware.

Essas etapas podem ter esta aparência no código. Observe que os detalhes específicos do hardware, como como postar descritores no hardware ou liberar uma transação pós-transação bem-sucedida, são deixados de fora para maior clareza.

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

Enviando dados fora de ordem

Para drivers cujos dispositivos podem concluir transmissões fora de ordem, a principal diferença dos dispositivos em ordem está em quem aloca os buffers de transmissão e como o driver lida com o teste para conclusão da transmissão. Para transmissões em ordem com uma NIC PCI compatível com DMA, o sistema operacional normalmente aloca, anexa e, por fim, possui os buffers de fragmento. Em seguida, em ordem, o driver cliente pode testar o sinalizador de propriedade de hardware correspondente de cada fragmento durante EvtPacketQueueAdvance.

Ao contrário desse modelo, considere uma NIC típica baseada em USB. Nessa situação, a pilha USB possui os buffers de memória para transmissão e esses buffers podem estar localizados em outro lugar na memória do sistema. A pilha USB indica conclusões para o driver cliente fora de ordem, portanto, o driver cliente precisa registrar a conclusão de um pacote status separadamente durante sua rotina de retorno de chamada de conclusão. Para fazer isso, o driver do cliente pode usar o campo Scratch do pacote ou pode usar algum outro método, como armazenar informações em seu espaço de contexto de fila. Em seguida, na chamada para EvtPacketQueueAdvance, o driver do cliente verifica essas informações para testes de conclusão de pacote.