处理 PnP 分页请求
存储筛选器驱动程序必须处理 PnP 分页请求 (IRP_MJ_PNP ,并且 IRP_MN_DEVICE_USAGE_NOTIFICATION 和 Parameters.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) ) 然后
将本地布尔值设置为 TRUE,并且
如果未在 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 成功完成,则
调用 IoAdjustPagingPathCount (&PagingCount, Parameters.UsageNotification.InPath) 递增或递减 PagingCount。 IoAdjustPagingPathCount 根据 Parameters.UsageNotification.InPath 中的值执行 PagingCount 的 InterlockedIncrement 或 InterlockedDecrement。 值为 TRUE 表示正在添加分页文件,因此请递增 PagingCount;值为 FALSE 表示正在删除分页文件,因此请递减 PagingCount。
如果 Parameters.UsageNotification.InPath 为 TRUE,则添加分页文件,因此请清除 DO_POWER_PAGABLE 位。
F. 否则,如果 IRP 失败,则
检查本地布尔值,查看是否在向下的“筛选器 DO”中设置了 DO_POWER_PAGABLE 。
如果在向下的路上设置了 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.