There are currently three types of object_property_add_link() callers:

1. The link property may be set at any time.
2. The link property of a DeviceState instance may only be set before
   realize.
3. The link property may never be set, it is read-only.

Something similar can already be achieved with
object_property_add_str()'s set() argument.  Follow its example and add
a check() argument to object_property_add_link().

Also provide default check() functions for case #1 and #2.  Case #3 is
covered by passing a NULL function pointer.

Cc: "Andreas Färber" <afaer...@suse.de>
Cc: Peter Crosthwaite <peter.crosthwa...@petalogix.com>
Cc: Paolo Bonzini <pbonz...@redhat.com>
Cc: Alexander Graf <ag...@suse.de>
Cc: Anthony Liguori <aligu...@amazon.com>
Cc: "Michael S. Tsirkin" <m...@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefa...@redhat.com>
---
 hw/core/qdev-properties.c    | 12 ++++++++++++
 hw/core/qdev.c               |  8 ++++++--
 hw/dma/xilinx_axidma.c       |  4 ++++
 hw/net/xilinx_axienet.c      |  4 ++++
 hw/pcmcia/pxa2xx.c           |  4 +++-
 hw/s390x/s390-virtio-bus.c   |  1 +
 hw/s390x/virtio-ccw.c        |  1 +
 hw/virtio/virtio-pci.c       |  1 +
 hw/virtio/virtio-rng.c       |  1 +
 include/hw/qdev-properties.h | 11 +++++++++++
 include/qom/object.h         | 18 ++++++++++++++++++
 qom/object.c                 | 18 +++++++++++++++++-
 ui/console.c                 |  1 +
 13 files changed, 80 insertions(+), 4 deletions(-)

diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c
index 77d0c66..c67acf5 100644
--- a/hw/core/qdev-properties.c
+++ b/hw/core/qdev-properties.c
@@ -21,6 +21,18 @@ void qdev_prop_set_after_realize(DeviceState *dev, const 
char *name,
     }
 }
 
+void qdev_prop_allow_set_link_before_realize(Object *obj, const char *name,
+                                             Object *val, Error **errp)
+{
+    DeviceState *dev = DEVICE(obj);
+
+    if (dev->realized) {
+        error_setg(errp, "Attempt to set link property '%s' on device '%s' "
+                   "(type '%s') after it was realized",
+                   name, dev->id, object_get_typename(obj));
+    }
+}
+
 void *qdev_get_prop_ptr(DeviceState *dev, Property *prop)
 {
     void *ptr = dev;
diff --git a/hw/core/qdev.c b/hw/core/qdev.c
index a182917..97acf62 100644
--- a/hw/core/qdev.c
+++ b/hw/core/qdev.c
@@ -97,7 +97,10 @@ static void bus_add_child(BusState *bus, DeviceState *child)
     snprintf(name, sizeof(name), "child[%d]", kid->index);
     object_property_add_link(OBJECT(bus), name,
                              object_get_typename(OBJECT(child)),
-                             (Object **)&kid->child, 0, NULL);
+                             (Object **)&kid->child,
+                             NULL, /* read-only property */
+                             0, /* return ownership on prop deletion */
+                             NULL);
 }
 
 void qdev_set_parent_bus(DeviceState *dev, BusState *bus)
@@ -823,7 +826,7 @@ static void device_initfn(Object *obj)
     } while (class != object_class_by_name(TYPE_DEVICE));
 
     object_property_add_link(OBJECT(dev), "parent_bus", TYPE_BUS,
-                             (Object **)&dev->parent_bus, 0,
+                             (Object **)&dev->parent_bus, NULL, 0,
                              &error_abort);
 }
 
