diff options
Diffstat (limited to 'system/qdev-monitor.c')
-rw-r--r-- | system/qdev-monitor.c | 201 |
1 files changed, 120 insertions, 81 deletions
diff --git a/system/qdev-monitor.c b/system/qdev-monitor.c index 6af6ef7..5588ed2 100644 --- a/system/qdev-monitor.c +++ b/system/qdev-monitor.c @@ -22,13 +22,14 @@ #include "monitor/hmp.h" #include "monitor/monitor.h" #include "monitor/qdev.h" -#include "sysemu/arch_init.h" +#include "system/arch_init.h" +#include "system/runstate.h" #include "qapi/error.h" #include "qapi/qapi-commands-qdev.h" -#include "qapi/qmp/dispatch.h" -#include "qapi/qmp/qdict.h" +#include "qapi/qmp-registry.h" +#include "qobject/qdict.h" #include "qapi/qmp/qerror.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qstring.h" #include "qapi/qobject-input-visitor.h" #include "qemu/config-file.h" #include "qemu/error-report.h" @@ -36,7 +37,7 @@ #include "qemu/option.h" #include "qemu/qemu-print.h" #include "qemu/option_int.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "migration/misc.h" #include "qemu/cutils.h" #include "hw/qdev-properties.h" @@ -55,12 +56,18 @@ typedef struct QDevAlias } QDevAlias; /* default virtio transport per architecture */ -#define QEMU_ARCH_VIRTIO_PCI (QEMU_ARCH_ALPHA | QEMU_ARCH_ARM | \ - QEMU_ARCH_HPPA | QEMU_ARCH_I386 | \ - QEMU_ARCH_MIPS | QEMU_ARCH_PPC | \ - QEMU_ARCH_RISCV | QEMU_ARCH_SH4 | \ - QEMU_ARCH_SPARC | QEMU_ARCH_XTENSA | \ - QEMU_ARCH_LOONGARCH) +#define QEMU_ARCH_VIRTIO_PCI (QEMU_ARCH_ALPHA | \ + QEMU_ARCH_ARM | \ + QEMU_ARCH_HPPA | \ + QEMU_ARCH_I386 | \ + QEMU_ARCH_LOONGARCH | \ + QEMU_ARCH_MIPS | \ + QEMU_ARCH_OPENRISC | \ + QEMU_ARCH_PPC | \ + QEMU_ARCH_RISCV | \ + QEMU_ARCH_SH4 | \ + QEMU_ARCH_SPARC | \ + QEMU_ARCH_XTENSA) #define QEMU_ARCH_VIRTIO_CCW (QEMU_ARCH_S390X) #define QEMU_ARCH_VIRTIO_MMIO (QEMU_ARCH_M68K) @@ -125,7 +132,7 @@ static const char *qdev_class_get_alias(DeviceClass *dc) for (i = 0; qdev_alias_table[i].typename; i++) { if (qdev_alias_table[i].arch_mask && - !(qdev_alias_table[i].arch_mask & arch_type)) { + !qemu_arch_available(qdev_alias_table[i].arch_mask)) { continue; } @@ -211,7 +218,7 @@ static const char *find_typename_by_alias(const char *alias) for (i = 0; qdev_alias_table[i].alias; i++) { if (qdev_alias_table[i].arch_mask && - !(qdev_alias_table[i].arch_mask & arch_type)) { + !qemu_arch_available(qdev_alias_table[i].arch_mask)) { continue; } @@ -256,8 +263,7 @@ static DeviceClass *qdev_get_device_class(const char **driver, Error **errp) } dc = DEVICE_CLASS(oc); - if (!dc->user_creatable || - (phase_check(PHASE_MACHINE_READY) && !dc->hotpluggable)) { + if (!dc->user_creatable) { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "driver", "a pluggable device type"); return NULL; @@ -341,7 +347,7 @@ static Object *qdev_get_peripheral(void) static Object *dev; if (dev == NULL) { - dev = container_get(qdev_get_machine(), "/peripheral"); + dev = machine_get_container("peripheral"); } return dev; @@ -352,7 +358,7 @@ static Object *qdev_get_peripheral_anon(void) static Object *dev; if (dev == NULL) { - dev = container_get(qdev_get_machine(), "/peripheral-anon"); + dev = machine_get_container("peripheral-anon"); } return dev; @@ -624,6 +630,7 @@ DeviceState *qdev_device_add_from_qdict(const QDict *opts, char *id; DeviceState *dev = NULL; BusState *bus = NULL; + QDict *properties; driver = qdict_get_try_str(opts, "driver"); if (!driver) { @@ -668,12 +675,7 @@ DeviceState *qdev_device_add_from_qdict(const QDict *opts, return NULL; } - if (phase_check(PHASE_MACHINE_READY) && bus && !qbus_is_hotpluggable(bus)) { - error_setg(errp, "Bus '%s' does not support hotplugging", bus->name); - return NULL; - } - - if (!migration_is_idle()) { + if (migration_is_running()) { error_setg(errp, "device_add not allowed while migrating"); return NULL; } @@ -682,17 +684,9 @@ DeviceState *qdev_device_add_from_qdict(const QDict *opts, dev = qdev_new(driver); /* Check whether the hotplug is allowed by the machine */ - if (phase_check(PHASE_MACHINE_READY)) { - if (!qdev_hotplug_allowed(dev, errp)) { - goto err_del_dev; - } - - if (!bus && !qdev_get_machine_hotplug_handler(dev)) { - /* No bus, no machine hotplug handler --> device is not hotpluggable */ - error_setg(errp, "Device '%s' can not be hotplugged on this machine", - driver); - goto err_del_dev; - } + if (phase_check(PHASE_MACHINE_READY) && + !qdev_hotplug_allowed(dev, bus, errp)) { + goto err_del_dev; } /* @@ -705,13 +699,14 @@ DeviceState *qdev_device_add_from_qdict(const QDict *opts, } /* set properties */ - dev->opts = qdict_clone_shallow(opts); - qdict_del(dev->opts, "driver"); - qdict_del(dev->opts, "bus"); - qdict_del(dev->opts, "id"); + properties = qdict_clone_shallow(opts); + qdict_del(properties, "driver"); + qdict_del(properties, "bus"); + qdict_del(properties, "id"); - object_set_properties_from_keyval(&dev->parent_obj, dev->opts, from_json, + object_set_properties_from_keyval(&dev->parent_obj, properties, from_json, errp); + qobject_unref(properties); if (*errp) { goto err_del_dev; } @@ -745,19 +740,18 @@ DeviceState *qdev_device_add(QemuOpts *opts, Error **errp) #define qdev_printf(fmt, ...) monitor_printf(mon, "%*s" fmt, indent, "", ## __VA_ARGS__) -static void qdev_print_props(Monitor *mon, DeviceState *dev, Property *props, +static void qdev_print_props(Monitor *mon, DeviceState *dev, DeviceClass *dc, int indent) { - if (!props) - return; - for (; props->name; props++) { + for (int i = 0, n = dc->props_count_; i < n; ++i) { + const Property *prop = &dc->props_[i]; char *value; - char *legacy_name = g_strdup_printf("legacy-%s", props->name); + char *legacy_name = g_strdup_printf("legacy-%s", prop->name); if (object_property_get_type(OBJECT(dev), legacy_name, NULL)) { value = object_property_get_str(OBJECT(dev), legacy_name, NULL); } else { - value = object_property_print(OBJECT(dev), props->name, true, + value = object_property_print(OBJECT(dev), prop->name, true, NULL); } g_free(legacy_name); @@ -765,7 +759,7 @@ static void qdev_print_props(Monitor *mon, DeviceState *dev, Property *props, if (!value) { continue; } - qdev_printf("%s = %s\n", props->name, + qdev_printf("%s = %s\n", prop->name, *value ? value : "<null>"); g_free(value); } @@ -805,7 +799,7 @@ static void qdev_print(Monitor *mon, DeviceState *dev, int indent) } class = object_get_class(OBJECT(dev)); do { - qdev_print_props(mon, dev, DEVICE_CLASS(class)->props_, indent); + qdev_print_props(mon, dev, DEVICE_CLASS(class), indent); class = object_class_get_parent(class); } while (class != object_class_by_name(TYPE_DEVICE)); bus_print_dev(dev->parent_bus, mon, dev, indent); @@ -849,18 +843,9 @@ void hmp_info_qdm(Monitor *mon, const QDict *qdict) void qmp_device_add(QDict *qdict, QObject **ret_data, Error **errp) { - QemuOpts *opts; DeviceState *dev; - opts = qemu_opts_from_qdict(qemu_find_opts("device"), qdict, errp); - if (!opts) { - return; - } - if (!monitor_cur_is_qmp() && qdev_device_help(opts)) { - qemu_opts_del(opts); - return; - } - dev = qdev_device_add(opts, errp); + dev = qdev_device_add_from_qdict(qdict, true, errp); if (!dev) { /* * Drain all pending RCU callbacks. This is done because @@ -872,20 +857,24 @@ void qmp_device_add(QDict *qdict, QObject **ret_data, Error **errp) * to the user */ drain_call_rcu(); - - qemu_opts_del(opts); - return; } object_unref(OBJECT(dev)); } -static DeviceState *find_device_state(const char *id, Error **errp) +/* + * Note that creating new APIs using error classes other than GenericError is + * not recommended. Set use_generic_error=true for new interfaces. + */ +static DeviceState *find_device_state(const char *id, bool use_generic_error, + Error **errp) { Object *obj = object_resolve_path_at(qdev_get_peripheral(), id); DeviceState *dev; if (!obj) { - error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, + error_set(errp, + (use_generic_error ? + ERROR_CLASS_GENERIC_ERROR : ERROR_CLASS_DEVICE_NOT_FOUND), "Device '%s' not found", id); return NULL; } @@ -901,28 +890,15 @@ static DeviceState *find_device_state(const char *id, Error **errp) void qdev_unplug(DeviceState *dev, Error **errp) { - DeviceClass *dc = DEVICE_GET_CLASS(dev); HotplugHandler *hotplug_ctrl; HotplugHandlerClass *hdc; Error *local_err = NULL; - if (qdev_unplug_blocked(dev, errp)) { - return; - } - - if (dev->parent_bus && !qbus_is_hotpluggable(dev->parent_bus)) { - error_setg(errp, "Bus '%s' does not support hotplugging", - dev->parent_bus->name); - return; - } - - if (!dc->hotpluggable) { - error_setg(errp, "Device '%s' does not support hotplugging", - object_get_typename(OBJECT(dev))); + if (!qdev_hotunplug_allowed(dev, errp)) { return; } - if (!migration_is_idle() && !dev->allow_unplug_during_migration) { + if (migration_is_running() && !dev->allow_unplug_during_migration) { error_setg(errp, "device_del not allowed while migrating"); return; } @@ -950,7 +926,7 @@ void qdev_unplug(DeviceState *dev, Error **errp) void qmp_device_del(const char *id, Error **errp) { - DeviceState *dev = find_device_state(id, errp); + DeviceState *dev = find_device_state(id, false, errp); if (dev != NULL) { if (dev->pending_deleted_event && (dev->pending_deleted_expires_ms == 0 || @@ -964,11 +940,74 @@ void qmp_device_del(const char *id, Error **errp) } } +int qdev_sync_config(DeviceState *dev, Error **errp) +{ + DeviceClass *dc = DEVICE_GET_CLASS(dev); + + if (!dc->sync_config) { + error_setg(errp, "device-sync-config is not supported for '%s'", + object_get_typename(OBJECT(dev))); + return -ENOTSUP; + } + + return dc->sync_config(dev, errp); +} + +void qmp_device_sync_config(const char *id, Error **errp) +{ + DeviceState *dev; + + /* + * During migration there is a race between syncing`configuration + * and migrating it (if migrate first, that target would get + * outdated version), so let's just not allow it. + */ + + if (migration_is_running()) { + error_setg(errp, "Config synchronization is not allowed " + "during migration"); + return; + } + + dev = find_device_state(id, true, errp); + if (!dev) { + return; + } + + qdev_sync_config(dev, errp); +} + void hmp_device_add(Monitor *mon, const QDict *qdict) { Error *err = NULL; + QemuOpts *opts; + DeviceState *dev; + + opts = qemu_opts_from_qdict(qemu_find_opts("device"), qdict, &err); + if (!opts) { + goto out; + } + if (qdev_device_help(opts)) { + qemu_opts_del(opts); + return; + } + dev = qdev_device_add(opts, &err); + if (!dev) { + /* + * Drain all pending RCU callbacks. This is done because + * some bus related operations can delay a device removal + * (in this case this can happen if device is added and then + * removed due to a configuration error) + * to a RCU callback, but user might expect that this interface + * will finish its job completely once qmp command returns result + * to the user + */ + drain_call_rcu(); - qmp_device_add((QDict *)qdict, NULL, &err); + qemu_opts_del(opts); + } + object_unref(dev); +out: hmp_handle_error(mon, err); } @@ -1034,7 +1073,7 @@ static GSList *qdev_build_hotpluggable_device_list(Object *peripheral) static void peripheral_device_del_completion(ReadLineState *rs, const char *str) { - Object *peripheral = container_get(qdev_get_machine(), "/peripheral"); + Object *peripheral = machine_get_container("peripheral"); GSList *list, *item; list = qdev_build_hotpluggable_device_list(peripheral); @@ -1070,7 +1109,7 @@ BlockBackend *blk_by_qdev_id(const char *id, Error **errp) GLOBAL_STATE_CODE(); - dev = find_device_state(id, errp); + dev = find_device_state(id, false, errp); if (dev == NULL) { return NULL; } |