Отправка сетевых данных с помощью сетевых кругов

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

Обзор операций после передачи (Tx) и очистки

Следующая анимация показывает, как драйвер клиента для простого сетевого интерфейса PCI карта (NIC) выполняет операции после и стока для очереди передачи (Tx).

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

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

Отправка данных по порядку

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

  1. Вызовите NetTxQueueGetRingCollection , чтобы получить структуру кольцевой коллекции очереди передачи. Вы можете сохранить его в контекстном пространстве очереди, чтобы уменьшить количество вызовов из драйвера. Используйте кольцевую коллекцию для получения круга пакетов очереди передачи.
  2. Передача данных на оборудование.
    1. Выделите переменную UINT32 для индекса пакета и присвойте ей значение NextIndex кольца пакетов, которое является началом подраздела post круга.
    2. Выполните следующие действия в цикле:
      1. Получите пакет, вызвав NetRingGetPacketAtIndex с индексом пакета.
      2. Проверьте, следует ли игнорировать этот пакет. Если его следует игнорировать, перейдите к шагу 6 этого цикла. В противном случае продолжайте.
      3. Получение фрагментов этого пакета. Извлеките кольцо фрагмента очереди передачи из коллекции кругов, извлеките начало фрагментов пакета из элемента FragmentIndex пакета, а затем получите конец фрагментов пакета, вызвав NetRingIncrementIndex с параметром FragmentCount пакета.
      4. Выполните следующие действия в цикле:
        1. Вызовите NetRingGetFragmentAtIndex , чтобы получить фрагмент.
        2. Преобразуйте дескриптор NET_FRAGMENT в связанный дескриптор аппаратного фрагмента.
        3. Для повышения индекса фрагмента вызовите NetRingIncrementIndex.
      5. Обновите индекс Next круга фрагментов в соответствии с текущим индексом итератора фрагмента, который указывает, что публикация на оборудование завершена.
      6. Чтобы увеличить индекс пакета, вызовите NetRingIncrementIndex.
    3. Обновите nextIndex круга пакетов до индекса пакетов, чтобы завершить отправку пакетов на оборудование.
  3. Очистка завершенной передачи пакетов в ОС:
    1. Присвойте индексу пакета beginIndex кольца пакетов, который является началом подраздела стока круга.
    2. Выполните следующие действия в цикле:
      1. Получите пакет, вызвав NetRingGetPacketAtIndex с индексом пакета.
      2. Проверьте, завершена ли передача пакета. Если это не так, вырваться из цикла.
      3. Чтобы увеличить индекс пакета, вызовите NetRingIncrementIndex.
    3. Обновите beginIndex в пакетном кольце до индекса пакетов, чтобы завершить отправку пакетов на оборудование.

В коде эти действия могут выглядеть следующим образом. Обратите внимание, что сведения об оборудовании, такие как размещение дескрипторов на оборудовании или очистка успешной транзакции после завершения, не содержатся для ясности.

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

Отправка данных из строя

Для драйверов, устройства которых могут выполнять передачу не по порядку, основное отличие от устройств в порядке заключается в том, кто выделяет буферы передачи и как драйвер обрабатывает тест на завершение передачи. Для передачи данных в порядке с сетевым адаптером PCI, поддерживающим DMA, ОС обычно выделяет, присоединяет и в конечном счете владеет буферами фрагментов. Затем драйвер клиента может проверить соответствующий флаг владения оборудованием каждого фрагмента во время EvtPacketQueueAdvance.

В отличие от этой модели, рассмотрим типичный сетевой адаптер на основе USB. В этом случае USB-стеку принадлежат буферы памяти для передачи, и эти буферы могут находиться в другом месте системной памяти. Стек USB указывает на завершение для клиентского драйвера не по порядку, поэтому драйверу клиента необходимо отдельно записывать состояние завершения пакета во время процедуры обратного вызова завершения. Для этого драйвер клиента может использовать поле Scratch пакета или использовать другой метод, например хранение информации в пространстве контекста очереди. Затем в вызове EvtPacketQueueAdvance драйвер клиента проверяет эти сведения для проверки завершения пакета.