Author: cgutman Date: Wed Jun 1 19:32:12 2011 New Revision: 52046
URL: http://svn.reactos.org/svn/reactos?rev=52046&view=rev Log: [NTOSKRNL] - Massive rework of device removal - IRP_MN_QUERY_REMOVE_DEVICE is sent to all devices that will be removed in response to a removal/ejection - If any of the IRP_MN_QUERY_REMOVE_DEVICE requests fail, all devices which received a IRP_MN_QUERY_REMOVE_DEVICE will receive IRP_MN_CANCEL_REMOVE_DEVICE - If everything approves the remove request, IRP_MN_REMOVE_DEVICE will be sent to children, ejection and/or removal relations, and finally the device itself - Handle removal relations for all devices not just 1st level devices - Remove child devices before removing the parent - Move GUID_DEVICE_REMOVE_PENDING and GUID_DEVICE_REMOVAL_VETOED notifications to IopQueryRemoveDevice - Implement IopCancelRemoveDevice which sends IRP_MN_CANCEL_REMOVE_DEVICE to the driver stack and sends GUID_TARGET_DEVICE_REMOVE_CANCELLED to any drivers waiting on target device change - Use the IopRemoveDevice function to remove devices instead of calling IopQueryRemoveDevice and IopSendRemoveDevice manually so removal and/or ejection relations and children are processed - IRPs and PnP notifications sent upon device removal (surprise and safe) should now be compatible with XP/Vista/7
Modified: trunk/reactos/ntoskrnl/io/pnpmgr/pnpmgr.c
Modified: trunk/reactos/ntoskrnl/io/pnpmgr/pnpmgr.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/io/pnpmgr/pnpmgr.c... ============================================================================== --- trunk/reactos/ntoskrnl/io/pnpmgr/pnpmgr.c [iso-8859-1] (original) +++ trunk/reactos/ntoskrnl/io/pnpmgr/pnpmgr.c [iso-8859-1] Wed Jun 1 19:32:12 2011 @@ -45,6 +45,12 @@ IN ULONG CreateOptions, OUT PHANDLE Handle);
+VOID +IopCancelPrepareDeviceForRemoval(PDEVICE_OBJECT DeviceObject); + +NTSTATUS +IopPrepareDeviceForRemoval(PDEVICE_OBJECT DeviceObject); + PDEVICE_NODE FASTCALL IopGetDeviceNode(PDEVICE_OBJECT DeviceObject) @@ -125,6 +131,7 @@ return STATUS_SUCCESS; }
+static NTSTATUS NTAPI IopSendEject(IN PDEVICE_OBJECT DeviceObject) @@ -139,6 +146,7 @@ return IopSynchronousCall(DeviceObject, &Stack, &Dummy); }
+static VOID NTAPI IopSendSurpriseRemoval(IN PDEVICE_OBJECT DeviceObject) @@ -154,13 +162,20 @@ IopSynchronousCall(DeviceObject, &Stack, &Dummy); }
+static NTSTATUS NTAPI IopQueryRemoveDevice(IN PDEVICE_OBJECT DeviceObject) { + PDEVICE_NODE DeviceNode = IopGetDeviceNode(DeviceObject); IO_STACK_LOCATION Stack; PVOID Dummy; NTSTATUS Status; + + ASSERT(DeviceNode); + + IopQueueTargetDeviceEvent(&GUID_DEVICE_REMOVE_PENDING, + &DeviceNode->InstancePath);
RtlZeroMemory(&Stack, sizeof(IO_STACK_LOCATION)); Stack.MajorFunction = IRP_MJ_PNP; @@ -173,10 +188,18 @@ &GUID_TARGET_DEVICE_QUERY_REMOVE, NULL, NULL); + + if (!NT_SUCCESS(Status)) + { + DPRINT1("Removal vetoed by %wZ\n", &DeviceNode->InstancePath); + IopQueueTargetDeviceEvent(&GUID_DEVICE_REMOVAL_VETOED, + &DeviceNode->InstancePath); + }
return Status; }
+static NTSTATUS NTAPI IopQueryStopDevice(IN PDEVICE_OBJECT DeviceObject) @@ -191,6 +214,7 @@ return IopSynchronousCall(DeviceObject, &Stack, &Dummy); }
+static VOID NTAPI IopSendRemoveDevice(IN PDEVICE_OBJECT DeviceObject) @@ -212,6 +236,29 @@ NULL); }
+static +VOID +NTAPI +IopCancelRemoveDevice(IN PDEVICE_OBJECT DeviceObject) +{ + IO_STACK_LOCATION Stack; + PVOID Dummy; + + RtlZeroMemory(&Stack, sizeof(IO_STACK_LOCATION)); + Stack.MajorFunction = IRP_MJ_PNP; + Stack.MinorFunction = IRP_MN_CANCEL_REMOVE_DEVICE; + + /* Drivers should never fail a IRP_MN_CANCEL_REMOVE_DEVICE request */ + IopSynchronousCall(DeviceObject, &Stack, &Dummy); + + IopNotifyPlugPlayNotification(DeviceObject, + EventCategoryTargetDeviceChange, + &GUID_TARGET_DEVICE_REMOVE_CANCELLED, + NULL, + NULL); +} + +static VOID NTAPI IopSendStopDevice(IN PDEVICE_OBJECT DeviceObject) @@ -257,7 +304,7 @@ if (!NT_SUCCESS(Status)) { /* Send an IRP_MN_REMOVE_DEVICE request */ - IopSendRemoveDevice(DeviceObject); + IopRemoveDevice(DeviceNode);
/* Set the appropriate flag */ DeviceNode->Flags |= DNF_START_FAILED; @@ -3721,7 +3768,7 @@ DeviceNode->Flags &= ~(DNF_STARTED | DNF_START_REQUEST_PENDING); DeviceNode->Flags |= DNF_START_FAILED;
- IopSendRemoveDevice(PhysicalDeviceObject); + IopRemoveDevice(DeviceNode); } } } @@ -3881,8 +3928,213 @@ return Status; }
+static NTSTATUS -NTAPI +IopQueryRemoveChildDevices(PDEVICE_NODE ParentDeviceNode) +{ + PDEVICE_NODE ChildDeviceNode, NextDeviceNode, FailedRemoveDevice; + NTSTATUS Status; + KIRQL OldIrql; + + KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql); + ChildDeviceNode = ParentDeviceNode->Child; + while (ChildDeviceNode != NULL) + { + NextDeviceNode = ChildDeviceNode->Sibling; + KeReleaseSpinLock(&IopDeviceTreeLock, OldIrql); + + Status = IopPrepareDeviceForRemoval(ChildDeviceNode->PhysicalDeviceObject); + if (!NT_SUCCESS(Status)) + { + FailedRemoveDevice = ChildDeviceNode; + goto cleanup; + } + + KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql); + ChildDeviceNode = NextDeviceNode; + } + KeReleaseSpinLock(&IopDeviceTreeLock, OldIrql); + + return STATUS_SUCCESS; + +cleanup: + KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql); + ChildDeviceNode = ParentDeviceNode->Child; + while (ChildDeviceNode != NULL) + { + NextDeviceNode = ChildDeviceNode->Sibling; + KeReleaseSpinLock(&IopDeviceTreeLock, OldIrql); + + IopCancelPrepareDeviceForRemoval(ChildDeviceNode->PhysicalDeviceObject); + + /* IRP_MN_CANCEL_REMOVE_DEVICE is also sent to the device + * that failed the IRP_MN_QUERY_REMOVE_DEVICE request */ + if (ChildDeviceNode == FailedRemoveDevice) + return Status; + + ChildDeviceNode = NextDeviceNode; + + KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql); + } + KeReleaseSpinLock(&IopDeviceTreeLock, OldIrql); + + return Status; +} + +static +VOID +IopSendRemoveChildDevices(PDEVICE_NODE ParentDeviceNode) +{ + PDEVICE_NODE ChildDeviceNode, NextDeviceNode; + KIRQL OldIrql; + + KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql); + ChildDeviceNode = ParentDeviceNode->Child; + while (ChildDeviceNode != NULL) + { + NextDeviceNode = ChildDeviceNode->Sibling; + KeReleaseSpinLock(&IopDeviceTreeLock, OldIrql); + + IopSendRemoveDevice(ChildDeviceNode->PhysicalDeviceObject); + + ChildDeviceNode = NextDeviceNode; + + KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql); + } + KeReleaseSpinLock(&IopDeviceTreeLock, OldIrql); +} + +static +VOID +IopCancelRemoveChildDevices(PDEVICE_NODE ParentDeviceNode) +{ + PDEVICE_NODE ChildDeviceNode, NextDeviceNode; + KIRQL OldIrql; + + KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql); + ChildDeviceNode = ParentDeviceNode->Child; + while (ChildDeviceNode != NULL) + { + NextDeviceNode = ChildDeviceNode->Sibling; + KeReleaseSpinLock(&IopDeviceTreeLock, OldIrql); + + IopCancelPrepareDeviceForRemoval(ChildDeviceNode->PhysicalDeviceObject); + + ChildDeviceNode = NextDeviceNode; + + KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql); + } + KeReleaseSpinLock(&IopDeviceTreeLock, OldIrql); +} + +static +NTSTATUS +IopQueryRemoveDeviceRelations(PDEVICE_RELATIONS DeviceRelations) +{ + /* This function DOES NOT dereference the device objects on SUCCESS + * but it DOES dereference device objects on FAILURE */ + + ULONG i, j; + NTSTATUS Status; + + for (i = 0; i < DeviceRelations->Count; i++) + { + Status = IopPrepareDeviceForRemoval(DeviceRelations->Objects[i]); + if (!NT_SUCCESS(Status)) + { + j = i; + goto cleanup; + } + } + + return STATUS_SUCCESS; + +cleanup: + /* IRP_MN_CANCEL_REMOVE_DEVICE is also sent to the device + * that failed the IRP_MN_QUERY_REMOVE_DEVICE request */ + for (i = 0; i <= j; i++) + { + IopCancelPrepareDeviceForRemoval(DeviceRelations->Objects[i]); + ObDereferenceObject(DeviceRelations->Objects[i]); + DeviceRelations->Objects[i] = NULL; + } + for (; i < DeviceRelations->Count; i++) + { + ObDereferenceObject(DeviceRelations->Objects[i]); + DeviceRelations->Objects[i] = NULL; + } + ExFreePool(DeviceRelations); + + return Status; +} + +static +VOID +IopSendRemoveDeviceRelations(PDEVICE_RELATIONS DeviceRelations) +{ + /* This function DOES dereference the device objects in all cases */ + + ULONG i; + + for (i = 0; i < DeviceRelations->Count; i++) + { + IopSendRemoveDevice(DeviceRelations->Objects[i]); + ObDereferenceObject(DeviceRelations->Objects[i]); + DeviceRelations->Objects[i] = NULL; + } + + ExFreePool(DeviceRelations); +} + +static +VOID +IopCancelRemoveDeviceRelations(PDEVICE_RELATIONS DeviceRelations) +{ + /* This function DOES dereference the device objects in all cases */ + + ULONG i; + + for (i = 0; i < DeviceRelations->Count; i++) + { + IopCancelPrepareDeviceForRemoval(DeviceRelations->Objects[i]); + ObDereferenceObject(DeviceRelations->Objects[i]); + DeviceRelations->Objects[i] = NULL; + } + + ExFreePool(DeviceRelations); +} + +VOID +IopCancelPrepareDeviceForRemoval(PDEVICE_OBJECT DeviceObject) +{ + IO_STACK_LOCATION Stack; + IO_STATUS_BLOCK IoStatusBlock; + PDEVICE_RELATIONS DeviceRelations; + NTSTATUS Status; + + IopCancelRemoveDevice(DeviceObject); + + Stack.Parameters.QueryDeviceRelations.Type = RemovalRelations; + + Status = IopInitiatePnpIrp(DeviceObject, + &IoStatusBlock, + IRP_MN_QUERY_DEVICE_RELATIONS, + &Stack); + if (!NT_SUCCESS(Status)) + { + DPRINT("IopInitiatePnpIrp() failed with status 0x%08lx\n", Status); + DeviceRelations = NULL; + } + else + { + DeviceRelations = (PDEVICE_RELATIONS)IoStatusBlock.Information; + } + + if (DeviceRelations) + IopCancelRemoveDeviceRelations(DeviceRelations); +} + +NTSTATUS IopPrepareDeviceForRemoval(IN PDEVICE_OBJECT DeviceObject) { PDEVICE_NODE DeviceNode = IopGetDeviceNode(DeviceObject); @@ -3890,7 +4142,6 @@ IO_STATUS_BLOCK IoStatusBlock; PDEVICE_RELATIONS DeviceRelations; NTSTATUS Status; - ULONG i;
if (DeviceNode->UserFlags & DNUF_NOT_DISABLEABLE) { @@ -3898,15 +4149,9 @@ return STATUS_UNSUCCESSFUL; }
- IopQueueTargetDeviceEvent(&GUID_DEVICE_REMOVE_PENDING, - &DeviceNode->InstancePath); - if (IopQueryRemoveDevice(DeviceObject) != STATUS_SUCCESS) { DPRINT1("Removal vetoed by failing the query remove request\n"); - - IopQueueTargetDeviceEvent(&GUID_DEVICE_REMOVAL_VETOED, - &DeviceNode->InstancePath);
return STATUS_UNSUCCESSFUL; } @@ -3929,51 +4174,24 @@
if (DeviceRelations) { - for (i = 0; i < DeviceRelations->Count; i++) - { - PDEVICE_NODE RelationsDeviceNode = IopGetDeviceNode(DeviceRelations->Objects[i]); - - IopQueueTargetDeviceEvent(&GUID_DEVICE_REMOVE_PENDING, - &RelationsDeviceNode->InstancePath); - - if (IopRemoveDevice(RelationsDeviceNode) != STATUS_SUCCESS) - { - DPRINT1("Device removal vetoed by failing a dependent query remove request\n"); - - Status = STATUS_UNSUCCESSFUL; - - goto cleanup; - } - else - { - ObDereferenceObject(DeviceRelations->Objects[i]); - - DeviceRelations->Objects[i] = NULL; - } - } - - ExFreePool(DeviceRelations); - - DeviceRelations = NULL; - } - - Status = STATUS_SUCCESS; - -cleanup: + Status = IopQueryRemoveDeviceRelations(DeviceRelations); + if (!NT_SUCCESS(Status)) + return Status; + } + + Status = IopQueryRemoveChildDevices(DeviceNode); + if (!NT_SUCCESS(Status)) + { + if (DeviceRelations) + IopCancelRemoveDeviceRelations(DeviceRelations); + return Status; + } + if (DeviceRelations) - { - for (i = 0; i < DeviceRelations->Count; i++) - { - if (DeviceRelations->Objects[i]) - { - ObDereferenceObject(DeviceRelations->Objects[i]); - } - } - - ExFreePool(DeviceRelations); - } - - return Status; + IopSendRemoveDeviceRelations(DeviceRelations); + IopSendRemoveChildDevices(DeviceNode); + + return STATUS_SUCCESS; }
NTSTATUS @@ -3992,9 +4210,6 @@ DeviceNode->Flags |= DNF_WILL_BE_REMOVED; return STATUS_SUCCESS; } - - IopQueueTargetDeviceEvent(&GUID_DEVICE_REMOVAL_VETOED, - &DeviceNode->InstancePath);
return Status; } @@ -4012,16 +4227,13 @@ IO_STACK_LOCATION Stack; DEVICE_CAPABILITIES Capabilities; NTSTATUS Status; - ULONG i;
IopQueueTargetDeviceEvent(&GUID_DEVICE_KERNEL_INITIATED_EJECT, &DeviceNode->InstancePath);
if (IopQueryDeviceCapabilities(DeviceNode, &Capabilities) != STATUS_SUCCESS) { - IopQueueTargetDeviceEvent(&GUID_DEVICE_EJECT_VETOED, - &DeviceNode->InstancePath); - return; + goto cleanup; }
Stack.Parameters.QueryDeviceRelations.Type = EjectionRelations; @@ -4042,47 +4254,36 @@
if (DeviceRelations) { - for (i = 0; i < DeviceRelations->Count; i++) - { - PDEVICE_NODE RelationsDeviceNode = IopGetDeviceNode(DeviceRelations->Objects[i]); - - IopQueueTargetDeviceEvent(&GUID_DEVICE_REMOVE_PENDING, - &RelationsDeviceNode->InstancePath); - - if (IopRemoveDevice(RelationsDeviceNode) != STATUS_SUCCESS) - { - DPRINT1("Device removal vetoed by failing a query remove request (ejection relations)\n"); - - goto cleanup; - } - else - { - ObDereferenceObject(DeviceRelations->Objects[i]); - - DeviceRelations->Objects[i] = NULL; - } - } - - ExFreePool(DeviceRelations); - - DeviceRelations = NULL; + Status = IopQueryRemoveDeviceRelations(DeviceRelations); + if (!NT_SUCCESS(Status)) + goto cleanup; + } + + Status = IopQueryRemoveChildDevices(DeviceNode); + if (!NT_SUCCESS(Status)) + { + if (DeviceRelations) + IopCancelRemoveDeviceRelations(DeviceRelations); + goto cleanup; }
if (IopPrepareDeviceForRemoval(PhysicalDeviceObject) != STATUS_SUCCESS) { - IopQueueTargetDeviceEvent(&GUID_DEVICE_EJECT_VETOED, - &DeviceNode->InstancePath); - return; - } + if (DeviceRelations) + IopCancelRemoveDeviceRelations(DeviceRelations); + IopCancelRemoveChildDevices(DeviceNode); + goto cleanup; + } + + if (DeviceRelations) + IopSendRemoveDeviceRelations(DeviceRelations); + IopSendRemoveChildDevices(DeviceNode);
if (Capabilities.EjectSupported) { if (IopSendEject(PhysicalDeviceObject) != STATUS_SUCCESS) { - IopQueueTargetDeviceEvent(&GUID_DEVICE_EJECT_VETOED, - &DeviceNode->InstancePath); - - return; + goto cleanup; } } else @@ -4090,27 +4291,14 @@ DeviceNode->Flags |= DNF_DISABLED; }
+ IopQueueTargetDeviceEvent(&GUID_DEVICE_EJECT, + &DeviceNode->InstancePath); + + return; + cleanup: - if (DeviceRelations) - { - for (i = 0; i < DeviceRelations->Count; i++) - { - if (DeviceRelations->Objects[i]) - { - ObDereferenceObject(DeviceRelations->Objects[i]); - } - } - - ExFreePool(DeviceRelations); - - IopQueueTargetDeviceEvent(&GUID_DEVICE_EJECT_VETOED, - &DeviceNode->InstancePath); - } - else - { - IopQueueTargetDeviceEvent(&GUID_DEVICE_EJECT, - &DeviceNode->InstancePath); - } + IopQueueTargetDeviceEvent(&GUID_DEVICE_EJECT_VETOED, + &DeviceNode->InstancePath); }
/*