[NTOS:PNP] Halfplement IoInvalidateDeviceState

Implement the correct start-stop sequence for resource rebalancing
without the actual rebalancing. Also move IoInvalidateDeviceState
processing into the enumeration thread as it should be.

CORE-17519
This commit is contained in:
Victor Perevertkin 2021-10-13 02:28:24 +03:00
parent 8f40fc7ede
commit cf0bc1c132
No known key found for this signature in database
GPG key ID: C750B7222E9C7830
4 changed files with 169 additions and 85 deletions

View file

@ -527,7 +527,8 @@ typedef enum _DEVICE_ACTION
PiActionEnumRootDevices,
PiActionResetDevice,
PiActionAddBootDevices,
PiActionStartDevice
PiActionStartDevice,
PiActionQueryState,
} DEVICE_ACTION;
//
@ -1403,6 +1404,16 @@ PiIrpQueryDeviceRelations(
_In_ PDEVICE_NODE DeviceNode,
_In_ DEVICE_RELATION_TYPE Type);
NTSTATUS
PiIrpQueryResources(
_In_ PDEVICE_NODE DeviceNode,
_Out_ PCM_RESOURCE_LIST *Resources);
NTSTATUS
PiIrpQueryResourceRequirements(
_In_ PDEVICE_NODE DeviceNode,
_Out_ PIO_RESOURCE_REQUIREMENTS_LIST *Resources);
NTSTATUS
PiIrpQueryDeviceText(
_In_ PDEVICE_NODE DeviceNode,

View file

@ -1552,8 +1552,8 @@ PiStartDeviceFinal(
DPRINT("IopInitiatePnpIrp() failed (Status 0x%08lx)\n", Status);
}
/* Invalidate device state so IRP_MN_QUERY_PNP_DEVICE_STATE is sent */
IoInvalidateDeviceState(DeviceNode->PhysicalDeviceObject);
// Query the device state (IRP_MN_QUERY_PNP_DEVICE_STATE)
PiQueueDeviceAction(DeviceNode->PhysicalDeviceObject, PiActionQueryState, NULL, NULL);
DPRINT("Sending GUID_DEVICE_ARRIVAL %wZ\n", &DeviceNode->InstancePath);
IopQueueTargetDeviceEvent(&GUID_DEVICE_ARRIVAL, &DeviceNode->InstancePath);
@ -2025,25 +2025,27 @@ IopRemoveDevice(PDEVICE_NODE DeviceNode)
return Status;
}
/*
* @implemented
/**
* @brief Processes the IoInvalidateDeviceState request
*
* Sends IRP_MN_QUERY_PNP_DEVICE_STATE request and sets device node's flags
* according to the result.
* Tree reenumeration should be started upon a successful return of the function.
*
* @todo Do not return STATUS_SUCCESS if nothing is changed.
*/
VOID
NTAPI
IoInvalidateDeviceState(IN PDEVICE_OBJECT PhysicalDeviceObject)
static
NTSTATUS
PiUpdateDeviceState(
_In_ PDEVICE_NODE DeviceNode)
{
PDEVICE_NODE DeviceNode = IopGetDeviceNode(PhysicalDeviceObject);
PNP_DEVICE_STATE PnPFlags;
NTSTATUS Status;
Status = PiIrpQueryPnPDeviceState(DeviceNode, &PnPFlags);
if (!NT_SUCCESS(Status))
{
if (Status != STATUS_NOT_SUPPORTED)
{
DPRINT1("IRP_MN_QUERY_PNP_DEVICE_STATE failed with status 0x%lx\n", Status);
}
return;
return Status;
}
if (PnPFlags & PNP_DEVICE_NOT_DISABLEABLE)
@ -2056,83 +2058,37 @@ IoInvalidateDeviceState(IN PDEVICE_OBJECT PhysicalDeviceObject)
else
DeviceNode->UserFlags &= ~DNUF_DONT_SHOW_IN_UI;
if ((PnPFlags & PNP_DEVICE_REMOVED) ||
((PnPFlags & PNP_DEVICE_FAILED) && !(PnPFlags & PNP_DEVICE_RESOURCE_REQUIREMENTS_CHANGED)))
if (PnPFlags & PNP_DEVICE_REMOVED || PnPFlags & PNP_DEVICE_DISABLED)
{
/* Flag it if it's failed */
if (PnPFlags & PNP_DEVICE_FAILED)
{
PiSetDevNodeProblem(DeviceNode, CM_PROB_FAILED_POST_START);
}
PiSetDevNodeProblem(DeviceNode,
PnPFlags & PNP_DEVICE_DISABLED
? CM_PROB_HARDWARE_DISABLED
: CM_PROB_DEVICE_NOT_THERE);
DeviceNode->Flags |= DNF_DEVICE_GONE;
PiSetDevNodeState(DeviceNode, DeviceNodeAwaitingQueuedRemoval);
}
// it doesn't work anyway. A real resource rebalancing should be implemented
#if 0
else if ((PnPFlags & PNP_DEVICE_FAILED) && (PnPFlags & PNP_DEVICE_RESOURCE_REQUIREMENTS_CHANGED))
else if (PnPFlags & PNP_DEVICE_RESOURCE_REQUIREMENTS_CHANGED)
{
/* Stop for resource rebalance */
Status = IopStopDevice(DeviceNode);
if (!NT_SUCCESS(Status))
{
DPRINT1("Failed to stop device for rebalancing\n");
// Query resource rebalance
/* Stop failed so don't rebalance */
PnPFlags &= ~PNP_DEVICE_RESOURCE_REQUIREMENTS_CHANGED;
}
if (PnPFlags & PNP_DEVICE_FAILED)
DeviceNode->Flags &= DNF_NON_STOPPED_REBALANCE;
else
DeviceNode->Flags |= DNF_NON_STOPPED_REBALANCE;
// Clear DNF_NO_RESOURCE_REQUIRED just in case (will be set back if needed)
DeviceNode->Flags &= ~DNF_NO_RESOURCE_REQUIRED;
// This will be caught up later by enumeration
DeviceNode->Flags |= DNF_RESOURCE_REQUIREMENTS_CHANGED;
}
else if (PnPFlags & PNP_DEVICE_FAILED)
{
PiSetDevNodeProblem(DeviceNode, CM_PROB_FAILED_POST_START);
PiSetDevNodeState(DeviceNode, DeviceNodeAwaitingQueuedRemoval);
}
/* Resource rebalance */
if (PnPFlags & PNP_DEVICE_RESOURCE_REQUIREMENTS_CHANGED)
{
DPRINT("Sending IRP_MN_QUERY_RESOURCES to device stack\n");
Status = IopInitiatePnpIrp(PhysicalDeviceObject,
&IoStatusBlock,
IRP_MN_QUERY_RESOURCES,
NULL);
if (NT_SUCCESS(Status) && IoStatusBlock.Information)
{
DeviceNode->BootResources =
(PCM_RESOURCE_LIST)IoStatusBlock.Information;
IopDeviceNodeSetFlag(DeviceNode, DNF_HAS_BOOT_CONFIG);
}
else
{
DPRINT("IopInitiatePnpIrp() failed (Status %x) or IoStatusBlock.Information=NULL\n", Status);
DeviceNode->BootResources = NULL;
}
DPRINT("Sending IRP_MN_QUERY_RESOURCE_REQUIREMENTS to device stack\n");
Status = IopInitiatePnpIrp(PhysicalDeviceObject,
&IoStatusBlock,
IRP_MN_QUERY_RESOURCE_REQUIREMENTS,
NULL);
if (NT_SUCCESS(Status))
{
DeviceNode->ResourceRequirements =
(PIO_RESOURCE_REQUIREMENTS_LIST)IoStatusBlock.Information;
}
else
{
DPRINT("IopInitiatePnpIrp() failed (Status %08lx)\n", Status);
DeviceNode->ResourceRequirements = NULL;
}
/* IRP_MN_FILTER_RESOURCE_REQUIREMENTS is called indirectly by IopStartDevice */
if (IopStartDevice(DeviceNode) != STATUS_SUCCESS)
{
DPRINT1("Restart after resource rebalance failed\n");
DeviceNode->Flags &= ~(DNF_STARTED | DNF_START_REQUEST_PENDING);
DeviceNode->Flags |= DNF_START_FAILED;
IopRemoveDevice(DeviceNode);
}
}
#endif
return STATUS_SUCCESS;
}
static
@ -2327,6 +2283,30 @@ cleanup:
&DeviceNode->InstancePath);
}
static
VOID
PiFakeResourceRebalance(
_In_ PDEVICE_NODE DeviceNode)
{
ASSERT(DeviceNode->Flags & DNF_RESOURCE_REQUIREMENTS_CHANGED);
PCM_RESOURCE_LIST bootConfig = NULL;
PIO_RESOURCE_REQUIREMENTS_LIST resourceRequirements = NULL;
PiIrpQueryResources(DeviceNode, &bootConfig);
PiIrpQueryResourceRequirements(DeviceNode, &resourceRequirements);
DeviceNode->BootResources = bootConfig;
DeviceNode->ResourceRequirements = resourceRequirements;
if (bootConfig)
{
DeviceNode->Flags |= DNF_HAS_BOOT_CONFIG;
}
DeviceNode->Flags &= ~DNF_RESOURCE_REQUIREMENTS_CHANGED;
}
static
VOID
PiDevNodeStateMachine(
@ -2420,12 +2400,28 @@ PiDevNodeStateMachine(
PiSetDevNodeState(currentNode, DeviceNodeEnumerateCompletion);
doProcessAgain = TRUE;
}
else if (currentNode->Flags & DNF_RESOURCE_REQUIREMENTS_CHANGED)
{
if (currentNode->Flags & DNF_NON_STOPPED_REBALANCE)
{
PiFakeResourceRebalance(currentNode);
currentNode->Flags &= ~DNF_NON_STOPPED_REBALANCE;
}
else
{
PiIrpQueryStopDevice(currentNode);
PiSetDevNodeState(currentNode, DeviceNodeQueryStopped);
}
doProcessAgain = TRUE;
}
break;
case DeviceNodeQueryStopped:
// we're here after sending IRP_MN_QUERY_STOP_DEVICE
status = currentNode->CompletionStatus;
if (NT_SUCCESS(status))
{
PiIrpStopDevice(currentNode);
PiSetDevNodeState(currentNode, DeviceNodeStopped);
}
else
@ -2433,10 +2429,14 @@ PiDevNodeStateMachine(
PiIrpCancelStopDevice(currentNode);
PiSetDevNodeState(currentNode, DeviceNodeStarted);
}
doProcessAgain = TRUE;
break;
case DeviceNodeStopped:
// TODO: do resource rebalance (not implemented)
ASSERT(FALSE);
PiFakeResourceRebalance(currentNode);
PiSetDevNodeState(currentNode, DeviceNodeDriversAdded);
doProcessAgain = TRUE;
break;
case DeviceNodeRestartCompletion:
break;
@ -2522,6 +2522,8 @@ ActionToStr(
return "PiActionAddBootDevices";
case PiActionStartDevice:
return "PiActionStartDevice";
case PiActionQueryState:
return "PiActionQueryState";
default:
return "(request unknown)";
}
@ -2596,6 +2598,16 @@ PipDeviceActionWorker(
}
break;
case PiActionQueryState:
// First, do a IRP_MN_QUERY_PNP_DEVICE_STATE request,
// it will update node's flags and then do enumeration if something changed
status = PiUpdateDeviceState(deviceNode);
if (NT_SUCCESS(status))
{
PiDevNodeStateMachine(deviceNode);
}
break;
default:
DPRINT1("Unimplemented device action %u\n", Request->Action);
status = STATUS_NOT_IMPLEMENTED;

View file

@ -196,6 +196,58 @@ PiIrpQueryDeviceRelations(
return status;
}
// IRP_MN_QUERY_RESOURCES (0x0A)
NTSTATUS
PiIrpQueryResources(
_In_ PDEVICE_NODE DeviceNode,
_Out_ PCM_RESOURCE_LIST *Resources)
{
PAGED_CODE();
ASSERT(DeviceNode);
ULONG_PTR longRes;
IO_STACK_LOCATION stack = {
.MajorFunction = IRP_MJ_PNP,
.MinorFunction = IRP_MN_QUERY_RESOURCES
};
NTSTATUS status;
status = IopSynchronousCall(DeviceNode->PhysicalDeviceObject, &stack, (PVOID)&longRes);
if (NT_SUCCESS(status))
{
*Resources = (PVOID)longRes;
}
return status;
}
// IRP_MN_QUERY_RESOURCE_REQUIREMENTS (0x0B)
NTSTATUS
PiIrpQueryResourceRequirements(
_In_ PDEVICE_NODE DeviceNode,
_Out_ PIO_RESOURCE_REQUIREMENTS_LIST *Resources)
{
PAGED_CODE();
ASSERT(DeviceNode);
ULONG_PTR longRes;
IO_STACK_LOCATION stack = {
.MajorFunction = IRP_MJ_PNP,
.MinorFunction = IRP_MN_QUERY_RESOURCE_REQUIREMENTS
};
NTSTATUS status;
status = IopSynchronousCall(DeviceNode->PhysicalDeviceObject, &stack, (PVOID)&longRes);
if (NT_SUCCESS(status))
{
*Resources = (PVOID)longRes;
}
return status;
}
// IRP_MN_QUERY_DEVICE_TEXT (0x0C)
NTSTATUS
PiIrpQueryDeviceText(
@ -236,7 +288,8 @@ PiIrpQueryPnPDeviceState(
PAGED_CODE();
ASSERT(DeviceNode);
ASSERT(DeviceNode->State == DeviceNodeStartPostWork ||
ASSERT(DeviceNode->State == DeviceNodeResourcesAssigned ||
DeviceNode->State == DeviceNodeStartPostWork ||
DeviceNode->State == DeviceNodeStarted);
ULONG_PTR longState;

View file

@ -2412,3 +2412,11 @@ IoTranslateBusAddress(IN INTERFACE_TYPE InterfaceType,
AddressSpace,
TranslatedAddress);
}
VOID
NTAPI
IoInvalidateDeviceState(
IN PDEVICE_OBJECT DeviceObject)
{
PiQueueDeviceAction(DeviceObject, PiActionQueryState, NULL, NULL);
}