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


Режим опроса NDIS

Обзор режима опроса NDIS

Режим опроса NDIS — это модель выполнения опроса, контролируемая ОС, которая управляет данными сетевого интерфейса.

Ранее NDIS не имел официального определения контекста выполнения datapath. Драйверы NDIS обычно зависят от отложенных вызовов процедур (DPCs) для реализации модели выполнения. Однако использование ЦП может перегружать систему, когда выполняются длинные цепочки указаний и избегая этой проблемы, требует большого количества кода, который сложно получить правильно. Режим опроса NDIS предлагает альтернативу ЦП и аналогичным средствам выполнения.

Режим опроса NDIS перемещает сложность планирования решений от драйверов сетевого адаптера и в NDIS, где NDIS устанавливает ограничения работы на итерацию. Для достижения этого режима опроса предоставляется следующее:

  1. Механизм для операционной системы для обратного давления на сетевой адаптер.

  2. Механизм для тонкого управления прерываниями ос.

Режим опроса NDIS доступен для драйверов минипорта NDIS 6.85 и более поздних версий.

Проблемы с моделью DPC

На следующей схеме последовательности показан типичный пример того, как драйвер мини-порта NDIS обрабатывает всплеск пакетов Rx с помощью DPC. В этом примере оборудование является стандартным с точки зрения сетевых адаптеров PCIe. Он имеет очередь оборудования получения и маску прерываний для этой очереди.

Схема, на которой показана модель DPC NDIS с пакетами Rx и очередью получения оборудования.

Если сетевое действие отсутствует, оборудование включает прерывание Rx. При поступлении пакета Rx:

  1. Оборудование создает прерывание и NDIS вызывает функцию MiniportInterrupt драйвера (ISR).

  2. Драйвер очень мало работает в ISR, потому что они работают на очень высоком irQL. Драйвер отключает прерывание из ISR и откладывает аппаратную обработку функции MiniportInterruptDPC (DPC ).

  3. NDIS в конечном итоге вызывает DPC драйвера и драйвер очищает все завершения из очереди оборудования и указывает на них ОС.

Две точки боли могут повлиять на сетевой стек, когда драйвер отложит операции ввода-вывода в DPC:

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

  2. Так как драйвер использует DPC для отсрочки работы из isR, все признаки выполняются в DISPATCH_LEVEL. Это может перегружать систему при выполнении длинных цепочек индикаторов и вызвать проверку ошибок 0x133 DPC_WATCHDOG_VIOLATION.

Избегая этих точек боли, требуется много сложного кода в вашем драйвере. Хотя вы можете проверить, близко ли наблюдатель DPC к ограничению с помощью функции KeQueryDpcWatchdogInformation и вырваться из DPC, вам по-прежнему нужно создать инфраструктуру вокруг этого в драйвере: вам нужен какой-то способ приостановки немного, а затем продолжать указывать пакеты, и в то же время необходимо синхронизировать все это со временем существования пути данных.

Общие сведения о объектах Poll

В режиме опроса NDIS представлен объект Poll для устранения точек боли, связанных с ЦП. Объект Poll — это конструкция контекста выполнения. Драйверы минипорта могут использовать объект Poll вместо DPC при работе с операциями datapath.

Объект Poll предлагает следующее:

  • Он позволяет NDIS задавать ограничения работы на итерацию.

  • Он тесно связан с механизмом уведомлений. При этом операционная система и сетевой адаптер синхронизируются при необходимости обработки работы.

  • Он имеет концепцию итерации и прерываний, встроенных. При использовании ЦП драйверы вынуждены повторно включить прерывание при каждом завершении DPC. При использовании объектов Poll драйверы не должны повторно включить прерывание каждой итерации опроса, так как в режиме опроса драйвер будет знать, когда это делается опрос, и пришло время повторно включить прерывание.

  • При принятии решений о планировании система может быть умна в том, следует ли выполнять DISPATCH_LEVEL или PASSIVE_LEVEL. Это позволяет точно настроить приоритет трафика из разных сетевых карт и привести к более справедливому распределению рабочих нагрузок на компьютере.

  • Он имеет гарантии сериализации. После выполнения кода из контекста выполнения объекта Poll вы гарантируете, что другой код, связанный с тем же контекстом выполнения, не будет выполняться. Это позволяет драйверу сетевого адаптера иметь бесплатную реализацию путь к данным.

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

На следующей схеме последовательности показано, как тот же гипотетический драйвер PCIe NIC обрабатывает всплеск пакетов Rx с помощью объекта Poll вместо DPC.

