diff mbox series

[01/10] qdev: add "check if address free" callback for buses

Message ID 20200925172604.2142227-2-pbonzini@redhat.com
State New
Headers show
Series Fix scsi devices plug/unplug races w.r.t virtio-scsi iothread | expand

Commit Message

Paolo Bonzini Sept. 25, 2020, 5:25 p.m. UTC
Check if an address is free on the bus before plugging in the
device.  This makes it possible to do the check without any
side effects, and to detect the problem early without having
to do it in the realize callback.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 hw/core/qdev.c         | 17 +++++++++++++++--
 hw/net/virtio-net.c    |  2 +-
 hw/sd/core.c           |  3 ++-
 include/hw/qdev-core.h |  3 ++-
 4 files changed, 20 insertions(+), 5 deletions(-)

Comments

Stefan Hajnoczi Sept. 28, 2020, 9:30 a.m. UTC | #1
On Fri, Sep 25, 2020 at 01:25:55PM -0400, Paolo Bonzini wrote:
> diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h

> index 72064f4dd4..e62da68a26 100644

> --- a/include/hw/qdev-core.h

> +++ b/include/hw/qdev-core.h

> @@ -217,6 +217,7 @@ struct BusClass {

>       */

>      char *(*get_fw_dev_path)(DeviceState *dev);

>      void (*reset)(BusState *bus);

> +    bool (*check_address)(BusState *bus, DeviceState *dev, Error **errp);


Please document this function.
Maxim Levitsky Sept. 30, 2020, 2:27 p.m. UTC | #2
On Fri, 2020-09-25 at 13:25 -0400, Paolo Bonzini wrote:
> Check if an address is free on the bus before plugging in the

> device.  This makes it possible to do the check without any

> side effects, and to detect the problem early without having

> to do it in the realize callback.

> 

> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>

> ---

>  hw/core/qdev.c         | 17 +++++++++++++++--

>  hw/net/virtio-net.c    |  2 +-

>  hw/sd/core.c           |  3 ++-

>  include/hw/qdev-core.h |  3 ++-

>  4 files changed, 20 insertions(+), 5 deletions(-)

> 

> diff --git a/hw/core/qdev.c b/hw/core/qdev.c

> index 96772a15bd..74db78df36 100644

> --- a/hw/core/qdev.c

> +++ b/hw/core/qdev.c

> @@ -94,13 +94,23 @@ static void bus_add_child(BusState *bus, DeviceState *child)

>                               0);

>  }

>  

> -void qdev_set_parent_bus(DeviceState *dev, BusState *bus)

> +static bool bus_check_address(BusState *bus, DeviceState *child, Error **errp)

> +{

> +    BusClass *bc = BUS_GET_CLASS(bus);

> +    return !bc->check_address || bc->check_address(bus, child, errp);

> +}

> +

> +bool qdev_set_parent_bus(DeviceState *dev, BusState *bus, Error **errp)

>  {

>      BusState *old_parent_bus = dev->parent_bus;

>      DeviceClass *dc = DEVICE_GET_CLASS(dev);

>  

>      assert(dc->bus_type && object_dynamic_cast(OBJECT(bus), dc->bus_type));

>  

> +    if (!bus_check_address(bus, dev, errp)) {

> +        return false;

> +    }

> +

>      if (old_parent_bus) {

>          trace_qdev_update_parent_bus(dev, object_get_typename(OBJECT(dev)),

>              old_parent_bus, object_get_typename(OBJECT(old_parent_bus)),

> @@ -126,6 +136,7 @@ void qdev_set_parent_bus(DeviceState *dev, BusState *bus)

>          object_unref(OBJECT(old_parent_bus));

>          object_unref(OBJECT(dev));

>      }

> +    return true;

>  }

>  

>  DeviceState *qdev_new(const char *name)

> @@ -371,7 +382,9 @@ bool qdev_realize(DeviceState *dev, BusState *bus, Error **errp)

>      assert(!dev->realized && !dev->parent_bus);

>  

>      if (bus) {

> -        qdev_set_parent_bus(dev, bus);

> +        if (!qdev_set_parent_bus(dev, bus, errp)) {

> +            return false;

> +        }

>      } else {

>          assert(!DEVICE_GET_CLASS(dev)->bus_type);

>      }

> diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c

> index 7bf27b9db7..268cecc498 100644

> --- a/hw/net/virtio-net.c

> +++ b/hw/net/virtio-net.c

> @@ -3142,7 +3142,7 @@ static bool failover_replug_primary(VirtIONet *n, Error **errp)

>          error_setg(errp, "virtio_net: couldn't find primary bus");

>          return false;

>      }

