Поделиться через


Передача и получение очередей

Обзор

Объекты очереди пакетови очереди datapath были введены в NetAdapterCx, чтобы клиентские драйверы могли более явно моделировать свои аппаратные функции, такие как очереди передачи и приема аппаратуры, в программных драйверах. В этом разделе объясняется, как работать с очередями передачи и получения в NetAdapterCx.

Когда драйвер клиента вызывает NET_ADAPTER_DATAPATH_CALLBACKS_INIT, как правило, из функции обратного вызова событий EVT_WDF_DRIVER_DEVICE_ADD, он предоставляет два обратных вызова создания очереди: EVT_NET_ADAPTER_CREATE_TXQUEUE и EVT_NET_ADAPTER_CREATE_RXQUEUE. Клиент создает очереди передачи и получения в этих обратных вызовах соответственно.

Платформа очищает очереди перед переходом в состояние низкой мощности и удаляет их перед удалением адаптера.

Создание очередей пакетов

При создании очереди пакетов в очереди передачи или очереди получения клиент должен предоставить указатели на следующие три функции обратного вызова:

Кроме того, клиент может предоставить эти необязательные функции обратного вызова после инициализации структуры конфигурации очереди:

Создание очереди передачи

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

  • При необходимости зарегистрируйте обратные вызовы для старта и остановки очереди.
  • Вызовите NetTxQueueInitGetQueueId, чтобы получить идентификатор очереди передачи для настройки.
  • Вызовите NetTxQueueCreate, чтобы выделить очередь.
    • Если NetTxQueueCreate завершается ошибкой, функция обратного вызова EvtNetAdapterCreateTxQueue должна возвращать код ошибки.
  • Запрос сдвига расширения пакета.

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

NTSTATUS
EvtAdapterCreateTxQueue(
    _In_    NETADAPTER          Adapter,
    _Inout_ NETTXQUEUE_INIT *   TxQueueInit
)
{
    NTSTATUS status = STATUS_SUCCESS;

    // Prepare the configuration structure
    NET_PACKET_QUEUE_CONFIG txConfig;
    NET_PACKET_QUEUE_CONFIG_INIT(
        &txConfig,
        EvtTxQueueAdvance,
        EvtTxQueueSetNotificationEnabled,
        EvtTxQueueCancel);

    // Optional: register the queue's start and stop callbacks
    txConfig.EvtStart = EvtTxQueueStart;
    txConfig.EvtStop = EvtTxQueueStop;

    // Get the queue ID
    const ULONG queueId = NetTxQueueInitGetQueueId(TxQueueInit);

    // Create the transmit queue
    NETPACKETQUEUE txQueue;
    status = NetTxQueueCreate(
        TxQueueInit,
        &txAttributes,
        &txConfig,
        &txQueue);

    // Get the queue context for storing the queue ID and packet extension offset info
    PMY_TX_QUEUE_CONTEXT queueContext = GetMyTxQueueContext(txQueue);

    // Store the queue ID in the context
    queueContext->QueueId = queueId;

    // Query checksum packet extension offset and store it in the context
    NET_EXTENSION_QUERY extension;
    NET_EXTENSION_QUERY_INIT(
        &extension,
        NET_PACKET_EXTENSION_CHECKSUM_NAME,
        NET_PACKET_EXTENSION_CHECKSUM_VERSION_1);

    NetTxQueueGetExtension(txQueue, &extension, &queueContext->ChecksumExtension);

    // Query Large Send Offload packet extension offset and store it in the context
    NET_EXTENSION_QUERY_INIT(
        &extension,
        NET_PACKET_EXTENSION_LSO_NAME,
        NET_PACKET_EXTENSION_LSO_VERSION_1);
    
    NetTxQueueGetExtension(txQueue, &extension, &queueContext->LsoExtension);

    return status;
}

Создание очереди получения

Чтобы создать очередь получения из EVT_NET_ADAPTER_CREATE_RXQUEUE, используйте тот же шаблон, как и очередь передачи, и вызовите NetRxQueueCreate.

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

