處理 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) ) then
將本機布爾值設定為 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_POWER_PAGABLE 是否已在關閉時於 Filter DO 中設定。
如果 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.