qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH v6 5/8] Add dbus-vmstate object


From: Dr. David Alan Gilbert
Subject: Re: [PATCH v6 5/8] Add dbus-vmstate object
Date: Fri, 13 Dec 2019 16:32:30 +0000
User-agent: Mutt/1.13.0 (2019-11-30)

* Marc-André Lureau (address@hidden) 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 / address@hidden / Manchester, UK




reply via email to

[Prev in Thread] Current Thread [Next in Thread]