This add Resettable interface implementation for both Bus and Device. The init phase default implementation is to call the legacy reset handler.
Add a *resetting* counter in both Device and Bus. This counter allows us to handle reentrancy in reset operation. If a device/bus has multiple source triggering reset, theses sources do not have to care about the current device/bus resetting state. Each source can independantly assert and eventually deassert the reset. The device/bus will behave as expected and gets out-of-reset state only when all sources have deasserted the reset. Signed-off-by: Damien Hedde <damien.he...@greensocs.com> --- hw/core/bus.c | 60 +++++++++++++++++++++++++++++++++++++++++ hw/core/qdev.c | 61 ++++++++++++++++++++++++++++++++++++++++++ include/hw/qdev-core.h | 30 +++++++++++++++++++++ 3 files changed, 151 insertions(+) diff --git a/hw/core/bus.c b/hw/core/bus.c index e09843f6ab..0a60bb4b24 100644 --- a/hw/core/bus.c +++ b/hw/core/bus.c @@ -21,6 +21,7 @@ #include "qemu-common.h" #include "hw/qdev.h" #include "qapi/error.h" +#include "hw/resettable.h" void qbus_set_hotplug_handler(BusState *bus, Object *handler, Error **errp) { @@ -67,6 +68,56 @@ int qbus_walk_children(BusState *bus, return 0; } +void qbus_reset(BusState *bus, bool cold) +{ + resettable_reset(OBJECT(bus), cold); +} + +bool qbus_is_resetting(BusState *bus) +{ + return (bus->resetting != 0); +} + +static void bus_reset_init_phase(Object *obj, bool cold) +{ + BusState *bus = BUS(obj); + BusClass *bc = BUS_GET_CLASS(obj); + BusChild *kid; + + QTAILQ_FOREACH(kid, &bus->children, sibling) { + resettable_init_phase(OBJECT(kid->child), cold); + } + + bus->resetting += 1; + + if (bc->reset) { + bc->reset(bus); + } +} + +static void bus_reset_hold_phase(Object *obj) +{ + BusState *bus = BUS(obj); + BusChild *kid; + + QTAILQ_FOREACH(kid, &bus->children, sibling) { + resettable_hold_phase(OBJECT(kid->child)); + } +} + +static void bus_reset_exit_phase(Object *obj) +{ + BusState *bus = BUS(obj); + BusChild *kid; + + QTAILQ_FOREACH(kid, &bus->children, sibling) { + resettable_exit_phase(OBJECT(kid->child)); + } + + assert(bus->resetting > 0); + bus->resetting -= 1; +} + static void qbus_realize(BusState *bus, DeviceState *parent, const char *name) { const char *typename = object_get_typename(OBJECT(bus)); @@ -204,9 +255,14 @@ static char *default_bus_get_fw_dev_path(DeviceState *dev) 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_reset_init_phase; + rc->phases.hold = bus_reset_hold_phase; + rc->phases.exit = bus_reset_exit_phase; } static void qbus_finalize(Object *obj) @@ -225,6 +281,10 @@ static const TypeInfo bus_info = { .instance_init = qbus_initfn, .instance_finalize = qbus_finalize, .class_init = bus_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_RESETTABLE }, + { } + }, }; static void bus_register_types(void) diff --git a/hw/core/qdev.c b/hw/core/qdev.c index f9b6efe509..98d173f34f 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -37,6 +37,7 @@ #include "hw/hotplug.h" #include "hw/boards.h" #include "hw/sysbus.h" +#include "hw/resettable.h" bool qdev_hotplug = false; static bool qdev_hot_added = false; @@ -254,6 +255,56 @@ HotplugHandler *qdev_get_hotplug_handler(DeviceState *dev) return hotplug_ctrl; } +void qdev_reset(DeviceState *dev, bool cold) +{ + resettable_reset(OBJECT(dev), cold); +} + +bool qdev_is_resetting(DeviceState *dev) +{ + return (dev->resetting != 0); +} + +static void device_reset_init_phase(Object *obj, bool cold) +{ + DeviceState *dev = DEVICE(obj); + DeviceClass *dc = DEVICE_GET_CLASS(obj); + BusState *child; + + QLIST_FOREACH(child, &dev->child_bus, sibling) { + resettable_init_phase(OBJECT(child), cold); + } + + dev->resetting += 1; + + if (dc->reset) { + dc->reset(dev); + } +} + +static void device_reset_hold_phase(Object *obj) +{ + DeviceState *dev = DEVICE(obj); + BusState *child; + + QLIST_FOREACH(child, &dev->child_bus, sibling) { + resettable_hold_phase(OBJECT(child)); + } +} + +static void device_reset_exit_phase(Object *obj) +{ + DeviceState *dev = DEVICE(obj); + BusState *child; + + QLIST_FOREACH(child, &dev->child_bus, sibling) { + resettable_exit_phase(OBJECT(child)); + } + + assert(dev->resetting > 0); + dev->resetting -= 1; +} + static int qdev_reset_one(DeviceState *dev, void *opaque) { device_reset(dev); @@ -954,6 +1005,7 @@ static void device_initfn(Object *obj) dev->instance_id_alias = -1; dev->realized = false; + dev->resetting = 0; object_property_add_bool(obj, "realized", device_get_realized, device_set_realized, NULL); @@ -1049,6 +1101,7 @@ static void device_unparent(Object *obj) static void device_class_init(ObjectClass *class, void *data) { DeviceClass *dc = DEVICE_CLASS(class); + ResettableClass *rc = RESETTABLE_CLASS(class); class->unparent = device_unparent; @@ -1060,6 +1113,10 @@ static void device_class_init(ObjectClass *class, void *data) */ dc->hotpluggable = true; dc->user_creatable = true; + + rc->phases.init = device_reset_init_phase; + rc->phases.hold = device_reset_hold_phase; + rc->phases.exit = device_reset_exit_phase; } void device_class_set_parent_reset(DeviceClass *dc, @@ -1117,6 +1174,10 @@ static const TypeInfo device_type_info = { .class_init = device_class_init, .abstract = true, .class_size = sizeof(DeviceClass), + .interfaces = (InterfaceInfo[]) { + { TYPE_RESETTABLE }, + { } + }, }; static void qdev_register_types(void) diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h index 33ed3b8dde..f4aa8dbaa2 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -131,6 +131,8 @@ struct NamedGPIOList { /** * DeviceState: * @realized: Indicates whether the device has been fully constructed. + * @resetting: Indicates whether the device is under reset. Also + * used to count how many times reset has been initiated on the device. * * This structure should not be accessed directly. We declare it here * so that it can be embedded in individual device state structures. @@ -152,6 +154,7 @@ struct DeviceState { int num_child_bus; int instance_id_alias; int alias_required_for_version; + uint32_t resetting; }; struct DeviceListener { @@ -198,6 +201,8 @@ typedef struct BusChild { /** * BusState: * @hotplug_handler: link to a hotplug handler associated with bus. + * @resetting: Indicates whether the device is under reset. Also + * used to count how many times reset has been initiated on the device. */ struct BusState { Object obj; @@ -209,6 +214,7 @@ struct BusState { int num_children; QTAILQ_HEAD(, BusChild) children; QLIST_ENTRY(BusState) sibling; + uint32_t resetting; }; /** @@ -378,6 +384,30 @@ int qdev_walk_children(DeviceState *dev, qdev_walkerfn *post_devfn, qbus_walkerfn *post_busfn, void *opaque); +/** + * @qdev_reset: + * Reset the device @dev, @cold tell whether to do a cold or warm reset. + */ +void qdev_reset(DeviceState *dev, bool cold); + +/** + * @qbus_reset: + * Reset the bus @bus, @cold tell whether to do a cold or warm reset. + */ +void qbus_reset(BusState *bus, bool cold); + +/** + * @qdev_is_resetting: + * Tell whether the device @dev is currently under reset. + */ +bool qdev_is_resetting(DeviceState *dev); + +/** + * @qbus_is_resetting: + * Tell whether the bus @bus is currently under reset. + */ +bool qbus_is_resetting(BusState *bus); + void qdev_reset_all(DeviceState *dev); void qdev_reset_all_fn(void *opaque); -- 2.21.0