다음을 통해 공유


PnP 페이징 요청 처리

필터링 중인 함수 드라이버가 이 IRP를 처리하는 경우 스토리지 필터 드라이버는 PnP 페이징 요청(IRP_MN_DEVICE_USAGE_NOTIFICATIONParameters.UsageNotification.TypeDeviceUsageTypePaging으로 설정된 IRP_MJ_PNP)을 처리해야 합니다.

필터 DO의 DeviceExtension에 다음 항목을 추가해야 합니다.

ULONG PagingCount;

KEVENT PagingCountEvent;

PnP 페이징 요청을 받으면 스토리지 필터 드라이버는 필터 DO에서 PagingCount와 DO_POWER_PAGABLE 비트 설정을 모두 업데이트해야 합니다. DO_POWER_PAGABLE 비트의 업데이트 타이밍은 비트가 설정 또는 지워지는지 여부에 따라 달라집니다. IRP가 비트를 설정해야 한다고 나타내는 경우 필터 드라이버는 IRP를 드라이버 스택 아래로 전달 하기 전에 설정해야 합니다. 그러나 IRP가 비트를 지워야 한다고 나타내는 경우 필터 드라이버는 비트를 바로 지워서는 안 됩니다. 먼저 IRP를 전달한 다음 낮은 드라이버가 상태 반환할 때까지 기다렸다가 하위 드라이버가 STATUS_SUCCESS 반환하는 경우에만 비트를 지워야 합니다.

다음은 스토리지 필터 드라이버에서 수행한 작업의 흐름을 추적합니다. C 코드에서 이 개요의 표현을 보려면 개요 바로 아래에 있는 의사 코드 샘플을 참조하세요.

A. 디바이스가 시작되었는지 확인합니다. 그렇지 않은 경우 STATUS_DEVICE_NOT_READY 사용하여 실패합니다.

B. PagingCountEvent(KeWaitForSingleObject( PagingCountEvent, ...))에서 동기화합니다.

