@@ -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);
}
}
}
@@ -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);
@@ -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;