qemu with hax to log dma reads & writes jcs.org/2018/11/12/vfio

Merge remote-tracking branch 'remotes/elmarco/tags/dbus-vmstate7-pull-request' into staging

Add dbus-vmstate

Hi,

With external processes or helpers participating to the VM support, it
becomes necessary to handle their migration. Various options exist to
transfer their state:
1) as the VM memory, RAM or devices (we could say that's how
vhost-user devices can be handled today, they are expected to
restore from ring state)
2) other "vmstate" (as with TPM emulator state blobs)
3) left to be handled by management layer

1) is not practical, since an external processes may legitimatelly
need arbitrary state date to back a device or a service, or may not
even have an associated device.

2) needs ad-hoc code for each helper, but is simple and working

3) is complicated for management layer, QEMU has the migration timing

The proposed "dbus-vmstate" object will connect to a given D-Bus
address, and save/load from org.qemu.VMState1 owners on migration.

Thus helpers can easily have their state migrated with QEMU, without
implementing ad-hoc support (such as done for TPM emulation)

D-Bus is ubiquitous on Linux (it is systemd IPC), and can be made to
work on various other OSes. There are several implementations and good
bindings for various languages. (the tests/dbus-vmstate-test.c is a
good example of how simple the implementation of services can be, even
in C)

dbus-vmstate is put into use by the libvirt series "[PATCH 00/23] Use
a slirp helper process".

v2:
- fix build with broken mingw-glib

# gpg: Signature made Mon 06 Jan 2020 14:43:35 GMT
# gpg: using RSA key 87A9BD933F87C606D276F62DDAE8E10975969CE5
# gpg: issuer "marcandre.lureau@redhat.com"
# gpg: Good signature from "Marc-André Lureau <marcandre.lureau@redhat.com>" [full]
# gpg: aka "Marc-André Lureau <marcandre.lureau@gmail.com>" [full]
# Primary key fingerprint: 87A9 BD93 3F87 C606 D276 F62D DAE8 E109 7596 9CE5

* remotes/elmarco/tags/dbus-vmstate7-pull-request:
tests: add dbus-vmstate-test
tests: add migration-helpers unit
dockerfiles: add dbus-daemon to some of latest distributions
configure: add GDBUS_CODEGEN
Add dbus-vmstate object
util: add dbus helper unit
docs: start a document to describe D-Bus usage
vmstate: replace DeviceState with VMStateIf
vmstate: add qom interface to get id

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>

