[bus,dummy,libbus] Handle dummy PDO removal
authorShao Miller <Shao.Miller@yrdsb.edu.on.ca>
Wed, 5 Jan 2011 19:51:07 +0000 (14:51 -0500)
committerShao Miller <Shao.Miller@yrdsb.edu.on.ca>
Wed, 5 Jan 2011 19:51:07 +0000 (14:51 -0500)
The bus library can unlink a node, but should not be
responsible for deleting the corresponding PDO nor
for freeing associated resources.  A driver can test
for removal by checking the Linked member of the
WVL_S_BUS_NODE that is associated with the device.

This allows for the AoE bus to be uninstalled from
Device Manager without crashing!  What was happening
was that:
- The AoE bus got IRP_MN_REMOVE_DEVICE
  - It passed it on to the PDO
    - The dummy PDO handler unlinked the node and
      deleted the device.
    - It also triggered a re-enumeration of the
      WinVBlock bus.
- AoE shutdown
- The re-enumeration of the WinVBlock bus would find
  the PDO missing and send IRP_MN_REMOVE_DEVICE.
- The device was already deleted!

So now we merely unlink on the first removal IRP,
then delete the PDO on the second IRP, which will be
triggered by bus re-enumeration.

src/include/bus.h
src/winvblock/bus.c
src/winvblock/dummy.c
src/winvblock/libbus/libbus.c

index fdad308..1a459a3 100644 (file)
@@ -93,6 +93,7 @@ typedef struct WVL_BUS_NODE {
         /* The child's unit number relative to the parent bus. */
         UINT32 Num;
       } BusPrivate_;
+    BOOLEAN Linked;
   } WVL_S_BUS_NODE, * WVL_SP_BUS_NODE;
 
 /**
index 11a01b6..779f111 100644 (file)
@@ -341,6 +341,59 @@ BOOLEAN STDCALL WvBusAddDev(
     return TRUE;
   }
 
+/* Bus node removal thread item.  Internal. */
+typedef struct WV_BUS_NODE_REMOVAL_ {
+    WVL_S_THREAD_ITEM item;
+    WV_SP_DEV_T dev;
+    KEVENT signal;
+  } WV_S_BUS_NODE_REMOVAL_, * WV_SP_BUS_NODE_REMOVAL_;
+
+/* Remove a bus node within the bus thread's context.  Internal. */
+static WVL_F_THREAD_ITEM WvBusRemoveDev_;
+static VOID STDCALL WvBusRemoveDev_(IN OUT WVL_SP_THREAD_ITEM item) {
+    WV_SP_BUS_NODE_REMOVAL_ removal;
+
+    removal = CONTAINING_RECORD(item, WV_S_BUS_NODE_REMOVAL_, item);
+    /* If the node has been unlinked and we are called again, delete it. */
+    if (!removal->dev->BusNode.Linked) {
+        IoDeleteDevice(removal->dev->Self);
+        /* dev->ext is the PnP IDs' data. */
+        wv_free(removal->dev->ext);
+        wv_free(removal->dev);
+      } else {        
+        /* Enqueue the node's removal. */
+        WvlBusRemoveNode(&removal->dev->BusNode);
+        /* We are in the thread's context, so process the removal right now. */
+        WvlBusProcessWorkItems(&WvBus);
+      }
+    /* The removal should be complete.  Signal completion. */
+    KeSetEvent(&removal->signal, 0, FALSE);
+    return;
+  }
+
+/**
+ * Remove a device node from the WinVBlock bus.
+ *
+ * @v Dev               The device to remove.
+ * @ret NTSTATUS        The status of the operation.
+ */
+NTSTATUS STDCALL WvBusRemoveDev(IN WV_SP_DEV_T Dev) {
+    WV_S_BUS_NODE_REMOVAL_ removal;
+    NTSTATUS status;
+
+    removal.dev = Dev;
+    KeInitializeEvent(&removal.signal, SynchronizationEvent, FALSE);
+    removal.item.Func = WvBusRemoveDev_;
+    /* Enqueue the removal. */
+    status = WvlThreadAddItem(&WvBusThread_, &removal.item);
+    if (!NT_SUCCESS(status))
+      return status;
+
+    /* Wait for it. */
+    KeWaitForSingleObject(&removal.signal, Executive, KernelMode, FALSE, NULL);
+    return status;
+  }
+
 static NTSTATUS STDCALL WvBusDevCtlDetach(
     IN PIRP irp
   ) {
index fb17339..5553726 100644 (file)
@@ -35,8 +35,9 @@
 #include "dummy.h"
 #include "debug.h"
 
-/* From driver.c */
+/* From bus.c */
 extern WVL_S_BUS_T WvBus;
+extern NTSTATUS STDCALL WvBusRemoveDev(IN WV_SP_DEV_T);
 
 /* Forward declarations. */
 static NTSTATUS STDCALL WvDummyIds(IN PIRP, IN WV_SP_DUMMY_IDS);
@@ -60,11 +61,8 @@ static NTSTATUS STDCALL WvDummyPnp(
           return WvlIrpComplete(irp, 0, STATUS_SUCCESS);
 
         case IRP_MN_REMOVE_DEVICE:
-          WvlBusRemoveNode(&dev->BusNode);
-          IoDeleteDevice(dev->Self);
-          /* dev->ext is the PnP IDs' data. */
-          wv_free(dev->ext);
-          wv_free(dev);
+          /* Any error status for the removal slips away, here. */
+          WvBusRemoveDev(dev);
           return WvlIrpComplete(irp, 0, STATUS_SUCCESS);
 
         default:
@@ -240,8 +238,8 @@ WVL_M_LIB NTSTATUS STDCALL WvDummyAdd(
  *
  * It might actually be better to handle a PnP remove IOCTL.
  */
-WVL_M_LIB NTSTATUS STDCALL WvDummyRemove(IN PDEVICE_OBJECT Pdo) {
-    return WvlBusRemoveNode(&WvDevFromDevObj(Pdo)->BusNode);
+WVL_M_LIB NTSTATUS STDCALL WvDummyRemove(IN PDEVICE_OBJECT Pdo) {    
+    return WvBusRemoveDev(WvDevFromDevObj(Pdo));
   }
 
 /**
index 36df957..98a5380 100644 (file)
@@ -197,6 +197,7 @@ static VOID STDCALL WvlBusAddNode_(WVL_SP_BUS_T bus, WVL_SP_BUS_NODE new_node) {
       } /* while */
     /* Insert before walker. */
     InsertTailList(walker, &new_node->BusPrivate_.Link);
+    new_node->Linked = TRUE;
     return;
   }
 
@@ -219,6 +220,7 @@ static VOID STDCALL WvlBusRemoveNode_(
         (PVOID) bus
       );
     RemoveEntryList(&node->BusPrivate_.Link);
+    node->Linked = FALSE;
     ObDereferenceObject(node->BusPrivate_.Pdo);
     bus->BusPrivate_.NodeCount--;
     return;