qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [PATCH v3 01/33] Create Resettable QOM interface


From: Damien Hedde
Subject: Re: [Qemu-devel] [PATCH v3 01/33] Create Resettable QOM interface
Date: Wed, 7 Aug 2019 17:03:28 +0200
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Thunderbird/60.8.0


On 8/7/19 4:20 PM, Peter Maydell wrote:
> On Mon, 29 Jul 2019 at 15:58, Damien Hedde <address@hidden> wrote:
>>
>> 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
> 
> deassert
> 
>> 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.
> 
> Why do we execute the hold phase in child-to-parent order? I would
> have expected this to follow the same order as the init phase.

No particular reason, I just thought it was better that way. But I don't
have a use case where it matters.

If we do only an assert_reset, then top-level phases are
called first and last:
parent's init
  child A's init
  child_B's init
  child_A's hold
  child_B's hold
parent's hold

I can switch it.

> 
>> 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.
> 
> It makes sense that the exit phase is child-to-parent.
> 
>> 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)
> 
> Since this is an interface and not a complete class,
> we should name it TYPE_RESETTABLE_INTERFACE. We're rather
> inconsistent about naming of interfaces (in the codebase you
> can find "_INTERFACE suffix", "_IF suffix" and "no suffix").
> I think _INTERFACE suffix is best of these.
> (There's some discussion of this in this mailing list thread:
> https://lists.gnu.org/archive/html/qemu-devel/2019-03/msg03840.html
>  -- the idea is that it avoids confusion between whether an
> implementation of the type needs to be a subclass of it or
> if it has to be added as an interface to some other type.)
> 
>> +
>> +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;
>> +}
> 
> I don't think we really need to do this -- this is only used
> as an assert(), and the code is in any case going to exercise
> all the methods. Plus we only implement these methods in a couple
> of classes and we don't expect a lot of new implementations of them.
> 
>> +
>> +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);
> 
> I think I understand what this assert is for, but a slightly
> expanded comment would help.

is this better ?
/*
 * We limit the count to an arbitrary "big" value. The value is big
 * enough not to be triggered nominally.
 * The assert will catch a cycle in the reset tree which triggers
 * an infinite loop.
 */

Maybe I should drop the macro here, it will be more explicit.

> 
>> +
>> +    /*
>> +     * 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
> 
> "resettable"
> 
>> + * to interact with it.
> 
> I think we could improve this comment to be a bit clearer about
> who we expect to implement which methods. Something like:
> 
> /*
>  * 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:
>  *  - a reset count, which is incremented when the reset operation
>  *    starts and decremented when it finishes
>  *  - a 'cold' flag, which tracks whether the in-progress reset is
>  *    a warm reset or a cold reset
>  *  - a 'hold_needed' flag, which indicates that we need to
>  *    invoke the 'hold' phase handler for this object
>  * The base class implementation of the interface provides this
>  * state and implements the associated methods: set_cold,
>  * set_hold_needed, get_count, increment_count and decrement_count.
>  *
>  * 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_reset_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 functions resettable_assert_reset(),
>  * resettable_deassert_reset() or resettable_reset().
> 
> 
>> + *
>> + * @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.
> 
> This says that phases.init takes a 'cold' argument, but the prototype
> doesn't have one.

I finally removed the argument and forgot to change this bit.

> 
> "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."
> 
>> + *
>> + * @phases.hold: side-effects action on others objects due to staying in a
>> + * resetting state.
> 
> "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: leave the reset state, may do side-effects action on others
>> + * objects.
> 
> "This phase is called when the object leaves the reset state. Actions
> affecting other objects are permitted."
> 
>> + *
>> + * @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.
> 
> "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."
> 
>> + */
>> +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:
> 
> "Put the object into reset, and hold it there until
> the caller later calls resettable_deassert_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.
> 
> "This will reset the specified object and all of its 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:
> 
> "Take the object out of 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
> 
> "to calling"
> 
>> + * @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
> 
> "overridden"
> 
>> + * override a subset of phases.
>> + */
>> +void resettable_class_set_parent_reset_phases(ResettableClass *rc,
>> +                                              ResettableInitPhase init,
>> +                                              ResettableHoldPhase hold,
>> +                                              ResettableExitPhase exit,
>> +                                              ResettablePhases 
>> *parent_phases);
>> +
>> +#endif
> 
> This function name is quite long -- I think we could reasonably
> call it resettable_class_set_parent_phases().
> 
> thanks
> -- PMM
> 



reply via email to

[Prev in Thread] Current Thread [Next in Thread]