On Wed, Sep 11, 2019 at 04:56:13PM +0200, Damien Hedde wrote:
> 
> 
> On 9/11/19 10:06 AM, David Gibson wrote:
> > On Wed, Aug 21, 2019 at 06:33:33PM +0200, Damien Hedde wrote:
> >> This commit defines an interface allowing multi-phase reset. This aims
> >> to solve a problem of the actual single-phase reset (built in
> >> DeviceClass and BusClass): reset behavior is dependent on the order
> >> in which reset handlers are called. In particular doing external
> >> side-effect (like setting an qemu_irq) is problematic because receiving
> >> object may not be reset yet.
> >>
> >> The Resettable interface divides the reset in 3 well defined phases.
> >> To reset an object tree, all 1st phases are executed then all 2nd then
> >> all 3rd. See the comments in include/hw/resettable.h for a more complete
> >> description. There is 3 phases to allow a device to be held in reset
> >> state; the ability to control this state will be added in a following
> >> commits.
> >>
> >> The qdev/qbus reset in DeviceClass and BusClass will be modified in
> >> following commits to use this interface.
> >> No change of behavior is expected because the init phase execution order
> >> follows the children-then-parent order inside a tree. Since this is the
> >> actual order of qdev/qbus reset, we will be able to map current reset
> >> handlers on init phase for example.
> >>
> >> In this patch only cold reset is introduced, which is pretty much the
> >> actual semantics of the current reset handlers. The interface can be
> >> extended to support more reset types.
> >>
> >> Documentation will be added in a following commit.
> >>
> >> Signed-off-by: Damien Hedde <damien.he...@greensocs.com>
> >> ---
> >>
> >> I kept the non-recursive count approach (a given object counts reset
> >> initiated on it as well as reset initiated on its parents in reset
> >> hierarchy). I implemented the other approach, it is possible but is more
> >> complex (an object has to know its direct parent(s) and we need to scan
> >> the reset hierarchy to know if we are in reset) so I prefer not
> >> to introduce it here.
> >> Moreover I think it has drawbacks if we want to handle complex reset
> >> use cases with more reset type.
> >> Anyway, as long as we don't migrate the reset-related state, there is
> >> no problem to switch between approaches.
> >> ---
> > 
> > So, I certainly prefer the more general "reset type" approach taken in
> > this version.  That said, I find it pretty hard to imagine what types
> > of reset other than cold will exist that have well enough defined
> > semantics to be meaningfully used from an external subsystem.
> 
> Maybe I should completely remove the type then ?

That makes sense to me.  I don't know if other possible users of the
mechanism have different opinions though.