C. 마지막 페이징 디바이스를 제거하는 경우( (! Parameters.UsageNotification.InPath && (PagingCount == 1) )

  1. 로컬 부울을 TRUE로 설정하고

  2. 필터 DO에서 DO_POWER_INRUSH 비트가 설정되지 않은 경우 DO_POWER_PAGABLE 비트를 설정합니다.

    다음은 DO_POWER_PAGABLE 비트가 위로 올라가지 않고 내려가는 길에 설정되어야 하는 이유를 설명합니다.

    전원 요구 사항에 따르면 더 낮은 디바이스 개체가 DO_POWER_PAGABLE 비트를 설정하는 경우 모든 상위 수준 드라이버가 동일한 작업을 수행해야 합니다. 페이징 요청 IRP를 스택 아래로 보내기 전에 필터 드라이버가 DO_POWER_PAGABLE 비트를 설정하지 못하면 다음과 같이 이 조건을 위반할 수 있습니다.

    필터 드라이버가 페이징 요청 IRP를 드라이버 스택의 아래에 있는 드라이버로 전달하기 전에 Filter DO에서 DO_POWER_PAGABLE 비트를 설정하지 않았다고 가정합니다. 다음으로 낮은 드라이버가 자체 DO에서 DO_POWER_PAGABLE 비트를 설정한다고 가정합니다. 마지막으로 필터 드라이버에서 IRP를 완료하기 전에 전원 IRP가 발생한다고 가정합니다. 이때 필터 DO에서 DO_POWER_PAGABLE 비트가 지워지지만 하위 수준 드라이버의 DO에 설정되어 시스템 크래시가 발생합니다.

    더 이상 필터 드라이버의 디바이스 에 활성 페이징 파일이 없으므로 더 이상 페이징 I/O가 발생하지 않으므로 페이징 요청을 스택 아래로 전달하기 전에 DO_POWER_PAGABLE 비트를 설정하는 것이 안전합니다. 이 페이징 파일을 제거하라는 요청이 성공하면 필터 드라이버가 수행됩니다. 요청이 실패하면 필터 드라이버는 IRP를 완료하기 전에 DO_POWER_PAGABLE 비트를 지우기만 하면 플래그의 원래 상태를 복원할 수 있습니다. 페이징 파일 요청이 직렬화되므로 필터 드라이버가 마지막으로 변경한 이후 다른 스레드가 이 비트를 수정할 위험이 없습니다.

D. IRP를 하위 드라이버로 동기적으로 전달합니다.

E. IRP가 성공적으로 완료되면

  1. IoAdjustPagingPathCount(&PagingCount, Parameters.UsageNotification.InPath)를 호출하여 PagingCount를 증가하거나 감소합니다. IoAdjustPagingPathCount는 Parameters.UsageNotification.InPath의 값에 따라 PagingCount의 InterlockedIncrement 또는 InterlockedDecrement를 수행합니다. TRUE 값은 페이징 파일이 추가되고 있음을 나타내므로 PagingCount를 증분합니다. FALSE 값은 페이징 파일이 제거되고 있음을 나타내므로 PagingCount가 감소합니다.

  2. Parameters.UsageNotification.InPathTRUE이면 페이징 파일이 추가되므로 DO_POWER_PAGABLE 비트가 지워집니다.

F. 그렇지 않으면 IRP가 실패합니다.

  1. 로컬 부울을 확인하여 DO_POWER_PAGABLE 가는 길에 필터 DO에서 설정되었는지 확인 합니다.

  2. DO_POWER_PAGABLE 내려가는 길에 설정된 경우 지웁니다.

G. 동기화 종료(KeSetEvent(PagingCountEvent, ...)).

의사 코드 예제

다음 코드 샘플에서 문자(//A, //B 등)로 표시된 섹션은 위의 개요 문자에 매핑됩니다.

case DeviceUsageTypePaging: { 
    BOOLEAN setPageable = FALSE; 
    BOOLEAN addPageFile = irpStack -> 
                          Parameters.UsageNotification.InPath; 
 
 // A 
    if((addPageFile) && 
        (extension -> CurrentPnpState != 
        IRP_MN_START_DEVICE)) { 
            status = STATUS_DEVICE_NOT_READY; 
            break; 
        } 
 // B 
    KeWaitForSingleObject(&commonExtension -> PagingCountEvent, 
                                    Executive, KernelMode, 
                                    FALSE, NULL); 
 // C 
    if (!addPageFile && commonExtension -> PagingCount == 1 ) { 
        // The last paging file is no longer active.
        // Set the DO_POWER_PAGABLE bit before 
        // forwarding the paging request down the 
        // stack.
        if (!(DeviceObject->Flags & DO_POWER_INRUSH)) { 
            DeviceObject->Flags |=             DO_POWER_PAGABLE; 
            setPageable = TRUE; 
        ) 
    ) 
 // D 
        status = ForwardIrpSynchronous(commonExtension, Irp); 
 // E
        if (NT_SUCCESS(status)) { 
            IoAdjustPagingPathCount(&commonExtension -> PagingCount, 
                                    addPageFile); 
        if (addPageFile && commonExtension -> PagingCount == 1) { 
            // Once the lower device objects have 
            // succeeded the addition of the paging 
            // file, it is illegal to fail the 
            // request. It is also the time to 
            // clear the Filter DO's 
            //DO_POWER_PAGABLE flag.
 
            DeviceObject->Flags &= ~DO_POWER_PAGABLE; 
            } 
        } else { 
 // F 
        if (setPageable == TRUE) { 
            DeviceObject->Flags &= ~DO_POWER_PAGABLE; 
            setPageable = FALSE; 
        } 
    } 
 // G 
        KeSetEvent(&commonExtension->PagingCountEvent, 
                                    IO_NO_INCREMENT, FALSE); 
                                    break;
    } *Do not use or delete the last paragraph mark. It maintains the template setup and formats.