From: Liu Ping Fan <pingf...@linux.vnet.ibm.com> To achieve uplug a sub tree, we propagate unplug event on the tree.
Signed-off-by: Liu Ping Fan <pingf...@linux.vnet.ibm.com> --- hw/acpi_piix4.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++- hw/qdev.c | 7 ++++- hw/qdev.h | 2 + 3 files changed, 77 insertions(+), 3 deletions(-) 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; -- 1.7.4.4