Схема с режимом опроса NDIS с пакетами Rx и очередью получения оборудования.

Как и модель DPC, когда пакет Rx прибывает, оборудование создает прерывание, NDIS вызывает ISR драйвера, а драйвер отключает прерывание из ISR. На этом этапе модель режима опроса расходится:

  1. Вместо очереди DPC драйвер помещает объект Poll (который он ранее создал) из ISR, чтобы уведомить NDIS о том, что новая работа готова к обработке.

  2. В какой-то момент в будущем NDIS вызывает обработчик итерации опроса драйвера для обработки работы. В отличие от DPC, драйвер не может указывать столько NBL RX, сколько элементов готовы к работе в очереди оборудования. Драйвер должен вместо этого проверить параметр опроса обработчика, чтобы получить максимальное количество NBL, которое может указывать.

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

  3. NDIS продолжает опрос водителя, пока он не оценивает, что водитель больше не делает прогресс вперед. На этом этапе NDIS остановит опрос и попросит водителя повторно включить прерывание.

Стандартное ключевое слово INF для режима опроса NDIS

Для включения или отключения поддержки режима опроса NDIS необходимо использовать следующее ключевое слово:

*NdisPoll Enumeration стандартизированные ключевые слова INF имеют следующие атрибуты:

SubkeyName
Имя ключевого слова, которое необходимо указать в INF-файле и которое отображается в реестре.

ParamDesc
Отображаемый текст, связанный с SubkeyName.

Значение
Целочисленное значение перечисления, связанное с каждым параметром в списке. Это значение хранится в значении NDI\params\ SubkeyName\.

EnumDesc
Отображаемый текст, связанный с каждым значением, отображаемым в меню.

По умолчанию.
Значение по умолчанию для меню.

SubkeyName ParamDesc Значение EnumDesc
*NdisPoll Режим опроса Ndis 0 Выключено
1 (по умолчанию) Включен

Дополнительные сведения об использовании ключевых слов перечисления см. в разделе "Ключевые слова перечисления".

Создание объекта Poll

Чтобы создать объект Poll, драйвер минипорта выполняет следующие действия в функции обратного вызова MiniportInitializeEx:

  1. Выделяет контекст частного минипорта.
  2. Выделяет структуру NDIS_POLL_CHARACTERISTICS для указания точек входа для функций обратного вызова NdisPoll и NdisSetPollNotification.
  3. Вызывает NdisRegisterPoll , чтобы создать объект Poll и сохранить его в контексте мини-порта.

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

NDIS_SET_POLL_NOTIFICATION NdisSetPollNotification; 
NDIS_POLL NdisPoll; 

NDIS_STATUS 
MiniportInitialize( 
    _In_ NDIS_HANDLE NdisAdapterHandle, 
    _In_ NDIS_HANDLE MiniportDriverContext, 
    _In_ NDIS_MINIPORT_INIT_PARAMETERS * MiniportInitParameters 
) 
{ 
    // Allocate a private miniport context 
    MINIPORT_CONTEXT * miniportContext = ...;
 
    NDIS_POLL_CHARACTERISTICS pollCharacteristics; 
    pollCharacteristics.Header.Type = NDIS_OBJECT_TYPE_DEFAULT; 
    pollCharacteristics.Header.Revision = NDIS_POLL_CHARACTERISTICS_REVISION_1; 
    pollCharacteristics.Header.Size = NDIS_SIZEOF_NDIS_POLL_CHARACTERISTICS_REVISION_1; 
    pollCharacteristics.SetPollNotificationHandler = NdisSetPollNotification; 
    pollCharacteristics.PollHandler = NdisPoll; 

    // Create a Poll object and store it in the miniport context 
    NdisRegisterPoll( 
        NdisAdapterHandle, 
        miniportContext, 
        &pollCharacteristics, 
        &miniportContext->RxPoll); 
 
    return NDIS_STATUS_SUCCESS; 
} 

Очередь объекта Poll для выполнения

Из ISR минипорт-драйверы вызывают NdisRequestPoll , чтобы очередь объекта Poll для выполнения. В следующем примере показана обработка получения, но не учитывается общий доступ к строкам прерываний для простоты.

BOOLEAN 
MiniportIsr( 
  KINTERRUPT * Interrupt, 
  void * Context 
) 
{ 
    auto miniportContext = static_cast<MINIPORT_CONTEXT *>(Context); 
    auto hardwareContext = miniportContext->HardwareContext; 

    // Check if this interrupt is due to a received packet 
    if (hardwareContext->ISR & RX_OK) 
    { 
        // Disable the receive interrupt and queue the Poll 
        hardwareContext->IMR &= ~RX_OK; 
        NdisRequestPoll(miniportContext->RxPoll, nullptr); 
    }

    return TRUE; 
} 