> -    qdev_set_parent_bus(n->primary_dev, n->primary_bus);

> +    qdev_set_parent_bus(n->primary_dev, n->primary_bus, &error_abort);

>      n->primary_should_be_hidden = false;

>      if (!qemu_opt_set_bool(n->primary_device_opts,

>                             "partially_hotplugged", true, errp)) {

> diff --git a/hw/sd/core.c b/hw/sd/core.c

> index 957d116f1a..08c93b5903 100644

> --- a/hw/sd/core.c

> +++ b/hw/sd/core.c

> @@ -23,6 +23,7 @@

>  #include "hw/qdev-core.h"

>  #include "hw/sd/sd.h"

>  #include "qemu/module.h"

> +#include "qapi/error.h"

>  #include "trace.h"

>  

>  static inline const char *sdbus_name(SDBus *sdbus)

> @@ -240,7 +241,7 @@ void sdbus_reparent_card(SDBus *from, SDBus *to)

>      readonly = sc->get_readonly(card);

>  

>      sdbus_set_inserted(from, false);

> -    qdev_set_parent_bus(DEVICE(card), &to->qbus);

> +    qdev_set_parent_bus(DEVICE(card), &to->qbus, &error_abort);

>      sdbus_set_inserted(to, true);

>      sdbus_set_readonly(to, readonly);

>  }

> diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h

> index 72064f4dd4..e62da68a26 100644

> --- a/include/hw/qdev-core.h

> +++ b/include/hw/qdev-core.h

> @@ -217,6 +217,7 @@ struct BusClass {

>       */

>      char *(*get_fw_dev_path)(DeviceState *dev);

>      void (*reset)(BusState *bus);

> +    bool (*check_address)(BusState *bus, DeviceState *dev, Error **errp);

>      BusRealize realize;

>      BusUnrealize unrealize;

>  

> @@ -788,7 +789,7 @@ const char *qdev_fw_name(DeviceState *dev);

>  Object *qdev_get_machine(void);

>  

>  /* FIXME: make this a link<> */

> -void qdev_set_parent_bus(DeviceState *dev, BusState *bus);

> +bool qdev_set_parent_bus(DeviceState *dev, BusState *bus, Error **errp);

>  

>  extern bool qdev_hotplug;

>  extern bool qdev_hot_removed;



I like that idea, however I wonder why this was needed.
My patch that switches the direction in scsi_device_find, is supposed to be completely equavalent, 
based on the following train of thought:

If scsi_device_find finds an exact match it returns only it, as before.

Otherwise scsi_device_find were to scan from end of the list to the start, and every time,
it finds a device with same channel/id it would update the target_dev
and return it when it reaches the end of the list. 

If I am not mistaken this means that it would return _first_ device in the 
list that matches the channel/id.
This is exactly what new version of scsi_device_find does.

Anyway, since I don't see anything wrong with doing what this patch does other
than adding a documentation to the function as Stefan pointed out,

Reviewed-by: Maxim Levitsky <mlevitsk@redhat.com>


Best regards,
	Maxim Levitsky
Paolo Bonzini Sept. 30, 2020, 5:48 p.m. UTC | #3
On 28/09/20 11:30, Stefan Hajnoczi wrote:
>> +    bool (*check_address)(BusState *bus, DeviceState *dev, Error **errp);

> Please document this function.


I will add this:

	/*
	 * Return whether the device can be added to @bus,
	 * based on the address that was set (via device properties)
	 * before realize.  If not, on return @errp contains the
	 * human-readable error message.
	 */
Paolo Bonzini Sept. 30, 2020, 11:37 p.m. UTC | #4
On 30/09/20 16:27, Maxim Levitsky wrote:
> My patch that switches the direction in scsi_device_find, is supposed to be completely equavalent, 

> based on the following train of thought:

> 

> If scsi_device_find finds an exact match it returns only it, as before.

> 

> Otherwise scsi_device_find were to scan from end of the list to the start, and every time,

> it finds a device with same channel/id it would update the target_dev

> and return it when it reaches the end of the list. 

> 

> If I am not mistaken this means that it would return _first_ device in the 

> list that matches the channel/id.

> This is exactly what new version of scsi_device_find does.


Oh!  I missed that subtlety.  Thanks, that makes sense.

Paolo
diff mbox series

Patch

diff --git a/hw/core/qdev.c b/hw/core/qdev.c
index 96772a15bd..74db78df36 100644
--- a/hw/core/qdev.c
+++ b/hw/core/qdev.c
@@ -94,13 +94,23 @@  static void bus_add_child(BusState *bus, DeviceState *child)
                              0);
 }
 
