다음을 통해 공유


NDIS 6.80의 동기 OID 요청 인터페이스

Windows 네트워크 드라이버는 OID 요청을 사용하여 NDIS 바인딩 스택 아래로 제어 메시지를 보냅니다. TCPIP 또는 vSwitch와 같은 프로토콜 드라이버는 수십 개의 OID를 사용하여 기본 NIC 드라이버의 각 기능을 구성합니다. 버전 1709를 Windows 10 전에 OID 요청은 정규 및 직접이라는 두 가지 방법으로 전송되었습니다.

이 항목에서는 OID 호출의 세 번째 스타일인 동기를 소개합니다. 동기 호출은 짧은 대기 시간, 비블로킹, 확장 가능 및 신뢰할 수 있는 호출을 의미합니다. 동기 OID 요청 인터페이스는 Windows 10 버전 1709 이상에 포함된 NDIS 6.80부터 사용할 수 있습니다.

일반 및 직접 OID 요청과 비교

동기 OID 요청의 경우 호출(OID 자체)의 페이로드는 일반 및 직접 OID 요청과 정확히 동일합니다. 유일한 차이점은 호출 자체에 있습니다. 따라서 은 세 가지 유형의 OID에서 모두 동일합니다. 은 어떻게 다른지 만.

다음 표에서는 일반 OID, 직접 OID 및 동기 OID 간의 차이점을 설명합니다.

attribute 일반 OID 직접 OID 동기 OID
Payload NDIS_OID_REQUEST NDIS_OID_REQUEST NDIS_OID_REQUEST
OID 형식 통계, 쿼리, Set, 메서드 통계, 쿼리, Set, 메서드 통계, 쿼리, Set, 메서드
다음을 통해 발급할 수 있습니다. 프로토콜, 필터 프로토콜, 필터 프로토콜, 필터
완료 가능 미니포트, 필터 미니포트, 필터 미니포트, 필터
필터는 수정할 수 있습니다. Yes Yes Yes
NDIS는 메모리를 할당합니다. 각 필터에 대해(OID 복제) 각 필터에 대해(OID 복제) 비정상적으로 많은 수의 필터가 있는 경우에만(호출 컨텍스트)
보류 가능 Yes Yes 아니요
차단 가능 Yes 아니요 아니요
Irql == 수동 <= DISPATCH <= DISPATCH
NDIS로 직렬화 Yes 아니요 아니요
필터가 호출됩니다. Recursively Recursively 반복적 으로
필터는 OID를 복제합니다. Yes Yes 아니요

필터링

다른 두 가지 유형의 OID 호출과 마찬가지로 필터 드라이버는 동기 호출에서 OID 요청을 완전히 제어할 수 있습니다. 필터 드라이버는 동기 OID를 관찰, 가로채기, 수정 및 발급할 수 있습니다. 그러나 효율성을 위해 동기 OID의 메커니즘은 다소 다릅니다.

통과, 가로채기 및 출처

개념적으로 모든 OID 요청은 상위 드라이버에서 발급되고 하위 드라이버에 의해 완료됩니다. 그 과정에서 OID 요청은 임의의 필터 드라이버를 통과할 수 있습니다.

가장 일반적인 경우 프로토콜 드라이버는 OID 요청을 발급하고 모든 필터는 수정되지 않은 OID 요청을 전달하기만 하면 됩니다. 다음 그림에서는 이 일반적인 시나리오를 보여 줍니다.

프로토콜에서 시작된 일반적인 OID 경로입니다.

그러나 모든 필터 모듈은 OID 요청을 가로채서 완료할 수 있습니다. 이 경우 요청은 다음 다이어그램과 같이 낮은 드라이버로 전달되지 않습니다.

프로토콜에서 시작되고 필터에 의해 가로채는 일반적인 OID 경로입니다.

경우에 따라 필터 모듈이 자체 OID 요청을 시작하도록 결정할 수 있습니다. 이 요청은 필터 모듈 수준에서 시작되며 다음 다이어그램과 같이 하위 드라이버만 트래버스합니다.

필터에서 시작된 일반적인 OID 경로입니다.

모든 OID 요청에는 이러한 기본 흐름이 있습니다. 더 높은 드라이버(프로토콜 또는 필터 드라이버)는 요청을 실행하고 더 낮은 드라이버(미니포트 또는 필터 드라이버)가 완료합니다.

일반 및 직접 OID 요청의 작동 방식

일반 또는 직접 OID 요청은 재귀적으로 디스패치됩니다. 다음 다이어그램은 함수 호출 시퀀스를 보여줍니다. 시퀀스 자체는 이전 섹션의 다이어그램에 설명된 시퀀스와 비슷하지만 요청의 재귀 특성을 표시하도록 정렬됩니다.

