qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [RFC PATCH 6/6] pc: Support firmware configuration with


From: Markus Armbruster
Subject: Re: [Qemu-devel] [RFC PATCH 6/6] pc: Support firmware configuration with -blockdev
Date: Mon, 04 Mar 2019 18:50:40 +0100
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/26.1 (gnu/linux)

The problem at hand is how to destroy a device created with
qdev_create() without ever realizing it.

This hack passes tests:

diff --git a/hw/i386/pc_sysfw.c b/hw/i386/pc_sysfw.c
index ed608a53d3..1bd538796b 100644
--- a/hw/i386/pc_sysfw.c
+++ b/hw/i386/pc_sysfw.c
@@ -116,14 +116,9 @@ static void pc_system_flash_cleanup_unused(PCMachineState 
*pcms)
             prop_name = g_strdup_printf("pflash%d", i);
             object_property_del(OBJECT(pcms), prop_name, &error_abort);
             g_free(prop_name);
-            /*
-             * FIXME delete @dev_obj.  Straight object_unref() is
-             * wrong, since it leaves dangling references in the
-             * parent bus behind.  object_unparent() doesn't work,
-             * either: since @dev_obj hasn't been realized,
-             * dev_obj->parent is null, and object_unparent() does
-             * nothing.
-             */
+            object_ref(dev_obj);
+            object_get_class(dev_obj)->unparent(dev_obj);
+            object_unref(dev_obj);
             pcms->flash[i] = NULL;
         }
     }

If you have a better idea, please let me know.

Here's how I arrived at my hack.  We need to undo qdev_create().
qdev_create() is qdev_try_create() plus error handling.  The latter is
uninteresting here, so let's have a look at just the former:

    DeviceState *qdev_try_create(BusState *bus, const char *type)
    {
        DeviceState *dev;

        if (object_class_by_name(type) == NULL) {
            return NULL;
        }
        dev = DEVICE(object_new(type));
        if (!dev) {
            return NULL;
        }

        if (!bus) {
            /* Assert that the device really is a SysBusDevice before
             * we put it onto the sysbus. Non-sysbus devices which aren't
             * being put onto a bus should be created with object_new(TYPE_FOO),
             * not qdev_create(NULL, TYPE_FOO).
             */
            g_assert(object_dynamic_cast(OBJECT(dev), TYPE_SYS_BUS_DEVICE));
            bus = sysbus_get_default();
        }

--->    qdev_set_parent_bus(dev, bus);
        object_unref(OBJECT(dev));
        return dev;
    }

We need to undo qdev_set_parent_bus().

    void qdev_set_parent_bus(DeviceState *dev, BusState *bus)
    {
        bool replugging = dev->parent_bus != NULL;

        if (replugging) {
            /* Keep a reference to the device while it's not plugged into
             * any bus, to avoid it potentially evaporating when it is
             * dereffed in bus_remove_child().
             */
            object_ref(OBJECT(dev));
            bus_remove_child(dev->parent_bus, dev);
            object_unref(OBJECT(dev->parent_bus));
        }
        dev->parent_bus = bus;
        object_ref(OBJECT(bus));
--->    bus_add_child(bus, dev);
        if (replugging) {
            object_unref(OBJECT(dev));
        }
    }

dev->parent_bus is null, since we didn't realize the device.  All we
need to undo is bus_add_child(), with bus_remove_child().  Destruction
of realized devices does that here:

    static void device_unparent(Object *obj)
    {
        DeviceState *dev = DEVICE(obj);
        BusState *bus;

        if (dev->realized) {
            object_property_set_bool(obj, false, "realized", NULL);
        }
        while (dev->num_child_bus) {
            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;
        }
    }

dev->realized is false, dev->num_child_bus is zero, dev->parent_bus is
main_system_bus.  Okay, this does what we need.  Now how do we call it?
It's static...  but it's a method:

    static void device_class_init(ObjectClass *class, void *data)
    {
        DeviceClass *dc = DEVICE_CLASS(class);

        class->unparent = device_unparent;
        [...]
    }

Alright, we can call object_get_class(dev_obj)->unparent(dev_obj).

Final complication: if I call just that, the device's reference counter
goes down to zero in the middle of device_unparent(), and we use after
free.  So I bracket he call with object_ref() and object_unref().



reply via email to

[Prev in Thread] Current Thread [Next in Thread]