-void qdev_set_parent_bus(DeviceState *dev, BusState *bus)
+static bool bus_check_address(BusState *bus, DeviceState *child, Error **errp)
+{
+    BusClass *bc = BUS_GET_CLASS(bus);
+    return !bc->check_address || bc->check_address(bus, child, errp);
+}
+
+bool qdev_set_parent_bus(DeviceState *dev, BusState *bus, Error **errp)
 {
     BusState *old_parent_bus = dev->parent_bus;
     DeviceClass *dc = DEVICE_GET_CLASS(dev);
 
     assert(dc->bus_type && object_dynamic_cast(OBJECT(bus), dc->bus_type));
 
+    if (!bus_check_address(bus, dev, errp)) {
+        return false;
+    }
+
     if (old_parent_bus) {
         trace_qdev_update_parent_bus(dev, object_get_typename(OBJECT(dev)),
             old_parent_bus, object_get_typename(OBJECT(old_parent_bus)),
@@ -126,6 +136,7 @@  void qdev_set_parent_bus(DeviceState *dev, BusState *bus)
         object_unref(OBJECT(old_parent_bus));
         object_unref(OBJECT(dev));
     }
+    return true;
 }
 
 DeviceState *qdev_new(const char *name)
@@ -371,7 +382,9 @@  bool qdev_realize(DeviceState *dev, BusState *bus, Error **errp)
     assert(!dev->realized && !dev->parent_bus);
 
     if (bus) {
-        qdev_set_parent_bus(dev, bus);
+        if (!qdev_set_parent_bus(dev, bus, errp)) {
+            return false;
+        }
     } else {
         assert(!DEVICE_GET_CLASS(dev)->bus_type);
     }
diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
index 7bf27b9db7..268cecc498 100644
--- a/hw/net/virtio-net.c
+++ b/hw/net/virtio-net.c
@@ -3142,7 +3142,7 @@  static bool failover_replug_primary(VirtIONet *n, Error **errp)
         error_setg(errp, "virtio_net: couldn't find primary bus");
         return false;
     }
-    qdev_set_parent_bus(n->primary_dev, n->primary_bus);
+    qdev_set_parent_bus(n->primary_dev, n->primary_bus, &error_abort);
     n->primary_should_be_hidden = false;
     if (!qemu_opt_set_bool(n->primary_device_opts,
                            "partially_hotplugged", true, errp)) {
diff --git a/hw/sd/core.c b/hw/sd/core.c
index 957d116f1a..08c93b5903 100644
--- a/hw/sd/core.c
+++ b/hw/sd/core.c
@@ -23,6 +23,7 @@ 
 #include "hw/qdev-core.h"
 #include "hw/sd/sd.h"
 #include "qemu/module.h"
+#include "qapi/error.h"
 #include "trace.h"
 
 static inline const char *sdbus_name(SDBus *sdbus)
@@ -240,7 +241,7 @@  void sdbus_reparent_card(SDBus *from, SDBus *to)
     readonly = sc->get_readonly(card);
 
     sdbus_set_inserted(from, false);
-    qdev_set_parent_bus(DEVICE(card), &to->qbus);
+    qdev_set_parent_bus(DEVICE(card), &to->qbus, &error_abort);
     sdbus_set_inserted(to, true);
     sdbus_set_readonly(to, readonly);
 }
diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h
index 72064f4dd4..e62da68a26 100644
--- a/include/hw/qdev-core.h
+++ b/include/hw/qdev-core.h
@@ -217,6 +217,7 @@  struct BusClass {
      */
     char *(*get_fw_dev_path)(DeviceState *dev);
     void (*reset)(BusState *bus);
+    bool (*check_address)(BusState *bus, DeviceState *dev, Error **errp);
     BusRealize realize;
     BusUnrealize unrealize;
 
@@ -788,7 +789,7 @@  const char *qdev_fw_name(DeviceState *dev);
 Object *qdev_get_machine(void);
 
 /* FIXME: make this a link<> */
-void qdev_set_parent_bus(DeviceState *dev, BusState *bus);
+bool qdev_set_parent_bus(DeviceState *dev, BusState *bus, Error **errp);
 
 extern bool qdev_hotplug;
 extern bool qdev_hot_removed;