NetAdapterCx 클라이언트 드라이버는 프레임워크가 수신 큐에 대한 EvtPacketQueueAdvance 콜백 함수를 호출할 때 네트워크 데이터를 받습니다. 이 콜백 중에 클라이언트 드라이버는 수신된 조각 및 패킷을 OS로 드레이닝하여 수신을 표시한 다음, 하드웨어에 새 버퍼를 게시합니다.
수신(Rx) 및 배수 작업 개요
다음 애니메이션은 간단한 PCI NIC(네트워크 인터페이스 카드)의 클라이언트 드라이버가 수신(Rx) 큐에 대한 사후 및 드레이닝 작업을 수행하는 방법을 보여 줍니다. 이 예제 시나리오의 조각 버퍼는 OS에 의해 할당되고 조각 링에 연결됩니다. 이 예제에서는 하드웨어 수신 큐와 OS 수신 큐 간의 일대일 관계를 가정합니다.
이 애니메이션에서 클라이언트 드라이버가 소유한 패킷은 연한 파란색과 진한 파란색으로 강조 표시되고 클라이언트 드라이버가 소유한 조각은 노란색과 주황색으로 강조 표시됩니다. 밝은 색은 드라이버가 소유한 요소의 드레이닝 하위 섹션을 나타내고 어두운 색은 드라이버가 소유한 요소의 포스트 하위 섹션을 나타냅니다.
순서대로 데이터 받기
다음은 패킷당 하나의 조각이 있는 순서대로 데이터를 수신하는 드라이버에 대한 일반적인 시퀀스입니다.
- NetRxQueueGetRingCollection호출하여 수신 큐의 링 컬렉션 구조를 검색합니다. 이를 큐의 컨텍스트 공간에 저장하여 드라이버의 호출을 줄일 수 있습니다. 링 컬렉션을 사용하여 수신 큐의 조각 링 및 패킷 링에 대한 드레이닝 반복기를 검색합니다.
- 네트워크 링을 비워 OS에 수신된 데이터를 알립니다.
- 조각 링의 현재 인덱스 및 패킷 링의 현재 인덱스를 추적하기 위해 UINT32 변수를 할당합니다. 이 변수를 해당 네트 링의 하위 섹션인 드레인 부분의 시작인 BeginIndex로 설정합니다. 조각 링의 드레이닝 섹션 끝에 UINT32 변수를 할당한 후, 해당 변수를 조각 링의 NextIndex로 설정합니다.
- 루프에서 다음을 수행합니다.
- 조각이 하드웨어에서 수신되었는지 확인합니다. 그렇지 않은 경우 루프를 중단합니다.
- NetRingGetFragmentAtIndex 호출하여 조각을 가져옵니다.
- 일치하는 하드웨어 설명자에 따라 부분의 정보를 입력하십시오, 예를 들어 ValidLength과 같은 정보를 입력하십시오.
- NetRingGetPacketAtIndex호출하여 이 조각에 대한 패킷을 가져옵니다.
- 패킷의 FragmentIndex 조각 링의 현재 인덱스로 설정하고 조각 수를 적절하게 설정하여 패킷에 조각을 바인딩합니다(이 예제에서는 1로 설정됨).
- 필요에 따라 체크섬 정보와 같은 다른 패킷 정보를 입력합니다.
- NetRingIncrementIndex호출하여 조각 인덱스를 증가시킵니다.
- NetRingIncrementIndex호출하여 패킷 인덱스를 진행합니다.
- 조각 링의 BeginIndex 현재 조각 인덱스 변수로 업데이트하고 패킷 링의 BeginIndex 현재 패킷 인덱스로 업데이트하여 수신된 패킷과 해당 조각을 OS로 표시하도록 마무리합니다.
- 다음 수신을 위해 조각 버퍼를 하드웨어에 게시합니다.
- 현재 조각 인덱스를 링의 포스트 하위 섹션 시작 부분인 NextIndex로 설정합니다. 조각 링의 EndIndex에 조각 끝 인덱스를 설정하시오.
- 루프에서 다음을 수행합니다.
- 일치하는 하드웨어 설명자에 조각의 정보를 게시합니다.
- NetRingIncrementIndex호출하여 조각 인덱스를 증가시킵니다.
- 조각 링의 NextIndex을 현재 조각 인덱스 변수로 업데이트하여 하드웨어에 조각 게시 프로세스를 완료합니다.
이러한 단계는 코드에서 다음과 같이 보일 수 있습니다.
void
MyEvtRxQueueAdvance(
NETPACKETQUEUE RxQueue
)
{
//
// Retrieve the receive queue's ring collection and net rings.
// This example stores the Rx queue's ring collection in its queue context space.
//
PMY_RX_QUEUE_CONTEXT rxQueueContext = MyGetRxQueueContext(RxQueue);
NET_RING_COLLECTION const * ringCollection = rxQueueContext->RingCollection;
NET_RING * packetRing = ringCollection->Rings[NET_RING_TYPE_PACKET];
NET_RING * fragmentRing = ringCollection->Rings[NET_RING_TYPE_FRAGMENT];
UINT32 currentPacketIndex = 0;
UINT32 currentFragmentIndex = 0;
UINT32 fragmentEndIndex = 0;
//
// Indicate receives by draining the rings
//
currentPacketIndex = packetRing->BeginIndex;
currentFragmentIndex = fragmentRing->BeginIndex;
fragmentEndIndex = fragmentRing->NextIndex;
while(currentFragmentIndex != fragmentEndIndex)
{
// Test for fragment reception. Break if fragment has not been received.
...
//
NET_FRAGMENT * fragment = NetRingGetFragmentAtIndex(fragmentRing, currentFragmentIndex);
fragment->ValidLength = ... ;
NET_PACKET * packet = NetRingGetPacketAtIndex(packetRing, currentPacketIndex);
packet->FragmentIndex = currentFragmentIndex;
packet->FragmentCount = 1;
if(rxQueueContext->IsChecksumExtensionEnabled)
{
// Fill in checksum info
...
//
}
currentFragmentIndex = NetRingIncrementIndex(fragmentRing, currentFragmentIndex);
currentPacketIndex = NetRingIncrementIndex(packetRing, currentPacketIndex);
}
fragmentRing->BeginIndex = currentFragmentIndex;
packetRing->BeginIndex = currentPacketIndex;
//
// Post fragment buffers to hardware
//
currentFragmentIndex = fragmentRing->NextIndex;
fragmentEndIndex = fragmentRing->EndIndex;
while(currentFragmentIndex != fragmentEndIndex)
{
// Post fragment information to hardware descriptor
...
//
currentFragmentIndex = NetRingIncrementIndex(fragmentRing, currentFragmentIndex);
}
fragmentRing->NextIndex = currentFragmentIndex;
}
잘못된 데이터 수신
Tx 큐와 달리 클라이언트 드라이버는 일반적으로 하드웨어 수신 큐당 하나의 OS 수신 큐가 있는 경우 데이터를 순서대로 수신하지 않습니다. 이는 클라이언트 드라이버의 NIC 유형에 관계없이 수행됩니다. 디바이스가 PCI 기반이고 OS가 수신 버퍼를 할당 및 소유하는지 또는 디바이스가 USB 기반이고 USB 스택이 수신 버퍼를 소유하는지 여부에 관계없이 클라이언트 드라이버는 수신된 각 조각에 대한 패킷을 초기화하고 OS에 나타냅니다. 이 경우 순서는 중요하지 않습니다.
하드웨어가 하드웨어 수신 큐당 둘 이상의 OS 수신 큐를 지원하는 경우 수신 버퍼에 대한 액세스를 동기화해야 합니다. 이렇게 하는 범위는 이 항목의 범위를 벗어나며 하드웨어의 디자인에 따라 달라집니다.