diff mbox series

[v4,03/10] hw/core: add Resettable interface in Bus and Device classes

Message ID 20190821163341.16309-4-damien.hedde@greensocs.com (mailing list archive)
State New, archived
Headers show
Series Multi-phase reset mechanism | expand

Commit Message

Damien Hedde Aug. 21, 2019, 4:33 p.m. UTC
Implement Resettable interface in BusClass and DeviceClass; it adds the
ResetState structure into BusState and DeviceState and the required methods
in the classes.

Helpers device_cold_reset and bus_cold_reset are defined to call to
use the new interface. Also add a [device|bus]_is_resetting telling if the
device/bus is currently under reset.

Compatibility with existing code base is ensured; The legacy bus or device
reset method is called in the new init phase. Since this phase is called
for children before for parent in qbus hierarchy, the new reset behavior
will be the same as qdev/qbus_reset_all functions; qdev/qbus_reset_all
are now wrappers around device/bus_cold_reset functions.

Deprecate old reset apis (device_legacy_reset and qdev/qbus_reset_all).
These functions will be removed when transition to the new api is complete.

At this point no migration support is required since there is no way to
held an object into reset. This will be added later on.

Signed-off-by: Damien Hedde <damien.hedde@greensocs.com>
---

I've kept the device/bus_cold_reset name; I think that even if we don't
introduce other reset types (like warm), it has to advantage to clarify
that current reset is "cold" (it put back what is the initial state when
qemu starts).
---
 hw/core/bus.c          | 53 +++++++++++++++++++++++++++++++
 hw/core/qdev.c         | 71 +++++++++++++++++++++++++++++++-----------
 include/hw/qdev-core.h | 62 +++++++++++++++++++++++++++++++++---
 tests/Makefile.include |  1 +
 4 files changed, 164 insertions(+), 23 deletions(-)
diff mbox series

Patch

diff --git a/hw/core/bus.c b/hw/core/bus.c
index 7f3d2a3dbd..3be65ad041 100644
--- a/hw/core/bus.c
+++ b/hw/core/bus.c
@@ -22,6 +22,7 @@ 
 #include "qemu/ctype.h"
 #include "qemu/module.h"
 #include "qapi/error.h"
+#include "hw/resettable.h"
 
 void qbus_set_hotplug_handler(BusState *bus, Object *handler, Error **errp)
 {
@@ -68,6 +69,34 @@  int qbus_walk_children(BusState *bus,
     return 0;
 }
 
+void bus_cold_reset(BusState *bus)
+{
+    resettable_reset(OBJECT(bus), RESET_TYPE_COLD);
+}
+
+bool bus_is_resetting(BusState *bus)
+{
+    return resettable_is_resetting(OBJECT(bus));
+}
+
+static ResetState *bus_get_reset_state(Object *obj)
+{
+    BusState *bus = BUS(obj);
+    return &bus->reset;
+}
+
+static void bus_reset_foreach_child(Object *obj,
+                                    void (*func)(Object *, ResetType),
+                                    ResetType type)
+{
+    BusState *bus = BUS(obj);
+    BusChild *kid;
+
+    QTAILQ_FOREACH(kid, &bus->children, sibling) {
+        func(OBJECT(kid->child), type);
+    }
+}
+
 static void qbus_realize(BusState *bus, DeviceState *parent, const char *name)
 {
     const char *typename = object_get_typename(OBJECT(bus));
@@ -199,12 +228,32 @@  static char *default_bus_get_fw_dev_path(DeviceState *dev)
     return g_strdup(object_get_typename(OBJECT(dev)));
 }
 
+/**
+ * bus_legacy_reset_wrapper:
+ * Compatibility wrapper during the transition to multi-phase reset.
+ * This function should be deleted when all buses are converted.
+ */
+static void bus_legacy_reset_wrapper(Object *obj, ResetType type)
+{
+    BusState *bus = BUS(obj);
+    BusClass *bc = BUS_GET_CLASS(obj);
+
+    if (bc->reset) {
+        bc->reset(bus);
+    }
+}
+
 static void bus_class_init(ObjectClass *class, void *data)
 {
     BusClass *bc = BUS_CLASS(class);
+    ResettableClass *rc = RESETTABLE_CLASS(class);
 
     class->unparent = bus_unparent;
     bc->get_fw_dev_path = default_bus_get_fw_dev_path;
+
+    rc->phases.init = bus_legacy_reset_wrapper;
+    rc->get_state = bus_get_reset_state;
+    rc->foreach_child = bus_reset_foreach_child;
 }
 
 static void qbus_finalize(Object *obj)
@@ -223,6 +272,10 @@  static const TypeInfo bus_info = {
     .instance_init = qbus_initfn,
     .instance_finalize = qbus_finalize,
     .class_init = bus_class_init,
+    .interfaces = (InterfaceInfo[]) {
+        { TYPE_RESETTABLE_INTERFACE },
+        { }
+    },
 };
 
 static void bus_register_types(void)
diff --git a/hw/core/qdev.c b/hw/core/qdev.c
index a95d4fa87d..85c85822fd 100644
--- a/hw/core/qdev.c
+++ b/hw/core/qdev.c
@@ -255,25 +255,9 @@  HotplugHandler *qdev_get_hotplug_handler(DeviceState *dev)
     return hotplug_ctrl;
 }
 
