diff mbox series

[RFC,v2,02/15] qdev: Add the interface to reparent the device

Message ID 20240919015533.766754-3-zhao1.liu@intel.com
State New
Headers show
Series qom-topo: Abstract CPU Topology Level to Topology Device | expand

Commit Message

Zhao Liu Sept. 19, 2024, 1:55 a.m. UTC
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@intel.com>
---
 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 mbox series

Patch

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