Recebendo dados de rede com anéis de rede

Os drivers de cliente NetAdapterCx recebem dados de rede quando a estrutura invoca a função de retorno de chamada EvtPacketQueueAdvance para uma fila de recebimento. Durante esse retorno de chamada, os drivers de cliente indicam recebimentos drenando fragmentos e pacotes recebidos para o sistema operacional e, em seguida, postam novos buffers no hardware.

Visão geral da operação de pós-recebimento (Rx) e dreno

A animação a seguir ilustra como um driver de cliente para uma NIC (cartão interface de rede PCI) simples executa operações post and drain para uma fila de recebimento (Rx). Os buffers de fragmento neste cenário de exemplo são alocados e anexados ao anel de fragmento pelo sistema operacional. Este exemplo pressupõe uma relação um-para-um entre a fila de recebimento de hardware e a fila de recebimento do sistema operacional.

Animação ilustrando operações de postagem de anel de rede e esvaziamento para recebimento de 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 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.

Recebendo dados em ordem

Aqui está uma sequência típica para um driver que recebe dados em ordem, com um fragmento por pacote.

  1. Chame NetRxQueueGetRingCollection para recuperar a estrutura de coleção de anéis da fila de recebimento. Você pode armazená-lo no espaço de contexto da fila para reduzir as chamadas do driver. Use a coleção de anéis para recuperar o iterador de drenagem para o anel de fragmento e o anel de pacote da fila de recebimento.
  2. Indique os dados recebidos para o sistema operacional drenando os anéis de rede:
    1. Aloque variáveis UINT32 para acompanhar o índice atual do anel de fragmento e o índice atual do anel de pacotes. Defina essas variáveis como BeginIndex de seus respectivos anéis de rede, que é o início da subseção drain do anel. Aloque uma variável UINT32 para o final da seção esvaziar do anel de fragmento definindo-a como NextIndex do anel de fragmento.
    2. Faça o seguinte em um loop:
      1. Verifique se o fragmento foi recebido pelo hardware. Caso contrário, saia do loop.
      2. Chame NetRingGetFragmentAtIndex para obter um fragmento.
      3. Preencha as informações do fragmento, como o ValidLength, com base no descritor de hardware correspondente.
      4. Obtenha um pacote para esse fragmento chamando NetRingGetPacketAtIndex.
      5. Associe o fragmento ao pacote definindo FragmentIndex do pacote como o índice atual do fragmento no anel de fragmento e definindo o número de fragmentos adequadamente (neste exemplo, ele é definido como 1).
      6. Opcionalmente, preencha qualquer outra informação de pacote, como informações de soma de verificação.
      7. Avance o índice de fragmento chamando NetRingIncrementIndex.
      8. Avance o índice de pacotes chamando NetRingIncrementIndex.
    3. Atualize BeginIndex do anel de fragmento para a variável de índice de fragmento atual e atualize BeginIndex do anel de pacotes para o índice de pacotes atual para finalizar a indicação de pacotes recebidos e seus fragmentos para o sistema operacional.
  3. Postar buffers de fragmento no hardware para os próximos recebimentos:
    1. Defina o índice de fragmento atual como NextIndex do anel de fragmento, que é o início da subseção pós-seção do anel. Defina o índice final do fragmento como EndIndex do anel de fragmento.
    2. Faça o seguinte em um loop:
      1. Poste as informações do fragmento no descritor de hardware correspondente.
      2. Avance o índice de fragmento chamando NetRingIncrementIndex.
    3. Atualize NextIndex do anel de fragmento para a variável de índice de fragmento atual para finalizar a postagem de fragmentos no hardware.

Estas etapas podem ter esta aparência no código:

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

Recebendo dados fora de ordem

Ao contrário de uma fila Tx , os drivers cliente normalmente não recebem dados fora de ordem se tiverem uma fila de recebimento de sistema operacional por fila de recebimento de hardware. Isso é independentemente do tipo da NIC do driver cliente. Se o dispositivo é baseado em PCI e o sistema operacional aloca e possui os buffers de recebimento ou se o dispositivo é baseado em USB e a pilha USB possui os buffers de recebimento, o driver cliente inicializa um pacote para cada fragmento recebido e indica-o para o sistema operacional. A ordem não é importante neste caso.

Se o hardware der suporte a mais de uma fila de recebimento de sistema operacional por fila de recebimento de hardware, você deverá sincronizar o acesso aos buffers de recebimento. O escopo de fazer isso está fora deste tópico e depende do design do hardware.