+1660 -205
+12
MAINTAINERS
··· 2196 2196 M: Juan Quintela <quintela@redhat.com> 2197 2197 M: Dr. David Alan Gilbert <dgilbert@redhat.com> 2198 2198 S: Maintained 2199 + F: hw/core/vmstate-if.c 2200 + F: include/hw/vmstate-if.h 2199 2201 F: include/migration/ 2200 2202 F: migration/ 2201 2203 F: scripts/vmstate-static-checker.py ··· 2203 2205 F: tests/migration-test.c 2204 2206 F: docs/devel/migration.rst 2205 2207 F: qapi/migration.json 2208 + 2209 + D-Bus 2210 + M: Marc-André Lureau <marcandre.lureau@redhat.com> 2211 + S: Maintained 2212 + F: backends/dbus-vmstate.c 2213 + F: tests/dbus-vmstate* 2214 + F: util/dbus.c 2215 + F: include/qemu/dbus.h 2216 + F: docs/interop/dbus.rst 2217 + F: docs/interop/dbus-vmstate.rst 2206 2218 2207 2219 Seccomp 2208 2220 M: Eduardo Otubo <otubo@redhat.com>
+1
Makefile.objs
··· 128 128 trace-events-subdirs = 129 129 trace-events-subdirs += accel/kvm 130 130 trace-events-subdirs += accel/tcg 131 + trace-events-subdirs += backends 131 132 trace-events-subdirs += crypto 132 133 trace-events-subdirs += monitor 133 134 ifeq ($(CONFIG_USER_ONLY),y)
+4
backends/Makefile.objs
··· 17 17 common-obj-$(call land,$(CONFIG_VHOST_USER),$(CONFIG_VIRTIO)) += vhost-user.o 18 18 19 19 common-obj-$(CONFIG_LINUX) += hostmem-memfd.o 20 + 21 + common-obj-$(CONFIG_GIO) += dbus-vmstate.o 22 + dbus-vmstate.o-cflags = $(GIO_CFLAGS) 23 + dbus-vmstate.o-libs = $(GIO_LIBS)
+510
backends/dbus-vmstate.c
··· 1 + /* 2 + * QEMU dbus-vmstate 3 + * 4 + * Copyright (C) 2019 Red Hat Inc 5 + * 6 + * Authors: 7 + * Marc-André Lureau <marcandre.lureau@redhat.com> 8 + * 9 + * This work is licensed under the terms of the GNU GPL, version 2 or later. 10 + * See the COPYING file in the top-level directory. 11 + */ 12 + 13 + #include "qemu/osdep.h" 14 + #include "qemu/units.h" 15 + #include "qemu/dbus.h" 16 + #include "qemu/error-report.h" 17 + #include "qapi/error.h" 18 + #include "qom/object_interfaces.h" 19 + #include "qapi/qmp/qerror.h" 20 + #include "migration/vmstate.h" 21 + #include "trace.h" 22 + 23 + typedef struct DBusVMState DBusVMState; 24 + typedef struct DBusVMStateClass DBusVMStateClass; 25 + 26 + #define TYPE_DBUS_VMSTATE "dbus-vmstate" 27 + #define DBUS_VMSTATE(obj) \ 28 + OBJECT_CHECK(DBusVMState, (obj), TYPE_DBUS_VMSTATE) 29 + #define DBUS_VMSTATE_GET_CLASS(obj) \ 30 + OBJECT_GET_CLASS(DBusVMStateClass, (obj), TYPE_DBUS_VMSTATE) 31 + #define DBUS_VMSTATE_CLASS(klass) \ 32 + OBJECT_CLASS_CHECK(DBusVMStateClass, (klass), TYPE_DBUS_VMSTATE) 33 + 34 + struct DBusVMStateClass { 35 + ObjectClass parent_class; 36 + }; 37 + 38 + struct DBusVMState { 39 + Object parent; 40 + 41 + GDBusConnection *bus; 42 + char *dbus_addr; 43 + char *id_list; 44 + 45 + uint32_t data_size; 46 + uint8_t *data; 47 + }; 48 + 49 + static const GDBusPropertyInfo vmstate_property_info[] = { 50 + { -1, (char *) "Id", (char *) "s", 51 + G_DBUS_PROPERTY_INFO_FLAGS_READABLE, NULL }, 52 + }; 53 + 54 + static const GDBusPropertyInfo * const vmstate_property_info_pointers[] = { 55 + &vmstate_property_info[0], 56 + NULL 57 + }; 58 + 59 + static const GDBusInterfaceInfo vmstate1_interface_info = { 60 + -1, 61 + (char *) "org.qemu.VMState1", 62 + (GDBusMethodInfo **) NULL, 63 + (GDBusSignalInfo **) NULL, 64 + (GDBusPropertyInfo **) &vmstate_property_info_pointers, 65 + NULL, 66 + }; 67 + 68 + #define DBUS_VMSTATE_SIZE_LIMIT (1 * MiB) 69 + 70 + static GHashTable * 71 + get_id_list_set(DBusVMState *self) 72 + { 73 + g_auto(GStrv) ids = NULL; 74 + g_autoptr(GHashTable) set = NULL; 75 + int i; 76 + 77 + if (!self->id_list) { 78 + return NULL; 79 + } 80 + 81 + ids = g_strsplit(self->id_list, ",", -1); 82 + set = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); 83 + for (i = 0; ids[i]; i++) { 84 + g_hash_table_add(set, ids[i]); 85 + ids[i] = NULL; 86 + } 87 + 88 + return g_steal_pointer(&set); 89 + } 90 + 91 + static GHashTable * 92 + dbus_get_proxies(DBusVMState *self, GError **err) 93 + { 94 + g_autoptr(GHashTable) proxies = NULL; 95 + g_autoptr(GHashTable) ids = NULL; 96 + g_auto(GStrv) names = NULL; 97 + Error *error = NULL; 98 + size_t i; 99 + 100 + ids = get_id_list_set(self); 101 + proxies = g_hash_table_new_full(g_str_hash, g_str_equal, 102 + g_free, g_object_unref); 103 + 104 + names = qemu_dbus_get_queued_owners(self->bus, "org.qemu.VMState1", &error); 105 + if (!names) { 106 + g_set_error(err, G_IO_ERROR, G_IO_ERROR_FAILED, "%s", 107 + error_get_pretty(error)); 108 + error_free(error); 109 + return NULL; 110 + } 111 + 112 + for (i = 0; names[i]; i++) { 113 + g_autoptr(GDBusProxy) proxy = NULL; 114 + g_autoptr(GVariant) result = NULL; 115 + g_autofree char *id = NULL; 116 + size_t size; 117 + 118 + proxy = g_dbus_proxy_new_sync(self->bus, G_DBUS_PROXY_FLAGS_NONE, 119 + (GDBusInterfaceInfo *) &vmstate1_interface_info, 120 + names[i], 121 + "/org/qemu/VMState1", 122 + "org.qemu.VMState1", 123 + NULL, err); 124 + if (!proxy) { 125 + return NULL; 126 + } 127 + 128 + result = g_dbus_proxy_get_cached_property(proxy, "Id"); 129 + if (!result) { 130 + g_set_error_literal(err, G_IO_ERROR, G_IO_ERROR_FAILED, 131 + "VMState Id property is missing."); 132 + return NULL; 133 + } 134 + 135 + id = g_variant_dup_string(result, &size); 136 + if (ids && !g_hash_table_remove(ids, id)) { 137 + g_clear_pointer(&id, g_free); 138 + g_clear_object(&proxy); 139 + continue; 140 + } 141 + if (size == 0 || size >= 256) { 142 + g_set_error(err, G_IO_ERROR, G_IO_ERROR_FAILED, 143 + "VMState Id '%s' is invalid.", id); 144 + return NULL; 145 + } 146 + 147 + if (!g_hash_table_insert(proxies, id, proxy)) { 148 + g_set_error(err, G_IO_ERROR, G_IO_ERROR_FAILED, 149 + "Duplicated VMState Id '%s'", id); 150 + return NULL; 151 + } 152 + id = NULL; 153 + proxy = NULL; 154 + 155 + g_clear_pointer(&result, g_variant_unref); 156 + } 157 + 158 + if (ids) { 159 + g_autofree char **left = NULL; 160 + 161 + left = (char **)g_hash_table_get_keys_as_array(ids, NULL); 162 + if (*left) { 163 + g_autofree char *leftids = g_strjoinv(",", left); 164 + g_set_error(err, G_IO_ERROR, G_IO_ERROR_FAILED, 165 + "Required VMState Id are missing: %s", leftids); 166 + return NULL; 167 + } 168 + } 169 + 170 + return g_steal_pointer(&proxies); 171 + } 172 + 173 + static int 174 + dbus_load_state_proxy(GDBusProxy *proxy, const uint8_t *data, size_t size) 175 + { 176 + g_autoptr(GError) err = NULL; 177 + g_autoptr(GVariant) result = NULL; 178 + g_autoptr(GVariant) value = NULL; 179 + 180 + value = g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, 181 + data, size, sizeof(char)); 182 + result = g_dbus_proxy_call_sync(proxy, "Load", 183 + g_variant_new("(@ay)", 184 + g_steal_pointer(&value)), 185 + G_DBUS_CALL_FLAGS_NO_AUTO_START, 186 + -1, NULL, &err); 187 + if (!result) { 188 + error_report("%s: Failed to Load: %s", __func__, err->message); 189 + return -1; 190 + } 191 + 192 + return 0; 193 + } 194 + 195 + static int dbus_vmstate_post_load(void *opaque, int version_id) 196 + { 197 + DBusVMState *self = DBUS_VMSTATE(opaque); 198 + g_autoptr(GInputStream) m = NULL; 199 + g_autoptr(GDataInputStream) s = NULL; 200 + g_autoptr(GError) err = NULL; 201 + g_autoptr(GHashTable) proxies = NULL; 202 + uint32_t nelem; 203 + 204 + trace_dbus_vmstate_post_load(version_id); 205 + 206 + proxies = dbus_get_proxies(self, &err); 207 + if (!proxies) { 208 + error_report("%s: Failed to get proxies: %s", __func__, err->message); 209 + return -1; 210 + } 211 + 212 + m = g_memory_input_stream_new_from_data(self->data, self->data_size, NULL); 213 + s = g_data_input_stream_new(m); 214 + g_data_input_stream_set_byte_order(s, G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN); 215 + 216 + nelem = g_data_input_stream_read_uint32(s, NULL, &err); 217 + if (err) { 218 + goto error; 219 + } 220 + 221 + while (nelem > 0) { 222 + GDBusProxy *proxy = NULL; 223 + uint32_t len; 224 + gsize bytes_read, avail; 225 + char id[256]; 226 + 227 + len = g_data_input_stream_read_uint32(s, NULL, &err); 228 + if (err) { 229 + goto error; 230 + } 231 + if (len >= 256) { 232 + error_report("%s: Invalid DBus vmstate proxy name %u", 233 + __func__, len); 234 + return -1; 235 + } 236 + if (!g_input_stream_read_all(G_INPUT_STREAM(s), id, len, 237 + &bytes_read, NULL, &err)) { 238 + goto error; 239 + } 240 + g_return_val_if_fail(bytes_read == len, -1); 241 + id[len] = 0; 242 + 243 + trace_dbus_vmstate_loading(id); 244 + 245 + proxy = g_hash_table_lookup(proxies, id); 246 + if (!proxy) { 247 + error_report("%s: Failed to find proxy Id '%s'", __func__, id); 248 + return -1; 249 + } 250 + 251 + len = g_data_input_stream_read_uint32(s, NULL, &err); 252 + avail = g_buffered_input_stream_get_available( 253 + G_BUFFERED_INPUT_STREAM(s)); 254 + 255 + if (len > DBUS_VMSTATE_SIZE_LIMIT || len > avail) { 256 + error_report("%s: Invalid vmstate size: %u", __func__, len); 257 + return -1; 258 + } 259 + 260 + if (dbus_load_state_proxy(proxy, 261 + g_buffered_input_stream_peek_buffer(G_BUFFERED_INPUT_STREAM(s), 262 + NULL), 263 + len) < 0) { 264 + error_report("%s: Failed to restore Id '%s'", __func__, id); 265 + return -1; 266 + } 267 + 268 + if (!g_seekable_seek(G_SEEKABLE(s), len, G_SEEK_CUR, NULL, &err)) { 269 + goto error; 270 + } 271 + 272 + nelem -= 1; 273 + } 274 + 275 + return 0; 276 + 277 + error: 278 + error_report("%s: Failed to read from stream: %s", __func__, err->message); 279 + return -1; 280 + } 281 + 282 + static void 283 + dbus_save_state_proxy(gpointer key, 284 + gpointer value, 285 + gpointer user_data) 286 + { 287 + GDataOutputStream *s = user_data; 288 + const char *id = key; 289 + GDBusProxy *proxy = value; 290 + g_autoptr(GVariant) result = NULL; 291 + g_autoptr(GVariant) child = NULL; 292 + g_autoptr(GError) err = NULL; 293 + const uint8_t *data; 294 + gsize size; 295 + 296 + trace_dbus_vmstate_saving(id); 297 + 298 + result = g_dbus_proxy_call_sync(proxy, "Save", 299 + NULL, G_DBUS_CALL_FLAGS_NO_AUTO_START, 300 + -1, NULL, &err); 301 + if (!result) { 302 + error_report("%s: Failed to Save: %s", __func__, err->message); 303 + return; 304 + } 305 + 306 + child = g_variant_get_child_value(result, 0); 307 + data = g_variant_get_fixed_array(child, &size, sizeof(char)); 308 + if (!data) { 309 + error_report("%s: Failed to Save: not a byte array", __func__); 310 + return; 311 + } 312 + if (size > DBUS_VMSTATE_SIZE_LIMIT) { 313 + error_report("%s: Too large vmstate data to save: %zu", 314 + __func__, (size_t)size); 315 + return; 316 + } 317 + 318 + if (!g_data_output_stream_put_uint32(s, strlen(id), NULL, &err) || 319 + !g_data_output_stream_put_string(s, id, NULL, &err) || 320 + !g_data_output_stream_put_uint32(s, size, NULL, &err) || 321 + !g_output_stream_write_all(G_OUTPUT_STREAM(s), 322 + data, size, NULL, NULL, &err)) { 323 + error_report("%s: Failed to write to stream: %s", 324 + __func__, err->message); 325 + } 326 + } 327 + 328 + static int dbus_vmstate_pre_save(void *opaque) 329 + { 330 + DBusVMState *self = DBUS_VMSTATE(opaque); 331 + g_autoptr(GOutputStream) m = NULL; 332 + g_autoptr(GDataOutputStream) s = NULL; 333 + g_autoptr(GHashTable) proxies = NULL; 334 + g_autoptr(GError) err = NULL; 335 + 336 + trace_dbus_vmstate_pre_save(); 337 + 338 + proxies = dbus_get_proxies(self, &err); 339 + if (!proxies) { 340 + error_report("%s: Failed to get proxies: %s", __func__, err->message); 341 + return -1; 342 + } 343 + 344 + m = g_memory_output_stream_new_resizable(); 345 + s = g_data_output_stream_new(m); 346 + g_data_output_stream_set_byte_order(s, G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN); 347 + 348 + if (!g_data_output_stream_put_uint32(s, g_hash_table_size(proxies), 349 + NULL, &err)) { 350 + error_report("%s: Failed to write to stream: %s", 351 + __func__, err->message); 352 + return -1; 353 + } 354 + 355 + g_hash_table_foreach(proxies, dbus_save_state_proxy, s); 356 + 357 + if (g_memory_output_stream_get_size(G_MEMORY_OUTPUT_STREAM(m)) 358 + > UINT32_MAX) { 359 + error_report("%s: DBus vmstate buffer is too large", __func__); 360 + return -1; 361 + } 362 + 363 + if (!g_output_stream_close(G_OUTPUT_STREAM(m), NULL, &err)) { 364 + error_report("%s: Failed to close stream: %s", __func__, err->message); 365 + return -1; 366 + } 367 + 368 + g_free(self->data); 369 + self->data_size = 370 + g_memory_output_stream_get_size(G_MEMORY_OUTPUT_STREAM(m)); 371 + self->data = 372 + g_memory_output_stream_steal_data(G_MEMORY_OUTPUT_STREAM(m)); 373 + 374 + return 0; 375 + } 376 + 377 + static const VMStateDescription dbus_vmstate = { 378 + .name = TYPE_DBUS_VMSTATE, 379 + .version_id = 0, 380 + .pre_save = dbus_vmstate_pre_save, 381 + .post_load = dbus_vmstate_post_load, 382 + .fields = (VMStateField[]) { 383 + VMSTATE_UINT32(data_size, DBusVMState), 384 + VMSTATE_VBUFFER_ALLOC_UINT32(data, DBusVMState, 0, 0, data_size), 385 + VMSTATE_END_OF_LIST() 386 + } 387 + }; 388 + 389 + static void 390 + dbus_vmstate_complete(UserCreatable *uc, Error **errp) 391 + { 392 + DBusVMState *self = DBUS_VMSTATE(uc); 393 + g_autoptr(GError) err = NULL; 394 + 395 + if (!object_resolve_path_type("", TYPE_DBUS_VMSTATE, NULL)) { 396 + error_setg(errp, "There is already an instance of %s", 397 + TYPE_DBUS_VMSTATE); 398 + return; 399 + } 400 + 401 + if (!self->dbus_addr) { 402 + error_setg(errp, QERR_MISSING_PARAMETER, "addr"); 403 + return; 404 + } 405 + 406 + self->bus = g_dbus_connection_new_for_address_sync(self->dbus_addr, 407 + G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT | 408 + G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION, 409 + NULL, NULL, &err); 410 + if (err) { 411 + error_setg(errp, "failed to connect to DBus: '%s'", err->message); 412 + return; 413 + } 414 + 415 + if (vmstate_register(VMSTATE_IF(self), -1, &dbus_vmstate, self) < 0) { 416 + error_setg(errp, "Failed to register vmstate"); 417 + } 418 + } 419 + 420 + static void 421 + dbus_vmstate_finalize(Object *o) 422 + { 423 + DBusVMState *self = DBUS_VMSTATE(o); 424 + 425 + vmstate_unregister(VMSTATE_IF(self), &dbus_vmstate, self); 426 + 427 + g_clear_object(&self->bus); 428 + g_free(self->dbus_addr); 429 + g_free(self->id_list); 430 + g_free(self->data); 431 + } 432 + 433 + static char * 434 + get_dbus_addr(Object *o, Error **errp) 435 + { 436 + DBusVMState *self = DBUS_VMSTATE(o); 437 + 438 + return g_strdup(self->dbus_addr); 439 + } 440 + 441 + static void 442 + set_dbus_addr(Object *o, const char *str, Error **errp) 443 + { 444 + DBusVMState *self = DBUS_VMSTATE(o); 445 + 446 + g_free(self->dbus_addr); 447 + self->dbus_addr = g_strdup(str); 448 + } 449 + 450 + static char * 451 + get_id_list(Object *o, Error **errp) 452 + { 453 + DBusVMState *self = DBUS_VMSTATE(o); 454 + 455 + return g_strdup(self->id_list); 456 + } 457 + 458 + static void 459 + set_id_list(Object *o, const char *str, Error **errp) 460 + { 461 + DBusVMState *self = DBUS_VMSTATE(o); 462 + 463 + g_free(self->id_list); 464 + self->id_list = g_strdup(str); 465 + } 466 + 467 + static char * 468 + dbus_vmstate_get_id(VMStateIf *vmif) 469 + { 470 + return g_strdup(TYPE_DBUS_VMSTATE); 471 + } 472 + 473 + static void 474 + dbus_vmstate_class_init(ObjectClass *oc, void *data) 475 + { 476 + UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); 477 + VMStateIfClass *vc = VMSTATE_IF_CLASS(oc); 478 + 479 + ucc->complete = dbus_vmstate_complete; 480 + vc->get_id = dbus_vmstate_get_id; 481 + 482 + object_class_property_add_str(oc, "addr", 483 + get_dbus_addr, set_dbus_addr, 484 + &error_abort); 485 + object_class_property_add_str(oc, "id-list", 486 + get_id_list, set_id_list, 487 + &error_abort); 488 + } 489 + 490 + static const TypeInfo dbus_vmstate_info = { 491 + .name = TYPE_DBUS_VMSTATE, 492 + .parent = TYPE_OBJECT, 493 + .instance_size = sizeof(DBusVMState), 494 + .instance_finalize = dbus_vmstate_finalize, 495 + .class_size = sizeof(DBusVMStateClass), 496 + .class_init = dbus_vmstate_class_init, 497 + .interfaces = (InterfaceInfo[]) { 498 + { TYPE_USER_CREATABLE }, 499 + { TYPE_VMSTATE_IF }, 500 + { } 501 + } 502 + }; 503 + 504 + static void 505 + register_types(void) 506 + { 507 + type_register_static(&dbus_vmstate_info); 508 + } 509 + 510 + type_init(register_types);
+7
backends/trace-events
··· 1 + # See docs/devel/tracing.txt for syntax documentation. 2 + 3 + # dbus-vmstate.c 4 + dbus_vmstate_pre_save(void) 5 + dbus_vmstate_post_load(int version_id) "version_id: %d" 6 + dbus_vmstate_loading(const char *id) "id: %s" 7 + dbus_vmstate_saving(const char *id) "id: %s"
+7
configure
··· 3701 3701 gio=yes 3702 3702 gio_cflags=$($pkg_config --cflags gio-2.0) 3703 3703 gio_libs=$($pkg_config --libs gio-2.0) 3704 + gdbus_codegen=$($pkg_config --variable=gdbus_codegen gio-2.0) 3704 3705 else 3705 3706 gio=no 3707 + fi 3708 + 3709 + if $pkg_config --atleast-version=$glib_req_ver gio-unix-2.0; then 3710 + gio_cflags="$gio_cflags $($pkg_config --cflags gio-unix-2.0)" 3711 + gio_libs="$gio_libs $($pkg_config --libs gio-unix-2.0)" 3706 3712 fi 3707 3713 3708 3714 # Sanity check that the current size_t matches the ··· 6904 6910 echo "CONFIG_GIO=y" >> $config_host_mak 6905 6911 echo "GIO_CFLAGS=$gio_cflags" >> $config_host_mak 6906 6912 echo "GIO_LIBS=$gio_libs" >> $config_host_mak 6913 + echo "GDBUS_CODEGEN=$gdbus_codegen" >> $config_host_mak 6907 6914 fi 6908 6915 echo "CONFIG_TLS_PRIORITY=\"$tls_priority\"" >> $config_host_mak 6909 6916 if test "$gnutls" = "yes" ; then
+74
docs/interop/dbus-vmstate.rst
··· 1 + ============= 2 + D-Bus VMState 3 + ============= 4 + 5 + Introduction 6 + ============ 7 + 8 + The QEMU dbus-vmstate object's aim is to migrate helpers' data running 9 + on a QEMU D-Bus bus. (refer to the :doc:`dbus` document for 10 + some recommendations on D-Bus usage) 11 + 12 + Upon migration, QEMU will go through the queue of 13 + ``org.qemu.VMState1`` D-Bus name owners and query their ``Id``. It 14 + must be unique among the helpers. 15 + 16 + It will then save arbitrary data of each Id to be transferred in the 17 + migration stream and restored/loaded at the corresponding destination 18 + helper. 19 + 20 + For now, the data amount to be transferred is arbitrarily limited to 21 + 1Mb. The state must be saved quickly (a fraction of a second). (D-Bus 22 + imposes a time limit on reply anyway, and migration would fail if data 23 + isn't given quickly enough.) 24 + 25 + dbus-vmstate object can be configured with the expected list of 26 + helpers by setting its ``id-list`` property, with a comma-separated 27 + ``Id`` list. 28 + 29 + Interface 30 + ========= 31 + 32 + On object path ``/org/qemu/VMState1``, the following 33 + ``org.qemu.VMState1`` interface should be implemented: 34 + 35 + .. code:: xml 36 + 37 + <interface name="org.qemu.VMState1"> 38 + <property name="Id" type="s" access="read"/> 39 + <method name="Load"> 40 + <arg type="ay" name="data" direction="in"/> 41 + </method> 42 + <method name="Save"> 43 + <arg type="ay" name="data" direction="out"/> 44 + </method> 45 + </interface> 46 + 47 + "Id" property 48 + ------------- 49 + 50 + A string that identifies the helper uniquely. (maximum 256 bytes 51 + including terminating NUL byte) 52 + 53 + .. note:: 54 + 55 + The helper ID namespace is a separate namespace. In particular, it is not 56 + related to QEMU "id" used in -object/-device objects. 57 + 58 + Load(in u8[] bytes) method 59 + -------------------------- 60 + 61 + The method called on destination with the state to restore. 62 + 63 + The helper may be initially started in a waiting state (with 64 + an --incoming argument for example), and it may resume on success. 65 + 66 + An error may be returned to the caller. 67 + 68 + Save(out u8[] bytes) method 69 + --------------------------- 70 + 71 + The method called on the source to get the current state to be 72 + migrated. The helper should continue to run normally. 73 + 74 + An error may be returned to the caller.
+110
docs/interop/dbus.rst
··· 1 + ===== 2 + D-Bus 3 + ===== 4 + 5 + Introduction 6 + ============ 7 + 8 + QEMU may be running with various helper processes involved: 9 + - vhost-user* processes (gpu, virtfs, input, etc...) 10 + - TPM emulation (or other devices) 11 + - user networking (slirp) 12 + - network services (DHCP/DNS, samba/ftp etc) 13 + - background tasks (compression, streaming etc) 14 + - client UI 15 + - admin & cli 16 + 17 + Having several processes allows stricter security rules, as well as 18 + greater modularity. 19 + 20 + While QEMU itself uses QMP as primary IPC (and Spice/VNC for remote 21 + display), D-Bus is the de facto IPC of choice on Unix systems. The 22 + wire format is machine friendly, good bindings exist for various 23 + languages, and there are various tools available. 24 + 25 + Using a bus, helper processes can discover and communicate with each 26 + other easily, without going through QEMU. The bus topology is also 27 + easier to apprehend and debug than a mesh. However, it is wise to 28 + consider the security aspects of it. 29 + 30 + Security 31 + ======== 32 + 33 + A QEMU D-Bus bus should be private to a single VM. Thus, only 34 + cooperative tasks are running on the same bus to serve the VM. 35 + 36 + D-Bus, the protocol and standard, doesn't have mechanisms to enforce 37 + security between peers once the connection is established. Peers may 38 + have additional mechanisms to enforce security rules, based for 39 + example on UNIX credentials. 40 + 41 + The daemon can control which peers can send/recv messages using 42 + various metadata attributes, however, this is alone is not generally 43 + sufficient to make the deployment secure. The semantics of the actual 44 + methods implemented using D-Bus are just as critical. Peers need to 45 + carefully validate any information they received from a peer with a 46 + different trust level. 47 + 48 + dbus-daemon policy 49 + ------------------ 50 + 51 + dbus-daemon can enforce various policies based on the UID/GID of the 52 + processes that are connected to it. It is thus a good idea to run 53 + helpers as different UID from QEMU and set appropriate policies. 54 + 55 + Depending on the use case, you may choose different scenarios: 56 + 57 + - Everything the same UID 58 + 59 + - Convenient for developers 60 + - Improved reliability - crash of one part doens't take 61 + out entire VM 62 + - No security benefit over traditional QEMU, unless additional 63 + unless additional controls such as SELinux or AppArmor are 64 + applied 65 + 66 + - Two UIDs, one for QEMU, one for dbus & helpers 67 + 68 + - Moderately improved user based security isolation 69 + 70 + - Many UIDs, one for QEMU one for dbus and one for each helpers 71 + 72 + - Best user based security isolation 73 + - Complex to manager distinct UIDs needed for each VM 74 + 75 + For example, to allow only ``qemu`` user to talk to ``qemu-helper`` 76 + ``org.qemu.Helper1`` service, a dbus-daemon policy may contain: 77 + 78 + .. code:: xml 79 + 80 + <policy user="qemu"> 81 + <allow send_destination="org.qemu.Helper1"/> 82 + <allow receive_sender="org.qemu.Helper1"/> 83 + </policy> 84 + 85 + <policy user="qemu-helper"> 86 + <allow own="org.qemu.Helper1"/> 87 + </policy> 88 + 89 + 90 + dbus-daemon can also perfom SELinux checks based on the security 91 + context of the source and the target. For example, ``virtiofs_t`` 92 + could be allowed to send a message to ``svirt_t``, but ``virtiofs_t`` 93 + wouldn't be allowed to send a message to ``virtiofs_t``. 94 + 95 + See dbus-daemon man page for details. 96 + 97 + Guidelines 98 + ========== 99 + 100 + When implementing new D-Bus interfaces, it is recommended to follow 101 + the "D-Bus API Design Guidelines": 102 + https://dbus.freedesktop.org/doc/dbus-api-design.html 103 + 104 + The "org.qemu.*" prefix is reserved for services implemented & 105 + distributed by the QEMU project. 106 + 107 + QEMU Interfaces 108 + =============== 109 + 110 + :doc:`dbus-vmstate`
+2
docs/interop/index.rst
··· 13 13 :maxdepth: 2 14 14 15 15 bitmaps 16 + dbus 17 + dbus-vmstate 16 18 live-block-operations 17 19 pr-helper 18 20 qemu-ga
+1 -1
hw/block/onenand.c
··· 822 822 onenand_mem_setup(s); 823 823 sysbus_init_irq(sbd, &s->intr); 824 824 sysbus_init_mmio(sbd, &s->container); 825 - vmstate_register(dev, 825 + vmstate_register(VMSTATE_IF(dev), 826 826 ((s->shift & 0x7f) << 24) 827 827 | ((s->id.man & 0xff) << 16) 828 828 | ((s->id.dev & 0xff) << 8)
+1
hw/core/Makefile.objs
··· 9 9 common-obj-$(CONFIG_SOFTMMU) += nmi.o 10 10 common-obj-$(CONFIG_SOFTMMU) += vm-change-state-handler.o 11 11 common-obj-y += cpu.o 12 + common-obj-y += vmstate-if.o 12 13 13 14 common-obj-$(CONFIG_EMPTY_SLOT) += empty_slot.o 14 15 common-obj-$(CONFIG_XILINX_AXI) += stream.o
+18 -3
hw/core/qdev.c
··· 889 889 dev->canonical_path = object_get_canonical_path(OBJECT(dev)); 890 890 891 891 if (qdev_get_vmsd(dev)) { 892 - if (vmstate_register_with_alias_id(dev, -1, qdev_get_vmsd(dev), dev, 892 + if (vmstate_register_with_alias_id(VMSTATE_IF(dev), 893 + -1, qdev_get_vmsd(dev), dev, 893 894 dev->instance_id_alias, 894 895 dev->alias_required_for_version, 895 896 &local_err) < 0) { ··· 923 924 local_err ? NULL : &local_err); 924 925 } 925 926 if (qdev_get_vmsd(dev)) { 926 - vmstate_unregister(dev, qdev_get_vmsd(dev), dev); 927 + vmstate_unregister(VMSTATE_IF(dev), qdev_get_vmsd(dev), dev); 927 928 } 928 929 if (dc->unrealize) { 929 930 dc->unrealize(dev, local_err ? NULL : &local_err); ··· 947 948 } 948 949 949 950 if (qdev_get_vmsd(dev)) { 950 - vmstate_unregister(dev, qdev_get_vmsd(dev), dev); 951 + vmstate_unregister(VMSTATE_IF(dev), qdev_get_vmsd(dev), dev); 951 952 } 952 953 953 954 post_realize_fail: ··· 1087 1088 } 1088 1089 } 1089 1090 1091 + static char * 1092 + device_vmstate_if_get_id(VMStateIf *obj) 1093 + { 1094 + DeviceState *dev = DEVICE(obj); 1095 + 1096 + return qdev_get_dev_path(dev); 1097 + } 1098 + 1090 1099 static void device_class_init(ObjectClass *class, void *data) 1091 1100 { 1092 1101 DeviceClass *dc = DEVICE_CLASS(class); 1102 + VMStateIfClass *vc = VMSTATE_IF_CLASS(class); 1093 1103 1094 1104 class->unparent = device_unparent; 1095 1105 ··· 1101 1111 */ 1102 1112 dc->hotpluggable = true; 1103 1113 dc->user_creatable = true; 1114 + vc->get_id = device_vmstate_if_get_id; 1104 1115 } 1105 1116 1106 1117 void device_class_set_parent_reset(DeviceClass *dc, ··· 1158 1169 .class_init = device_class_init, 1159 1170 .abstract = true, 1160 1171 .class_size = sizeof(DeviceClass), 1172 + .interfaces = (InterfaceInfo[]) { 1173 + { TYPE_VMSTATE_IF }, 1174 + { } 1175 + } 1161 1176 }; 1162 1177 1163 1178 static void qdev_register_types(void)
+23
hw/core/vmstate-if.c
··· 1 + /* 2 + * VMState interface 3 + * 4 + * Copyright (c) 2009-2019 Red Hat Inc 5 + * This work is licensed under the terms of the GNU GPL, version 2 or later. 6 + * See the COPYING file in the top-level directory. 7 + */ 8 + 9 + #include "qemu/osdep.h" 10 + #include "hw/vmstate-if.h" 11 + 12 + static const TypeInfo vmstate_if_info = { 13 + .name = TYPE_VMSTATE_IF, 14 + .parent = TYPE_INTERFACE, 15 + .class_size = sizeof(VMStateIfClass), 16 + }; 17 + 18 + static void vmstate_register_types(void) 19 + { 20 + type_register_static(&vmstate_if_info); 21 + } 22 + 23 + type_init(vmstate_register_types);
+1 -1
hw/ide/cmd646.c
··· 302 302 } 303 303 g_free(irq); 304 304 305 - vmstate_register(DEVICE(dev), 0, &vmstate_ide_pci, d); 305 + vmstate_register(VMSTATE_IF(dev), 0, &vmstate_ide_pci, d); 306 306 qemu_register_reset(cmd646_reset, d); 307 307 } 308 308
+1 -1
hw/ide/isa.c
··· 75 75 ide_init_ioport(&s->bus, isadev, s->iobase, s->iobase2); 76 76 isa_init_irq(isadev, &s->irq, s->isairq); 77 77 ide_init2(&s->bus, s->irq); 78 - vmstate_register(dev, 0, &vmstate_ide_isa, s); 78 + vmstate_register(VMSTATE_IF(dev), 0, &vmstate_ide_isa, s); 79 79 ide_register_restart_cb(&s->bus); 80 80 } 81 81
+1 -1
hw/ide/piix.c
··· 156 156 bmdma_setup_bar(d); 157 157 pci_register_bar(dev, 4, PCI_BASE_ADDRESS_SPACE_IO, &d->bmdma_bar); 158 158 159 - vmstate_register(DEVICE(dev), 0, &vmstate_ide_pci, d); 159 + vmstate_register(VMSTATE_IF(dev), 0, &vmstate_ide_pci, d); 160 160 161 161 pci_piix_init_ports(d); 162 162 }
+1 -1
hw/ide/via.c
··· 190 190 bmdma_setup_bar(d); 191 191 pci_register_bar(dev, 4, PCI_BASE_ADDRESS_SPACE_IO, &d->bmdma_bar); 192 192 193 - vmstate_register(DEVICE(dev), 0, &vmstate_ide_pci, d); 193 + vmstate_register(VMSTATE_IF(dev), 0, &vmstate_ide_pci, d); 194 194 195 195 for (i = 0; i < 2; i++) { 196 196 ide_bus_new(&d->bus[i], sizeof(d->bus[i]), DEVICE(d), i, 2);
+1 -1
hw/misc/max111x.c
··· 146 146 s->input[7] = 0x80; 147 147 s->com = 0; 148 148 149 - vmstate_register(dev, -1, &vmstate_max111x, s); 149 + vmstate_register(VMSTATE_IF(dev), -1, &vmstate_max111x, s); 150 150 return 0; 151 151 } 152 152
+2 -2
hw/net/eepro100.c
··· 1815 1815 { 1816 1816 EEPRO100State *s = DO_UPCAST(EEPRO100State, dev, pci_dev); 1817 1817 1818 - vmstate_unregister(&pci_dev->qdev, s->vmstate, s); 1818 + vmstate_unregister(VMSTATE_IF(&pci_dev->qdev), s->vmstate, s); 1819 1819 g_free(s->vmstate); 1820 1820 eeprom93xx_free(&pci_dev->qdev, s->eeprom); 1821 1821 qemu_del_nic(s->nic); ··· 1874 1874 1875 1875 s->vmstate = g_memdup(&vmstate_eepro100, sizeof(vmstate_eepro100)); 1876 1876 s->vmstate->name = qemu_get_queue(s->nic)->model; 1877 - vmstate_register(&pci_dev->qdev, -1, s->vmstate, s); 1877 + vmstate_register(VMSTATE_IF(&pci_dev->qdev), -1, s->vmstate, s); 1878 1878 } 1879 1879 1880 1880 static void eepro100_instance_init(Object *obj)
+2 -1
hw/net/virtio-net.c
··· 2853 2853 2854 2854 if (migration_in_setup(s) && !should_be_hidden) { 2855 2855 if (failover_unplug_primary(n)) { 2856 - vmstate_unregister(n->primary_dev, qdev_get_vmsd(n->primary_dev), 2856 + vmstate_unregister(VMSTATE_IF(n->primary_dev), 2857 + qdev_get_vmsd(n->primary_dev), 2857 2858 n->primary_dev); 2858 2859 qapi_event_send_unplug_primary(n->primary_device_id); 2859 2860 atomic_set(&n->primary_should_be_hidden, true);
+2 -2
hw/nvram/eeprom93xx.c
··· 321 321 /* Output DO is tristate, read results in 1. */ 322 322 eeprom->eedo = 1; 323 323 logout("eeprom = 0x%p, nwords = %u\n", eeprom, nwords); 324 - vmstate_register(dev, 0, &vmstate_eeprom, eeprom); 324 + vmstate_register(VMSTATE_IF(dev), 0, &vmstate_eeprom, eeprom); 325 325 return eeprom; 326 326 } 327 327 ··· 329 329 { 330 330 /* Destroy EEPROM. */ 331 331 logout("eeprom = 0x%p\n", eeprom); 332 - vmstate_unregister(dev, &vmstate_eeprom, eeprom); 332 + vmstate_unregister(VMSTATE_IF(dev), &vmstate_eeprom, eeprom); 333 333 g_free(eeprom); 334 334 } 335 335
+5 -4
hw/ppc/spapr_drc.c
··· 511 511 error_propagate(errp, err); 512 512 return; 513 513 } 514 - vmstate_register(DEVICE(drc), spapr_drc_index(drc), &vmstate_spapr_drc, 514 + vmstate_register(VMSTATE_IF(drc), spapr_drc_index(drc), &vmstate_spapr_drc, 515 515 drc); 516 516 trace_spapr_drc_realize_complete(spapr_drc_index(drc)); 517 517 } ··· 523 523 gchar *name; 524 524 525 525 trace_spapr_drc_unrealize(spapr_drc_index(drc)); 526 - vmstate_unregister(DEVICE(drc), &vmstate_spapr_drc, drc); 526 + vmstate_unregister(VMSTATE_IF(drc), &vmstate_spapr_drc, drc); 527 527 root_container = container_get(object_get_root(), DRC_CONTAINER_PATH); 528 528 name = g_strdup_printf("%x", spapr_drc_index(drc)); 529 529 object_property_del(root_container, name, errp); ··· 619 619 return; 620 620 } 621 621 622 - vmstate_register(DEVICE(drcp), spapr_drc_index(SPAPR_DR_CONNECTOR(drcp)), 622 + vmstate_register(VMSTATE_IF(drcp), 623 + spapr_drc_index(SPAPR_DR_CONNECTOR(drcp)), 623 624 &vmstate_spapr_drc_physical, drcp); 624 625 qemu_register_reset(drc_physical_reset, drcp); 625 626 } ··· 635 636 return; 636 637 } 637 638 638 - vmstate_unregister(DEVICE(drcp), &vmstate_spapr_drc_physical, drcp); 639 + vmstate_unregister(VMSTATE_IF(drcp), &vmstate_spapr_drc_physical, drcp); 639 640 qemu_unregister_reset(drc_physical_reset, drcp); 640 641 } 641 642
+2 -2
hw/ppc/spapr_iommu.c
··· 317 317 318 318 QLIST_INSERT_HEAD(&spapr_tce_tables, tcet, list); 319 319 320 - vmstate_register(DEVICE(tcet), tcet->liobn, &vmstate_spapr_tce_table, 320 + vmstate_register(VMSTATE_IF(tcet), tcet->liobn, &vmstate_spapr_tce_table, 321 321 tcet); 322 322 } 323 323 ··· 420 420 { 421 421 SpaprTceTable *tcet = SPAPR_TCE_TABLE(dev); 422 422 423 - vmstate_unregister(DEVICE(tcet), &vmstate_spapr_tce_table, tcet); 423 + vmstate_unregister(VMSTATE_IF(tcet), &vmstate_spapr_tce_table, tcet); 424 424 425 425 QLIST_REMOVE(tcet, list); 426 426
+1 -1
hw/s390x/s390-skeys.c
··· 392 392 register_savevm_live(TYPE_S390_SKEYS, 0, 1, 393 393 &savevm_s390_storage_keys, ss); 394 394 } else { 395 - unregister_savevm(DEVICE(ss), TYPE_S390_SKEYS, ss); 395 + unregister_savevm(VMSTATE_IF(ss), TYPE_S390_SKEYS, ss); 396 396 } 397 397 } 398 398
+40
include/hw/vmstate-if.h
··· 1 + /* 2 + * VMState interface 3 + * 4 + * Copyright (c) 2009-2019 Red Hat Inc 5 + * This work is licensed under the terms of the GNU GPL, version 2 or later. 6 + * See the COPYING file in the top-level directory. 7 + */ 8 + 9 + #ifndef VMSTATE_IF_H 10 + #define VMSTATE_IF_H 11 + 12 + #include "qom/object.h" 13 + 14 + #define TYPE_VMSTATE_IF "vmstate-if" 15 + 16 + #define VMSTATE_IF_CLASS(klass) \ 17 + OBJECT_CLASS_CHECK(VMStateIfClass, (klass), TYPE_VMSTATE_IF) 18 + #define VMSTATE_IF_GET_CLASS(obj) \ 19 + OBJECT_GET_CLASS(VMStateIfClass, (obj), TYPE_VMSTATE_IF) 20 + #define VMSTATE_IF(obj) \ 21 + INTERFACE_CHECK(VMStateIf, (obj), TYPE_VMSTATE_IF) 22 + 23 + typedef struct VMStateIf VMStateIf; 24 + 25 + typedef struct VMStateIfClass { 26 + InterfaceClass parent_class; 27 + 28 + char * (*get_id)(VMStateIf *obj); 29 + } VMStateIfClass; 30 + 31 + static inline char *vmstate_if_get_id(VMStateIf *vmif) 32 + { 33 + if (!vmif) { 34 + return NULL; 35 + } 36 + 37 + return VMSTATE_IF_GET_CLASS(vmif)->get_id(vmif); 38 + } 39 + 40 + #endif /* VMSTATE_IF_H */
+3 -1
include/migration/register.h
··· 14 14 #ifndef MIGRATION_REGISTER_H 15 15 #define MIGRATION_REGISTER_H 16 16 17 + #include "hw/vmstate-if.h" 18 + 17 19 typedef struct SaveVMHandlers { 18 20 /* This runs inside the iothread lock. */ 19 21 SaveStateHandler *save_state; ··· 74 76 const SaveVMHandlers *ops, 75 77 void *opaque); 76 78 77 - void unregister_savevm(DeviceState *dev, const char *idstr, void *opaque); 79 + void unregister_savevm(VMStateIf *obj, const char *idstr, void *opaque); 78 80 79 81 #endif
+6 -4
include/migration/vmstate.h
··· 27 27 #ifndef QEMU_VMSTATE_H 28 28 #define QEMU_VMSTATE_H 29 29 30 + #include "hw/vmstate-if.h" 31 + 30 32 typedef struct VMStateInfo VMStateInfo; 31 33 typedef struct VMStateField VMStateField; 32 34 ··· 1156 1158 bool vmstate_save_needed(const VMStateDescription *vmsd, void *opaque); 1157 1159 1158 1160 /* Returns: 0 on success, -1 on failure */ 1159 - int vmstate_register_with_alias_id(DeviceState *dev, int instance_id, 1161 + int vmstate_register_with_alias_id(VMStateIf *obj, int instance_id, 1160 1162 const VMStateDescription *vmsd, 1161 1163 void *base, int alias_id, 1162 1164 int required_for_version, 1163 1165 Error **errp); 1164 1166 1165 1167 /* Returns: 0 on success, -1 on failure */ 1166 - static inline int vmstate_register(DeviceState *dev, int instance_id, 1168 + static inline int vmstate_register(VMStateIf *obj, int instance_id, 1167 1169 const VMStateDescription *vmsd, 1168 1170 void *opaque) 1169 1171 { 1170 - return vmstate_register_with_alias_id(dev, instance_id, vmsd, 1172 + return vmstate_register_with_alias_id(obj, instance_id, vmsd, 1171 1173 opaque, -1, 0, NULL); 1172 1174 } 1173 1175 1174 - void vmstate_unregister(DeviceState *dev, const VMStateDescription *vmsd, 1176 + void vmstate_unregister(VMStateIf *obj, const VMStateDescription *vmsd, 1175 1177 void *opaque); 1176 1178 1177 1179 struct MemoryRegion;
+19
include/qemu/dbus.h
··· 1 + /* 2 + * Helpers for using D-Bus 3 + * 4 + * Copyright (C) 2019 Red Hat, Inc. 5 + * 6 + * This work is licensed under the terms of the GNU GPL, version 2. See 7 + * the COPYING file in the top-level directory. 8 + */ 9 + 10 + #ifndef DBUS_H 11 + #define DBUS_H 12 + 13 + #include <gio/gio.h> 14 + 15 + GStrv qemu_dbus_get_queued_owners(GDBusConnection *connection, 16 + const char *name, 17 + Error **errp); 18 + 19 + #endif /* DBUS_H */
+10 -10
migration/savevm.c
··· 760 760 return 0; 761 761 } 762 762 763 - void unregister_savevm(DeviceState *dev, const char *idstr, void *opaque) 763 + void unregister_savevm(VMStateIf *obj, const char *idstr, void *opaque) 764 764 { 765 765 SaveStateEntry *se, *new_se; 766 766 char id[256] = ""; 767 767 768 - if (dev) { 769 - char *path = qdev_get_dev_path(dev); 770 - if (path) { 771 - pstrcpy(id, sizeof(id), path); 768 + if (obj) { 769 + char *oid = vmstate_if_get_id(obj); 770 + if (oid) { 771 + pstrcpy(id, sizeof(id), oid); 772 772 pstrcat(id, sizeof(id), "/"); 773 - g_free(path); 773 + g_free(oid); 774 774 } 775 775 } 776 776 pstrcat(id, sizeof(id), idstr); ··· 784 784 } 785 785 } 786 786 787 - int vmstate_register_with_alias_id(DeviceState *dev, int instance_id, 787 + int vmstate_register_with_alias_id(VMStateIf *obj, int instance_id, 788 788 const VMStateDescription *vmsd, 789 789 void *opaque, int alias_id, 790 790 int required_for_version, ··· 802 802 se->vmsd = vmsd; 803 803 se->alias_id = alias_id; 804 804 805 - if (dev) { 806 - char *id = qdev_get_dev_path(dev); 805 + if (obj) { 806 + char *id = vmstate_if_get_id(obj); 807 807 if (id) { 808 808 if (snprintf(se->idstr, sizeof(se->idstr), "%s/", id) >= 809 809 sizeof(se->idstr)) { ··· 834 834 return 0; 835 835 } 836 836 837 - void vmstate_unregister(DeviceState *dev, const VMStateDescription *vmsd, 837 + void vmstate_unregister(VMStateIf *obj, const VMStateDescription *vmsd, 838 838 void *opaque) 839 839 { 840 840 SaveStateEntry *se, *new_se;
+2 -2
stubs/vmstate.c
··· 3 3 4 4 const VMStateDescription vmstate_dummy = {}; 5 5 6 - int vmstate_register_with_alias_id(DeviceState *dev, 6 + int vmstate_register_with_alias_id(VMStateIf *obj, 7 7 int instance_id, 8 8 const VMStateDescription *vmsd, 9 9 void *base, int alias_id, ··· 13 13 return 0; 14 14 } 15 15 16 - void vmstate_unregister(DeviceState *dev, 16 + void vmstate_unregister(VMStateIf *obj, 17 17 const VMStateDescription *vmsd, 18 18 void *opaque) 19 19 {
+23 -2
tests/Makefile.include
··· 158 158 159 159 check-qtest-generic-y += tests/device-introspect-test$(EXESUF) 160 160 check-qtest-generic-y += tests/cdrom-test$(EXESUF) 161 + DBUS_DAEMON := $(shell which dbus-daemon 2>/dev/null) 162 + ifneq ($(GDBUS_CODEGEN),) 163 + ifneq ($(DBUS_DAEMON),) 164 + check-qtest-pci-$(CONFIG_GIO) += tests/dbus-vmstate-test$(EXESUF) 165 + endif 166 + endif 161 167 162 168 check-qtest-pci-$(CONFIG_RTL8139_PCI) += tests/rtl8139-test$(EXESUF) 163 169 check-qtest-pci-$(CONFIG_VGA) += tests/display-vga-test$(EXESUF) 164 170 check-qtest-pci-$(CONFIG_HDA) += tests/intel-hda-test$(EXESUF) 165 171 check-qtest-pci-$(CONFIG_IVSHMEM_DEVICE) += tests/ivshmem-test$(EXESUF) 166 - 167 172 check-qtest-i386-$(CONFIG_ISA_TESTDEV) = tests/endianness-test$(EXESUF) 168 173 check-qtest-i386-y += tests/fdc-test$(EXESUF) 169 174 check-qtest-i386-y += tests/ide-test$(EXESUF) ··· 579 584 hw/core/irq.o \ 580 585 hw/core/fw-path-provider.o \ 581 586 hw/core/reset.o \ 587 + hw/core/vmstate-if.o \ 582 588 $(test-qapi-obj-y) 583 589 tests/test-vmstate$(EXESUF): tests/test-vmstate.o \ 584 590 migration/vmstate.o migration/vmstate-types.o migration/qemu-file.o \ ··· 633 639 @mv tests/qapi-schema/doc-good-qapi-doc.texi $@ 634 640 @rm -f tests/qapi-schema/doc-good-qapi-*.[ch] tests/qapi-schema/doc-good-qmp-*.[ch] 635 641 642 + tests/dbus-vmstate1.h tests/dbus-vmstate1.c: tests/dbus-vmstate1-gen-timestamp ; 643 + tests/dbus-vmstate1-gen-timestamp: $(SRC_PATH)/tests/dbus-vmstate1.xml 644 + $(call quiet-command,$(GDBUS_CODEGEN) $< \ 645 + --interface-prefix org.qemu --generate-c-code tests/dbus-vmstate1, \ 646 + "GEN","$(@:%-timestamp=%)") 647 + @>$@ 648 + 649 + tests/dbus-vmstate-test.o-cflags := -DSRCDIR="$(SRC_PATH)" 650 + tests/dbus-vmstate1.o-cflags := $(GIO_CFLAGS) 651 + tests/dbus-vmstate1.o-libs := $(GIO_LIBS) 652 + 653 + tests/dbus-vmstate-test.o: tests/dbus-vmstate1.h 654 + 636 655 tests/test-string-output-visitor$(EXESUF): tests/test-string-output-visitor.o $(test-qapi-obj-y) 637 656 tests/test-string-input-visitor$(EXESUF): tests/test-string-input-visitor.o $(test-qapi-obj-y) 638 657 tests/test-qmp-event$(EXESUF): tests/test-qmp-event.o $(test-qapi-obj-y) tests/test-qapi-events.o ··· 826 845 tests/usb-hcd-ehci-test$(EXESUF): tests/usb-hcd-ehci-test.o $(libqos-usb-obj-y) 827 846 tests/usb-hcd-xhci-test$(EXESUF): tests/usb-hcd-xhci-test.o $(libqos-usb-obj-y) 828 847 tests/cpu-plug-test$(EXESUF): tests/cpu-plug-test.o 829 - tests/migration-test$(EXESUF): tests/migration-test.o 848 + tests/migration-test$(EXESUF): tests/migration-test.o tests/migration-helpers.o 830 849 tests/qemu-iotests/socket_scm_helper$(EXESUF): tests/qemu-iotests/socket_scm_helper.o 831 850 tests/test-qemu-opts$(EXESUF): tests/test-qemu-opts.o $(test-util-obj-y) 832 851 tests/test-keyval$(EXESUF): tests/test-keyval.o $(test-util-obj-y) $(test-qapi-obj-y) ··· 836 855 tests/test-filter-redirector$(EXESUF): tests/test-filter-redirector.o $(qtest-obj-y) 837 856 tests/test-x86-cpuid-compat$(EXESUF): tests/test-x86-cpuid-compat.o $(qtest-obj-y) 838 857 tests/ivshmem-test$(EXESUF): tests/ivshmem-test.o contrib/ivshmem-server/ivshmem-server.o $(libqos-pc-obj-y) $(libqos-spapr-obj-y) 858 + tests/dbus-vmstate-test$(EXESUF): tests/dbus-vmstate-test.o tests/migration-helpers.o tests/dbus-vmstate1.o $(libqos-pc-obj-y) $(libqos-spapr-obj-y) 839 859 tests/vhost-user-bridge$(EXESUF): tests/vhost-user-bridge.o $(test-util-obj-y) libvhost-user.a 840 860 tests/test-uuid$(EXESUF): tests/test-uuid.o $(test-util-obj-y) 841 861 tests/test-arm-mptimer$(EXESUF): tests/test-arm-mptimer.o ··· 1194 1214 rm -rf $(check-unit-y) tests/*.o $(QEMU_IOTESTS_HELPERS-y) 1195 1215 rm -rf $(sort $(foreach target,$(SYSEMU_TARGET_LIST), $(check-qtest-$(target)-y)) $(check-qtest-generic-y)) 1196 1216 rm -f tests/test-qapi-gen-timestamp 1217 + rm -f tests/dbus-vmstate1-gen-timestamp 1197 1218 rm -rf $(TESTS_VENV_DIR) $(TESTS_RESULTS_DIR) 1198 1219 1199 1220 clean: check-clean
+95
tests/dbus-vmstate-daemon.sh
··· 1 + #!/bin/sh 2 + 3 + # dbus-daemon wrapper script for dbus-vmstate testing 4 + # 5 + # This script allows to tweak the dbus-daemon policy during the test 6 + # to test different configurations. 7 + # 8 + # This program is free software; you can redistribute it and/or modify 9 + # it under the terms of the GNU General Public License as published by 10 + # the Free Software Foundation; either version 2 of the License, or 11 + # (at your option) any later version. 12 + # 13 + # This program is distributed in the hope that it will be useful, 14 + # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 + # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 + # GNU General Public License for more details. 17 + # 18 + # You should have received a copy of the GNU General Public License 19 + # along with this program; if not, see <http://www.gnu.org/licenses/>. 20 + # 21 + # Copyright (C) 2019 Red Hat, Inc. 22 + 23 + write_config() 24 + { 25 + CONF="$1" 26 + cat > "$CONF" <<EOF 27 + <busconfig> 28 + <type>session</type> 29 + <listen>unix:tmpdir=$DBUS_VMSTATE_TEST_TMPDIR</listen> 30 + 31 + <policy context="default"> 32 + <!-- Holes must be punched in service configuration files for 33 + name ownership and sending method calls --> 34 + <deny own="*"/> 35 + <deny send_type="method_call"/> 36 + 37 + <!-- Signals and reply messages (method returns, errors) are allowed 38 + by default --> 39 + <allow send_type="signal"/> 40 + <allow send_requested_reply="true" send_type="method_return"/> 41 + <allow send_requested_reply="true" send_type="error"/> 42 + 43 + <!-- All messages may be received by default --> 44 + <allow receive_type="method_call"/> 45 + <allow receive_type="method_return"/> 46 + <allow receive_type="error"/> 47 + <allow receive_type="signal"/> 48 + 49 + <!-- Allow anyone to talk to the message bus --> 50 + <allow send_destination="org.freedesktop.DBus" 51 + send_interface="org.freedesktop.DBus" /> 52 + <allow send_destination="org.freedesktop.DBus" 53 + send_interface="org.freedesktop.DBus.Introspectable"/> 54 + <allow send_destination="org.freedesktop.DBus" 55 + send_interface="org.freedesktop.DBus.Properties"/> 56 + <!-- But disallow some specific bus services --> 57 + <deny send_destination="org.freedesktop.DBus" 58 + send_interface="org.freedesktop.DBus" 59 + send_member="UpdateActivationEnvironment"/> 60 + <deny send_destination="org.freedesktop.DBus" 61 + send_interface="org.freedesktop.DBus.Debug.Stats"/> 62 + <deny send_destination="org.freedesktop.DBus" 63 + send_interface="org.freedesktop.systemd1.Activator"/> 64 + 65 + <allow own="org.qemu.VMState1"/> 66 + <allow send_destination="org.qemu.VMState1"/> 67 + <allow receive_sender="org.qemu.VMState1"/> 68 + 69 + </policy> 70 + 71 + <include if_selinux_enabled="yes" 72 + selinux_root_relative="yes">contexts/dbus_contexts</include> 73 + 74 + </busconfig> 75 + EOF 76 + } 77 + 78 + ARGS= 79 + for arg in "$@" 80 + do 81 + case $arg in 82 + --config-file=*) 83 + CONF="${arg#*=}" 84 + write_config "$CONF" 85 + ARGS="$ARGS $1" 86 + shift 87 + ;; 88 + *) 89 + ARGS="$ARGS $1" 90 + shift 91 + ;; 92 + esac 93 + done 94 + 95 + exec dbus-daemon $ARGS
+382
tests/dbus-vmstate-test.c
··· 1 + #include "qemu/osdep.h" 2 + #include <glib/gstdio.h> 3 + #include <gio/gio.h> 4 + #include "libqtest.h" 5 + #include "qemu-common.h" 6 + #include "dbus-vmstate1.h" 7 + #include "migration-helpers.h" 8 + 9 + static char *workdir; 10 + 11 + typedef struct TestServerId { 12 + const char *name; 13 + const char *data; 14 + size_t size; 15 + } TestServerId; 16 + 17 + static const TestServerId idA = { 18 + "idA", "I'am\0idA!", sizeof("I'am\0idA!") 19 + }; 20 + 21 + static const TestServerId idB = { 22 + "idB", "I'am\0idB!", sizeof("I'am\0idB!") 23 + }; 24 + 25 + typedef struct TestServer { 26 + const TestServerId *id; 27 + bool save_called; 28 + bool load_called; 29 + } TestServer; 30 + 31 + typedef struct Test { 32 + const char *id_list; 33 + bool migrate_fail; 34 + bool without_dst_b; 35 + TestServer srcA; 36 + TestServer dstA; 37 + TestServer srcB; 38 + TestServer dstB; 39 + GMainLoop *loop; 40 + QTestState *src_qemu; 41 + } Test; 42 + 43 + static gboolean 44 + vmstate_load(VMState1 *object, GDBusMethodInvocation *invocation, 45 + const gchar *arg_data, gpointer user_data) 46 + { 47 + TestServer *h = user_data; 48 + g_autoptr(GVariant) var = NULL; 49 + GVariant *args; 50 + const uint8_t *data; 51 + size_t size; 52 + 53 + args = g_dbus_method_invocation_get_parameters(invocation); 54 + var = g_variant_get_child_value(args, 0); 55 + data = g_variant_get_fixed_array(var, &size, sizeof(char)); 56 + g_assert_cmpuint(size, ==, h->id->size); 57 + g_assert(!memcmp(data, h->id->data, h->id->size)); 58 + h->load_called = true; 59 + 60 + g_dbus_method_invocation_return_value(invocation, g_variant_new("()")); 61 + return TRUE; 62 + } 63 + 64 + static gboolean 65 + vmstate_save(VMState1 *object, GDBusMethodInvocation *invocation, 66 + gpointer user_data) 67 + { 68 + TestServer *h = user_data; 69 + GVariant *var; 70 + 71 + var = g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, 72 + h->id->data, h->id->size, sizeof(char)); 73 + g_dbus_method_invocation_return_value(invocation, 74 + g_variant_new("(@ay)", var)); 75 + h->save_called = true; 76 + 77 + return TRUE; 78 + } 79 + 80 + typedef struct WaitNamed { 81 + GMainLoop *loop; 82 + bool named; 83 + } WaitNamed; 84 + 85 + static void 86 + named_cb(GDBusConnection *connection, 87 + const gchar *name, 88 + gpointer user_data) 89 + { 90 + WaitNamed *t = user_data; 91 + 92 + t->named = true; 93 + g_main_loop_quit(t->loop); 94 + } 95 + 96 + static GDBusConnection * 97 + get_connection(Test *test, guint *ownid) 98 + { 99 + g_autofree gchar *addr = NULL; 100 + WaitNamed *wait; 101 + GError *err = NULL; 102 + GDBusConnection *c; 103 + 104 + wait = g_new0(WaitNamed, 1); 105 + wait->loop = test->loop; 106 + addr = g_dbus_address_get_for_bus_sync(G_BUS_TYPE_SESSION, NULL, &err); 107 + g_assert_no_error(err); 108 + 109 + c = g_dbus_connection_new_for_address_sync( 110 + addr, 111 + G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION | 112 + G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, 113 + NULL, NULL, &err); 114 + g_assert_no_error(err); 115 + *ownid = g_bus_own_name_on_connection(c, "org.qemu.VMState1", 116 + G_BUS_NAME_OWNER_FLAGS_NONE, 117 + named_cb, named_cb, wait, g_free); 118 + if (!wait->named) { 119 + g_main_loop_run(wait->loop); 120 + } 121 + 122 + return c; 123 + } 124 + 125 + static GDBusObjectManagerServer * 126 + get_server(GDBusConnection *conn, TestServer *s, const TestServerId *id) 127 + { 128 + g_autoptr(GDBusObjectSkeleton) sk = NULL; 129 + g_autoptr(VMState1Skeleton) v = NULL; 130 + GDBusObjectManagerServer *os; 131 + 132 + s->id = id; 133 + os = g_dbus_object_manager_server_new("/org/qemu"); 134 + sk = g_dbus_object_skeleton_new("/org/qemu/VMState1"); 135 + 136 + v = VMSTATE1_SKELETON(vmstate1_skeleton_new()); 137 + g_object_set(v, "id", id->name, NULL); 138 + 139 + g_signal_connect(v, "handle-load", G_CALLBACK(vmstate_load), s); 140 + g_signal_connect(v, "handle-save", G_CALLBACK(vmstate_save), s); 141 + 142 + g_dbus_object_skeleton_add_interface(sk, G_DBUS_INTERFACE_SKELETON(v)); 143 + g_dbus_object_manager_server_export(os, sk); 144 + g_dbus_object_manager_server_set_connection(os, conn); 145 + 146 + return os; 147 + } 148 + 149 + static void 150 + set_id_list(Test *test, QTestState *s) 151 + { 152 + if (!test->id_list) { 153 + return; 154 + } 155 + 156 + g_assert(!qmp_rsp_is_err(qtest_qmp(s, 157 + "{ 'execute': 'qom-set', 'arguments': " 158 + "{ 'path': '/objects/dv', 'property': 'id-list', 'value': %s } }", 159 + test->id_list))); 160 + } 161 + 162 + static gpointer 163 + dbus_vmstate_thread(gpointer data) 164 + { 165 + GMainLoop *loop = data; 166 + 167 + g_main_loop_run(loop); 168 + 169 + return NULL; 170 + } 171 + 172 + static void 173 + test_dbus_vmstate(Test *test) 174 + { 175 + g_autofree char *src_qemu_args = NULL; 176 + g_autofree char *dst_qemu_args = NULL; 177 + g_autoptr(GTestDBus) srcbus = NULL; 178 + g_autoptr(GTestDBus) dstbus = NULL; 179 + g_autoptr(GDBusConnection) srcconnA = NULL; 180 + g_autoptr(GDBusConnection) srcconnB = NULL; 181 + g_autoptr(GDBusConnection) dstconnA = NULL; 182 + g_autoptr(GDBusConnection) dstconnB = NULL; 183 + g_autoptr(GDBusObjectManagerServer) srcserverA = NULL; 184 + g_autoptr(GDBusObjectManagerServer) srcserverB = NULL; 185 + g_autoptr(GDBusObjectManagerServer) dstserverA = NULL; 186 + g_autoptr(GDBusObjectManagerServer) dstserverB = NULL; 187 + g_auto(GStrv) srcaddr = NULL; 188 + g_auto(GStrv) dstaddr = NULL; 189 + g_autoptr(GThread) thread = NULL; 190 + g_autoptr(GMainLoop) loop = NULL; 191 + g_autofree char *uri = NULL; 192 + QTestState *src_qemu = NULL, *dst_qemu = NULL; 193 + guint ownsrcA, ownsrcB, owndstA, owndstB; 194 + 195 + uri = g_strdup_printf("unix:%s/migsocket", workdir); 196 + 197 + loop = g_main_loop_new(NULL, FALSE); 198 + test->loop = loop; 199 + 200 + srcbus = g_test_dbus_new(G_TEST_DBUS_NONE); 201 + g_test_dbus_up(srcbus); 202 + srcconnA = get_connection(test, &ownsrcA); 203 + srcserverA = get_server(srcconnA, &test->srcA, &idA); 204 + srcconnB = get_connection(test, &ownsrcB); 205 + srcserverB = get_server(srcconnB, &test->srcB, &idB); 206 + 207 + /* remove ,guid=foo part */ 208 + srcaddr = g_strsplit(g_test_dbus_get_bus_address(srcbus), ",", 2); 209 + src_qemu_args = 210 + g_strdup_printf("-object dbus-vmstate,id=dv,addr=%s", srcaddr[0]); 211 + 212 + dstbus = g_test_dbus_new(G_TEST_DBUS_NONE); 213 + g_test_dbus_up(dstbus); 214 + dstconnA = get_connection(test, &owndstA); 215 + dstserverA = get_server(dstconnA, &test->dstA, &idA); 216 + if (!test->without_dst_b) { 217 + dstconnB = get_connection(test, &owndstB); 218 + dstserverB = get_server(dstconnB, &test->dstB, &idB); 219 + } 220 + 221 + dstaddr = g_strsplit(g_test_dbus_get_bus_address(dstbus), ",", 2); 222 + dst_qemu_args = 223 + g_strdup_printf("-object dbus-vmstate,id=dv,addr=%s -incoming %s", 224 + dstaddr[0], uri); 225 + 226 + src_qemu = qtest_init(src_qemu_args); 227 + dst_qemu = qtest_init(dst_qemu_args); 228 + set_id_list(test, src_qemu); 229 + set_id_list(test, dst_qemu); 230 + 231 + thread = g_thread_new("dbus-vmstate-thread", dbus_vmstate_thread, loop); 232 + 233 + migrate_qmp(src_qemu, uri, "{}"); 234 + test->src_qemu = src_qemu; 235 + if (test->migrate_fail) { 236 + wait_for_migration_fail(src_qemu, true); 237 + qtest_set_expected_status(dst_qemu, 1); 238 + } else { 239 + wait_for_migration_complete(src_qemu); 240 + } 241 + 242 + qtest_quit(dst_qemu); 243 + qtest_quit(src_qemu); 244 + g_bus_unown_name(ownsrcA); 245 + g_bus_unown_name(ownsrcB); 246 + g_bus_unown_name(owndstA); 247 + if (!test->without_dst_b) { 248 + g_bus_unown_name(owndstB); 249 + } 250 + 251 + g_main_loop_quit(test->loop); 252 + } 253 + 254 + static void 255 + check_not_migrated(TestServer *s, TestServer *d) 256 + { 257 + assert(!s->save_called); 258 + assert(!s->load_called); 259 + assert(!d->save_called); 260 + assert(!d->load_called); 261 + } 262 + 263 + static void 264 + check_migrated(TestServer *s, TestServer *d) 265 + { 266 + assert(s->save_called); 267 + assert(!s->load_called); 268 + assert(!d->save_called); 269 + assert(d->load_called); 270 + } 271 + 272 + static void 273 + test_dbus_vmstate_without_list(void) 274 + { 275 + Test test = { 0, }; 276 + 277 + test_dbus_vmstate(&test); 278 + 279 + check_migrated(&test.srcA, &test.dstA); 280 + check_migrated(&test.srcB, &test.dstB); 281 + } 282 + 283 + static void 284 + test_dbus_vmstate_with_list(void) 285 + { 286 + Test test = { .id_list = "idA,idB" }; 287 + 288 + test_dbus_vmstate(&test); 289 + 290 + check_migrated(&test.srcA, &test.dstA); 291 + check_migrated(&test.srcB, &test.dstB); 292 + } 293 + 294 + static void 295 + test_dbus_vmstate_only_a(void) 296 + { 297 + Test test = { .id_list = "idA" }; 298 + 299 + test_dbus_vmstate(&test); 300 + 301 + check_migrated(&test.srcA, &test.dstA); 302 + check_not_migrated(&test.srcB, &test.dstB); 303 + } 304 + 305 + static void 306 + test_dbus_vmstate_missing_src(void) 307 + { 308 + Test test = { .id_list = "idA,idC", .migrate_fail = true }; 309 + 310 + /* run in subprocess to silence QEMU error reporting */ 311 + if (g_test_subprocess()) { 312 + test_dbus_vmstate(&test); 313 + check_not_migrated(&test.srcA, &test.dstA); 314 + check_not_migrated(&test.srcB, &test.dstB); 315 + return; 316 + } 317 + 318 + g_test_trap_subprocess(NULL, 0, 0); 319 + g_test_trap_assert_passed(); 320 + } 321 + 322 + static void 323 + test_dbus_vmstate_missing_dst(void) 324 + { 325 + Test test = { .id_list = "idA,idB", 326 + .without_dst_b = true, 327 + .migrate_fail = true }; 328 + 329 + /* run in subprocess to silence QEMU error reporting */ 330 + if (g_test_subprocess()) { 331 + test_dbus_vmstate(&test); 332 + assert(test.srcA.save_called); 333 + assert(test.srcB.save_called); 334 + assert(!test.dstB.save_called); 335 + return; 336 + } 337 + 338 + g_test_trap_subprocess(NULL, 0, 0); 339 + g_test_trap_assert_passed(); 340 + } 341 + 342 + int 343 + main(int argc, char **argv) 344 + { 345 + GError *err = NULL; 346 + g_autofree char *dbus_daemon = NULL; 347 + int ret; 348 + 349 + dbus_daemon = g_build_filename(G_STRINGIFY(SRCDIR), 350 + "tests", 351 + "dbus-vmstate-daemon.sh", 352 + NULL); 353 + g_setenv("G_TEST_DBUS_DAEMON", dbus_daemon, true); 354 + 355 + g_test_init(&argc, &argv, NULL); 356 + 357 + workdir = g_dir_make_tmp("dbus-vmstate-test-XXXXXX", &err); 358 + if (!workdir) { 359 + g_error("Unable to create temporary dir: %s\n", err->message); 360 + exit(1); 361 + } 362 + 363 + g_setenv("DBUS_VMSTATE_TEST_TMPDIR", workdir, true); 364 + 365 + qtest_add_func("/dbus-vmstate/without-list", 366 + test_dbus_vmstate_without_list); 367 + qtest_add_func("/dbus-vmstate/with-list", 368 + test_dbus_vmstate_with_list); 369 + qtest_add_func("/dbus-vmstate/only-a", 370 + test_dbus_vmstate_only_a); 371 + qtest_add_func("/dbus-vmstate/missing-src", 372 + test_dbus_vmstate_missing_src); 373 + qtest_add_func("/dbus-vmstate/missing-dst", 374 + test_dbus_vmstate_missing_dst); 375 + 376 + ret = g_test_run(); 377 + 378 + rmdir(workdir); 379 + g_free(workdir); 380 + 381 + return ret; 382 + }
+12
tests/dbus-vmstate1.xml
··· 1 + <?xml version="1.0"?> 2 + <node name="/" xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd"> 3 + <interface name="org.qemu.VMState1"> 4 + <property name="Id" type="s" access="read"/> 5 + <method name="Load"> 6 + <arg type="ay" name="data" direction="in"/> 7 + </method> 8 + <method name="Save"> 9 + <arg type="ay" name="data" direction="out"/> 10 + </method> 11 + </interface> 12 + </node>
+1
tests/docker/dockerfiles/centos7.docker
··· 8 8 bzip2-devel \ 9 9 ccache \ 10 10 csnappy-devel \ 11 + dbus-daemon \ 11 12 flex \ 12 13 gcc-c++ \ 13 14 gcc \
+1
tests/docker/dockerfiles/debian10.docker
··· 21 21 build-essential \ 22 22 ca-certificates \ 23 23 clang \ 24 + dbus \ 24 25 flex \ 25 26 gettext \ 26 27 git \
+1
tests/docker/dockerfiles/fedora.docker
··· 8 8 ccache \ 9 9 clang \ 10 10 cyrus-sasl-devel \ 11 + dbus-daemon \ 11 12 device-mapper-multipath-devel \ 12 13 findutils \ 13 14 flex \
+1
tests/docker/dockerfiles/ubuntu.docker
··· 13 13 ENV PACKAGES flex bison \ 14 14 ccache \ 15 15 clang \ 16 + dbus \ 16 17 gcc \ 17 18 gettext \ 18 19 git \
+167
tests/migration-helpers.c
··· 1 + /* 2 + * QTest migration helpers 3 + * 4 + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates 5 + * based on the vhost-user-test.c that is: 6 + * Copyright (c) 2014 Virtual Open Systems Sarl. 7 + * 8 + * This work is licensed under the terms of the GNU GPL, version 2 or later. 9 + * See the COPYING file in the top-level directory. 10 + * 11 + */ 12 + 13 + #include "qemu/osdep.h" 14 + #include "qapi/qmp/qjson.h" 15 + 16 + #include "migration-helpers.h" 17 + 18 + bool got_stop; 19 + 20 + static void stop_cb(void *opaque, const char *name, QDict *data) 21 + { 22 + if (!strcmp(name, "STOP")) { 23 + got_stop = true; 24 + } 25 + } 26 + 27 + /* 28 + * Events can get in the way of responses we are actually waiting for. 29 + */ 30 + QDict *wait_command_fd(QTestState *who, int fd, const char *command, ...) 31 + { 32 + va_list ap; 33 + 34 + va_start(ap, command); 35 + qtest_qmp_vsend_fds(who, &fd, 1, command, ap); 36 + va_end(ap); 37 + 38 + return qtest_qmp_receive_success(who, stop_cb, NULL); 39 + } 40 + 41 + /* 42 + * Events can get in the way of responses we are actually waiting for. 43 + */ 44 + QDict *wait_command(QTestState *who, const char *command, ...) 45 + { 46 + va_list ap; 47 + 48 + va_start(ap, command); 49 + qtest_qmp_vsend(who, command, ap); 50 + va_end(ap); 51 + 52 + return qtest_qmp_receive_success(who, stop_cb, NULL); 53 + } 54 + 55 + /* 56 + * Send QMP command "migrate". 57 + * Arguments are built from @fmt... (formatted like 58 + * qobject_from_jsonf_nofail()) with "uri": @uri spliced in. 59 + */ 60 + void migrate_qmp(QTestState *who, const char *uri, const char *fmt, ...) 61 + { 62 + va_list ap; 63 + QDict *args, *rsp; 64 + 65 + va_start(ap, fmt); 66 + args = qdict_from_vjsonf_nofail(fmt, ap); 67 + va_end(ap); 68 + 69 + g_assert(!qdict_haskey(args, "uri")); 70 + qdict_put_str(args, "uri", uri); 71 + 72 + rsp = qtest_qmp(who, "{ 'execute': 'migrate', 'arguments': %p}", args); 73 + 74 + g_assert(qdict_haskey(rsp, "return")); 75 + qobject_unref(rsp); 76 + } 77 + 78 + /* 79 + * Note: caller is responsible to free the returned object via 80 + * qobject_unref() after use 81 + */ 82 + QDict *migrate_query(QTestState *who) 83 + { 84 + return wait_command(who, "{ 'execute': 'query-migrate' }"); 85 + } 86 + 87 + /* 88 + * Note: caller is responsible to free the returned object via 89 + * g_free() after use 90 + */ 91 + static gchar *migrate_query_status(QTestState *who) 92 + { 93 + QDict *rsp_return = migrate_query(who); 94 + gchar *status = g_strdup(qdict_get_str(rsp_return, "status")); 95 + 96 + g_assert(status); 97 + qobject_unref(rsp_return); 98 + 99 + return status; 100 + } 101 + 102 + static bool check_migration_status(QTestState *who, const char *goal, 103 + const char **ungoals) 104 + { 105 + bool ready; 106 + char *current_status; 107 + const char **ungoal; 108 + 109 + current_status = migrate_query_status(who); 110 + ready = strcmp(current_status, goal) == 0; 111 + if (!ungoals) { 112 + g_assert_cmpstr(current_status, !=, "failed"); 113 + /* 114 + * If looking for a state other than completed, 115 + * completion of migration would cause the test to 116 + * hang. 117 + */ 118 + if (strcmp(goal, "completed") != 0) { 119 + g_assert_cmpstr(current_status, !=, "completed"); 120 + } 121 + } else { 122 + for (ungoal = ungoals; *ungoal; ungoal++) { 123 + g_assert_cmpstr(current_status, !=, *ungoal); 124 + } 125 + } 126 + g_free(current_status); 127 + return ready; 128 + } 129 + 130 + void wait_for_migration_status(QTestState *who, 131 + const char *goal, const char **ungoals) 132 + { 133 + while (!check_migration_status(who, goal, ungoals)) { 134 + usleep(1000); 135 + } 136 + } 137 + 138 + void wait_for_migration_complete(QTestState *who) 139 + { 140 + wait_for_migration_status(who, "completed", NULL); 141 + } 142 + 143 + void wait_for_migration_fail(QTestState *from, bool allow_active) 144 + { 145 + QDict *rsp_return; 146 + char *status; 147 + bool failed; 148 + 149 + do { 150 + status = migrate_query_status(from); 151 + bool result = !strcmp(status, "setup") || !strcmp(status, "failed") || 152 + (allow_active && !strcmp(status, "active")); 153 + if (!result) { 154 + fprintf(stderr, "%s: unexpected status status=%s allow_active=%d\n", 155 + __func__, status, allow_active); 156 + } 157 + g_assert(result); 158 + failed = !strcmp(status, "failed"); 159 + g_free(status); 160 + } while (!failed); 161 + 162 + /* Is the machine currently running? */ 163 + rsp_return = wait_command(from, "{ 'execute': 'query-status' }"); 164 + g_assert(qdict_haskey(rsp_return, "running")); 165 + g_assert(qdict_get_bool(rsp_return, "running")); 166 + qobject_unref(rsp_return); 167 + }
+37
tests/migration-helpers.h
··· 1 + /* 2 + * QTest migration helpers 3 + * 4 + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates 5 + * based on the vhost-user-test.c that is: 6 + * Copyright (c) 2014 Virtual Open Systems Sarl. 7 + * 8 + * This work is licensed under the terms of the GNU GPL, version 2 or later. 9 + * See the COPYING file in the top-level directory. 10 + * 11 + */ 12 + #ifndef MIGRATION_HELPERS_H_ 13 + #define MIGRATION_HELPERS_H_ 14 + 15 + #include "libqtest.h" 16 + 17 + extern bool got_stop; 18 + 19 + GCC_FMT_ATTR(3, 4) 20 + QDict *wait_command_fd(QTestState *who, int fd, const char *command, ...); 21 + 22 + GCC_FMT_ATTR(2, 3) 23 + QDict *wait_command(QTestState *who, const char *command, ...); 24 + 25 + GCC_FMT_ATTR(3, 4) 26 + void migrate_qmp(QTestState *who, const char *uri, const char *fmt, ...); 27 + 28 + QDict *migrate_query(QTestState *who); 29 + 30 + void wait_for_migration_status(QTestState *who, 31 + const char *goal, const char **ungoals); 32 + 33 + void wait_for_migration_complete(QTestState *who); 34 + 35 + void wait_for_migration_fail(QTestState *from, bool allow_active); 36 + 37 + #endif /* MIGRATION_HELPERS_H_ */
+11 -165
tests/migration-test.c
··· 14 14 15 15 #include "libqtest.h" 16 16 #include "qapi/qmp/qdict.h" 17 - #include "qapi/qmp/qjson.h" 18 17 #include "qemu/module.h" 19 18 #include "qemu/option.h" 20 19 #include "qemu/range.h" ··· 24 23 #include "qapi/qobject-input-visitor.h" 25 24 #include "qapi/qobject-output-visitor.h" 26 25 26 + #include "migration-helpers.h" 27 27 #include "migration/migration-test.h" 28 28 29 29 /* TODO actually test the results and get rid of this */ ··· 31 31 32 32 unsigned start_address; 33 33 unsigned end_address; 34 - bool got_stop; 35 34 static bool uffd_feature_thread_id; 36 35 37 36 #if defined(__linux__) ··· 157 156 } while (true); 158 157 } 159 158 160 - static void stop_cb(void *opaque, const char *name, QDict *data) 161 - { 162 - if (!strcmp(name, "STOP")) { 163 - got_stop = true; 164 - } 165 - } 166 - 167 - /* 168 - * Events can get in the way of responses we are actually waiting for. 169 - */ 170 - GCC_FMT_ATTR(3, 4) 171 - static QDict *wait_command_fd(QTestState *who, int fd, const char *command, ...) 172 - { 173 - va_list ap; 174 - 175 - va_start(ap, command); 176 - qtest_qmp_vsend_fds(who, &fd, 1, command, ap); 177 - va_end(ap); 178 - 179 - return qtest_qmp_receive_success(who, stop_cb, NULL); 180 - } 181 - 182 - /* 183 - * Events can get in the way of responses we are actually waiting for. 184 - */ 185 - GCC_FMT_ATTR(2, 3) 186 - static QDict *wait_command(QTestState *who, const char *command, ...) 187 - { 188 - va_list ap; 189 - 190 - va_start(ap, command); 191 - qtest_qmp_vsend(who, command, ap); 192 - va_end(ap); 193 - 194 - return qtest_qmp_receive_success(who, stop_cb, NULL); 195 - } 196 - 197 - /* 198 - * Note: caller is responsible to free the returned object via 199 - * qobject_unref() after use 200 - */ 201 - static QDict *migrate_query(QTestState *who) 202 - { 203 - return wait_command(who, "{ 'execute': 'query-migrate' }"); 204 - } 205 - 206 - /* 207 - * Note: caller is responsible to free the returned object via 208 - * g_free() after use 209 - */ 210 - static gchar *migrate_query_status(QTestState *who) 211 - { 212 - QDict *rsp_return = migrate_query(who); 213 - gchar *status = g_strdup(qdict_get_str(rsp_return, "status")); 214 - 215 - g_assert(status); 216 - qobject_unref(rsp_return); 217 - 218 - return status; 219 - } 220 - 221 159 /* 222 160 * It's tricky to use qemu's migration event capability with qtest, 223 161 * events suddenly appearing confuse the qmp()/hmp() responses. ··· 265 203 qobject_unref(rsp_return); 266 204 } 267 205 268 - static bool check_migration_status(QTestState *who, const char *goal, 269 - const char **ungoals) 270 - { 271 - bool ready; 272 - char *current_status; 273 - const char **ungoal; 274 - 275 - current_status = migrate_query_status(who); 276 - ready = strcmp(current_status, goal) == 0; 277 - if (!ungoals) { 278 - g_assert_cmpstr(current_status, !=, "failed"); 279 - /* 280 - * If looking for a state other than completed, 281 - * completion of migration would cause the test to 282 - * hang. 283 - */ 284 - if (strcmp(goal, "completed") != 0) { 285 - g_assert_cmpstr(current_status, !=, "completed"); 286 - } 287 - } else { 288 - for (ungoal = ungoals; *ungoal; ungoal++) { 289 - g_assert_cmpstr(current_status, !=, *ungoal); 290 - } 291 - } 292 - g_free(current_status); 293 - return ready; 294 - } 295 - 296 - static void wait_for_migration_status(QTestState *who, 297 - const char *goal, 298 - const char **ungoals) 299 - { 300 - while (!check_migration_status(who, goal, ungoals)) { 301 - usleep(1000); 302 - } 303 - } 304 - 305 - static void wait_for_migration_complete(QTestState *who) 306 - { 307 - wait_for_migration_status(who, "completed", NULL); 308 - } 309 - 310 206 static void wait_for_migration_pass(QTestState *who) 311 207 { 312 208 uint64_t initial_pass = get_migration_pass(who); ··· 502 398 "'capabilities': [ { " 503 399 "'capability': %s, 'state': %i } ] } }", 504 400 capability, value); 505 - g_assert(qdict_haskey(rsp, "return")); 506 - qobject_unref(rsp); 507 - } 508 - 509 - /* 510 - * Send QMP command "migrate". 511 - * Arguments are built from @fmt... (formatted like 512 - * qobject_from_jsonf_nofail()) with "uri": @uri spliced in. 513 - */ 514 - GCC_FMT_ATTR(3, 4) 515 - static void migrate(QTestState *who, const char *uri, const char *fmt, ...) 516 - { 517 - va_list ap; 518 - QDict *args, *rsp; 519 - 520 - va_start(ap, fmt); 521 - args = qdict_from_vjsonf_nofail(fmt, ap); 522 - va_end(ap); 523 - 524 - g_assert(!qdict_haskey(args, "uri")); 525 - qdict_put_str(args, "uri", uri); 526 - 527 - rsp = qtest_qmp(who, "{ 'execute': 'migrate', 'arguments': %p}", args); 528 - 529 401 g_assert(qdict_haskey(rsp, "return")); 530 402 qobject_unref(rsp); 531 403 } ··· 800 672 /* Wait for the first serial output from the source */ 801 673 wait_for_serial("src_serial"); 802 674 803 - migrate(from, uri, "{}"); 675 + migrate_qmp(from, uri, "{}"); 804 676 g_free(uri); 805 677 806 678 wait_for_migration_pass(from); ··· 891 763 wait_for_migration_status(from, "postcopy-paused", 892 764 (const char * []) { "failed", "active", 893 765 "completed", NULL }); 894 - migrate(from, uri, "{'resume': true}"); 766 + migrate_qmp(from, uri, "{'resume': true}"); 895 767 g_free(uri); 896 768 897 769 /* Restore the postcopy bandwidth to unlimited */ ··· 900 772 migrate_postcopy_complete(from, to); 901 773 } 902 774 903 - static void wait_for_migration_fail(QTestState *from, bool allow_active) 904 - { 905 - QDict *rsp_return; 906 - char *status; 907 - bool failed; 908 - 909 - do { 910 - status = migrate_query_status(from); 911 - bool result = !strcmp(status, "setup") || !strcmp(status, "failed") || 912 - (allow_active && !strcmp(status, "active")); 913 - if (!result) { 914 - fprintf(stderr, "%s: unexpected status status=%s allow_active=%d\n", 915 - __func__, status, allow_active); 916 - } 917 - g_assert(result); 918 - failed = !strcmp(status, "failed"); 919 - g_free(status); 920 - } while (!failed); 921 - 922 - /* Is the machine currently running? */ 923 - rsp_return = wait_command(from, "{ 'execute': 'query-status' }"); 924 - g_assert(qdict_haskey(rsp_return, "running")); 925 - g_assert(qdict_get_bool(rsp_return, "running")); 926 - qobject_unref(rsp_return); 927 - } 928 - 929 775 static void test_baddest(void) 930 776 { 931 777 MigrateStart *args = migrate_start_new(); ··· 936 782 if (test_migrate_start(&from, &to, "tcp:0:0", args)) { 937 783 return; 938 784 } 939 - migrate(from, "tcp:0:0", "{}"); 785 + migrate_qmp(from, "tcp:0:0", "{}"); 940 786 wait_for_migration_fail(from, false); 941 787 test_migrate_end(from, to, false); 942 788 } ··· 963 809 /* Wait for the first serial output from the source */ 964 810 wait_for_serial("src_serial"); 965 811 966 - migrate(from, uri, "{}"); 812 + migrate_qmp(from, uri, "{}"); 967 813 968 814 wait_for_migration_pass(from); 969 815 ··· 1000 846 /* Wait for the first serial output from the source */ 1001 847 wait_for_serial("src_serial"); 1002 848 1003 - migrate(from, uri, "{}"); 849 + migrate_qmp(from, uri, "{}"); 1004 850 1005 851 wait_for_migration_pass(from); 1006 852 ··· 1047 893 /* Wait for the first serial output from the source */ 1048 894 wait_for_serial("src_serial"); 1049 895 1050 - migrate(from, uri, "{}"); 896 + migrate_qmp(from, uri, "{}"); 1051 897 1052 898 wait_for_migration_pass(from); 1053 899 ··· 1098 944 1099 945 uri = migrate_get_socket_address(to, "socket-address"); 1100 946 1101 - migrate(from, uri, "{}"); 947 + migrate_qmp(from, uri, "{}"); 1102 948 1103 949 wait_for_migration_pass(from); 1104 950 ··· 1167 1013 close(pair[1]); 1168 1014 1169 1015 /* Start migration to the 2nd socket*/ 1170 - migrate(from, "fd:fd-mig", "{}"); 1016 + migrate_qmp(from, "fd:fd-mig", "{}"); 1171 1017 1172 1018 wait_for_migration_pass(from); 1173 1019 ··· 1222 1068 /* Wait for the first serial output from the source */ 1223 1069 wait_for_serial("src_serial"); 1224 1070 1225 - migrate(from, uri, "{}"); 1071 + migrate_qmp(from, uri, "{}"); 1226 1072 1227 1073 if (should_fail) { 1228 1074 qtest_set_expected_status(to, 1); ··· 1316 1162 /* Wait for the first serial output from the source */ 1317 1163 wait_for_serial("src_serial"); 1318 1164 1319 - migrate(from, uri, "{}"); 1165 + migrate_qmp(from, uri, "{}"); 1320 1166 1321 1167 /* Wait for throttling begins */ 1322 1168 percentage = 0;
+3
util/Makefile.objs
··· 56 56 util-obj-$(CONFIG_LINUX) += vfio-helpers.o 57 57 util-obj-$(CONFIG_POSIX) += drm.o 58 58 util-obj-y += guest-random.o 59 + util-obj-$(CONFIG_GIO) += dbus.o 60 + dbus.o-cflags = $(GIO_CFLAGS) 61 + dbus.o-libs = $(GIO_LIBS)
+57
util/dbus.c
··· 1 + /* 2 + * Helpers for using D-Bus 3 + * 4 + * Copyright (C) 2019 Red Hat, Inc. 5 + * 6 + * This work is licensed under the terms of the GNU GPL, version 2. See 7 + * the COPYING file in the top-level directory. 8 + */ 9 + 10 + #include "qemu/osdep.h" 11 + #include "qemu/dbus.h" 12 + #include "qemu/error-report.h" 13 + #include "qapi/error.h" 14 + 15 + /* 16 + * qemu_dbus_get_queued_owners() - return the list of queued unique names 17 + * @connection: A GDBusConnection 18 + * @name: a service name 19 + * 20 + * Return: a GStrv of unique names, or NULL on failure. 21 + */ 22 + GStrv 23 + qemu_dbus_get_queued_owners(GDBusConnection *connection, const char *name, 24 + Error **errp) 25 + { 26 + g_autoptr(GDBusProxy) proxy = NULL; 27 + g_autoptr(GVariant) result = NULL; 28 + g_autoptr(GVariant) child = NULL; 29 + g_autoptr(GError) err = NULL; 30 + 31 + proxy = g_dbus_proxy_new_sync(connection, G_DBUS_PROXY_FLAGS_NONE, NULL, 32 + "org.freedesktop.DBus", 33 + "/org/freedesktop/DBus", 34 + "org.freedesktop.DBus", 35 + NULL, &err); 36 + if (!proxy) { 37 + error_setg(errp, "Failed to create DBus proxy: %s", err->message); 38 + return NULL; 39 + } 40 + 41 + result = g_dbus_proxy_call_sync(proxy, "ListQueuedOwners", 42 + g_variant_new("(s)", name), 43 + G_DBUS_CALL_FLAGS_NO_AUTO_START, 44 + -1, NULL, &err); 45 + if (!result) { 46 + if (g_error_matches(err, 47 + G_DBUS_ERROR, 48 + G_DBUS_ERROR_NAME_HAS_NO_OWNER)) { 49 + return g_new0(char *, 1); 50 + } 51 + error_setg(errp, "Failed to call ListQueuedOwners: %s", err->message); 52 + return NULL; 53 + } 54 + 55 + child = g_variant_get_child_value(result, 0); 56 + return g_variant_dup_strv(child, NULL); 57 + }