aboutsummaryrefslogtreecommitdiff
path: root/system/qdev-monitor.c
diff options
context:
space:
mode:
Diffstat (limited to 'system/qdev-monitor.c')
-rw-r--r--system/qdev-monitor.c201
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;
}