Реализация обработчика итерации опроса

NDIS вызывает обратный вызов драйвера минипорта NdisPoll для опроса для получения показаний и отправки завершений. NDIS сначала вызывает NdisPoll, когда драйвер вызывает NdisRequestPoll в очередь объекта Poll. NDIS будет продолжать вызывать NdisPoll , пока драйвер выполняет переадресацию на получение признаков или передачи завершений.

Для получения показаний драйвер должен выполнить следующие действия в NdisPoll:

  1. Проверьте параметр получения структуры NDIS_POLL_DATA, чтобы получить максимальное количество NBLs, которое может указываться.
  2. Максимальное число пакетов Rx.
  3. Инициализировать NBL.
  4. Добавьте их в очередь NBL, предоставляемую структурой NDIS_POLL_RECEIVE_DATA (расположенной в NDIS_POLL_DATA структуре параметра NdisPoll PollData).
  5. Выход из обратного вызова.

Для завершения передачи драйвер должен выполнять следующие действия в NdisPoll:

  1. Проверьте параметр передачи структуры NDIS_POLL_DATA, чтобы получить максимальное количество NBLs, которые он может завершить.
  2. Максимальное число пакетов Tx.
  3. Завершите NBL.
  4. Добавьте их в очередь NBL, предоставляемую структурой NDIS_POLL_TRANSMIT_DATA (расположенной в NDIS_POLL_DATA структуре параметра NdisPoll PollData).
  5. Выход из обратного вызова.

Драйвер не должен включать прерывание объекта Poll перед выходом из функции NdisPoll . NDIS будет продолжать опрос водителя, пока он не оценивает, что прогресс вперед не будет достигнут. На этом этапе NDIS остановит опрос и попросит водителя повторно включить прерывание.

Вот как драйвер может реализовать NdisPoll для потока очереди получения.

_Use_decl_annotations_ 
void 
NdisPoll( 
    void * Context, 
    NDIS_POLL_DATA * PollData 
) 
{ 
    auto miniportContext = static_cast<MINIPORT_CONTEXT *>(Context); 
    auto hardwareContext = miniportContext->HardwareContext; 

    // Drain received frames 
    auto & receive = PollData->Receive; 
    receive.NumberOfRemainingNbls = NDIS_ANY_NUMBER_OF_NBLS; 
    receive.Flags = NDIS_RECEIVE_FLAGS_SHARED_MEMORY_VALID; 

    while (receive.NumberOfIndicatedNbls < receive.MaxNblsToIndicate) 
    { 
        auto rxDescriptor = HardwareQueueGetNextDescriptorToCheck(hardwareContext->RxQueue); 

        // If this descriptor is still owned by hardware stop draining packets 
        if ((rxDescriptor->Status & HW_OWN) != 0) 
            break; 

        auto nbl = MakeNblFromRxDescriptor(miniportContext->NblPool, rxDescriptor); 

        AppendNbl(&receive.IndicatedNblChain, nbl); 
        receive.NumberOfIndicatedNbls++; 

        // Move to next descriptor 
        HardwareQueueAdvanceNextDescriptorToCheck(hardwareContext->RxQueue); 
    } 
} 

Управление прерываниями

Драйверы минипорта реализуют обратный вызов NdisSetPollNotification , чтобы включить или отключить прерывание, связанное с объектом Poll. NDIS обычно вызывает обратный вызов NdisSetPollNotification, когда он обнаруживает, что драйвер минипорта не выполняет прогресс вперед в NdisPoll. NDIS использует NdisSetPollNotification , чтобы сообщить драйверу, что он перестанет вызывать NdisPoll. Драйвер должен вызвать NdisRequestPoll , когда новая работа готова к обработке.

Вот как драйвер может реализовать NdisSetPollNotification для потока очереди получения.

_Use_decl_annotations_ 
void 
NdisSetPollNotification( 
    void * Context, 
    NDIS_POLL_NOTIFICATION * Notification 
) 
{ 
    auto miniportContext = static_cast<MINIPORT_CONTEXT *>(Context); 
    auto hardwareContext = miniportContext->HardwareContext; 

    if (Notification->Enabled) 
    { 
        hardwareContext->IMR |= RX_OK; 
    } 
    else 
    { 
        hardwareContext->IMR &= ~RX_OK; 
    } 
}