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


Управление буфером сетевых данных

Управление буферами — это функция, которая позволяет клиентским драйверам сетевой карты (NIC) и операционной системе совместно работать при выделении буферов данных пакетов из системной памяти для путей передачи (Tx) и получения (Rx). Это может привести к более быстрой производительности сетевого адаптера, упрощению управления временем существования памяти для клиентского драйвера сетевого адаптера и большему управлению системой над памятью.

Преимущества управления буферами в NetAdapterCx

Выбор откуда буферы данных выделяются из системной памяти для загрузки пакетов критически важен для производительности хода данных. В NetAdapterCx модель управления буферами оптимизирована для оборудования сетевого адаптера, поддерживающего DMA, и лучший способ использования клиентских драйверов — разрешить системе выделять буферы данных от их имени как для путей Tx, так и для Rx. Однако драйверы клиентов по-прежнему могут влиять на то, где и как система выделяет буферы данных, чтобы их можно было легко использовать оборудованием клиента.

Рассмотрим типичный сетевой адаптер, поддерживающий DMA, например. Существует несколько преимуществ для этого подхода.

  1. Буферы данных выделяются и освобождаются системой. Таким образом, драйвер клиента освобождается от бремени управления временем существования памяти.
  2. Система гарантирует, что выделенные буферы данных готовы для непосредственного доступа к памяти (DMA) оборудованием сетевого интерфейса в соответствии с возможностями, объявленными драйвером клиента. Затем драйвер клиента может просто запрограммировать буферы данных в их оборудование as-is без выполнения дополнительных операций сопоставления DMA.
  3. Система может учитывать потребности приложений верхнего уровня при выделении буферов данных, поэтому она может решить оптимизироваться для глобальной сквозной производительности, а не только локальной сквозной производительности.

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

Использование управления буферами

Важный

Если оборудование поддерживает DMA, перед настройкой возможностей Rx и Tx необходимо создать объект WDFDMAENABLER. При настройке объекта WDFDMAENABLER со структурой WDF_DMA_ENABLER_CONFIG убедитесь, что член WdmDmaVersionOverride3, чтобы указать DMA версии 3.

Чтобы войти в систему управления буферами, выполните следующие действия.

  1. При запуске сетевого адаптера, но перед вызовом NetAdapterStart, сообщите системе о возможностях и ограничениях буфера данных оборудования с помощью NET_ADAPTER_RX_CAPABILITIES и NET_ADAPTER_TX_CAPABILITIES структуры данных для пути Rx и Tx соответственно.
  2. Инициализировать две структуры возможностей путем вызова одной из функций инициализации. Например, драйвер клиента сетевого адаптера, поддерживающий DMA, будет использовать NET_ADAPTER_TX_CAPABILITIES_INIT_FOR_DMA и NET_ADAPTER_RX_CAPABILITIES_INIT_SYSTEM_MANAGED_DMA для объявления своих аппаратных возможностей DMA и указания системе полностью управлять буферами данных от его имени.
  3. Передайте инициализированные структуры возможностей Tx и Rx в метод NetAdapterSetDatapathCapabilities.

Пример

В следующем примере показаны основные шаги, описанные в предыдущем разделе о начале работы с диспетчером буферов в драйвере клиента сетевого адаптера. В примере используется DMA для Tx и Rx, поэтому ранее он создал объект WDFDMAENABLER, хранящийся в пространстве контекста устройства.

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

Обработка ошибок была упущена для ясности.

VOID
MyAdapterSetDatapathCapabilities(
    _In_ NETADAPTER Adapter
)
{
    // Get the device context
    PMY_DEVICE_CONTEXT deviceContext = GetMyContextFromDevice(Adapter);

    // Set various capabilities such as link layer MTU size, link layer capabilities, and power capabilities
    ...   

    // Initialize the Tx DMA capabilities structure
    NET_ADAPTER_DMA_CAPABILITIES txDmaCapabilities;
    NET_ADAPTER_DMA_CAPABILITIES_INIT(&txDmaCapabilities,
                                      deviceContext->dmaEnabler);

    // Set Tx capabilities
    NET_ADAPTER_TX_CAPABILITIES txCapabilities;
    NET_ADAPTER_TX_CAPABILITIES_INIT_FOR_DMA(&txCapabilities,
                                             &txDmaCapabilities,
                                             1);
    txCapabilities.FragmentRingNumberOfElementsHint = deviceContext->NumTransmitControlBlocks * MAX_PHYS_BUF_COUNT;
    txCapabilities.MaximumNumberOfFragments = MAX_PHYS_BUF_COUNT;

    // Initialize the Rx DMA capabilities structure
    NET_ADAPTER_DMA_CAPABILITIES rxDmaCapabilities;
    NET_ADAPTER_DMA_CAPABILITIES_INIT(&rxDmaCapabilities,
                                      deviceContext->dmaEnabler);

    // Set Rx capabilities
    NET_ADAPTER_RX_CAPABILITIES rxCapabilities;
    NET_ADAPTER_RX_CAPABILITIES_INIT_SYSTEM_MANAGED_DMA(&rxCapabilities,
                                                        &rxDmaCapabilities,
                                                        MAX_PACKET_SIZE + FRAME_CRC_SIZE + RSVD_BUF_SIZE,
                                                        1);
    rxCapabilities.FragmentBufferAlignment = 64;
    rxCapabilities.FragmentRingNumberOfElementsHint = deviceContext->NumReceiveBuffers;

    // Set the adapter's datapath capabilities
    NetAdapterSetDatapathCapabilities(Adapter, 
                                      &txCapabilities, 
                                      &rxCapabilities);
}