다음을 통해 공유


DMA 전송 요청 분할

모든 드라이버는 다음에 따라 전송 요청을 분할하고 지정된 IRP를 충족하기 위해 둘 이상의 DMA 전송 작업을 수행해야 할 수 있습니다.

  • IoGetDmaAdapter에서 반환된 맵 레지스터

  • IRP에 대한 드라이버의 I/O 스택 위치의 Length 멤버에 포함된 전송할 데이터의 바이트입니다.

  • 드라이버가 데이터를 전송할 버퍼에 대한 시스템 실제 메모리의 페이지 경계 수입니다.

  • 드라이버의 DMA 작업에 대한 디바이스별 제약 조건입니다. 예를 들어 시스템 "AT" 디스크 드라이버는 디스크 컨트롤러의 제한 사항으로 인해 256개 이상의 섹터에 대한 전송 요청을 분할해야 합니다.

드라이버는 다음과 같이 IRP에 지정된 모든 데이터를 전송하는 데 필요한 맵 레지스터 수를 확인할 수 있습니다.

  1. MmGetMdlVirtualAddress를 호출하여 Irp-MdlAddress>의 MDL에 대한 포인터를 전달하여 버퍼의 시작 가상 주소를 가져옵니다. 드라이버는 이 가상 주소를 사용하여 메모리에 액세스하려고 시도해서는 안됩니다. MmGetMdlVirtualAddress에서 반환되는 값은 반드시 유효한 주소가 아니라 MDL의 인덱스입니다.

  2. IRP의 드라이버 I/O 스택 위치에 있는 반환된 인덱스 및 Length 값을 ADDRESS_AND_SIZE_TO_SPAN_PAGES 매크로에 전달합니다.

ADDRESS_AND_SIZE_TO_SPAN_PAGES 반환된 값이 IoGetDmaAdapter에서 반환한 NumberOfMapRegisters 값보다 큰 경우 드라이버는 단일 DMA 작업에서 이 IRP에 대해 요청된 모든 데이터를 전송할 수 없습니다. 대신 다음을 수행해야 합니다.

  1. 버퍼를 사용 가능한 맵 레지스터 수(및 디바이스별 DMA 제약 조건)에 맞게 크기가 조정된 조각으로 분할합니다.

  2. 전송 요청을 충족하는 데 필요한 만큼의 DMA 작업을 수행합니다.

예를 들어 ADDRESS_AND_SIZE_TO_SPAN_PAGES 전송 요청을 충족하기 위해 12개의 맵 레지스터가 필요하지만 IoGetDmaAdapter에서 반환된 NumberOfMapRegisters 값은 5개에 불과하다고 가정합니다. (디바이스별 DMA 제약 조건이 없다고 가정합니다.) 이 경우 드라이버는 3개의 DMA 전송 작업을 수행해야 하며, MapTransfer 를 세 번 호출하여 IRP에서 요청한 모든 데이터를 전송해야 합니다.

시스템의 DMA 디바이스 드라이버는 단일 I/O 작업으로 IRP를 만족시킬 수 있는 지도 레지스터가 충분하지 않은 경우 다양한 기술을 사용하여 DMA 전송을 분할합니다. 사용할 수 있는 한 가지 방법은 다음과 같습니다.

  1. IoAllocateMdl을 호출하여 사용자 버퍼의 일부를 설명하는 MDL을 할당합니다.

  2. MmProbeAndLockPages를 호출하여 사용자 버퍼의 해당 부분을 잠급니다.

  3. 버퍼의 해당 부분에 대한 데이터를 전송합니다.

  4. MmUnlockPages를 호출하고 다음 중 하나를 수행합니다.

    • 드라이버가 1단계에서 할당한 MDL이 다음 전송에 충분히 큰 경우 MmPrepareMdlForReuse 를 호출하고 2~4단계를 반복합니다.
    • 그렇지 않으면 IoFreeMdl을 호출하고 1~4단계를 반복합니다.
  5. 모든 데이터가 전송되면 MmUnlockPagesIoFreeMdl 을 호출합니다.

최상위 드라이버가 메모리가 제한된 컴퓨터에서 MmProbeAndLockPages 를 사용하여 전체 사용자 버퍼를 잠글 수 없는 경우 다음을 수행할 수 있습니다.

  1. IoBuildSynchronousFsdRequest를 호출하여 부분 전송 IRP를 할당하고 사용자 버퍼의 일부를 잠급니다. 잠긴 영역은 일반적으로 PAGE_SIZE 배수이거나 기본 디바이스의 전송 용량에 맞게 크기가 조정됩니다.

  2. 부분 전송 IRP에 대해 IoCallDriver 를 호출하고, 하위 드라이버가 STATUS_PENDING 반환하는 경우 드라이버가 부분 전송 IRP와 연결되도록 설정한 이벤트 개체를 기다리려면 KeWaitForSingleObject 를 호출합니다.

  3. 제어권을 다시 얻으면 모든 데이터가 전송될 때까지 1단계와 2단계를 반복한 다음 원래 IRP를 완료합니다.

스토리지 클래스 드라이버가 기본 SCSI 포트/미니포트 드라이버에 대한 대용량 전송 요청을 분할하는 경우 전송 요청의 각 부분에 대해 추가 IRP를 할당합니다. 드라이버 할당 IRP마다 IoCompletion 루틴을 등록하여 전체 전송 요청의 상태 추적하고 드라이버 할당 IRP를 해제합니다. 그런 다음 IoCallDriver를 사용하여 포트 드라이버에 이러한 IRP를 보냅니다.

다른 클래스/포트 드라이버는 클래스 드라이버가 포트 드라이버에 사용할 수 있는 맵 레지스터 수를 결정할 수 있는 경우에만 이 기술을 사용할 수 있습니다. 포트 드라이버는 쌍을 이루는 클래스 드라이버의 레지스트리에 이 구성 정보를 저장해야 합니다. 또는 쌍을 이루는 드라이버는 내부 디바이스 I/O 제어 요청을 사용하여 프라이빗 인터페이스를 정의하여 포트 드라이버에서 클래스 드라이버로 사용 가능한 맵 레지스터 수에 대한 구성 정보를 전달해야 합니다.

DMA 디바이스에 대한 모놀리식 드라이버(즉, 클래스/포트 쌍의 일부가 아닌 드라이버)는 자체에 대한 큰 전송 요청을 분할해야 합니다. 이러한 드라이버는 일반적으로 큰 요청을 조각으로 분할하고 IRP를 충족하기 위해 일련의 DMA 작업을 수행합니다.

전송 요청이 너무 커서 기본 디바이스 드라이버가 처리할 수 없는 경우 상위 수준 드라이버는 MmGetMdlVirtualAddressIoBuildPartialMdl을 호출한 다음 기본 디바이스 드라이버에 대한 부분 전송 IRP 시퀀스를 설정할 수 있습니다.