For PCIe and SHPC hotplug it's important to track led indicators, especially the power led. Add an event that helps.
This commits adds infrastructure for HOTPLUG_STATE event and corresponding query command. The implementation for PCIe and SHPC will follow in further commits. Signed-off-by: Vladimir Sementsov-Ogievskiy <vsement...@yandex-team.ru> --- qapi/qdev.json | 175 +++++++++++++++++++++++++++++++++++++++++ include/hw/hotplug.h | 12 +++ include/monitor/qdev.h | 5 ++ hw/core/hotplug.c | 13 +++ softmmu/qdev-monitor.c | 50 ++++++++++++ 5 files changed, 255 insertions(+) diff --git a/qapi/qdev.json b/qapi/qdev.json index 135cd81586..6f2d8d6647 100644 --- a/qapi/qdev.json +++ b/qapi/qdev.json @@ -173,3 +173,178 @@ # ## { 'event': 'DEVICE_UNPLUG_GUEST_ERROR', 'data': 'DeviceAndPath' } + +## +# @LedActivity: +# +# Three-state led indicator state. +# +# @on: Indicator is on. +# +# @blink: Indicator is blinking. +# +# @off: Indicator is off. +# +# Since: 8.0 +## +{ 'enum': 'LedActivity', + 'data': [ 'on', 'blink', 'off' ] } + +## +# @HotplugSHPCSlotState: +# +# Standard Hot-Plug Controller slot state. +# +# @power-only: Slot is powered on but neither clock nor bus are connected. +# +# @enabled: Slot is powered on, clock and bus are connected, the card is +# fully functional from a hardware standpoint. +# +# @disabled: Slot is disabled, card is safe to be removed. +# +# Since: 8.0 +## +{ 'enum': 'HotplugSHPCSlotState', + 'data': [ 'power-only', 'enabled', 'disabled' ] } + +## +# @HotplugBaseState: +# +# Base structure for SHPC and PCIe-native hotplug. +# +# @power-led: Power indicator. When power indicator is on the device is +# ready and accepted by guest. Off status means that device +# is safe to remove and blinking is an intermediate state of +# hot-plug or hot-unplug. +# +# @attention-led: Attention indicator. Off status means normal operation, +# On signals about operational problem, Blinking is for +# locating the slot. +# +# Since: 8.0 +## +{ 'struct': 'HotplugBaseState', + 'data': { '*power-led': 'LedActivity', + '*attention-led': 'LedActivity' } } + +## +# @HotplugSHPCState: +# +# Standard Hot Plug Controller state. +# +# @slot-state: The slot state field of Slot Status. +# +# Since: 8.0 +## +{ 'struct': 'HotplugSHPCState', + 'base': 'HotplugBaseState', + 'data': { '*slot-state': 'HotplugSHPCSlotState' } } + +## +# @HotplugPCIeNativeState: +# +# PCIe Native hotplug slot state. +# +# @power-on: PCIe Power Controller Control of Slot Control Register. +# True means Power On (Power Controller Control bit is 0), +# False means Power Off (Power Controller Control bit is 1). +# +# Since: 8.0 +## +{ 'struct': 'HotplugPCIeNativeState', + 'base': 'HotplugBaseState', + 'data': { '*power-on': 'bool' } } + +## +# @HotplugType: +# +# Type of hotplug controller / provider. +# +# @shpc: Standard Hot Plug Controller +# +# @pcie-native: PCIe Native hotplug +# +# Since: 8.0 +## +{ 'enum': 'HotplugType', + 'data': ['shpc', 'pcie-native'] } + +## +# @HotplugState: +# +# Generic hotplug slot state. +# +# @type: type of the hotplug (shpc or pcie-native) +# +# Single: 8.0 +## +{ 'union': 'HotplugState', + 'base': { 'type': 'HotplugType' }, + 'discriminator': 'type', + 'data': { 'shpc': 'HotplugSHPCState', + 'pcie-native': 'HotplugPCIeNativeState' } } + +## +# @HotplugBase: +# +# @bus: The QOM path of the parent bus where device is hotplugged. +# +# @addr: The bus address for hotplugged device if applicable. +# +# @child: the hotplugged device's QOM path. The field absent if +# there is no device at the moment. +# +# Since: 8.0 +## +{ 'struct': 'HotplugBase', + 'data': { 'bus': 'DeviceAndPath', + '*addr': 'str', + 'child': 'DeviceAndPath' } } + +## +# @HotplugEvent: +# +# @changed: The hotplug controller states being changed. The state +# fields that are not changed are not included into @changed. +# +# Since: 8.0 +## +{ 'struct': 'HotplugEvent', + 'base': 'HotplugBase', + 'data': { 'changed': 'HotplugState' } } + +## +# @HotplugInfo: +# +# @state: Current hotplug controller state. All applicable fields are +# included into @state. +# +# Since: 8.0 +## +{ 'struct': 'HotplugInfo', + 'base': 'HotplugBase', + 'data': { 'state': 'HotplugState' } } + +## +# @HOTPLUG_STATE: +# +# Emitted whenever the state of hotplug controller changes. +# Only changed values are included into event. Any change of any field +# of the state trigger the event. Several fields are included into one +# event if they are changed simultaneously. +# +# Since: 8.0 +## +{ 'event': 'HOTPLUG_STATE', + 'data': 'HotplugEvent' } + +## +# @query-hotplug: +# +# Query the state of hotplug controller. +# +# Since: 8.0 +## +{ 'command': 'query-hotplug', + 'data': { 'id': 'str' }, + 'returns': 'HotplugInfo' } diff --git a/include/hw/hotplug.h b/include/hw/hotplug.h index e15f59c8b3..b82261d91e 100644 --- a/include/hw/hotplug.h +++ b/include/hw/hotplug.h @@ -13,6 +13,7 @@ #define HOTPLUG_H #include "qom/object.h" +#include "qapi/qapi-types-qdev.h" #define TYPE_HOTPLUG_HANDLER "hotplug-handler" @@ -58,6 +59,8 @@ struct HotplugHandlerClass { hotplug_fn plug; hotplug_fn unplug_request; hotplug_fn unplug; + HotplugInfo *(*get_hotplug_state)(HotplugHandler *plug_handler, + DeviceState *plugged_dev, Error **errp); }; /** @@ -94,4 +97,13 @@ void hotplug_handler_unplug_request(HotplugHandler *plug_handler, void hotplug_handler_unplug(HotplugHandler *plug_handler, DeviceState *plugged_dev, Error **errp); + +/** + * hotplug_handler_get_hotplug_state: + * + * Calls #HotplugHandlerClass.get_hotplug_state callback of @plug_handler. + */ +HotplugInfo *hotplug_handler_get_hotplug_state(HotplugHandler *plug_handler, + DeviceState *plugged_dev, + Error **errp); #endif diff --git a/include/monitor/qdev.h b/include/monitor/qdev.h index 1d57bf6577..a61d5f8f1f 100644 --- a/include/monitor/qdev.h +++ b/include/monitor/qdev.h @@ -1,6 +1,8 @@ #ifndef MONITOR_QDEV_H #define MONITOR_QDEV_H +#include "qapi/qapi-types-qdev.h" + /*** monitor commands ***/ void hmp_info_qtree(Monitor *mon, const QDict *qdict); @@ -36,4 +38,7 @@ DeviceState *qdev_device_add_from_qdict(const QDict *opts, */ const char *qdev_set_id(DeviceState *dev, char *id, Error **errp); +void qdev_hotplug_state_event(DeviceState *bus, const char *addr, + DeviceState *child, HotplugState *changed_state); + #endif diff --git a/hw/core/hotplug.c b/hw/core/hotplug.c index 17ac986685..08d6d03760 100644 --- a/hw/core/hotplug.c +++ b/hw/core/hotplug.c @@ -57,6 +57,19 @@ void hotplug_handler_unplug(HotplugHandler *plug_handler, } } +HotplugInfo *hotplug_handler_get_hotplug_state(HotplugHandler *plug_handler, + DeviceState *plugged_dev, + Error **errp) +{ + HotplugHandlerClass *hdc = HOTPLUG_HANDLER_GET_CLASS(plug_handler); + + if (hdc->get_hotplug_state) { + return hdc->get_hotplug_state(plug_handler, plugged_dev, errp); + } + + return NULL; +} + static const TypeInfo hotplug_handler_info = { .name = TYPE_HOTPLUG_HANDLER, .parent = TYPE_INTERFACE, diff --git a/softmmu/qdev-monitor.c b/softmmu/qdev-monitor.c index b8d2c4dadd..d1cc770d11 100644 --- a/softmmu/qdev-monitor.c +++ b/softmmu/qdev-monitor.c @@ -25,6 +25,7 @@ #include "sysemu/arch_init.h" #include "qapi/error.h" #include "qapi/qapi-commands-qdev.h" +#include "qapi/qapi-events-qdev.h" #include "qapi/qmp/dispatch.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qerror.h" @@ -956,6 +957,36 @@ void qmp_device_del(const char *id, Error **errp) } } +HotplugInfo *qmp_query_hotplug(const char *id, Error **errp) +{ + DeviceState *dev = find_device_state(id, errp); + HotplugHandler *hotplug_ctrl; + + if (!dev) { + return NULL; + } + + if (dev->parent_bus && !qbus_is_hotpluggable(dev->parent_bus)) { + error_setg(errp, QERR_BUS_NO_HOTPLUG, dev->parent_bus->name); + return NULL; + } + + if (!DEVICE_GET_CLASS(dev)->hotpluggable) { + error_setg(errp, QERR_DEVICE_NO_HOTPLUG, + object_get_typename(OBJECT(dev))); + return NULL; + } + + hotplug_ctrl = qdev_get_hotplug_handler(dev); + /* + * hotpluggable device MUST have HotplugHandler, if it doesn't + * then something is very wrong with it. + */ + g_assert(hotplug_ctrl); + + return hotplug_handler_get_hotplug_state(hotplug_ctrl, dev, errp); +} + void hmp_device_add(Monitor *mon, const QDict *qdict) { Error *err = NULL; @@ -1146,3 +1177,22 @@ bool qmp_command_available(const QmpCommand *cmd, Error **errp) } return true; } + +void qdev_hotplug_state_event(DeviceState *bus, const char *addr, + DeviceState *child, HotplugState *changed_state) +{ + DeviceAndPath child_desc, bus_desc = { + .device = bus->id, + .path = bus->canonical_path, + }; + + if (child) { + child_desc = (DeviceAndPath) { + .device = child->id, + .path = child->canonical_path, + }; + } + + qapi_event_send_hotplug_state(&bus_desc, addr, child ? &child_desc : NULL, + changed_state); +} -- 2.34.1