일반 및 직접 OID 요청에 대한 함수 호출 시퀀스입니다.

충분한 필터가 설치된 경우 NDIS는 재귀를 더 깊게 유지하기 위해 새 스레드 스택을 할당해야 합니다.

NDIS는 스택을 따라 단일 홉에 대해서만 유효한 NDIS_OID_REQUEST 구조체를 고려합니다. 필터 드라이버가 요청을 다음 하위 드라이버(대부분의 OID의 경우)로 전달하려는 경우 필터 드라이버는 OID 요청을 복제하기 위해 수십 줄의 상용구 코드를 삽입 해야 합니다 . 이 상용구에는 다음과 같은 몇 가지 문제가 있습니다.

  1. 메모리 할당이 강제로 OID를 복제합니다. 메모리 풀을 누르면 속도가 느리고 OID 요청의 진행률을 보장할 수 없습니다.
  2. 모든 필터 드라이버가 한 NDIS_OID_REQUEST 내용을 다른 NDIS_OID_REQUEST 복사하는 메커니즘을 하드 코딩하기 때문에 OID 구조 디자인은 시간이 지남에 따라 동일하게 유지되어야 합니다.
  3. 상용구가 너무 많이 필요하면 필터가 실제로 수행하는 작업이 모호해집니다.

동기 OID 요청에 대한 필터링 모델

동기 OID 요청에 대한 필터링 모델은 호출의 동기 특성을 활용하여 이전 섹션에서 설명한 문제를 해결합니다.

문제 및 완료 처리기

일반 및 직접 OID 요청과 달리 동기 OID 요청에는 문제 처리기와 완료 처리기라는 두 가지 필터 후크가 있습니다. 필터 드라이버는 둘 다, 하나 또는 두 후크를 등록할 수 없습니다.

스택의 맨 위에서부터 스택의 아래쪽까지 각 필터 드라이버에 대해 문제 호출이 호출됩니다. 필터의 문제 호출은 OID가 아래로 계속 진행되는 것을 중지하고 일부 상태 코드로 OID를 완료할 수 있습니다. 필터가 OID를 가로채지 않으면 OID가 NIC 드라이버에 도달하여 OID를 동기적으로 완료해야 합니다.

OID가 완료되면 스택에서 OID가 완료된 위치부터 스택의 맨 위까지 각 필터 드라이버에 대해 전체 호출이 호출됩니다. Complete 호출은 OID 요청을 검사하거나 수정하고 OID의 완료 상태 코드를 검사하거나 수정할 수 있습니다.

다음 다이어그램에서는 프로토콜이 동기 OID 요청을 실행하고 필터가 요청을 가로채지 않는 일반적인 사례를 보여 줍니다.

동기 OID 요청에 대한 함수 호출 시퀀스입니다.

동기 OID에 대한 호출 모델은 반복적입니다. 이렇게 하면 스택 사용량이 상수로 제한되어 스택을 확장할 필요가 없습니다.

필터 드라이버가 문제 처리기에서 동기 OID를 가로채는 경우 OID는 하위 필터 또는 NIC 드라이버에 제공되지 않습니다. 그러나 다음 다이어그램과 같이 상위 필터에 대한 전체 처리기는 여전히 호출됩니다.

필터에 의한 가로채기가 있는 동기 OID 요청에 대한 함수 호출 시퀀스입니다.

최소 메모리 할당

일반 및 직접 OID 요청에는 필터 드라이버가 NDIS_OID_REQUEST 복제해야 합니다. 반면 동기 OID 요청은 복제할 수 없습니다. 이 디자인의 장점은 동기 OID의 대기 시간이 짧고 OID 요청이 필터 스택 아래로 이동할 때 반복적으로 복제되지 않으며 실패할 기회가 적다는 것입니다.

그러나, 그것은 새로운 문제를 제기 않습니다. OID를 복제할 수 없는 경우 필터 드라이버는 요청당 상태를 어디에 저장하나요? 예를 들어 필터 드라이버가 한 OID를 다른 OID로 변환한다고 가정해 보겠습니다. 스택 아래로 가는 길에 필터는 이전 OID를 저장해야 합니다. 스택을 백업하는 동안 필터는 이전 OID를 복원해야 합니다.

이 문제를 해결하기 위해 NDIS는 각 실행 중인 동기 OID 요청에 대해 각 필터 드라이버에 포인터 크기의 슬롯을 할당합니다. NDIS는 필터의 문제 처리기에서 Complete 처리기로 호출을 통해 이 슬롯을 유지합니다. 이렇게 하면 문제 처리기가 나중에 Complete 처리기에서 사용하는 상태를 저장할 수 있습니다. 다음 코드 조각은 예제를 보여 줍니다.