-static int qdev_reset_one(DeviceState *dev, void *opaque)
-{
-    device_legacy_reset(dev);
-
-    return 0;
-}
-
-static int qbus_reset_one(BusState *bus, void *opaque)
-{
-    BusClass *bc = BUS_GET_CLASS(bus);
-    if (bc->reset) {
-        bc->reset(bus);
-    }
-    return 0;
-}
-
 void qdev_reset_all(DeviceState *dev)
 {
-    qdev_walk_children(dev, NULL, NULL, qdev_reset_one, qbus_reset_one, NULL);
+    device_cold_reset(dev);
 }
 
 void qdev_reset_all_fn(void *opaque)
@@ -283,7 +267,7 @@  void qdev_reset_all_fn(void *opaque)
 
 void qbus_reset_all(BusState *bus)
 {
-    qbus_walk_children(bus, NULL, NULL, qdev_reset_one, qbus_reset_one, NULL);
+    bus_cold_reset(bus);
 }
 
 void qbus_reset_all_fn(void *opaque)
@@ -292,6 +276,33 @@  void qbus_reset_all_fn(void *opaque)
     qbus_reset_all(bus);
 }
 
+void device_cold_reset(DeviceState *dev)
+{
+    resettable_reset(OBJECT(dev), RESET_TYPE_COLD);
+}
+
+bool device_is_resetting(DeviceState *dev)
+{
+    return resettable_is_resetting(OBJECT(dev));
+}
+
+static ResetState *device_get_reset_state(Object *obj)
+{
+    DeviceState *dev = DEVICE(obj);
+    return &dev->reset;
+}
+
+static void device_reset_foreach_child(Object *obj,
+                                       void (*func)(Object *, ResetType),
+                                       ResetType type)
+{
+    DeviceState *dev = DEVICE(obj);
+    BusState *bus;
+    QLIST_FOREACH(bus, &dev->child_bus, sibling) {
+        func(OBJECT(bus), type);
+    }
+}
+
 /* can be used as ->unplug() callback for the simple cases */
 void qdev_simple_device_unplug_cb(HotplugHandler *hotplug_dev,
                                   DeviceState *dev, Error **errp)
@@ -1047,9 +1058,25 @@  static void device_unparent(Object *obj)
     }
 }
 
