处理 PnP 分页请求

存储筛选器驱动程序必须处理 PnP 分页请求 (IRP_MJ_PNP ,并且 IRP_MN_DEVICE_USAGE_NOTIFICATIONParameters.UsageNotification.Type 设置为 DeviceUsageTypePaging) ,前提是它正在筛选的函数驱动程序处理此 IRP。

必须将以下项添加到 Filter DO 的 DeviceExtension 中:

ULONG PagingCount;

KEVENT PagingCountEvent;

收到 PnP 分页请求后,存储筛选器驱动程序必须更新 PagingCount 和 Filter DO 中 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. 如果未在 Filter DO 中设置 DO_POWER_INRUSH 位,则设置 DO_POWER_PAGABLE 位。

    下面解释了为什么 必须设置DO_POWER_PAGABLE 位,而不是在上升的路上:

    电源要求规定,如果任何较低的设备对象设置 DO_POWER_PAGABLE 位,则所有较高级别的驱动程序都必须执行相同的操作。 如果筛选器驱动程序在将分页请求 IRP 发送到堆栈之前未能设置 DO_POWER_PAGABLE 位,则可能会违反此条件,如下所示:

    假设筛选器驱动程序未在其 Filter DO 中设置 DO_POWER_PAGABLE 位,然后再将分页请求 IRP 转发到驱动程序堆栈中其下的驱动程序。 接下来,假设较低的驱动程序在其自己的 DO 中设置 DO_POWER_PAGABLE 位。 最后,假设在筛选器驱动程序完成 IRP 之前发生了电源 IRP。 此时, DO_POWER_PAGABLE 位将在 Filter DO 中清除,但在较低级别驱动程序的 DO 中设置,从而导致系统崩溃。

    在将分页请求转发到堆栈之前,可以安全地设置 DO_POWER_PAGABLE 位,因为筛选器驱动程序的设备上不再存在活动的分页文件,因此不会再发生分页 I/O。 如果删除此分页文件的请求成功,筛选器驱动程序将完成。 如果请求失败,筛选器驱动程序只需在完成 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”中设置了 DO_POWER_PAGABLE

  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.