qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Damien Hedde <damien.hedde@greensocs.com>
To: qemu-devel@nongnu.org
Cc: Damien Hedde <damien.hedde@greensocs.com>,
	peter.maydell@linaro.org, berrange@redhat.com,
	ehabkost@redhat.com, cohuck@redhat.com,
	mark.burton@greensocs.com, qemu-s390x@nongnu.org,
	edgari@xilinx.com, qemu-arm@nongnu.org, pbonzini@redhat.com,
	philmd@redhat.com, david@gibson.dropbear.id.au
Subject: [PATCH v5 04/13] hw/core: add Resettable support to BusClass and DeviceClass
Date: Fri, 18 Oct 2019 17:06:21 +0200	[thread overview]
Message-ID: <20191018150630.31099-5-damien.hedde@greensocs.com> (raw)
In-Reply-To: <20191018150630.31099-1-damien.hedde@greensocs.com>

This commit adds support of Resettable interface to buses and devices:
+ ResettableState structure is added in the Bus/Device state
+ Resettable methods are implemented.
+ device/bus_is_in_reset function defined

This commit allows to transition the objects to the new
multi-phase interface without changing the reset behavior at all.
Object single reset method can be split into the 3 different phases
but the 3 phases are still executed in a row for a given object.
From the qdev/qbus reset api point of view, nothing is changed.
qdev_reset_all() and qbus_reset_all() are not modified as well as
device_legacy_reset().

Transition of an object must be done from mother class to daughter
classes. Care has been taken to allow the transition of a mother class
without requiring the daughter classes to be transitioned at the same
time. Note that SysBus and SysBusDevice class do not need any transition
because they do not override the legacy reset method.

Signed-off-by: Damien Hedde <damien.hedde@greensocs.com>
---
 hw/core/bus.c          | 97 ++++++++++++++++++++++++++++++++++++++++++
 hw/core/qdev.c         | 97 ++++++++++++++++++++++++++++++++++++++++++
 include/hw/qdev-core.h | 27 ++++++++++++
 tests/Makefile.include |  1 +
 4 files changed, 222 insertions(+)

diff --git a/hw/core/bus.c b/hw/core/bus.c
index 7f3d2a3dbd..7c05e80db8 100644
--- a/hw/core/bus.c
+++ b/hw/core/bus.c
@@ -68,6 +68,28 @@ int qbus_walk_children(BusState *bus,
     return 0;
 }
 
+bool bus_is_in_reset(BusState *bus)
+{
+    return resettable_is_in_reset(OBJECT(bus));
+}
+
+static ResettableState *bus_get_reset_state(Object *obj)
+{
+    BusState *bus = BUS(obj);
+    return &bus->reset;
+}
+
+static void bus_reset_child_foreach(Object *obj, ResettableChildCallback cb,
+                                    void *opaque, ResetType type)
+{
+    BusState *bus = BUS(obj);
+    BusChild *kid;
+
+    QTAILQ_FOREACH(kid, &bus->children, sibling) {
+        cb(OBJECT(kid->child), opaque, type);
+    }
+}
+
 static void qbus_realize(BusState *bus, DeviceState *parent, const char *name)
 {
     const char *typename = object_get_typename(OBJECT(bus));
@@ -199,12 +221,83 @@ static char *default_bus_get_fw_dev_path(DeviceState *dev)
     return g_strdup(object_get_typename(OBJECT(dev)));
 }
 
