[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-ppc] [PATCH v3 01/33] Create Resettable QOM interface
From: |
Damien Hedde |
Subject: |
[Qemu-ppc] [PATCH v3 01/33] Create Resettable QOM interface |
Date: |
Mon, 29 Jul 2019 16:56:22 +0200 |
This commit defines an interface allowing multi-phase reset.
The phases are INIT, HOLD and EXIT. Each phase has an associated method
in the class.
The reset of a Resettable is controlled with 2 functions:
- resettable_assert_reset which starts the reset operation.
- resettable_deassert_reset which ends the reset operation.
There is also a `resettable_reset` helper function which does assert then
deassert allowing to do a proper reset in one call.
In addition, two functions, *resettable_reset_cold_fn* and
*resettable_reset_warm_fn*, are defined. They take only an opaque argument
(for the Resettable object) and can be used as callbacks.
The Resettable interface is "reentrant", _assert_ can be called several
times and _deasert_ must be called the same number of times so that the
Resettable leave reset state. It is supported by keeping a counter of
the current number of _assert_ calls. The counter maintainance is done
though 3 methods get/increment/decrement_count. A method set_cold is used
to set the cold flag of the reset. Another method set_host_needed is used
to ensure hold phase is executed only if required.
Reset hierarchy is also supported. Each Resettable may have
sub-Resettable objects. When resetting a Resettable, it is propagated to
its children using the foreach_child method.
When entering reset, init phases are executed parent-to-child order then
hold phases are executed child-parent order.
When leaving reset, exit phases are executed in child-to-parent order.
This will allow to replace current qdev_reset mechanism by this interface
without side-effects on reset order.
Note: I used an uint32 for the count. This match the type already used
in the existing resetting counter in hw/scsi/vmw_pvscsi.c for the
PVSCSIState.
Signed-off-by: Damien Hedde <address@hidden>
---
Makefile.objs | 1 +
hw/core/Makefile.objs | 1 +
hw/core/resettable.c | 220 ++++++++++++++++++++++++++++++++++++++++
hw/core/trace-events | 39 +++++++
include/hw/resettable.h | 126 +++++++++++++++++++++++
5 files changed, 387 insertions(+)
create mode 100644 hw/core/resettable.c
create mode 100644 hw/core/trace-events
create mode 100644 include/hw/resettable.h
diff --git a/Makefile.objs b/Makefile.objs
index 6a143dcd57..a723a47e14 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -191,6 +191,7 @@ trace-events-subdirs += migration
trace-events-subdirs += net
trace-events-subdirs += ui
endif
+trace-events-subdirs += hw/core
trace-events-subdirs += hw/display
trace-events-subdirs += qapi
trace-events-subdirs += qom
diff --git a/hw/core/Makefile.objs b/hw/core/Makefile.objs
index f8481d959f..d9234aa98a 100644
--- a/hw/core/Makefile.objs
+++ b/hw/core/Makefile.objs
@@ -1,6 +1,7 @@
# core qdev-related obj files, also used by *-user:
common-obj-y += qdev.o qdev-properties.o
common-obj-y += bus.o reset.o
+common-obj-y += resettable.o
common-obj-$(CONFIG_SOFTMMU) += qdev-fw.o
common-obj-$(CONFIG_SOFTMMU) += fw-path-provider.o
# irq.o needed for qdev GPIO handling:
diff --git a/hw/core/resettable.c b/hw/core/resettable.c
new file mode 100644
index 0000000000..d7d631ce8b
--- /dev/null
+++ b/hw/core/resettable.c
@@ -0,0 +1,220 @@
+/*
+ * Resettable interface.
+ *
+ * Copyright (c) 2019 GreenSocs SAS
+ *
+ * Authors:
+ * Damien Hedde
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/module.h"
+#include "hw/resettable.h"
+#include "trace.h"
+
+#define RESETTABLE_MAX_COUNT 50
+
+#define RESETTABLE_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(ResettableClass, (obj), TYPE_RESETTABLE)
+
+static void resettable_init_reset(Object *obj, bool cold);
+
+static bool resettable_class_check(ResettableClass *rc)
+{
+ if (!rc->set_cold) {
+ return false;
+ }
+ if (!rc->set_hold_needed) {
+ return false;
+ }
+ if (!rc->increment_count) {
+ return false;
+ }
+ if (!rc->decrement_count) {
+ return false;
+ }
+ if (!rc->get_count) {
+ return false;
+ }
+ return true;
+}
+
+static void resettable_foreach_child(ResettableClass *rc,
+ Object *obj,
+ void (*func)(Object *))
+{
+ if (rc->foreach_child) {
+ rc->foreach_child(obj, func);
+ }
+}
+
+static void resettable_init_cold_reset(Object *obj)
+{
+ resettable_init_reset(obj, true);
+}
+
+static void resettable_init_warm_reset(Object *obj)
+{
+ resettable_init_reset(obj, false);
+}
+
+static void resettable_init_reset(Object *obj, bool cold)
+{
+ void (*func)(Object *);
+ ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
+ uint32_t count;
+ bool action_needed = false;
+ bool prev_cold;
+
+ assert(resettable_class_check(rc));
+
+ count = rc->increment_count(obj);
+ /* this assert is here to catch an eventual reset loop */
+ assert(count <= RESETTABLE_MAX_COUNT);
+
+ /*
+ * only take action if:
+ * + we really enter reset for the 1st time
+ * + or we are in warm reset and start a cold one
+ */
+ prev_cold = rc->set_cold(obj, cold);
+ if (count == 1) {
+ action_needed = true;
+ } else if (cold && !prev_cold) {
+ action_needed = true;
+ }
+ trace_resettable_phase_init(obj, object_get_typename(obj), cold, count,
+ action_needed);
+
+ /* exec init phase */
+ if (action_needed) {
+ rc->set_hold_needed(obj, true);
+ if (rc->phases.init) {
+ rc->phases.init(obj);
+ }
+ }
+
+ /*
+ * handle the children even if action_needed is at false so that
+ * children counts are incremented too
+ */
+ func = cold ? resettable_init_cold_reset : resettable_init_warm_reset;
+ resettable_foreach_child(rc, obj, func);
+ trace_resettable_phase_init_end(obj);
+}
+
+static void resettable_hold_reset(Object *obj)
+{
+ ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
+ bool hold_needed;
+
+ assert(resettable_class_check(rc));
+ trace_resettable_phase_hold(obj, object_get_typename(obj));
+
+ /* handle children first */
+ resettable_foreach_child(rc, obj, resettable_hold_reset);
+
+ /* exec hold phase */
+ hold_needed = rc->set_hold_needed(obj, false);
+ if (hold_needed) {
+ if (rc->phases.hold) {
+ rc->phases.hold(obj);
+ }
+ }
+ trace_resettable_phase_hold_end(obj, hold_needed);
+}
+
+static void resettable_exit_reset(Object *obj)
+{
+ uint32_t count;
+ ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
+
+ assert(resettable_class_check(rc));
+ trace_resettable_phase_exit(obj, object_get_typename(obj));
+
+ resettable_foreach_child(rc, obj, resettable_deassert_reset);
+
+ count = rc->get_count(obj);
+ /*
+ * we could assert that count > 0 but there are some corner cases
+ * where we prefer to let it go as it is probably harmless.
+ * For example: if there is reset support addition between
+ * hosts when doing a migration. We may do such things as
+ * deassert a non-existing reset.
+ */
+ if (count > 0) {
+ count = rc->decrement_count(obj);
+ } else {
+ trace_resettable_count_underflow(obj);
+ }
+ if (count == 0) {
+ if (rc->phases.exit) {
+ rc->phases.exit(obj);
+ }
+ }
+ trace_resettable_phase_exit_end(obj, count);
+}
+
+void resettable_assert_reset(Object *obj, bool cold)
+{
+ trace_resettable_reset_assert(obj, object_get_typename(obj), cold);
+ resettable_init_reset(obj, cold);
+ resettable_hold_reset(obj);
+}
+
+void resettable_deassert_reset(Object *obj)
+{
+ trace_resettable_reset_deassert(obj, object_get_typename(obj));
+ resettable_exit_reset(obj);
+}
+
+void resettable_reset(Object *obj, bool cold)
+{
+ trace_resettable_reset(obj, object_get_typename(obj), cold);
+ resettable_assert_reset(obj, cold);
+ resettable_deassert_reset(obj);
+}
+
+void resettable_reset_warm_fn(void *opaque)
+{
+ resettable_reset((Object *) opaque, false);
+}
+
+void resettable_reset_cold_fn(void *opaque)
+{
+ resettable_reset((Object *) opaque, true);
+}
+
+void resettable_class_set_parent_reset_phases(ResettableClass *rc,
+ ResettableInitPhase init,
+ ResettableHoldPhase hold,
+ ResettableExitPhase exit,
+ ResettablePhases *parent_phases)
+{
+ *parent_phases = rc->phases;
+ if (init) {
+ rc->phases.init = init;
+ }
+ if (hold) {
+ rc->phases.hold = hold;
+ }
+ if (exit) {
+ rc->phases.exit = exit;
+ }
+}
+
+static const TypeInfo resettable_interface_info = {
+ .name = TYPE_RESETTABLE,
+ .parent = TYPE_INTERFACE,
+ .class_size = sizeof(ResettableClass),
+};
+
+static void reset_register_types(void)
+{
+ type_register_static(&resettable_interface_info);
+}
+
+type_init(reset_register_types)
diff --git a/hw/core/trace-events b/hw/core/trace-events
new file mode 100644
index 0000000000..489d96d445
--- /dev/null
+++ b/hw/core/trace-events
@@ -0,0 +1,39 @@
+# See docs/devel/tracing.txt for syntax documentation.
+#
+# This file is processed by the tracetool script during the build.
+#
+# To add a new trace event:
+#
+# 1. Choose a name for the trace event. Declare its arguments and format
+# string.
+#
+# 2. Call the trace event from code using trace_##name, e.g. multiwrite_cb() ->
+# trace_multiwrite_cb(). The source file must #include "trace.h".
+#
+# Format of a trace event:
+#
+# [disable] <name>(<type1> <arg1>[, <type2> <arg2>] ...) "<format-string>"
+#
+# Example: g_malloc(size_t size) "size %zu"
+#
+# The "disable" keyword will build without the trace event.
+#
+# The <name> must be a valid as a C function name.
+#
+# Types should be standard C types. Use void * for pointers because the trace
+# system may not have the necessary headers included.
+#
+# The <format-string> should be a sprintf()-compatible format string.
+
+# resettable.c
+resettable_reset(void *obj, const char *type, int cold) "obj=%p(%s) cold=%d"
+resettable_reset_assert(void *obj, const char *type, int cold) "obj=%p(%s)
cold=%d"
+resettable_reset_deassert(void *obj, const char *type) "obj=%p(%s)"
+resettable_reset_deassert_end(void *obj) "obj=%p"
+resettable_phase_init(void *obj, const char *type, int cold, uint32_t count,
int needed) "obj=%p(%s) cold=%d count=%" PRIu32 " needed=%d"
+resettable_phase_init_end(void *obj) "obj=%p"
+resettable_phase_hold(void *obj, const char *type) "obj=%p(%s)"
+resettable_phase_hold_end(void *obj, int needed) "obj=%p needed=%d"
+resettable_phase_exit(void *obj, const char *type) "obj=%p(%s)"
+resettable_phase_exit_end(void *obj, uint32_t count) "obj=%p count=%" PRIu32
+resettable_count_underflow(void *obj) "obj=%p"
diff --git a/include/hw/resettable.h b/include/hw/resettable.h
new file mode 100644
index 0000000000..e617a8e875
--- /dev/null
+++ b/include/hw/resettable.h
@@ -0,0 +1,126 @@
+#ifndef HW_RESETTABLE_H
+#define HW_RESETTABLE_H
+
+#include "qom/object.h"
+
+#define TYPE_RESETTABLE "resettable"
+
+#define RESETTABLE_CLASS(class) \
+ OBJECT_CLASS_CHECK(ResettableClass, (class), TYPE_RESETTABLE)
+
+/*
+ * ResettableClass:
+ * Interface for resettable objects.
+ *
+ * The reset operation is divided in several phases each represented by a
+ * method.
+ *
+ * Each Ressetable must maintain a reset counter in its state, 3 methods allows
+ * to interact with it.
+ *
+ * @phases.init: should reset local state only. Takes a bool @cold argument
+ * specifying whether the reset is cold or warm. It must not do side-effect
+ * on others objects.
+ *
+ * @phases.hold: side-effects action on others objects due to staying in a
+ * resetting state.
+ *
+ * @phases.exit: leave the reset state, may do side-effects action on others
+ * objects.
+ *
+ * @set_cold: Set whether the current reset is cold or warm. Return the
+ * previous flag value. Return value has no meaning if @get_count returns
+ * a zero value.
+ *
+ * @set_hold_needed: Set hold_needed flag. Return the previous flag value.
+ *
+ * @get_count: Get the current reset count
+ * @increment_count: Increment the reset count, returns the new count
+ * @decrement_count: decrement the reset count, returns the new count
+ *
+ * @foreach_child: Executes a given function on every Resettable child.
+ * A child is not a QOM child, but a child a reset meaning.
+ */
+typedef void (*ResettableInitPhase)(Object *obj);
+typedef void (*ResettableHoldPhase)(Object *obj);
+typedef void (*ResettableExitPhase)(Object *obj);
+typedef bool (*ResettableSetCold)(Object *obj, bool cold);
+typedef bool (*ResettableSetHoldNeeded)(Object *obj, bool hold_needed);
+typedef uint32_t (*ResettableGetCount)(Object *obj);
+typedef uint32_t (*ResettableIncrementCount)(Object *obj);
+typedef uint32_t (*ResettableDecrementCount)(Object *obj);
+typedef void (*ResettableForeachChild)(Object *obj, void (*visitor)(Object *));
+typedef struct ResettableClass {
+ InterfaceClass parent_class;
+
+ struct ResettablePhases {
+ ResettableInitPhase init;
+ ResettableHoldPhase hold;
+ ResettableExitPhase exit;
+ } phases;
+
+ ResettableSetCold set_cold;
+ ResettableSetHoldNeeded set_hold_needed;
+ ResettableGetCount get_count;
+ ResettableIncrementCount increment_count;
+ ResettableDecrementCount decrement_count;
+ ResettableForeachChild foreach_child;
+} ResettableClass;
+typedef struct ResettablePhases ResettablePhases;
+
+/**
+ * resettable_assert_reset:
+ * Increments the reset count and executes the init and hold phases.
+ * Each time resettable_assert_reset is called, resettable_deassert_reset
+ * must eventually be called once.
+ * It will also impact reset children.
+ *
+ * @obj object to reset, must implement Resettable interface.
+ * @cold boolean indicating the type of reset (cold or warm)
+ */
+void resettable_assert_reset(Object *obj, bool cold);
+
+/**
+ * resettable_deassert_reset:
+ * Decrements the reset count by one and executes the exit phase if it hits
+ * zero.
+ * It will also impact reset children.
+ *
+ * @obj object to reset, must implement Resettable interface.
+ */
+void resettable_deassert_reset(Object *obj);
+
+/**
+ * resettable_reset:
+ * Calling this function is equivalent to call @assert_reset then
+ * @deassert_reset.
+ */
+void resettable_reset(Object *obj, bool cold);
+
+/**
+ * resettable_reset_warm_fn:
+ * Helper to call resettable_reset(opaque, false)
+ */
+void resettable_reset_warm_fn(void *opaque);
+
+/**
+ * resettable_reset_cold_fn:
+ * Helper to call resettable_reset(opaque, true)
+ */
+void resettable_reset_cold_fn(void *opaque);
+
+/**
+ * resettable_class_set_parent_reset_phases:
+ *
+ * Save @rc current reset phases into @parent_phases and override @rc phases
+ * by the given new methods (@init, @hold and @exit).
+ * Each phase is overriden only if the new one is not NULL allowing to
+ * override a subset of phases.
+ */
+void resettable_class_set_parent_reset_phases(ResettableClass *rc,
+ ResettableInitPhase init,
+ ResettableHoldPhase hold,
+ ResettableExitPhase exit,
+ ResettablePhases *parent_phases);
+
+#endif
--
2.22.0
- Re: [Qemu-ppc] [PATCH v3 09/33] add doc about Resettable interface, (continued)
- [Qemu-ppc] [PATCH v3 21/33] hw/intc/spapr_xive.c: remove device_legacy_reset call, Damien Hedde, 2019/07/29
- [Qemu-ppc] [PATCH v3 15/33] hw/ide/piix.c: remove qdev_reset_all call, Damien Hedde, 2019/07/29
- [Qemu-ppc] [PATCH v3 26/33] hw/s390x/s390-pci-inst.c: remove device_legacy_reset call, Damien Hedde, 2019/07/29
- [Qemu-ppc] [PATCH v3 30/33] convert cadence_uart to 3-phases reset, Damien Hedde, 2019/07/29
- [Qemu-ppc] [PATCH v3 33/33] Connect the uart reset gpios in the zynq platform, Damien Hedde, 2019/07/29
- [Qemu-ppc] [PATCH v3 06/33] add the vmstate description for device reset state, Damien Hedde, 2019/07/29
- [Qemu-ppc] [PATCH v3 01/33] Create Resettable QOM interface,
Damien Hedde <=
- Re: [Qemu-ppc] [PATCH v3 01/33] Create Resettable QOM interface, Cornelia Huck, 2019/07/30
- Re: [Qemu-ppc] [PATCH v3 01/33] Create Resettable QOM interface, Peter Maydell, 2019/07/30
- Re: [Qemu-ppc] [PATCH v3 01/33] Create Resettable QOM interface, Cornelia Huck, 2019/07/30
- Re: [Qemu-ppc] [PATCH v3 01/33] Create Resettable QOM interface, Peter Maydell, 2019/07/30
- Re: [Qemu-ppc] [PATCH v3 01/33] Create Resettable QOM interface, Damien Hedde, 2019/07/30
- Re: [Qemu-ppc] [PATCH v3 01/33] Create Resettable QOM interface, Cornelia Huck, 2019/07/30
- Re: [Qemu-ppc] [PATCH v3 01/33] Create Resettable QOM interface, David Gibson, 2019/07/31
- Re: [Qemu-ppc] [Qemu-devel] [PATCH v3 01/33] Create Resettable QOM interface, Christophe de Dinechin, 2019/07/31
[Qemu-ppc] [PATCH v3 05/33] Switch to new api in qdev/bus, Damien Hedde, 2019/07/29