From: "Dr. David Alan Gilbert" <dgilbert@redhat.com>
To: "Marc-André Lureau" <marcandre.lureau@redhat.com>
Cc: mprivozn@redhat.com, pbonzini@redhat.com, berrange@redhat.com,
qemu-devel@nongnu.org, quintela@redhat.com
Subject: Re: [PATCH v6 5/8] Add dbus-vmstate object
Date: Fri, 13 Dec 2019 16:32:30 +0000 [thread overview]
Message-ID: <20191213163230.GL3713@work-vm> (raw)
In-Reply-To: <20191211134506.1803403-6-marcandre.lureau@redhat.com>
* Marc-André Lureau (marcandre.lureau@redhat.com) wrote:
<snip>
Generally from the migration side I'm OK; I don't know that
much glib stuff as you're using, so I'll leave that to Dan.
> + if (!result) {
> + error_report("Failed to Load: %s", err->message);
> + return -1;
> + }
> +
> + return 0;
> +}
> +
> +static int dbus_vmstate_post_load(void *opaque, int version_id)
> +{
> + DBusVMState *self = DBUS_VMSTATE(opaque);
> + g_autoptr(GInputStream) m = NULL;
> + g_autoptr(GDataInputStream) s = NULL;
> + g_autoptr(GError) err = NULL;
> + g_autoptr(GHashTable) proxies = NULL;
> + uint32_t nelem;
> +
> + proxies = dbus_get_proxies(self, &err);
> + if (!proxies) {
> + error_report("Failed to get proxies: %s", err->message);
Generally can you put either the function name or something in the error
to point us in the right direction; then if a user gets an error and it
says dbus_vmstate_post_load: Failed to get proxies (e.g. by using %s:
...__func__) then any random qemu dev will be able to resolve the blame
pointer quickly and not trying to guess which proxies we're talking
about.
You might also like to add some trace_ calls to watch what is going on.
Dave
> + return -1;
> + }
> +
> + m = g_memory_input_stream_new_from_data(self->data, self->data_size, NULL);
> + s = g_data_input_stream_new(m);
> + g_data_input_stream_set_byte_order(s, G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN);
> +
> + nelem = g_data_input_stream_read_uint32(s, NULL, &err);
> + if (err) {
> + goto error;
> + }
> +
> + while (nelem > 0) {
> + GDBusProxy *proxy = NULL;
> + uint32_t len;
> + gsize bytes_read, avail;
> + char id[256];
> +
> + len = g_data_input_stream_read_uint32(s, NULL, &err);
> + if (err) {
> + goto error;
> + }
> + if (len >= 256) {
> + error_report("Invalid DBus vmstate proxy name %u", len);
> + return -1;
> + }
> + if (!g_input_stream_read_all(G_INPUT_STREAM(s), id, len,
> + &bytes_read, NULL, &err)) {
> + goto error;
> + }
> + g_return_val_if_fail(bytes_read == len, -1);
> + id[len] = 0;
> +
> + proxy = g_hash_table_lookup(proxies, id);
> + if (!proxy) {
> + error_report("Failed to find proxy Id '%s'", id);
> + return -1;
> + }
> +
> + len = g_data_input_stream_read_uint32(s, NULL, &err);
> + avail = g_buffered_input_stream_get_available(
> + G_BUFFERED_INPUT_STREAM(s));
> +
> + if (len > DBUS_VMSTATE_SIZE_LIMIT || len > avail) {
> + error_report("Invalid vmstate size: %u", len);
> + return -1;
> + }
> +
> + if (dbus_load_state_proxy(proxy,
> + g_buffered_input_stream_peek_buffer(G_BUFFERED_INPUT_STREAM(s),
> + NULL),
> + len) < 0) {
> + error_report("Failed to restore Id '%s'", id);
> + return -1;
> + }
> +
> + if (!g_seekable_seek(G_SEEKABLE(s), len, G_SEEK_CUR, NULL, &err)) {
> + goto error;
> + }
> +
> + nelem -= 1;
> + }
> +
> + return 0;
> +
> +error:
> + error_report("Failed to read from stream: %s", err->message);
> + return -1;
> +}
> +
> +static void
> +dbus_save_state_proxy(gpointer key,
> + gpointer value,
> + gpointer user_data)
> +{
> + GDataOutputStream *s = user_data;
> + const char *id = key;
> + GDBusProxy *proxy = value;
> + g_autoptr(GVariant) result = NULL;
> + g_autoptr(GVariant) child = NULL;
> + g_autoptr(GError) err = NULL;
> + const uint8_t *data;
> + gsize size;
> +
> + result = g_dbus_proxy_call_sync(proxy, "Save",
> + NULL, G_DBUS_CALL_FLAGS_NO_AUTO_START,
> + -1, NULL, &err);
> + if (!result) {
> + error_report("Failed to Save: %s", err->message);
> + return;
> + }
> +
> + child = g_variant_get_child_value(result, 0);
> + data = g_variant_get_fixed_array(child, &size, sizeof(char));
> + if (!data) {
> + error_report("Failed to Save: not a byte array");
> + return;
> + }
> + if (size > DBUS_VMSTATE_SIZE_LIMIT) {
> + error_report("Too large vmstate data to save: %" G_GSIZE_FORMAT, size);
> + return;
> + }
> +
> + if (!g_data_output_stream_put_uint32(s, strlen(id), NULL, &err) ||
> + !g_data_output_stream_put_string(s, id, NULL, &err) ||
> + !g_data_output_stream_put_uint32(s, size, NULL, &err) ||
> + !g_output_stream_write_all(G_OUTPUT_STREAM(s),
> + data, size, NULL, NULL, &err)) {
> + error_report("Failed to write to stream: %s", err->message);
> + }
> +}
> +
> +static int dbus_vmstate_pre_save(void *opaque)
> +{
> + DBusVMState *self = DBUS_VMSTATE(opaque);
> + g_autoptr(GOutputStream) m = NULL;
> + g_autoptr(GDataOutputStream) s = NULL;
> + g_autoptr(GHashTable) proxies = NULL;
> + g_autoptr(GError) err = NULL;
> +
> + proxies = dbus_get_proxies(self, &err);
> + if (!proxies) {
> + error_report("Failed to get proxies: %s", err->message);
> + return -1;
> + }
> +
> + m = g_memory_output_stream_new_resizable();
> + s = g_data_output_stream_new(m);
> + g_data_output_stream_set_byte_order(s, G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN);
> +
> + if (!g_data_output_stream_put_uint32(s, g_hash_table_size(proxies),
> + NULL, &err)) {
> + error_report("Failed to write to stream: %s", err->message);
> + return -1;
> + }
> +
> + g_hash_table_foreach(proxies, dbus_save_state_proxy, s);
> +
> + if (g_memory_output_stream_get_size(G_MEMORY_OUTPUT_STREAM(m))
> + > UINT32_MAX) {
> + error_report("DBus vmstate buffer is too large");
> + return -1;
> + }
> +
> + if (!g_output_stream_close(G_OUTPUT_STREAM(m), NULL, &err)) {
> + error_report("Failed to close stream: %s", err->message);
> + return -1;
> + }
> +
> + g_free(self->data);
> + self->data_size =
> + g_memory_output_stream_get_size(G_MEMORY_OUTPUT_STREAM(m));
> + self->data =
> + g_memory_output_stream_steal_data(G_MEMORY_OUTPUT_STREAM(m));
> +
> + return 0;
> +}
> +
> +static const VMStateDescription dbus_vmstate = {
> + .name = TYPE_DBUS_VMSTATE,
> + .version_id = 0,
> + .pre_save = dbus_vmstate_pre_save,
> + .post_load = dbus_vmstate_post_load,
> + .fields = (VMStateField[]) {
> + VMSTATE_UINT32(data_size, DBusVMState),
> + VMSTATE_VBUFFER_ALLOC_UINT32(data, DBusVMState, 0, 0, data_size),
> + VMSTATE_END_OF_LIST()
> + }
> +};
> +
> +static void
> +dbus_vmstate_complete(UserCreatable *uc, Error **errp)
> +{
> + DBusVMState *self = DBUS_VMSTATE(uc);
> + GError *err = NULL;
> + GDBusConnection *bus;
> +
> + if (!object_resolve_path_type("", TYPE_DBUS_VMSTATE, NULL)) {
> + error_setg(errp, "There is already an instance of %s",
> + TYPE_DBUS_VMSTATE);
> + return;
> + }
> +
> + if (!self->dbus_addr) {
> + error_setg(errp, QERR_MISSING_PARAMETER, "addr");
> + return;
> + }
> +
> + bus = g_dbus_connection_new_for_address_sync(self->dbus_addr,
> + G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT |
> + G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION,
> + NULL, NULL, &err);
> + if (err) {
> + error_setg(errp, "failed to connect to DBus: '%s'", err->message);
> + g_clear_error(&err);
> + }
> +
> + self->bus = bus;
> +
> + if (vmstate_register(VMSTATE_IF(self), -1, &dbus_vmstate, self) < 0) {
> + error_setg(errp, "Failed to register vmstate");
> + }
> +}
> +
> +static void
> +dbus_vmstate_finalize(Object *o)
> +{
> + DBusVMState *self = DBUS_VMSTATE(o);
> +
> + vmstate_unregister(VMSTATE_IF(self), &dbus_vmstate, self);
> +
> + g_clear_object(&self->bus);
> + g_free(self->dbus_addr);
> + g_free(self->id_list);
> + g_free(self->data);
> +}
> +
> +static char *
> +get_dbus_addr(Object *o, Error **errp)
> +{
> + DBusVMState *self = DBUS_VMSTATE(o);
> +
> + return g_strdup(self->dbus_addr);
> +}
> +
> +static void
> +set_dbus_addr(Object *o, const char *str, Error **errp)
> +{
> + DBusVMState *self = DBUS_VMSTATE(o);
> +
> + g_free(self->dbus_addr);
> + self->dbus_addr = g_strdup(str);
> +}
> +
> +static char *
> +get_id_list(Object *o, Error **errp)
> +{
> + DBusVMState *self = DBUS_VMSTATE(o);
> +
> + return g_strdup(self->id_list);
> +}
> +
> +static void
> +set_id_list(Object *o, const char *str, Error **errp)
> +{
> + DBusVMState *self = DBUS_VMSTATE(o);
> +
> + g_free(self->id_list);
> + self->id_list = g_strdup(str);
> +}
> +
> +static char *
> +dbus_vmstate_get_id(VMStateIf *vmif)
> +{
> + return g_strdup(TYPE_DBUS_VMSTATE);
> +}
> +
> +static void
> +dbus_vmstate_class_init(ObjectClass *oc, void *data)
> +{
> + UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
> + VMStateIfClass *vc = VMSTATE_IF_CLASS(oc);
> +
> + ucc->complete = dbus_vmstate_complete;
> + vc->get_id = dbus_vmstate_get_id;
> +
> + object_class_property_add_str(oc, "addr",
> + get_dbus_addr, set_dbus_addr,
> + &error_abort);
> + object_class_property_add_str(oc, "id-list",
> + get_id_list, set_id_list,
> + &error_abort);
> +}
> +
> +static const TypeInfo dbus_vmstate_info = {
> + .name = TYPE_DBUS_VMSTATE,
> + .parent = TYPE_OBJECT,
> + .instance_size = sizeof(DBusVMState),
> + .instance_finalize = dbus_vmstate_finalize,
> + .class_size = sizeof(DBusVMStateClass),
> + .class_init = dbus_vmstate_class_init,
> + .interfaces = (InterfaceInfo[]) {
> + { TYPE_USER_CREATABLE },
> + { TYPE_VMSTATE_IF },
> + { }
> + }
> +};
> +
> +static void
> +register_types(void)
> +{
> + type_register_static(&dbus_vmstate_info);
> +}
> +
> +type_init(register_types);
> diff --git a/docs/interop/dbus-vmstate.rst b/docs/interop/dbus-vmstate.rst
> new file mode 100644
> index 0000000000..8693891640
> --- /dev/null
> +++ b/docs/interop/dbus-vmstate.rst
> @@ -0,0 +1,74 @@
> +=============
> +D-Bus VMState
> +=============
> +
> +Introduction
> +============
> +
> +The QEMU dbus-vmstate object's aim is to migrate helpers' data running
> +on a QEMU D-Bus bus. (refer to the :doc:`dbus` document for
> +recommendation on D-Bus usage)
> +
> +Upon migration, QEMU will go through the queue of
> +``org.qemu.VMState1`` D-Bus name owners and query their ``Id``. It
> +must be unique among the helpers.
> +
> +It will then save arbitrary data of each Id to be transferred in the
> +migration stream and restored/loaded at the corresponding destination
> +helper.
> +
> +The data amount to be transferred is limited to 1Mb. The state must be
> +saved quickly (a few seconds maximum). (D-Bus imposes a time limit on
> +reply anyway, and migration would fail if data isn't given quickly
> +enough.)
> +
> +dbus-vmstate object can be configured with the expected list of
> +helpers by setting its ``id-list`` property, with a comma-separated
> +``Id`` list.
> +
> +Interface
> +=========
> +
> +On object path ``/org/qemu/VMState1``, the following
> +``org.qemu.VMState1`` interface should be implemented:
> +
> +.. code:: xml
> +
> + <interface name="org.qemu.VMState1">
> + <property name="Id" type="s" access="read"/>
> + <method name="Load">
> + <arg type="ay" name="data" direction="in"/>
> + </method>
> + <method name="Save">
> + <arg type="ay" name="data" direction="out"/>
> + </method>
> + </interface>
> +
> +"Id" property
> +-------------
> +
> +A string that identifies the helper uniquely. (maximum 256 bytes
> +including terminating NUL byte)
> +
> +.. note::
> +
> + The helper ID namespace is a separate namespace. In particular, it is not
> + related to QEMU "id" used in -object/-device objects.
> +
> +Load(in u8[] bytes) method
> +--------------------------
> +
> +The method called on destination with the state to restore.
> +
> +The helper may be initially started in a waiting state (with
> +an --incoming argument for example), and it may resume on success.
> +
> +An error may be returned to the caller.
> +
> +Save(out u8[] bytes) method
> +---------------------------
> +
> +The method called on the source to get the current state to be
> +migrated. The helper should continue to run normally.
> +
> +An error may be returned to the caller.
> diff --git a/docs/interop/dbus.rst b/docs/interop/dbus.rst
> index 3d760e4882..d9af608dc9 100644
> --- a/docs/interop/dbus.rst
> +++ b/docs/interop/dbus.rst
> @@ -97,3 +97,8 @@ the "D-Bus API Design Guidelines":
> https://dbus.freedesktop.org/doc/dbus-api-design.html
>
> The "org.qemu.*" prefix is reserved for the QEMU project.
> +
> +QEMU Interfaces
> +===============
> +
> +:doc:`dbus-vmstate`
> diff --git a/docs/interop/index.rst b/docs/interop/index.rst
> index ded134ea75..049387ac6d 100644
> --- a/docs/interop/index.rst
> +++ b/docs/interop/index.rst
> @@ -14,6 +14,7 @@ Contents:
>
> bitmaps
> dbus
> + dbus-vmstate
> live-block-operations
> pr-helper
> qemu-ga
> --
> 2.24.0.308.g228f53135a
>
--
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK
next prev parent reply other threads:[~2019-12-13 21:23 UTC|newest]
Thread overview: 22+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-12-11 13:44 [PATCH v6 0/8] Add dbus-vmstate Marc-André Lureau
2019-12-11 13:44 ` [PATCH v6 1/8] vmstate: add qom interface to get id Marc-André Lureau
2019-12-11 13:45 ` [PATCH v6 2/8] vmstate: replace DeviceState with VMStateIf Marc-André Lureau
2019-12-11 13:45 ` [PATCH v6 3/8] docs: start a document to describe D-Bus usage Marc-André Lureau
2019-12-12 11:36 ` Daniel P. Berrangé
2019-12-11 13:45 ` [PATCH v6 4/8] util: add dbus helper unit Marc-André Lureau
2019-12-12 11:38 ` Daniel P. Berrangé
2019-12-11 13:45 ` [PATCH v6 5/8] Add dbus-vmstate object Marc-André Lureau
2019-12-12 12:03 ` Daniel P. Berrangé
2019-12-16 7:44 ` Marc-André Lureau
2019-12-13 16:32 ` Dr. David Alan Gilbert [this message]
2019-12-11 13:45 ` [PATCH v6 6/8] configure: add GDBUS_CODEGEN Marc-André Lureau
2019-12-12 12:05 ` Daniel P. Berrangé
2019-12-19 12:54 ` Marc-André Lureau
2019-12-11 13:45 ` [PATCH v6 7/8] dockerfiles: add dbus-daemon to some of latest distributions Marc-André Lureau
2019-12-12 12:06 ` Daniel P. Berrangé
2019-12-19 12:23 ` Marc-André Lureau
2019-12-19 12:30 ` Daniel P. Berrangé
2019-12-11 13:45 ` [PATCH v6 8/8] tests: add dbus-vmstate-test Marc-André Lureau
2019-12-12 12:11 ` Daniel P. Berrangé
2019-12-13 18:20 ` Dr. David Alan Gilbert
2019-12-16 9:58 ` Daniel P. Berrangé
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20191213163230.GL3713@work-vm \
--to=dgilbert@redhat.com \
--cc=berrange@redhat.com \
--cc=marcandre.lureau@redhat.com \
--cc=mprivozn@redhat.com \
--cc=pbonzini@redhat.com \
--cc=qemu-devel@nongnu.org \
--cc=quintela@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).