+/**
+ * bus_phases_reset:
+ * Transition reset method for buses to allow moving
+ * smoothly from legacy reset method to multi-phases
+ */
+static void bus_phases_reset(BusState *bus)
+{
+    ResettableClass *rc = RESETTABLE_GET_CLASS(bus);
+
+    if (rc->phases.enter) {
+        rc->phases.enter(OBJECT(bus), RESET_TYPE_COLD);
+    }
+    if (rc->phases.hold) {
+        rc->phases.hold(OBJECT(bus));
+    }
+    if (rc->phases.exit) {
+        rc->phases.exit(OBJECT(bus));
+    }
+}
+
+static void bus_transitional_reset(Object *obj)
+{
+    BusClass *bc = BUS_GET_CLASS(obj);
+
+    /*
+     * This will call either @bus_phases_reset (for multi-phases transitioned
+     * buses) or a bus's specific method for not-yet transitioned buses.
+     * In both case, it does not reset children.
+     */
+    if (bc->reset) {
+        bc->reset(BUS(obj));
+    }
+}
+
+/**
+ * bus_get_transitional_reset:
+ * check if the bus's class is ready for multi-phase
+ */
+static ResettableTrFunction bus_get_transitional_reset(Object *obj)
+{
+    BusClass *dc = BUS_GET_CLASS(obj);
+    if (dc->reset != bus_phases_reset) {
+        /*
+         * dc->reset has been overridden by a subclass,
+         * the bus is not ready for multi phase yet.
+         */
+        return bus_transitional_reset;
+    }
+    return NULL;
+}
+
 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->get_state = bus_get_reset_state;
+    rc->child_foreach = bus_reset_child_foreach;
+
+    /*
+     * @bus_phases_reset is put as the default reset method below, allowing
+     * to do the multi-phase transition from base classes to leaf classes. It
+     * allows a legacy-reset Bus class to extend a multi-phases-reset
+     * Bus class for the following reason:
+     * + If a base class B has been moved to multi-phase, then it does not
+     *   override this default reset method and may have defined phase methods.
+     * + A daughter class C (extending class B) which uses
+     *   bus_class_set_parent_reset() (or similar means) to override the
+     *   reset method will still work as expected. @bus_phases_reset function
+     *   will be registered as the parent reset method and effectively call
+     *   parent reset phases.
+     */
+    bc->reset = bus_phases_reset;
+    rc->get_transitional_function = bus_get_transitional_reset;
 }
 
 static void qbus_finalize(Object *obj)
@@ -223,6 +316,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 f230063189..81784c73fb 100644
--- a/hw/core/qdev.c
+++ b/hw/core/qdev.c
@@ -329,6 +329,28 @@ void qbus_reset_all_fn(void *opaque)
     qbus_reset_all(bus);
 }
 
+bool device_is_in_reset(DeviceState *dev)
+{
+    return resettable_is_in_reset(OBJECT(dev));
+}
+
+static ResettableState *device_get_reset_state(Object *obj)
+{
+    DeviceState *dev = DEVICE(obj);
+    return &dev->reset;
+}
+
+static void device_reset_child_foreach(Object *obj, ResettableChildCallback cb,
+                                       void *opaque, ResetType type)
+{
+    DeviceState *dev = DEVICE(obj);
+    BusState *bus;
+
+    QLIST_FOREACH(bus, &dev->child_bus, sibling) {
+        cb(OBJECT(bus), opaque, type);
+    }
+}
+
 /* can be used as ->unplug() callback for the simple cases */
 void qdev_simple_device_unplug_cb(HotplugHandler *hotplug_dev,
                                   DeviceState *dev, Error **errp)
@@ -1084,9 +1106,61 @@ static void device_unparent(Object *obj)
     }
 }
 
