User created devices may need to adjust their default object parent or parent bus. User created devices are QOM parented to one of the peripheral containers ("/peripheral" or "/peripheral-anon") in qdev_set_id() by default. Sometimes, it is necessary to reparent a device to another object to express the more accurate child<> relationship, as in the cases of the PnvPHBRootPort device or subsequent topology devices. The current pnv_phb_user_get_parent() implements such reparenting logic. To allow it to be used by topology devices as well, transform it into a generic qdev interface with custom device id ("default_id" parameter). And add the code to handle the failure of object_property_add_child(). Signed-off-by: Zhao Liu <zhao1.liu@xxxxxxxxx> --- hw/core/qdev.c | 52 +++++++++++++++++++++++++++++++++++++ hw/pci-host/pnv_phb.c | 59 +++++++++--------------------------------- include/hw/qdev-core.h | 3 +++ 3 files changed, 67 insertions(+), 47 deletions(-) diff --git a/hw/core/qdev.c b/hw/core/qdev.c index 4429856eaddd..ff073cbff56d 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -143,6 +143,58 @@ bool qdev_set_parent_bus(DeviceState *dev, BusState *bus, Error **errp) return true; } +/* + * Set the QOM parent and parent bus of an object child. If the device + * state associated with the child has an id, use it as QOM id. + * Otherwise use default_id as QOM id. + * + * This helper does both operations at the same time because setting + * a new QOM child will erase the bus parent of the device. This happens + * because object_unparent() will call object_property_del_child(), + * which in turn calls the property release callback prop->release if + * it's defined. In our case this callback is set to + * object_finalize_child_property(), which was assigned during the + * first object_property_add_child() call. This callback will end up + * calling device_unparent(), and this function removes the device + * from its parent bus. + * + * The QOM and parent bus to be set aren't necessarily related, so + * let's receive both as arguments. + */ +bool qdev_set_parent(DeviceState *dev, BusState *bus, Object *parent, + char *default_id, Error **errp) +{ + Object *child = OBJECT(dev); + ObjectProperty *prop; + + if (!dev->id && !default_id) { + error_setg(errp, "unknown device id"); + return false; + } + + if (child->parent == parent) { + return true; + } + + object_ref(child); + object_unparent(child); + prop = object_property_add_child(parent, + dev->id ? dev->id : default_id, + child); + object_unref(child); + + if (!prop) { + error_setg(errp, "couldn't change parent"); + return false; + } + + if (!qdev_set_parent_bus(dev, bus, errp)) { + return false; + } + + return true; +} + DeviceState *qdev_new(const char *name) { ObjectClass *oc = object_class_by_name(name); diff --git a/hw/pci-host/pnv_phb.c b/hw/pci-host/pnv_phb.c index d4c118d44362..a26e7b7aec5c 100644 --- a/hw/pci-host/pnv_phb.c +++ b/hw/pci-host/pnv_phb.c @@ -19,49 +19,6 @@ #include "qom/object.h" #include "sysemu/sysemu.h" - -/* - * Set the QOM parent and parent bus of an object child. If the device - * state associated with the child has an id, use it as QOM id. - * Otherwise use object_typename[index] as QOM id. - * - * This helper does both operations at the same time because setting - * a new QOM child will erase the bus parent of the device. This happens - * because object_unparent() will call object_property_del_child(), - * which in turn calls the property release callback prop->release if - * it's defined. In our case this callback is set to - * object_finalize_child_property(), which was assigned during the - * first object_property_add_child() call. This callback will end up - * calling device_unparent(), and this function removes the device - * from its parent bus. - * - * The QOM and parent bus to be set aren´t necessarily related, so - * let's receive both as arguments. - */ -static bool pnv_parent_fixup(Object *parent, BusState *parent_bus, - Object *child, int index, - Error **errp) -{ - g_autofree char *default_id = - g_strdup_printf("%s[%d]", object_get_typename(child), index); - const char *dev_id = DEVICE(child)->id; - - if (child->parent == parent) { - return true; - } - - object_ref(child); - object_unparent(child); - object_property_add_child(parent, dev_id ? dev_id : default_id, child); - object_unref(child); - - if (!qdev_set_parent_bus(DEVICE(child), parent_bus, errp)) { - return false; - } - - return true; -} - static Object *pnv_phb_user_get_parent(PnvChip *chip, PnvPHB *phb, Error **errp) { if (phb->version == 3) { @@ -82,6 +39,7 @@ static bool pnv_phb_user_device_init(PnvPHB *phb, Error **errp) PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine()); PnvChip *chip = pnv_get_chip(pnv, phb->chip_id); Object *parent = NULL; + g_autofree char *default_id = NULL; if (!chip) { error_setg(errp, "invalid chip id: %d", phb->chip_id); @@ -98,8 +56,11 @@ static bool pnv_phb_user_device_init(PnvPHB *phb, Error **errp) * correctly the device tree. pnv_xscom_dt() needs every * PHB to be a child of the chip to build the DT correctly. */ - if (!pnv_parent_fixup(parent, qdev_get_parent_bus(DEVICE(chip)), - OBJECT(phb), phb->phb_id, errp)) { + default_id = g_strdup_printf("%s[%d]", + object_get_typename(OBJECT(phb)), + phb->phb_id); + if (!qdev_set_parent(DEVICE(phb), qdev_get_parent_bus(DEVICE(chip)), + parent, default_id, errp)) { return false; } @@ -246,6 +207,7 @@ static void pnv_phb_root_port_realize(DeviceState *dev, Error **errp) uint16_t device_id = 0; Error *local_err = NULL; int chip_id, index; + g_autofree char *default_id = NULL; /* * 'index' will be used both as a PCIE slot value and to calculate @@ -273,8 +235,11 @@ static void pnv_phb_root_port_realize(DeviceState *dev, Error **errp) * parent bus. Change the QOM parent to be the same as the * parent bus it's already assigned to. */ - if (!pnv_parent_fixup(OBJECT(bus), BUS(bus), OBJECT(dev), - index, errp)) { + default_id = g_strdup_printf("%s[%d]", + object_get_typename(OBJECT(dev)), + index); + if (!qdev_set_parent(dev, BUS(bus), OBJECT(bus), + default_id, errp)) { return; } diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h index 85c7d462dfba..7cbc5fb97298 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -1011,6 +1011,9 @@ char *qdev_get_human_name(DeviceState *dev); /* FIXME: make this a link<> */ bool qdev_set_parent_bus(DeviceState *dev, BusState *bus, Error **errp); +bool qdev_set_parent(DeviceState *dev, BusState *bus, Object *parent, + char *default_id, Error **errp); + extern bool qdev_hot_removed; char *qdev_get_dev_path(DeviceState *dev); -- 2.34.1