网络数据缓冲区管理

缓冲区管理是一项功能,使网络接口卡 (NIC) 客户端驱动程序和操作系统在为传输 (Tx) 分配数据包数据缓冲区并接收 (Rx) 数据路径时协同工作。 这可以加快 NIC 的性能,简化 NIC 客户端驱动程序的内存生存期管理,并增加系统对内存的控制。

NetAdapterCx 中缓冲区管理的优势

选择从系统内存中为数据包有效负载分配数据缓冲区的位置对于数据路径的性能至关重要。 在 NetAdapterCx 中,缓冲区管理模型针对支持 DMA 的 NIC 硬件进行了优化,客户端驱动程序利用它的最佳方式是让系统代表它们为 Tx 和 Rx 路径分配数据缓冲区。 但是,客户端驱动程序仍会影响系统分配数据缓冲区的位置和方式,以便客户端的硬件可以轻松使用它们。

例如,请考虑支持 DMA 的典型 NIC。 此方法有一些好处:

  1. 数据缓冲区由系统分配和释放。 因此,客户端驱动程序可以摆脱内存生存期管理的负担。
  2. 系统根据客户端驱动程序声明的功能,确保分配的数据缓冲区已为 NIC 硬件准备好 DMA。 然后,客户端驱动程序只需将数据缓冲区按原样编程到其硬件中,而无需执行任何其他 DMA 映射操作。
  3. 分配数据缓冲区时,系统可以考虑上层应用程序的需求,因此可以决定针对全局端到端性能进行优化,而不是只优化本地端到端性能。

对于非 DMA capabile NIC(如基于 USB 的网络硬件保护装置)或其他高级/软件 NIC,缓冲区管理模型还提供了将数据缓冲区管理完全留给客户端驱动程序的选项。

如何利用缓冲区管理

重要

如果硬件支持 DMA,则需要在设置 Rx 和 Tx 功能之前创建 WDFDMAENABLER 对象。 使用 WDF_DMA_ENABLER_CONFIG 结构配置 WDFDMAENABLER 对象时,请确保将 WdmDmaVersionOverride 成员设置为 3 以指定 DMA 版本 3。

若要选择加入缓冲区管理,请执行以下步骤:

  1. 启动 Net 适配器时,但在调用 NetAdapterStart 之前,请分别使用 Rx 和 Tx 路径 的NET_ADAPTER_RX_CAPABILITIESNET_ADAPTER_TX_CAPABILITIES 数据结构告知系统硬件的数据缓冲区功能和约束。
  2. 通过调用其中一个初始化函数来初始化两个功能结构。 例如,支持 DMA 的 NIC 客户端驱动程序将使用 NET_ADAPTER_TX_CAPABILITIES_INIT_FOR_DMANET_ADAPTER_RX_CAPABILITIES_INIT_SYSTEM_MANAGED_DMA 来声明其硬件 DMA 上限,并指示系统代表其完全管理数据缓冲区。
  3. 将初始化的 Tx 和 Rx 功能结构传递给 NetAdapterSetDatapathCapabilities 方法。

示例

以下示例演示了上一部分中概述的基本步骤,了解如何开始使用 NIC 客户端驱动程序中的缓冲区管理器。 该示例对 Tx 和 Rx 使用 DMA,因此它以前创建了存储在设备上下文空间中的 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);
}