+/**
+ * device_phases_reset:
+ * Transition reset method for devices to allow moving
+ * smoothly from legacy reset method to multi-phases
+ */
+static void device_phases_reset(DeviceState *dev)
+{
+    ResettableClass *rc = RESETTABLE_GET_CLASS(dev);
+
+    if (rc->phases.enter) {
+        rc->phases.enter(OBJECT(dev), RESET_TYPE_COLD);
+    }
+    if (rc->phases.hold) {
+        rc->phases.hold(OBJECT(dev));
+    }
+    if (rc->phases.exit) {
+        rc->phases.exit(OBJECT(dev));
+    }
+}
+
+static void device_transitional_reset(Object *obj)
+{
+    DeviceClass *dc = DEVICE_GET_CLASS(obj);
+
+    /*
+     * This will call either @device_phases_reset (for multi-phases transitioned
+     * devices) or a device's specific method for not-yet transitioned devices.
+     * In both case, it does not reset children.
+     */
+    if (dc->reset) {
+        dc->reset(DEVICE(obj));
+    }
+}
+
+/**
+ * device_get_transitional_reset:
+ * check if the device's class is ready for multi-phase
+ */
+static ResettableTrFunction device_get_transitional_reset(Object *obj)
+{
+    DeviceClass *dc = DEVICE_GET_CLASS(obj);
+    if (dc->reset != device_phases_reset) {
+        /*
+         * dc->reset has been overridden by a subclass,
+         * the device is not ready for multi phase yet.
+         */
+        return device_transitional_reset;
+    }
+    return NULL;
+}
+
 static void device_class_init(ObjectClass *class, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(class);
+    ResettableClass *rc = RESETTABLE_CLASS(class);
 
     class->unparent = device_unparent;
 
@@ -1098,6 +1172,25 @@ static void device_class_init(ObjectClass *class, void *data)
      */
     dc->hotpluggable = true;
     dc->user_creatable = true;
+
+    rc->get_state = device_get_reset_state;
+    rc->child_foreach = device_reset_child_foreach;
+
+    /*
+     * @device_phases_reset is put as the default reset method below, allowing
+     * to do the multi-phase transition from base classes to leaf classes. It
+     * allows a legacy-reset Device class to extend a multi-phases-reset
+     * Device class for the following reason:
+     * + If a base class B has been moved to multi-phase, then it does not
+     *   override this default reset method and may have defined phase methods.
+     * + A daughter class C (extending class B) which uses
+     *   device_class_set_parent_reset() (or similar means) to override the
+     *   reset method will still work as expected. @device_phases_reset function
+     *   will be registered as the parent reset method and effectively call
+     *   parent reset phases.
+     */
+    dc->reset = device_phases_reset;
+    rc->get_transitional_function = device_get_transitional_reset;
 }
 
 void device_class_set_parent_reset(DeviceClass *dc,
@@ -1156,6 +1249,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 01963984f8..2e3346600e 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: ResettableState 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;
+    ResettableState reset;
 };
 
 struct DeviceListener {
@@ -195,6 +203,7 @@ typedef struct BusChild {
 /**
  * BusState:
  * @hotplug_handler: link to a hotplug handler associated with bus.
+ * @reset: ResettableState 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;
+    ResettableState reset;
 };
 
 /**
@@ -392,6 +402,18 @@ void qdev_reset_all_fn(void *opaque);
 void qbus_reset_all(BusState *bus);
 void qbus_reset_all_fn(void *opaque);
 
+/**
+ * device_is_in_reset:
+ * Return true if the device @dev is currently being reset.
+ */
+bool device_is_in_reset(DeviceState *dev);
+
+/**
+ * bus_is_in_reset:
+ * Return true if the bus @bus is currently being reset.
+ */
+bool bus_is_in_reset(BusState *bus);
+
 /* This should go away once we get rid of the NULL bus hack */
 BusState *sysbus_get_default(void);
 
@@ -413,6 +435,11 @@ void qdev_machine_init(void);
  */
 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 3543451ed3..618ad55401 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -562,6 +562,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/bus.o \
+	hw/core/resettable.o \
 	hw/core/irq.o \
 	hw/core/fw-path-provider.o \
 	hw/core/reset.o \
-- 
2.23.0



  parent reply	other threads:[~2019-10-18 15:23 UTC|newest]

Thread overview: 57+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-10-18 15:06 [PATCH v5 00/13] Multi-phase reset mechanism Damien Hedde
2019-10-18 15:06 ` [PATCH v5 01/13] add device_legacy_reset function to prepare for reset api change Damien Hedde
2019-10-19 17:35   ` Richard Henderson
2019-12-03 10:55   ` Cornelia Huck
2019-10-18 15:06 ` [PATCH v5 02/13] hw/core/qdev: add trace events to help with resettable transition Damien Hedde
2019-10-19 17:44   ` Richard Henderson
2019-10-31 23:23   ` Philippe Mathieu-Daudé
2019-11-04 12:16     ` Damien Hedde
2019-11-04 14:33       ` Philippe Mathieu-Daudé
2019-12-03 10:57   ` Cornelia Huck
2019-10-18 15:06 ` [PATCH v5 03/13] hw/core: create Resettable QOM interface Damien Hedde
2019-11-29 18:32   ` Peter Maydell
2019-12-02 11:07     ` Damien Hedde
2019-12-02 11:14       ` Peter Maydell
2019-12-03 11:16   ` Cornelia Huck
2019-10-18 15:06 ` Damien Hedde [this message]
2019-10-19 18:49   ` [PATCH v5 04/13] hw/core: add Resettable support to BusClass and DeviceClass Richard Henderson
2019-11-29 18:36   ` Peter Maydell
2019-12-02 11:38     ` Damien Hedde
2019-10-18 15:06 ` [PATCH v5 05/13] hw/core/resettable: add support for changing parent Damien Hedde
2019-11-29 18:38   ` Peter Maydell
2019-12-02 11:43     ` Damien Hedde
2019-10-18 15:06 ` [PATCH v5 06/13] hw/core/qdev: handle parent bus change regarding resettable Damien Hedde
2019-11-29 18:41   ` Peter Maydell
2019-12-03 11:37     ` Cornelia Huck
2019-10-18 15:06 ` [PATCH v5 07/13] hw/core/qdev: update hotplug reset " Damien Hedde
2019-11-08 15:14   ` Damien Hedde
2019-10-18 15:06 ` [PATCH v5 08/13] hw/core: deprecate old reset functions and introduce new ones Damien Hedde
2019-10-31 23:35   ` Philippe Mathieu-Daudé
2019-11-04 12:01     ` Damien Hedde
2019-11-04 15:42       ` Philippe Mathieu-Daudé
2019-11-29 18:42   ` Peter Maydell
2019-10-18 15:06 ` [PATCH v5 09/13] docs/devel/reset.txt: add doc about Resettable interface Damien Hedde
2019-11-29 19:00   ` Peter Maydell
2019-12-06 15:40     ` Damien Hedde
2019-12-06 16:21     ` Damien Hedde
2019-10-18 15:06 ` [PATCH v5 10/13] vl: replace deprecated qbus_reset_all registration Damien Hedde
2019-11-29 19:01   ` Peter Maydell
2019-10-18 15:06 ` [PATCH v5 11/13] hw/s390x/ipl: replace deprecated qdev_reset_all registration Damien Hedde
2019-10-31 23:38   ` Philippe Mathieu-Daudé
2019-11-29 19:02   ` Peter Maydell
2019-12-03 11:41   ` Cornelia Huck
2019-10-18 15:06 ` [PATCH v5 12/13] hw/gpio/bcm2835_gpio: Isolate sdbus reparenting Damien Hedde
2019-11-29 19:05   ` Peter Maydell
2019-12-02 12:27     ` Damien Hedde
2019-12-02 12:33       ` Peter Maydell
2019-12-02 13:05         ` Damien Hedde
2019-12-02 13:10           ` Peter Maydell
2019-10-18 15:06 ` [PATCH v5 13/13] hw/gpio/bcm2835_gpio: Update to resettable Damien Hedde
2019-11-29 19:02   ` Peter Maydell
2019-10-19  9:01 ` [PATCH v5 00/13] Multi-phase reset mechanism no-reply
2019-10-29 15:53 ` Damien Hedde
2019-11-08 15:26   ` Damien Hedde
2019-11-08 15:28     ` Peter Maydell
2019-11-08 15:58       ` Damien Hedde
2019-11-29 19:07 ` Peter Maydell
2019-12-03 11:44 ` Cornelia Huck

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20191018150630.31099-5-damien.hedde@greensocs.com \
    --to=damien.hedde@greensocs.com \
    --cc=berrange@redhat.com \
    --cc=cohuck@redhat.com \
    --cc=david@gibson.dropbear.id.au \
    --cc=edgari@xilinx.com \
    --cc=ehabkost@redhat.com \
    --cc=mark.burton@greensocs.com \
    --cc=pbonzini@redhat.com \
    --cc=peter.maydell@linaro.org \
    --cc=philmd@redhat.com \
    --cc=qemu-arm@nongnu.org \
    --cc=qemu-devel@nongnu.org \
    --cc=qemu-s390x@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).