qdev: unplug request will propagate and release item bottom-up

Submitted by pingfan liu on Aug. 25, 2012, 7:26 a.m.

Details

Message ID 1345879607-15650-1-git-send-email-qemulist@gmail.com
State New
Headers show

Commit Message

pingfan liu Aug. 25, 2012, 7:26 a.m.
From: Liu Ping Fan <pingfank@linux.vnet.ibm.com>

To achieve uplug a sub tree, we propagate unplug event on the tree.

Signed-off-by: Liu Ping Fan <pingfank@linux.vnet.ibm.com>
---
 hw/acpi_piix4.c |   71 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
 hw/qdev.c       |    7 ++++-
 hw/qdev.h       |    2 +
 3 files changed, 77 insertions(+), 3 deletions(-)

Comments

Paolo Bonzini Aug. 26, 2012, 2:34 p.m.
Il 25/08/2012 09:26, Liu Ping Fan ha scritto:
> From: Liu Ping Fan <pingfank@linux.vnet.ibm.com>
> 
> To achieve uplug a sub tree, we propagate unplug event on the tree.
> 
> Signed-off-by: Liu Ping Fan <pingfank@linux.vnet.ibm.com>

You're improvising.

You need a clear plan of what you're setting to do, what
algorithms/techniques you will use, how you will test it, what changes
to other packages (kernel, BIOS) are needed, how you will handle the
transition while it is only partially done, etc.

Paolo

Patch hide | download patch | download mbox

diff --git a/hw/acpi_piix4.c b/hw/acpi_piix4.c
index 0aace60..49247c5 100644
--- a/hw/acpi_piix4.c
+++ b/hw/acpi_piix4.c
@@ -287,6 +287,74 @@  static const VMStateDescription vmstate_acpi = {
     }
 };
 
+static void check_release_bus(BusState *bus);
+
+static void check_release_device(DeviceState *dev)
+{
+    Object *obj = OBJECT(dev);
+    BusState *up_b = dev->parent_bus;
+    BusState *child;
+
+    if (dev->unplug_state == 1) {
+        /* a leaf device has no child bus, or empty child bus */
+        QLIST_FOREACH(child, &dev->child_bus, sibling) {
+            if (!QTAILQ_EMPTY(&child->children)) {
+                return;
+            }
+            child->parent = NULL;
+            QLIST_REMOVE(child, sibling);
+            dev->num_child_bus--;
+            object_property_del_child(OBJECT(dev), OBJECT(child), NULL);
+            /* when mmio-dispatch out of big lock, remove it!*/
+            g_assert(OBJECT(child)->ref == 1);
+            object_unref(OBJECT(child));
+        }
+
+        dev->parent_bus = NULL;
+        /* removed from list and bus->dev link */
+        bus_remove_child(up_b, dev);
+        /* remove bus<-dev link */
+        object_property_del(OBJECT(dev), "parent_bus", NULL);
+
+        /* when mmio-dispatch out of big lock, remove it! */
+        g_assert(obj->ref == 1);
+        object_unref(obj);
+        check_release_bus(up_b);
+    }
+}
+
+static void check_release_bus(BusState *bus)
+{
+    Object *obj = OBJECT(bus);
+    DeviceState *d = bus->parent;
+
+    if (bus->unplug_state == 1 && QTAILQ_EMPTY(&bus->children)) {
+        bus->parent = NULL;
+        QLIST_REMOVE(bus, sibling);
+        d->num_child_bus--;
+        object_property_del_child(OBJECT(d), OBJECT(bus), NULL);
+        /* when mmio-dispatch out of big lock, remove it!*/
+        g_assert(obj->ref == 1);
+        object_unref(obj);
+        check_release_device(d);
+    }
+}
+
+static void qdev_unplug_complete(DeviceState *qdev)
+{
+    BusState *child;
+
+    /* keep the child<> , until all of the children detached.
+    * Mark dev and its bus going.
+    */
+   qdev->unplug_state = 1;
+   QLIST_FOREACH(child, &qdev->child_bus, sibling) {
+       child->unplug_state = 1;
+   }
+   /* bottom-up through the release chain */
+   check_release_device(qdev);
+}
+
 static void acpi_piix_eject_slot(PIIX4PMState *s, unsigned slots)
 {
     BusChild *kid, *next;
@@ -305,8 +373,7 @@  static void acpi_piix_eject_slot(PIIX4PMState *s, unsigned slots)
             if (pc->no_hotplug) {
                 slot_free = false;
             } else {
-                object_unparent(OBJECT(dev));
-                qdev_free(qdev);
+                qdev_unplug_complete(qdev);
             }
         }
     }
diff --git a/hw/qdev.c b/hw/qdev.c
index b5b74b9..206e0eb 100644
--- a/hw/qdev.c
+++ b/hw/qdev.c
@@ -194,7 +194,7 @@  void qdev_set_legacy_instance_id(DeviceState *dev, int alias_id,
     dev->alias_required_for_version = required_for_version;
 }
 
-void qdev_unplug(DeviceState *dev, Error **errp)
+static void qdev_eject_unplug(DeviceState *dev, Error **errp)
 {
     DeviceClass *dc = DEVICE_GET_CLASS(dev);
 
@@ -212,6 +212,11 @@  void qdev_unplug(DeviceState *dev, Error **errp)
     }
 }
 
+void qdev_unplug(DeviceState *dev, Error **errp)
+{
+    qdev_walk_children(dev, qdev_eject_unplug, NULL, errp);
+}
+
 static int qdev_reset_one(DeviceState *dev, void *opaque)
 {
     device_reset(dev);
diff --git a/hw/qdev.h b/hw/qdev.h
index d699194..3c09ae7 100644
--- a/hw/qdev.h
+++ b/hw/qdev.h
@@ -67,6 +67,7 @@  struct DeviceState {
     enum DevState state;
     QemuOpts *opts;
     int hotplugged;
+    int unplug_state;
     BusState *parent_bus;
     int num_gpio_out;
     qemu_irq *gpio_out;
@@ -115,6 +116,7 @@  struct BusState {
     DeviceState *parent;
     const char *name;
     int allow_hotplug;
+    int unplug_state;
     bool qom_allocated;
     bool glib_allocated;
     int max_index;