Получение сетевых данных с сетевыми кольцами
Драйверы клиентов NetAdapterCx получают сетевые данные, когда фреймворк вызывает их EvtPacketQueueAdvance функцию обратного вызова для очереди получения. Во время обратного вызова клиентские драйверы указывают на получение путем очистки полученных фрагментов и пакетов в ОС, а затем отправки новых буферов в оборудование.
Обзор операций получения (Rx) сообщений и операций слива
В следующей анимации показано, как клиентский драйвер для простой карты сетевого интерфейса PCI (NIC) выполняет операции записи и очистки для очереди приема (Rx). Буферы фрагментов в этом примере выделяются и присоединяются к кругу фрагментов операционной системой. В этом примере предполагается связь "один к одному" между очередью получения оборудования и очередью получения ОС.
В этой анимации пакеты, принадлежащие драйверу клиента, выделены светло-синий и темно-синий, а фрагменты, принадлежащие драйверу клиента, выделены желтым и оранжевым. Более светлые цвета представляют подраздел элементов, принадлежащих водителю, а темные цвета представляют пост подраздел элементов, принадлежащих водителю.
Получение данных по порядку
Ниже приведена типичная последовательность для драйвера, получающего данные по порядку, с одним фрагментом на пакет.
- Вызовите NetRxQueueGetRingCollection, чтобы получить структуру коллекции колец очереди приёма. Вы можете хранить это в контекстном пространстве очереди, чтобы сократить количество вызовов драйвера. Используйте коллекцию кругов, чтобы получить итератор очистки для кольца фрагментов очереди получения и кольца пакетов.
- Передайте полученные данные операционной системе путем разгрузки сетевых колец.
- Выделите переменные UINT32 для отслеживания текущего индекса кольца фрагментов и текущего индекса кольца пакетов. Задайте эти переменные на BeginIndex их соответствующих сетевых колец, что является началом подраздела слива этих колец. Выделите переменную UINT32 для конца секции очистки кольца фрагментов, задав её значением NextIndexкольца фрагментов.
- Выполните следующие действия в цикле:
- Проверьте, получен ли фрагмент оборудованием. Если нет, выйдите из цикла.
- Вызовите NetRingGetFragmentAtIndex, чтобы получить фрагмент.
- Заполните сведения о фрагменте, например его ValidLength, на основе соответствующего дескриптора оборудования.
- Получите пакет для этого фрагмента, вызвав NetRingGetPacketAtIndex.
- Привяжите фрагмент к пакету, установив в FragmentIndex пакета текущий индекс фрагмента из кольца фрагментов и задав соответствующее число фрагментов (в этом примере оно установлено равным 1).
- При необходимости заполните все другие сведения о пакете, такие как сведения о контрольной сумме.
- Перенастроите индекс фрагмента путем вызова NetRingIncrementIndex.
- Перенастроите индекс пакета, вызвав NetRingIncrementIndex.
- Измените BeginIndex кольца фрагмента на текущую переменную индекса фрагмента, а также BeginIndex кольца пакетов на текущий индекс пакета, чтобы обеспечить правильную передачу информации о полученных пакетах и их фрагментах в ОС.
- Передать буферы фрагментов на оборудование для следующего приема.
- Задайте текущий индекс фрагмента NextIndex, который является началом подраздела post кольца. Установите конечный индекс фрагмента на конец кольца фрагмента EndIndex.
- Выполните следующие действия в цикле:
- Поместите сведения фрагмента в соответствующий дескриптор оборудования.
- Продвиньте индекс фрагмента путем вызова NetRingIncrementIndex.
- Обновите кольцо фрагментов 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-стек владеет буферами получения, клиентский драйвер инициализирует пакет для каждого полученного фрагмента и сообщает об этом операционной системе. Порядок не важен в этом случае.
Если ваше оборудование поддерживает более одной очереди получения ОС для каждой очереди получения оборудования, вам необходимо синхронизировать доступ к буферам получения. Область этого действия выходит за рамки этого раздела и зависит от проектирования оборудования.