Получение сетевых данных с помощью сетевых кругов

Клиентские драйверы NetAdapterCx получают сетевые данные, когда платформа вызывает функцию обратного вызова EvtPacketQueueAdvance для очереди получения. Во время этого обратного вызова клиентские драйверы указывают получение путем очистки полученных фрагментов и пакетов в ОС, а затем помещают новые буферы на оборудование.

Обзор операций получения (Rx) после и очистки

Следующая анимация показывает, как драйвер клиента для простого сетевого интерфейса PCI карта (NIC) выполняет операции пост- и стока для очереди получения (Rx). В этом примере буферы фрагментов выделяются и присоединяются к кольцу фрагмента операционной системой. В этом примере предполагается связь "один к одному" между очередью получения оборудования и очередью получения ОС.

Анимация, иллюстрирующая сетевые операции после кольцевых и стоков для получения для сетевого интерфейса PCI карта.

В этой анимации пакеты, принадлежащие драйверу клиента, выделяются светло-синим и темно-синим цветом, а фрагменты, принадлежащие драйверу клиента, выделяются желтым и оранжевым цветом. Более светлые цвета представляют подраздел стока элементов, принадлежащих драйверу, а более темные — подраздел post элементов, принадлежащих драйверу.

Получение данных по порядку

Ниже приведена типичная последовательность для драйвера, который получает данные по порядку с одним фрагментом на пакет.

  1. Вызовите NetRxQueueGetRingCollection , чтобы получить структуру кольцевой коллекции очереди получения. Вы можете сохранить его в контекстном пространстве очереди, чтобы уменьшить количество вызовов из драйвера. Используйте кольцевую коллекцию, чтобы получить итератор стока для круга фрагмента очереди получения и круга пакетов.
  2. Укажите полученные данные в ОС путем очистки сетевых кругов:
    1. Выделите переменные UINT32 для отслеживания текущего индекса кольца фрагментов и текущего индекса пакетного кольца. Задайте для этих переменных значение BeginIndex их соответствующих сетевых кругов, которое является началом подраздела стока кольца. Выделите переменную UINT32 для конца секции стока кольца фрагмента, задав для нее значение NextIndex кольца фрагмента.
    2. Выполните следующие действия в цикле:
      1. Проверьте, получен ли фрагмент оборудованием. В противном случае вырвите цикл.
      2. Вызовите NetRingGetFragmentAtIndex , чтобы получить фрагмент.
      3. Заполните сведения фрагмента, например ValidLength, на основе соответствующего аппаратного дескриптора.
      4. Получите пакет для этого фрагмента, вызвав NetRingGetPacketAtIndex.
      5. Привяжите фрагмент к пакету, задав для него значение FragmentIndex текущего индекса фрагмента в кольце фрагмента и соответствующее количество фрагментов (в этом примере задано значение 1).
      6. При необходимости введите любые другие сведения о пакете, например сведения о контрольной сумме.
      7. Для повышения индекса фрагмента вызовите NetRingIncrementIndex.
      8. Чтобы увеличить индекс пакета, вызовите NetRingIncrementIndex.
    3. Обновите beginIndex круга фрагментов до текущей переменной индекса фрагмента и обновите BeginIndex кольца пакетов до текущего индекса пакета, чтобы завершить указание полученных пакетов и их фрагментов в ОС.
  3. Post fragment buffers to hardware for the next receives:
    1. Задайте для текущего индекса фрагмента значение NextIndex кольца фрагмента, который является началом подраздела post кольца. Задайте для конечного индекса фрагмента значение EndIndex кольца фрагмента.
    2. Выполните следующие действия в цикле:
      1. Опубликуй сведения об фрагменте в соответствующий аппаратный дескриптор.
      2. Для повышения индекса фрагмента вызовите NetRingIncrementIndex.
    3. Обновите nextIndex круга фрагментов до текущей переменной индекса фрагмента, чтобы завершить размещение фрагментов на оборудовании.

В коде эти действия могут выглядеть следующим образом:

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

Получение данных не по порядку

В отличие от очереди Tx , клиентские драйверы обычно не получают данные не по порядку, если у них есть одна очередь получения ОС на каждую очередь получения оборудования. Это зависит от типа сетевой карты драйвера клиента. Независимо от того, является ли устройство на основе PCI, а ОС выделяет и владеет буферами приема, или же устройство основано на USB и USB-стек владеет буферами приема, драйвер клиента инициализирует пакет для каждого полученного фрагмента и указывает его операционной системе. Порядок в данном случае не важен.

Если оборудование поддерживает несколько очередей получения ОС на каждую очередь получения оборудования, необходимо синхронизировать доступ к буферам получения. Область этого действия выходит за рамки этого раздела и зависит от дизайна оборудования.