@@ -945,6 +948,7 @@ static void qbus_initfn(Object *obj)
     object_property_add_link(obj, QDEV_HOTPLUG_HANDLER_PROPERTY,
                              TYPE_HOTPLUG_HANDLER,
                              (Object **)&bus->hotplug_handler,
+                             object_property_allow_set_link,
                              OBJ_PROP_LINK_UNREF_ON_RELEASE,
                              NULL);
     object_property_add_bool(obj, "realized",
diff --git a/hw/dma/xilinx_axidma.c b/hw/dma/xilinx_axidma.c
index c8fda39..14b887b 100644
--- a/hw/dma/xilinx_axidma.c
+++ b/hw/dma/xilinx_axidma.c
@@ -538,10 +538,12 @@ static void xilinx_axidma_realize(DeviceState *dev, Error 
**errp)
 
     object_property_add_link(OBJECT(ds), "dma", TYPE_XILINX_AXI_DMA,
                              (Object **)&ds->dma,
+                             object_property_allow_set_link,
                              OBJ_PROP_LINK_UNREF_ON_RELEASE,
                              &local_errp);
     object_property_add_link(OBJECT(cs), "dma", TYPE_XILINX_AXI_DMA,
                              (Object **)&cs->dma,
+                             object_property_allow_set_link,
                              OBJ_PROP_LINK_UNREF_ON_RELEASE,
                              &local_errp);
     if (local_errp) {
@@ -576,11 +578,13 @@ static void xilinx_axidma_init(Object *obj)
 
     object_property_add_link(obj, "axistream-connected", TYPE_STREAM_SLAVE,
                              (Object **)&s->tx_data_dev,
+                             qdev_prop_allow_set_link_before_realize,
                              OBJ_PROP_LINK_UNREF_ON_RELEASE,
                              &error_abort);
     object_property_add_link(obj, "axistream-control-connected",
                              TYPE_STREAM_SLAVE,
                              (Object **)&s->tx_control_dev,
+                             qdev_prop_allow_set_link_before_realize,
                              OBJ_PROP_LINK_UNREF_ON_RELEASE,
                              &error_abort);
 
diff --git a/hw/net/xilinx_axienet.c b/hw/net/xilinx_axienet.c
index 7ecf925..839d97c 100644
--- a/hw/net/xilinx_axienet.c
+++ b/hw/net/xilinx_axienet.c
@@ -946,10 +946,12 @@ static void xilinx_enet_realize(DeviceState *dev, Error 
**errp)
 
     object_property_add_link(OBJECT(ds), "enet", "xlnx.axi-ethernet",
                              (Object **) &ds->enet,
+                             object_property_allow_set_link,
                              OBJ_PROP_LINK_UNREF_ON_RELEASE,
                              &local_errp);
     object_property_add_link(OBJECT(cs), "enet", "xlnx.axi-ethernet",
                              (Object **) &cs->enet,
+                             object_property_allow_set_link,
                              OBJ_PROP_LINK_UNREF_ON_RELEASE,
                              &local_errp);
     if (local_errp) {
@@ -987,11 +989,13 @@ static void xilinx_enet_init(Object *obj)
 
     object_property_add_link(obj, "axistream-connected", TYPE_STREAM_SLAVE,
                              (Object **) &s->tx_data_dev,
+                             qdev_prop_allow_set_link_before_realize,
                              OBJ_PROP_LINK_UNREF_ON_RELEASE,
                              &error_abort);
     object_property_add_link(obj, "axistream-control-connected",
                              TYPE_STREAM_SLAVE,
                              (Object **) &s->tx_control_dev,
+                             qdev_prop_allow_set_link_before_realize,
                              OBJ_PROP_LINK_UNREF_ON_RELEASE,
                              &error_abort);
 
diff --git a/hw/pcmcia/pxa2xx.c b/hw/pcmcia/pxa2xx.c
index 6949214..96f3774 100644
--- a/hw/pcmcia/pxa2xx.c
+++ b/hw/pcmcia/pxa2xx.c
@@ -198,7 +198,9 @@ static void pxa2xx_pcmcia_initfn(Object *obj)
     s->slot.irq = qemu_allocate_irqs(pxa2xx_pcmcia_set_irq, s, 1)[0];
 
     object_property_add_link(obj, "card", TYPE_PCMCIA_CARD,
-                             (Object **)&s->card, 0, NULL);
+                             (Object **)&s->card,
+                             NULL, /* read-only property */
+                             0, NULL);
 }
 
 /* Insert a new card into a slot */
diff --git a/hw/s390x/s390-virtio-bus.c b/hw/s390x/s390-virtio-bus.c
index 930b4f7..9c71afa 100644
--- a/hw/s390x/s390-virtio-bus.c
+++ b/hw/s390x/s390-virtio-bus.c
@@ -314,6 +314,7 @@ static void s390_virtio_rng_instance_init(Object *obj)
     object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL);
     object_property_add_link(obj, "rng", TYPE_RNG_BACKEND,
                              (Object **)&dev->vdev.conf.rng,
+                             qdev_prop_allow_set_link_before_realize,
                              OBJ_PROP_LINK_UNREF_ON_RELEASE, NULL);
 }
 
diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c
index aebb2de..2bf0af8 100644
--- a/hw/s390x/virtio-ccw.c
+++ b/hw/s390x/virtio-ccw.c
@@ -1273,6 +1273,7 @@ static void virtio_ccw_rng_instance_init(Object *obj)
     object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL);
     object_property_add_link(obj, "rng", TYPE_RNG_BACKEND,
                              (Object **)&dev->vdev.conf.rng,
+                             qdev_prop_allow_set_link_before_realize,
                              OBJ_PROP_LINK_UNREF_ON_RELEASE, NULL);
 }
 
diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c
index eebb819..ce97514 100644
--- a/hw/virtio/virtio-pci.c
+++ b/hw/virtio/virtio-pci.c
@@ -1518,6 +1518,7 @@ static void virtio_rng_initfn(Object *obj)
     object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL);
     object_property_add_link(obj, "rng", TYPE_RNG_BACKEND,
                              (Object **)&dev->vdev.conf.rng,
+                             qdev_prop_allow_set_link_before_realize,
                              OBJ_PROP_LINK_UNREF_ON_RELEASE, NULL);
 
 }
diff --git a/hw/virtio/virtio-rng.c b/hw/virtio/virtio-rng.c
index 2efda8b..cbf0138 100644
--- a/hw/virtio/virtio-rng.c
+++ b/hw/virtio/virtio-rng.c
@@ -224,6 +224,7 @@ static void virtio_rng_initfn(Object *obj)
 
     object_property_add_link(obj, "rng", TYPE_RNG_BACKEND,
                              (Object **)&vrng->conf.rng,
+                             qdev_prop_allow_set_link_before_realize,
                              OBJ_PROP_LINK_UNREF_ON_RELEASE, NULL);
 }
 
diff --git a/include/hw/qdev-properties.h b/include/hw/qdev-properties.h
index 3c000ee..68aaed6 100644
--- a/include/hw/qdev-properties.h
+++ b/include/hw/qdev-properties.h
@@ -204,4 +204,15 @@ void qdev_property_add_static(DeviceState *dev, Property 
*prop, Error **errp);
  */
 void qdev_prop_set_after_realize(DeviceState *dev, const char *name,
                                  Error **errp);
+
+/**
+ * @qdev_prop_allow_set_link_before_realize:
+ *
+ * Set the Error object if an attempt is made to set the link after realize.
+ * This function should be used as the check() argument to
+ * object_property_add_link().
+ */
+void qdev_prop_allow_set_link_before_realize(Object *obj, const char *name,
+                                             Object *val, Error **errp);
+
 #endif