NTSTATUS
EvtAdapterCreateRxQueue(
    _In_ NETADAPTER NetAdapter,
    _Inout_ PNETRXQUEUE_INIT RxQueueInit
)
{
    NTSTATUS status = STATUS_SUCCESS;

    // Prepare the configuration structure
    NET_PACKET_QUEUE_CONFIG rxConfig;
    NET_PACKET_QUEUE_CONFIG_INIT(
        &rxConfig,
        EvtRxQueueAdvance,
        EvtRxQueueSetNotificationEnabled,
        EvtRxQueueCancel);

    // Optional: register the queue's start and stop callbacks
    rxConfig.EvtStart = EvtRxQueueStart;
    rxConfig.EvtStop = EvtRxQueueStop;

    // Get the queue ID
    const ULONG queueId = NetRxQueueInitGetQueueId(RxQueueInit);

    // Create the receive queue
    NETPACKETQUEUE rxQueue;
    status = NetRxQueueCreate(
        RxQueueInit,
        &rxAttributes,
        &rxConfig,
        &rxQueue);

    // Get the queue context for storing the queue ID and packet extension offset info
    PMY_RX_QUEUE_CONTEXT queueContext = GetMyRxQueueContext(rxQueue);

    // Store the queue ID in the context
    queueContext->QueueId = queueId;

    // Query the checksum packet extension offset and store it in the context
    NET_EXTENSION_QUERY extension;
    NET_EXTENSION_QUERY_INIT(
        &extension,
        NET_PACKET_EXTENSION_CHECKSUM_NAME,
        NET_PACKET_EXTENSION_CHECKSUM_VERSION_1); 
          
    NetRxQueueGetExtension(rxQueue, &extension, &queueContext->ChecksumExtension);

    return status;
}

Модель опроса

Путь к данным NetAdapter — это модель опроса, а операция опроса в одной очереди пакетов полностью не зависит от других очередей. Модель опроса реализуется путем вызова обратных вызовов очереди драйвера клиента, как показано на следующем рисунке:

диаграмма, показывающая поток опроса в NetAdapterCx.

Продвижение очередей пакетов

Последовательность операции опроса в очереди пакетов выглядит следующим образом:

  1. ОС предоставляет буферы драйверу клиента для передачи или получения.
  2. Драйвер клиента программирует пакеты на оборудование.
  3. Драйвер клиента возвращает завершенные пакеты операционной системе.

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

Дополнительные сведения о сетевых кольцах см. в разделе Введение в сетевые кольца.

Пример реализации EvtPacketQueueAdvance для очереди передачи можно найти в разделе Оправка сетевых данных с помощью кольцевых буферов. Пример реализации EvtPacketQueueAdvance для очереди приема см. в разделе Получение сетевых данных с помощью сетевых колец.

Включение и отключение уведомления очереди пакетов

Когда драйвер клиента получает новые пакеты в сетевых кольцах очереди пакетов, NetAdapterCx вызывает функцию обратного вызова EvtPacketQueueSetNotificationEnabled драйвера клиента. Этот обратный вызов указывает драйверу клиента, что опрос (EvtPacketQueueAdvance или EvtPacketQueueCancel) будет остановлен и не возобновится, пока клиентский драйвер не вызовет NetTxQueueNotifyMoreCompletedPacketsAvailable или NetRxQueueNotifyMoreReceivedPacketsAvailable. Как правило, устройство PCI использует этот обратный вызов для включения прерываний Tx или Rx. После получения прерывания прерывание можно отключить снова, и драйвер клиента вызывает NetTxQueueNotifyMoreCompletedPacketsAvailable или NetRxQueueNotifyMoreReceivedPacketsAvailable, чтобы активировать платформу, чтобы начать опрос снова.

Включение и отключение уведомлений в очереди передачи

Для сетевого адаптера PCI включение уведомления о наличии данных в очереди передачи обычно означает активацию аппаратного прерывания этой очереди. При срабатывании аппаратного прерывания клиент вызывает NetTxQueueNotifyMoreCompletedPacketsAvailable из DPC.

Аналогичным образом для сетевого адаптера PCI отключение уведомления очереди означает отключение прерывания, связанного с очередью.

Для устройства с асинхронной моделью ввода-вывода клиент обычно использует внутренний флаг для отслеживания состояния включено. После завершения асинхронной операции обработчик завершения проверяет этот флаг и вызывает NetTxQueueNotifyMoreCompletedPacketsAvailable, если он задан.