+/**
+ * device_legacy_reset_wrapper:
+ * Compatibility wrapper during the transition to multi-phase reset.
+ * This function should be deleted when all devices are converted.
+ */
+static void device_legacy_reset_wrapper(Object *obj, ResetType type)
+{
+    DeviceState *dev = DEVICE(obj);
+    DeviceClass *dc = DEVICE_GET_CLASS(dev);
+
+    if (dc->reset) {
+        dc->reset(dev);
+    }
+}
+
 static void device_class_init(ObjectClass *class, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(class);
+    ResettableClass *rc = RESETTABLE_CLASS(class);
 
     class->unparent = device_unparent;
 
@@ -1061,6 +1088,10 @@  static void device_class_init(ObjectClass *class, void *data)
      */
     dc->hotpluggable = true;
     dc->user_creatable = true;
+
+    rc->phases.init = device_legacy_reset_wrapper;
+    rc->get_state = device_get_reset_state;
+    rc->foreach_child = device_reset_foreach_child;
 }
 
 void device_class_set_parent_reset(DeviceClass *dc,
@@ -1118,6 +1149,10 @@  static const TypeInfo device_type_info = {
     .class_init = device_class_init,
     .abstract = true,
     .class_size = sizeof(DeviceClass),
+    .interfaces = (InterfaceInfo[]) {
+        { TYPE_RESETTABLE_INTERFACE },
+        { }
+    },
 };
 
 static void qdev_register_types(void)
diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h
index 09e6dfd664..2976121fbc 100644
--- a/include/hw/qdev-core.h
+++ b/include/hw/qdev-core.h
@@ -5,6 +5,7 @@ 
 #include "qemu/bitmap.h"
 #include "qom/object.h"
 #include "hw/hotplug.h"
+#include "hw/resettable.h"
 
 enum {
     DEV_NVECTORS_UNSPECIFIED = -1,
@@ -104,6 +105,11 @@  typedef struct DeviceClass {
     bool hotpluggable;
 
     /* callbacks */
+    /*
+     * Reset method here is deprecated and replaced by methods in the
+     * resettable class interface to implement a multi-phase reset.
+     * TODO: remove once every reset callback is unused
+     */
     DeviceReset reset;
     DeviceRealize realize;
     DeviceUnrealize unrealize;
@@ -128,6 +134,7 @@  struct NamedGPIOList {
 /**
  * DeviceState:
  * @realized: Indicates whether the device has been fully constructed.
+ * @reset: ResetState for the device; handled by Resettable interface.
  *
  * This structure should not be accessed directly.  We declare it here
  * so that it can be embedded in individual device state structures.
@@ -149,6 +156,7 @@  struct DeviceState {
     int num_child_bus;
     int instance_id_alias;
     int alias_required_for_version;
+    ResetState reset;
 };
 
 struct DeviceListener {
@@ -195,6 +203,7 @@  typedef struct BusChild {
 /**
  * BusState:
  * @hotplug_handler: link to a hotplug handler associated with bus.
+ * @reset: ResetState for the bus; handled by Resettable interface.
  */
 struct BusState {
     Object obj;
@@ -206,6 +215,7 @@  struct BusState {
     int num_children;
     QTAILQ_HEAD(, BusChild) children;
     QLIST_ENTRY(BusState) sibling;
+    ResetState reset;
 };
 
 /**
@@ -375,22 +385,55 @@  int qdev_walk_children(DeviceState *dev,
                        qdev_walkerfn *post_devfn, qbus_walkerfn *post_busfn,
                        void *opaque);
 
-void qdev_reset_all(DeviceState *dev);
-void qdev_reset_all_fn(void *opaque);
-
 /**
- * @qbus_reset_all:
- * @bus: Bus to be reset.
+ * qbus/qdev_reset_all:
+ * @bus (resp. @dev): Bus (resp. Device) to be reset.
  *
  * Reset @bus and perform a bus-level ("hard") reset of all devices connected
  * to it, including recursive processing of all buses below @bus itself.  A
  * hard reset means that qbus_reset_all will reset all state of the device.
  * For PCI devices, for example, this will include the base address registers
  * or configuration space.
+ *
+ * These functions are deprecated and are wrappers around device_cold_reset and
+ * bus_cold_reset. Please use these instead.
+ * TODO: remove them when all occurrences are removed
  */
+void qdev_reset_all(DeviceState *dev);
+void qdev_reset_all_fn(void *opaque);
 void qbus_reset_all(BusState *bus);
 void qbus_reset_all_fn(void *opaque);
 
+/**
+ * device_cold_reset:
+ * Trigger a cold reset of the device @dev.
+ *
+ * Use the Resettable interface (see hw/interface.h); it also reset the
+ * device's qdev/qbus subtree.
+ */
+void device_cold_reset(DeviceState *dev);
+
+/**
+ * bus_cold_reset:
+ * Trigger a cold reset of the bus @bus.
+ *
+ * Use the Resettable interface (see hw/interface.h); it also reset the
+ * bus's qdev/qbus subtree.
+ */
+void bus_cold_reset(BusState *bus);
+
+/**
+ * device_is_resetting:
+ * Return true if the device @dev is currently being reset.
+ */
+bool device_is_resetting(DeviceState *dev);
+
+/**
+ * bus_is_resetting:
+ * Return true if the bus @bus is currently being reset.
+ */
+bool bus_is_resetting(BusState *bus);
+
 /* This should go away once we get rid of the NULL bus hack */
 BusState *sysbus_get_default(void);
 
@@ -409,9 +452,18 @@  void qdev_machine_init(void);
  * device_legacy_reset:
  *
  * Reset a single device (by calling the reset method).
+ *
+ * This function is deprecated, please use Resettable api instead (eg:
+ * device_cold_reset())
+ * TODO: remove the function when all occurrences are removed.
  */
 void device_legacy_reset(DeviceState *dev);
 
+/**
+ * device_class_set_parent_reset:
+ * TODO: remove the function when DeviceClass's reset method
+ * is not used anymore.
+ */
 void device_class_set_parent_reset(DeviceClass *dc,
                                    DeviceReset dev_reset,
                                    DeviceReset *parent_reset);
diff --git a/tests/Makefile.include b/tests/Makefile.include
index 6f02dfcc01..f0b4628cc6 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -561,6 +561,7 @@  tests/fp/%:
 
 tests/test-qdev-global-props$(EXESUF): tests/test-qdev-global-props.o \
 	hw/core/qdev.o hw/core/qdev-properties.o hw/core/hotplug.o\
+	hw/core/resettable.o \
 	hw/core/bus.o \
 	hw/core/irq.o \
 	hw/core/fw-path-provider.o \