diff --git a/include/qom/object.h b/include/qom/object.h
index 9feb441..a641dcd 100644
--- a/include/qom/object.h
+++ b/include/qom/object.h
@@ -1073,11 +1073,22 @@ typedef enum {
 } ObjectPropertyLinkFlags;
 
 /**
+ * object_property_allow_set_link:
+ *
+ * The default implementation of the object_property_add_link() check()
+ * callback function.  It allows the link property to be set and never returns
+ * an error.
+ */
+void object_property_allow_set_link(Object *, const char *,
+                                    Object *, Error **);
+
+/**
  * object_property_add_link:
  * @obj: the object to add a property to
  * @name: the name of the property
  * @type: the qobj type of the link
  * @child: a pointer to where the link object reference is stored
+ * @check: callback to veto setting or NULL if the property is read-only
  * @flags: additional options for the link
  * @errp: if an error occurs, a pointer to an area to store the area
  *
@@ -1087,6 +1098,11 @@ typedef enum {
  *
  * Links form the graph in the object model.
  *
+ * The <code>@check()</code> callback is invoked when
+ * object_property_set_link() is called and can raise an error to prevent the
+ * link being set.  If <code>@check</code> is NULL, the property is read-only
+ * and cannot be set.
+ *
  * Ownership of the pointer that @child points to is transferred to the
  * link property.  The reference count for <code>*@child</code> is
  * managed by the property from after the function returns till the
@@ -1096,6 +1112,8 @@ typedef enum {
  */
 void object_property_add_link(Object *obj, const char *name,
                               const char *type, Object **child,
+                              void (*check)(Object *obj, const char *name,
+                                            Object *val, Error **errp),
                               ObjectPropertyLinkFlags flags,
                               Error **errp);
 
diff --git a/qom/object.c b/qom/object.c
index 60aea9c..c9e8bcd 100644
--- a/qom/object.c
+++ b/qom/object.c
@@ -1023,8 +1023,15 @@ out:
     g_free(type);
 }
 
+void object_property_allow_set_link(Object *obj, const char *name,
+                                    Object *val, Error **errp)
+{
+    /* Allow the link to be set, always */
+}
+
 typedef struct {
     Object **child;
+    void (*check)(Object *, const char *, Object *, Error **);
     ObjectPropertyLinkFlags flags;
 } LinkProperty;
 
@@ -1104,6 +1111,12 @@ static void object_set_link_property(Object *obj, 
Visitor *v, void *opaque,
         return;
     }
 
+    prop->check(obj, name, new_target, &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        return;
+    }
+
     if (new_target) {
         object_ref(new_target);
     }
@@ -1126,6 +1139,8 @@ static void object_release_link_property(Object *obj, 
const char *name,
 
 void object_property_add_link(Object *obj, const char *name,
                               const char *type, Object **child,
+                              void (*check)(Object *, const char *,
+                                            Object *, Error **),
                               ObjectPropertyLinkFlags flags,
                               Error **errp)
 {
@@ -1134,13 +1149,14 @@ void object_property_add_link(Object *obj, const char 
*name,
     gchar *full_type;
 
     prop->child = child;
+    prop->check = check;
     prop->flags = flags;
 
     full_type = g_strdup_printf("link<%s>", type);
 
     object_property_add(obj, name, full_type,
                         object_get_link_property,
-                        object_set_link_property,
+                        check ? object_set_link_property : NULL,
                         object_release_link_property,
                         prop,
                         &local_err);
diff --git a/ui/console.c b/ui/console.c
index 9974212..e057755 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -1181,6 +1181,7 @@ static QemuConsole *new_console(DisplayState *ds, 
console_type_t console_type)
     s = QEMU_CONSOLE(obj);
     object_property_add_link(obj, "device", TYPE_DEVICE,
                              (Object **)&s->device,
+                             object_property_allow_set_link,
                              OBJ_PROP_LINK_UNREF_ON_RELEASE,
                              &local_err);
     object_property_add_uint32_ptr(obj, "head",
-- 
1.8.5.3


Reply via email to