Если NetAdapterCx вызывает EvtPacketQueueSetNotificationEnabled с NotificationEnabled, установленным в FALSE, клиент не должен вызывать NetTxQueueNotifyMoreCompletedPacketsAvailable, пока NetAdapterCx снова не вызовет эту функцию обратного вызова с NotificationEnabled, установленным в TRUE.

Например:

VOID
MyEvtTxQueueSetNotificationEnabled(
    _In_ NETPACKETQUEUE TxQueue,
    _In_ BOOLEAN NotificationEnabled
)
{
    // Optional: retrieve queue's WDF context
    MY_TX_QUEUE_CONTEXT *txContext = GetTxQueueContext(TxQueue);

    // If NotificationEnabled is TRUE, enable transmit queue's hardware interrupt
    ...
}

VOID
MyEvtTxInterruptDpc(
    _In_ WDFINTERRUPT Interrupt,
    _In_ WDFOBJECT AssociatedObject
    )
{
    MY_INTERRUPT_CONTEXT *interruptContext = GetInterruptContext(Interrupt);

    NetTxQueueNotifyMoreCompletedPacketsAvailable(interruptContext->TxQueue);
}

Включение и отключение уведомлений для очереди получения

Для сетевого адаптера PCI включение уведомления для очереди приема выглядит очень похоже на очередь Tx. Обычно это означает включение аппаратного прерывания очереди получения. Когда оборудование прерывает работу, клиент вызывает NetRxQueueNotifyMoreReceivedPacketsAvailable из DPC.

Например:

VOID
MyEvtRxQueueSetNotificationEnabled(
    _In_ NETPACKETQUEUE RxQueue,
    _In_ BOOLEAN NotificationEnabled
)
{
    // optional: retrieve queue's WDF Context
    MY_RX_QUEUE_CONTEXT *rxContext = GetRxQueueContext(RxQueue);

    // If NotificationEnabled is TRUE, enable receive queue's hardware interrupt
    ...
}

VOID
MyEvtRxInterruptDpc(
    _In_ WDFINTERRUPT Interrupt,
    _In_ WDFOBJECT AssociatedObject
    )
{
    MY_INTERRUPT_CONTEXT *interruptContext = GetInterruptContext(Interrupt);

    NetRxQueueNotifyMoreReceivedPacketsAvailable(interruptContext->RxQueue);
}

Для USB-устройства или любой другой очереди с механизмом завершения получения программного обеспечения драйвер клиента должен отслеживать в собственном контексте, включен ли уведомление очереди. Из подпрограммы завершения (активируется, например, когда сообщение становится доступным в USB-непрерывном считывателе), вызовите NetRxQueueNotifyMoreReceivedPacketsAvailable, если уведомление включено. В следующем примере показано, как это сделать.

VOID
UsbEvtReaderCompletionRoutine(
    _In_ WDFUSBPIPE Pipe,
    _In_ WDFMEMORY Buffer,
    _In_ size_t NumBytesTransferred,
    _In_ WDFCONTEXT Context
)
{
    UNREFERENCED_PARAMETER(Pipe);

    PUSB_RCB_POOL pRcbPool = *((PUSB_RCB_POOL*) Context);
    PUSB_RCB pRcb = (PUSB_RCB) WdfMemoryGetBuffer(Buffer, NULL);

    pRcb->DataOffsetCurrent = 0;
    pRcb->DataWdfMemory = Buffer;
    pRcb->DataValidSize = NumBytesTransferred;

    WdfObjectReference(pRcb->DataWdfMemory);

    ExInterlockedInsertTailList(&pRcbPool->ListHead,
                                &pRcb->Link,
                                &pRcbPool->ListSpinLock);

    if (InterlockedExchange(&pRcbPool->NotificationEnabled, FALSE) == TRUE)
    {
        NetRxQueueNotifyMoreReceivedPacketsAvailable(pRcbPool->RxQueue);
    }
}

Отмена очередей пакетов

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

Во время EvtPacketQueueCancelдрайверы возвращают пакеты операционной системе по мере необходимости. Примеры кода для отмены очереди передачи и очереди получения см. в разделе Отмена сетевых данных с помощью сетевых колец.

После вызова обратного вызова драйвера EvtPacketQueueCancel платформа продолжает опрашивать драйвера EvtPacketQueueAdvance до тех пор, пока все пакеты и буферы не будут возвращены в ОС.