> > 
> >>  Makefile.objs           |   1 +
> >>  hw/core/Makefile.objs   |   1 +
> >>  hw/core/resettable.c    | 186 ++++++++++++++++++++++++++++++++++++++++
> >>  hw/core/trace-events    |  36 ++++++++
> >>  include/hw/resettable.h | 159 ++++++++++++++++++++++++++++++++++
> >>  5 files changed, 383 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 b49f880a0c..69b408ad1c 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..b534c2c7a4
> >> --- /dev/null
> >> +++ b/hw/core/resettable.c
> >> @@ -0,0 +1,186 @@
> >> +/*
> >> + * 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_GET_CLASS(obj) \
> >> +    OBJECT_GET_CLASS(ResettableClass, (obj), TYPE_RESETTABLE_INTERFACE)
> >> +
> >> +static void resettable_foreach_child(ResettableClass *rc,
> >> +                                     Object *obj,
> >> +                                     void (*func)(Object *, ResetType 
> >> type),
> >> +                                     ResetType type)
> >> +{
> >> +    if (rc->foreach_child) {
> >> +        rc->foreach_child(obj, func, type);
> >> +    }
> >> +}
> >> +
> >> +static void resettable_init_reset(Object *obj, ResetType type)
> > 
> > I wonder if "enter reset" would be better terminology so this doesn't
> > get confused with the initial, well, initialization of the device.
> 
> Do you mean for the function here or in general for the name of the phase ?

In general.

> >> +{
> >> +    ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
> >> +    ResetState *s = rc->get_state(obj);
> >> +    bool action_needed = false;
> >> +
> >> +    /* Only take action if we really enter reset for the 1st time. */
> >> +    /*
> >> +     * TODO: if adding more ResetType support, some additional checks
> >> +     * are probably needed here.
> >> +     */
> >> +    if (s->count == 0) {
> >> +        action_needed = true;
> >> +    }
> >> +    s->count += 1;
> >> +    /*
> >> +     * We limit the count to an arbitrary "big" value. The value is big
> >> +     * enough not to be triggered nominally.
> >> +     * The assert will stop an infinite loop if there is a cycle in the
> >> +     * reset tree. The loop goes through resettable_foreach_child below
> >> +     * which at some point will call us again.
> >> +     */
> >> +    assert(s->count <= 50);
> >> +    trace_resettable_phase_init(obj, object_get_typename(obj), type,
> >> +                                s->count, action_needed);
> >> +
> >> +    /*
> >> +     * handle the children even if action_needed is at false so that
> >> +     * children counts are incremented too
> >> +     */
> >> +    resettable_foreach_child(rc, obj, resettable_init_reset, type);
> >> +
> >> +    /* exec init phase */
> >> +    if (action_needed) {
> >> +        s->hold_phase_needed = true;
> >> +        if (rc->phases.init) {
> >> +            rc->phases.init(obj, type);
> >> +        }
> >> +    }
> >> +    trace_resettable_phase_init_end(obj);
> >> +}
> >> +
> >> +static void resettable_hold_reset_child(Object *obj, ResetType type)
> >> +{
> >> +    ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
> >> +    ResetState *s = rc->get_state(obj);
> >> +
> >> +    trace_resettable_phase_hold(obj, object_get_typename(obj));
> >> +
> >> +    /* handle children first */
> >> +    resettable_foreach_child(rc, obj, resettable_hold_reset_child, 0);
> >> +
> >> +    /* exec hold phase */
> >> +    if (s->hold_phase_needed) {
> >> +        s->hold_phase_needed = false;
> >> +        if (rc->phases.hold) {
> >> +            rc->phases.hold(obj);
> > 
> > I was about to ask what purpose the hold phase has since it's always
> > executed right after the init phase, before realizing that it's
> > because it can happen after parent devices have completed their init
> > phase.
> > 
> > Point that out in a comment here might help to avoid confusion.
> 
> noted.
> 
> > 
> >> +        }
> >> +    }
> >> +    trace_resettable_phase_hold_end(obj, s->hold_phase_needed);
> >> +}
> >> +
> >> +static void resettable_hold_reset(Object *obj)
> >> +{
> >> +    /* we don't care of 2nd argument here */
> >> +    resettable_hold_reset_child(obj, 0);
> >> +}
> >> +
> >> +static void resettable_exit_reset_child(Object *obj, ResetType type)
> >> +{
> >> +    ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
> >> +    ResetState *s = rc->get_state(obj);
> >> +
> >> +    trace_resettable_phase_exit(obj, object_get_typename(obj));
> >> +
> >> +    resettable_foreach_child(rc, obj, resettable_exit_reset_child, 0);
> >> +
> >> +    /*
> >> +     * 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 (s->count > 0) {
> >> +        s->count -= 1;
> >> +    } else {
> >> +        trace_resettable_count_underflow(obj);
> > 
> > Should this be an assert(), IIUC this could only come about from an
> > error within the qemu code, right?
> 
> Initially I was thinking that so I put an assert.
> 
> But while testing I found out that it is triggered by the raspi machine
> during its reset because the qbus tree is modified during it.
> 
> So it depends if we consider this kind of action to be faulty. With no
> migration support, the only way to trigger it is to modify the qdev
> hierarchy during reset.

Hm, I see.  It feels like just ignoring underflow is ignoring the
error rather than really addressing it.  When we add a device to the
heirarchy, do we need to initialize its reset count based on its
parent's current count or something.

> But it made me think about the migration cases (with "staying in reset
> state"). There are some cases where migration between 2 different
> versions can lead to problem with the counter (typically if you migrate
> to a new version that have a new device on a bus held in reset).

Uh... migration is only permitted between identical machines.  I don't
see how a new device is possible.

> 
> > 
> >> +    }
> >> +    if (s->count == 0) {
> >> +        if (rc->phases.exit) {
> >> +            rc->phases.exit(obj);
> > 
> > Hm.  It's not really clear to me whether child resets should go before
> > or after the parent.  It seems odd that it would be the same for both
> > entering and exiting reset, though.
> 
> Since the whole point of the phases is to make the reset independent of
> the order in which it is "scheduled", I think we could consider it as
> "unspecified". If we find out that we need some hierarchical order for a
> yet unknown reason, we can change it.
> 
> I've used the order children then parent because it is the actual qdev
> reset order and we do not want to modify the actual order now.

Hm, ok.

> 
> > 
> >> +        }
> >> +    }
> >> +    trace_resettable_phase_exit_end(obj, s->count);
> >> +}
> >> +
> >> +static void resettable_exit_reset(Object *obj)
> >> +{
> >> +    /* we don't care of 2nd argument here */
> >> +    resettable_exit_reset_child(obj, 0);
> >> +}
> >> +
> >> +void resettable_reset(Object *obj, ResetType type)
> >> +{
> >> +    /* TODO: change that when adding support for other reset types */
> >> +    assert(type == RESET_TYPE_COLD);
> >> +    trace_resettable_reset(obj, type);
> >> +    resettable_init_reset(obj, type);
> >> +    resettable_hold_reset(obj);
> >> +    resettable_exit_reset(obj);
> >> +}
> >> +
> >> +void resettable_cold_reset_fn(void *opaque)
> >> +{
> >> +    resettable_reset((Object *) opaque, RESET_TYPE_COLD);
> >> +}
> >> +
> >> +bool resettable_is_resetting(Object *obj)
> >> +{
> >> +    ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
> >> +    ResetState *s = rc->get_state(obj);
> >> +
> >> +    return (s->count > 0);
> >> +}
> >> +
> >> +void resettable_class_set_parent_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_INTERFACE,
> >> +    .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..ecf966c314
> >> --- /dev/null
> >> +++ b/hw/core/trace-events
> >> @@ -0,0 +1,36 @@
> >> +# 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, int cold) "obj=%p cold=%d"
> >> +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..5808c3c187
> >> --- /dev/null
> >> +++ b/include/hw/resettable.h
> >> @@ -0,0 +1,159 @@
> >> +#ifndef HW_RESETTABLE_H
> >> +#define HW_RESETTABLE_H
> >> +
> >> +#include "qom/object.h"
> >> +
> >> +#define TYPE_RESETTABLE_INTERFACE "resettable"
> >> +
> >> +#define RESETTABLE_CLASS(class) \
> >> +    OBJECT_CLASS_CHECK(ResettableClass, (class), 
> >> TYPE_RESETTABLE_INTERFACE)
> >> +
> >> +typedef struct ResetState ResetState;
> >> +
> >> +/**
> >> + * ResetType:
> >> + * Types of reset.
> >> + *
> >> + * + Cold: reset resulting from a power cycle of the object.
> >> + *
> >> + * TODO: Support has to be added to handle more types. In particular,
> >> + * ResetState structure needs to be expanded.
> >> + */
> >> +typedef enum ResetType {
> >> +    RESET_TYPE_COLD,
> >> +} ResetType;
> >> +
> >> +/*
> >> + * ResettableClass:
> >> + * Interface for resettable objects.
> >> + *
> >> + * See docs/devel/reset.rst for more detailed information about
> >> + * how QEMU models reset.
> >> + *
> >> + * All objects which can be reset must implement this interface;
> >> + * it is usually provided by a base class such as DeviceClass or BusClass.
> >> + * Every Resettable object must maintain some state tracking the
> >> + * progress of a reset operation by providing a ResetState structure.
> >> + * The functions defined in this module take care of updating the
> >> + * state of the reset.
> >> + * The base class implementation of the interface provides this
> >> + * state and implements the associated method: get_state.
> >> + *
> >> + * Concrete object implementations (typically specific devices
> >> + * such as a UART model) should provide the functions
> >> + * for the phases.init, phases.hold and phases.exit methods, which
> >> + * they can set in their class init function, either directly or
> >> + * by calling resettable_class_set_parent_phases().
> >> + * The phase methods are guaranteed to only only ever be called once
> >> + * for any reset event, in the order 'init', 'hold', 'exit'.
> >> + * An object will always move quickly from 'init' to 'hold'
> >> + * but might remain in 'hold' for an arbitrary period of time
> >> + * before eventually reset is deasserted and the 'exit' phase is called.
> >> + * Object implementations should be prepared for functions handling
> >> + * inbound connections from other devices (such as qemu_irq handler
> >> + * functions) to be called at any point during reset after their
> >> + * 'init' method has been called.
> >> + *
> >> + * Users of a resettable object should not call these methods
> >> + * directly, but instead use the function resettable_reset().
> >> + *
> >> + * @phases.init: This phase is called when the object enters reset. It
> >> + * should reset local state of the object, but it must not do anything 
> >> that
> >> + * has a side-effect on other objects, such as raising or lowering a 
> >> qemu_irq
> >> + * line or reading or writing guest memory. It takes the reset's type as
> >> + * argument.
> >> + *
> >> + * @phases.hold: This phase is called for entry into reset, once every 
> >> object
> >> + * in the system which is being reset has had its @phases.init method 
> >> called.
> >> + * At this point devices can do actions that affect other objects.
> >> + *
> >> + * @phases.exit: This phase is called when the object leaves the reset 
> >> state.
> >> + * Actions affecting other objects are permitted.
> >> + *
> >> + * @get_state: Mandatory method which must return a pointer to a 
> >> ResetState.
> >> + *
> >> + * @foreach_child: Executes a given function on every Resettable child. 
> >> Child
> >> + * in this context means a child in the qbus tree, so the children of a 
> >> qbus
> >> + * are the devices on it, and the children of a device are all the buses 
> >> it
> >> + * owns. This is not the same as the QOM object hierarchy. The function 
> >> takes
> >> + * an additional ResetType argument which is passed to foreach_child.
> >> + */
> >> +typedef void (*ResettableInitPhase)(Object *obj, ResetType type);
> >> +typedef void (*ResettableHoldPhase)(Object *obj);
> >> +typedef void (*ResettableExitPhase)(Object *obj);
> >> +typedef ResetState * (*ResettableGetState)(Object *obj);
> >> +typedef void (*ResettableForeachChild)(Object *obj,
> >> +                                       void (*func)(Object *, ResetType 
> >> type),
> >> +                                       ResetType type);
> >> +typedef struct ResettableClass {
> >> +    InterfaceClass parent_class;
> >> +
> >> +    struct ResettablePhases {
> >> +        ResettableInitPhase init;
> >> +        ResettableHoldPhase hold;
> >> +        ResettableExitPhase exit;
> >> +    } phases;
> >> +
> >> +    ResettableGetState get_state;
> >> +    ResettableForeachChild foreach_child;
> >> +} ResettableClass;
> >> +typedef struct ResettablePhases ResettablePhases;
> >> +
> >> +/**
> >> + * ResetState:
> >> + * Structure holding reset related state. The fields should not be 
> >> accessed
> >> + * directly, the definition is here to allow further inclusion into other
> >> + * objects.
> >> + *
> >> + * @count: Number of reset level the object is into. It is incremented 
> >> when
> >> + * the reset operation starts and decremented when it finishes.
> >> + * @hold_phase_needed: flag which indicates that we need to invoke the 
> >> 'hold'
> >> + * phase handler for this object.
> >> + */
> >> +struct ResetState {
> >> +    uint32_t count;
> >> +    bool hold_phase_needed;
> >> +};
> >> +
> >> +/**
> >> + * resettable_is_resetting:
> >> + * Return true if @obj is under reset.
> >> + *
> >> + * @obj must implement Resettable interface.
> >> + */
> >> +bool resettable_is_resetting(Object *obj);
> >> +
> >> +/**
> >> + * resettable_reset:
> >> + * Trigger a reset on a object @obj of type @type. @obj must implement
> >> + * Resettable interface.
> >> + *
> >> + * Calling this function is equivalent to calling 
> >> @resettable_assert_reset then
> >> + * @resettable_deassert_reset.
> >> + */
> >> +void resettable_reset(Object *obj, ResetType type);
> >> +
> >> +/**
> >> + * resettable_cold_reset_fn:
> >> + * Helper to call resettable_reset((Object *) opaque, RESET_TYPE_COLD).
> >> + *
> >> + * This function is typically useful to register a reset handler with
> >> + * qemu_register_reset.
> >> + */
> >> +void resettable_cold_reset_fn(void *opaque);
> >> +
> >> +/**
> >> + * resettable_class_set_parent_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 overridden only if the new one is not NULL allowing to
> >> + * override a subset of phases.
> >> + */
> >> +void resettable_class_set_parent_phases(ResettableClass *rc,
> >> +                                        ResettableInitPhase init,
> >> +                                        ResettableHoldPhase hold,
> >> +                                        ResettableExitPhase exit,
> >> +                                        ResettablePhases *parent_phases);
> >> +
> >> +#endif
> > 
> 

-- 
David Gibson                    | I'll have my music baroque, and my code
david AT gibson.dropbear.id.au  | minimalist, thank you.  NOT _the_ _other_
                                | _way_ _around_!
http://www.ozlabs.org/~dgibson

Attachment: signature.asc
Description: PGP signature

Reply via email to