Quoting Paolo Bonzini (2015-04-30 08:35:13)
>
>
> On 29/04/2015 21:20, Michael Roth wrote:
> > If the parent is finalized as a result of object_unparent(), it
> > will still be attached to the composition tree at the time any
> > children are unparented as a result of that same call to
> > object_unparent(). However, in some cases, object_unparent()
> > will complete without finalizing the parent device, due to
> > lingering references that won't be released till some time later.
> > One such example is if the parent has MemoryRegion children (which
> > take a ref on their parent), who in turn have AddressSpace's (which
> > take a ref on their regions), since those AddressSpaces get cleaned
> > up asynchronously by the RCU thread.
> >
> > In this case qdev:device_unparent() may be called for a child Device
> > that no longer has a path to the root/machine container, causing
> > object_get_canonical_path() to assert.
>
> This doesn't seem right. Unparent callbacks are _not_ called when you
> finalize, they are called in post-order as soon as you unplug a device
> (the call tree is object_unparent ==> device_unparent(parent) ==>
> bus_unparent(parent->bus) ==> device_unparent(parent->bus->child[0])
> and so on).
Hmm, well, that does seem to be the case for devices that reside on a
bus, since the post-order traversal from the parent will eventually
reach any such devices.
And for a device that gets unparented before it's parent bus, I think
the fix you posted ends up not being needed because the child is
actually a link property of the parent bus, as opposed to an actual
child property, so removing the property doesn't "disconnect" the
device from the composition tree: presumably the *actual* parent
object/container (/machine/unattached I believe?) is still around
when the DEVICE_DELETED event is sent, so it still has a canonical
path and we don't get the assert from object_get_canonical_path().
In my case though I have a couple device types (spapr_drc, and
spapr_iommu) that are direct child properties of the PHB, and
from what I can tell the clean up path for those when we do
object_unparent(phb) goes something like:
object_unparent(o):
object_property_del_child(o->parent, o, NULL):
object_property_del(p, prop_name):
prop->release(p, prop_name, prop_opaque):
| object_finalize_child_property(p, prop_name, o):
| o->class->unparent(o):
| device_unparent(o) <- (post-order clean-up, but skips
| direct children like spapr_drc/spapr_iommu)
| o->parent = NULL
| object_unref(o):
| object_finalize(o): <- may happen asynchronously due to RCU cleanup
| for AddressSpace/MemoryRegion ref holder.
| object o will no longer be child prop of
| o->parent. actually, this seems like it would
| be the case during synchronous finalization
| as well now that I look at it more closely...
| object_property_del_all(o)
| for prop in o->properties:
| prop->release(o, prop->name, prop->opaque/o->child)
| object_finalize_child_property(o, prop_name, c):
| o->class->unparent(c):
| device_unparent(c) <- (spapr_drc/spapr_iommu children
| get their callbacks after being
| disconnected, no longer have
| canonical paths)
| QTAILQ_REMOVE(&o->properties, prop, node)
| object_deinit(o)
| o->free(o)
QTAILQ_REMOVE(&o->properties, prop, node)
I played around with the idea of temporarilly moving unparented, unfinalized
objects to an "orphan" container. It seemed like a fun way of tracking leaked
objects, and avoids the assert, but that got wierd pretty quickly... and
having DEVICE_DELETED randomly change up the device path didn't seem like
the intended behavior, so this hack ended up seeming pretty reasonable.
The other approach, which I hadn't looked into too closely, was to defer
unparenting an object until it's ref count goes to 0. Could maybe look into
that instead if it seems less hacky.
>
> DEVICE_DELETED is called after a device's children have been
> unparented. It could be called after a bus is dead though. Could it
> be that the patch you want is simply this:
>
> diff --git a/hw/core/qdev.c b/hw/core/qdev.c
> index 6e6a65d..46019c4 100644
> --- a/hw/core/qdev.c
> +++ b/hw/core/qdev.c
> @@ -1241,11 +1241,6 @@ static void device_unparent(Object *obj)
> bus = QLIST_FIRST(&dev->child_bus);
> object_unparent(OBJECT(bus));
> }
> - if (dev->parent_bus) {
> - bus_remove_child(dev->parent_bus, dev);
> - object_unref(OBJECT(dev->parent_bus));
> - dev->parent_bus = NULL;
> - }
>
> /* Only send event if the device had been completely realized */
> if (dev->pending_deleted_event) {
> @@ -1254,6 +1249,12 @@ static void device_unparent(Object *obj)
> qapi_event_send_device_deleted(!!dev->id, dev->id, path,
> &error_abort);
> g_free(path);
> }
> +
> + if (dev->parent_bus) {
> + bus_remove_child(dev->parent_bus, dev);
> + object_unref(OBJECT(dev->parent_bus));
> + dev->parent_bus = NULL;
> + }
> }
>
> static void device_class_init(ObjectClass *class, void *data)
>
> ?
>
> Paolo
>