NDIS_STATUS
MyFilterSynchronousOidRequest(
  _In_ NDIS_HANDLE FilterModuleContext,
  _Inout_ NDIS_OID_REQUEST *OidRequest,
  _Outptr_result_maybenull_ PVOID *CallContext)
{
  if ( . . . should intercept this OID . . . )
  {
    // preserve the original buffer in the CallContext
    *CallContext = OidRequest->DATA.SET_INFORMATION.InformationBuffer;

    // replace the buffer with a new one
    OidRequest->DATA.SET_INFORMATION.InformationBuffer = . . . something . . .;
  }

  return NDIS_STATUS_SUCCESS;
}

VOID
MyFilterSynchronousOidRequestComplete(
  _In_ NDIS_HANDLE FilterModuleContext,
  _Inout_ NDIS_OID_REQUEST *OidRequest,
  _Inout_ NDIS_STATUS *Status,
  _In_ PVOID CallContext)
{
  // if the context is not null, we must have replaced the buffer.
  if (CallContext != null)
  {
    // Copy the data from the miniport back into the protocol’s original buffer.
    RtlCopyMemory(CallContext, OidRequest->DATA.SET_INFORMATION.InformationBuffer,...);
     
    // restore the original buffer into the OID request
    OidRequest->DATA.SET_INFORMATION.InformationBuffer = CallContext;
  }
}

NDIS는 호출당 필터당 하나의 PVOID를 저장합니다. NDIS는 일반적으로 풀 할당이 0이 되도록 스택에 적절한 수의 슬롯을 추론적으로 할당합니다. 일반적으로 7개 이하의 필터입니다. 사용자가 병리학적 사례를 설정하는 경우 NDIS는 풀 할당으로 대체됩니다.

상용구 감소

일반 또는 직접 OID 요청을 처리하기 위한 예제 상용구의 상용구 고려 이 코드는 OID 처리기를 등록하기 위한 항목 비용입니다. 고유한 OID를 발급하려면 다른 12줄의 상용구 를 추가해야 합니다. 동기 OID를 사용하면 비동기 완료를 처리하는 추가 복잡성이 필요하지 않습니다. 따라서 상용구의 대부분을 잘라낼 수 있습니다.

다음은 동기 OID를 사용하는 최소한의 문제 처리기입니다.

NDIS_STATUS
MyFilterSynchronousOidRequest(
  NDIS_HANDLE FilterModuleContext,
  NDIS_OID_REQUEST *OidRequest,
  PVOID *CallContext)
{
  return NDIS_STATUS_SUCCESS;
}

특정 OID를 가로채거나 수정하려는 경우 몇 줄의 코드만 추가하여 이 작업을 수행할 수 있습니다. 최소 Complete 처리기는 더 간단합니다.

VOID
MyFilterSynchronousOidRequestComplete(
  NDIS_HANDLE FilterModuleContext,
  NDIS_OID_REQUEST *OidRequest,
  NDIS_STATUS *Status,
  PVOID CallContext)
{
  return;
}

마찬가지로 필터 드라이버는 한 줄의 코드만 사용하여 자체의 새 동기 OID 요청을 실행할 수 있습니다.

status = NdisFSynchronousOidRequest(binding->NdisBindingHandle, &oid);

반면, Regular 또는 Direct OID를 발급해야 하는 필터 드라이버는 비동기 완료 처리기를 설정하고 일부 코드를 구현하여 자체 OID 완성과 방금 복제한 OID 완료를 구분해야 합니다. 이 상용구의 예는 일반 OID 요청을 발급하기 위한 예제 상용구에 나와 있습니다.

상호 운용성

일반, 직접 및 동기 호출 스타일은 모두 동일한 데이터 구조를 사용하지만 파이프라인은 미니포트의 동일한 처리기로 이동하지 않습니다. 또한 일부 파이프라인에서는 일부 OID를 사용할 수 없습니다. 예를 들어 OID_PNP_SET_POWER 신중한 동기화가 필요하며 종종 미니포트가 차단 호출을 수행하도록 강제합니다. 이렇게 하면 Direct OID 콜백에서 처리하기가 어렵고 동기 OID 콜백에서 사용할 수 없습니다.

따라서 Direct OID 요청과 마찬가지로 동기 OID 호출은 OID 하위 집합에서만 사용할 수 있습니다. Windows 10 버전 1709에서는 RSSv2(Receive Side Scaling Version 2)에 사용되는 OID_GEN_RSS_SET_INDIRECTION_TABLE_ENTRIES OID만 동기 OID 경로에서 지원됩니다.

동기 OID 요청 구현

드라이버에서 동기 OID 요청 인터페이스를 구현하는 방법에 대한 자세한 내용은 다음 topics 참조하세요.