네트워크 데이터 버퍼 관리

버퍼 관리는 전송(Tx) 및 수신(Rx) 데이터 경로에 대한 시스템 메모리에서 패킷 데이터 버퍼를 할당할 때 NIC(네트워크 인터페이스 카드) 클라이언트 드라이버와 운영 체제가 함께 작동할 수 있도록 하는 기능입니다. 이로 인해 NIC의 성능이 빨라지고, NIC의 클라이언트 드라이버에 대한 메모리 수명 관리가 더 쉬워지고, 메모리를 통해 시스템에 대한 제어가 향상됩니다.

NetAdapterCx에서 버퍼 관리의 이점

패킷 페이로드에 대한 시스템 메모리에서 데이터 버퍼가 할당되는 위치를 선택하는 것은 데이터 경로의 성능에 매우 중요합니다. NetAdapterCx에서 버퍼 관리 모델은 DMA 지원 NIC 하드웨어에 최적화되어 있으며 클라이언트 드라이버가 이를 활용하는 가장 좋은 방법은 시스템에서 Tx 및 Rx 경로 모두에 대해 데이터 버퍼를 대신 할당하도록 하는 것입니다. 그러나 클라이언트 드라이버는 클라이언트 하드웨어에서 쉽게 사용할 수 있도록 시스템에서 데이터 버퍼를 할당하는 위치와 방법에 영향을 줄 수 있습니다.

예를 들어 일반적인 DMA 지원 NIC를 고려합니다. 이 접근 방식에는 다음과 같은 이점이 있습니다.

  1. 데이터 버퍼는 시스템에 의해 할당되고 해제됩니다. 따라서 클라이언트 드라이버는 메모리 수명 관리의 부담에서 해제됩니다.
  2. 시스템은 클라이언트 드라이버에서 선언한 기능에 따라 할당된 데이터 버퍼가 NIC 하드웨어에 대해 DMA 준비가 되었는지 확인합니다. 그런 다음, 클라이언트 드라이버는 추가 DMA 매핑 작업을 수행하지 않고도 데이터 버퍼를 있는 그대로 하드웨어에 프로그래밍할 수 있습니다.
  3. 시스템은 데이터 버퍼를 할당할 때 상층 애플리케이션의 요구 사항을 고려할 수 있으므로 로컬 엔드투엔드 성능 대신 글로벌 엔드투엔드 성능을 최적화하도록 결정할 수 있습니다.

USB 기반 네트워크 동글과 같은 비 DMA capabile NIC 또는 다른 고급/소프트웨어 NIC의 경우 버퍼 관리 모델은 데이터 버퍼 관리를 클라이언트 드라이버에 완전히 맡기기 위한 옵션도 제공합니다.

버퍼 관리를 활용하는 방법

중요

하드웨어가 DMA 지원인 경우 Rx 및 Tx 기능을 설정하기 전에 WDFDMAENABLER 개체를 만들어야 합니다. WDF_DMA_ENABLER_CONFIG 구조로 WDFDMAENABLER 개체를 구성하는 경우 WdmDmaVersionOverride 멤버를 3으로 설정하여 DMA 버전 3을 지정해야 합니다.

버퍼 관리를 옵트인하려면 다음 단계를 수행합니다.

  1. Net Adapter를 시작할 때 는 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);
}