[Spice-devel] [spice-gtk v1 6/6] Introduce SpiceMainChannelMigration

Victor Toso victortoso at redhat.com
Tue Sep 24 09:35:43 UTC 2019


Hi,

Sorry this commit log is actually horrible :)
I'll try to fix a little bit below but I hope review can still be
done on the overall idea.

On Tue, Sep 24, 2019 at 11:15:02AM +0200, Victor Toso wrote:
> From: Victor Toso <me at victortoso.com>
> 
> The migration is a complicated feature where there the Virtual
> Machine is its connection details from its host, now to be
> called source-host, to a new host, now called target-host.

The migration is a feature which the Virtual Machine will be
moved from the source-host to the target-host. The client will
connect to target-host during the migration.

> For the client, the migration can happen in different ways as
> supported by spice-protocol. The focus of this patch is to
> create an object that should be handling the state of the
> migration itself as much as possible.

Spice-protocol supports different types of migration but the
focus of this series is the seamless migration. Other types of
migration will be added later.

> With that goal in mind, we will be able to remove some code from
> channel-main and spice-session and point out what are their roles
> during the execution of each type of migration.
> 
> This patch introduces SpiceMainChannelMigration object. The name goes
> after channel-main as this is a feature that goes bounded to this
> channel. With this patch, we remove struct spice_migrate from
> channel-main and make that become the base for the new object.
> 
> Note that one design choice is to the channel-main not hold a
> reference to SpiceMainChannelMigration because, even if the migration
> messages comes from channel-main, the migration scenario happens for
> the whole session so, it is SpiceSession who holds a reference to
> SpiceMainChannelMigration.
> 
> In future patches, non-seamless migration can be moved to
> SpiceMainChannelMigration as well, reducing the logic in channel-main
> and spice-session.
> 
> This has been tested a few times with RHEL8 Guest under RHV 4.3, also
> under valgrind.
> 
> Signed-off-by: Victor Toso <victortoso at redhat.com>

Cheers,
Victor

> ---
>  doc/reference/meson.build    |   1 +
>  src/channel-main-migration.c | 398 +++++++++++++++++++++++++++++++++++
>  src/channel-main-migration.h |  63 ++++++
>  src/channel-main.c           | 217 ++++---------------
>  src/meson.build              |   2 +
>  src/spice-session-priv.h     |   4 +
>  src/spice-session.c          |  26 +++
>  7 files changed, 533 insertions(+), 178 deletions(-)
>  create mode 100644 src/channel-main-migration.c
>  create mode 100644 src/channel-main-migration.h
> 
> diff --git a/doc/reference/meson.build b/doc/reference/meson.build
> index 61c410f..d9d5203 100644
> --- a/doc/reference/meson.build
> +++ b/doc/reference/meson.build
> @@ -1,6 +1,7 @@
>  ignore_headers = [
>    'bio-gio.h',
>    'channel-display-priv.h',
> +  'channel-main-migration.h',
>    'channel-usbredir-priv.h',
>    'client_sw_canvas.h',
>    'continuation.h',
> diff --git a/src/channel-main-migration.c b/src/channel-main-migration.c
> new file mode 100644
> index 0000000..9063e0e
> --- /dev/null
> +++ b/src/channel-main-migration.c
> @@ -0,0 +1,398 @@
> +/*
> +   Copyright (C) 2019 Red Hat, Inc.
> +
> +   This library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   This library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with this library; if not, see <http://www.gnu.org/licenses/>.
> +*/
> +
> +#include "config.h"
> +
> +#include "channel-main-migration.h"
> +#include "spice-session-priv.h"
> +#include "spice-channel-priv.h"
> +
> +typedef struct _SpiceMainChannelMigrationPrivate SpiceMainChannelMigrationPrivate;
> +
> +struct _SpiceMainChannelMigrationPrivate
> +{
> +    SpiceSession *source_session;
> +    SpiceSession *target_session;
> +
> +    guint source_host_version;
> +    gboolean seamless_migration;
> +
> +    struct coroutine *source_coroutine;
> +
> +    guint num_channels; /* to migrate */
> +    guint num_migrating_channels;
> +};
> +
> +struct _SpiceMainChannelMigration
> +{
> +    GObject parent;
> +
> +    SpiceMainChannelMigrationPrivate *priv;
> +};
> +
> +struct _SpiceMainChannelMigrationClass
> +{
> +    GObjectClass parent_class;
> +};
> +
> +enum {
> +    PROP_COROUTINE_CONTEXT = 1,
> +    PROP_SEAMLESS_MIGRATION,
> +    PROP_SOURCE_HOST_VERSION,
> +    PROP_SOURCE_SESSION,
> +    PROP_TARGET_SESSION,
> +
> +    PROP_LAST,
> +};
> +
> +static GParamSpec *g_properties[PROP_LAST] = { NULL, };
> +
> +G_DEFINE_TYPE_WITH_PRIVATE(SpiceMainChannelMigration, spice_main_channel_migration, G_TYPE_OBJECT)
> +
> +/*******************************************************************************
> + * Helpers
> + ******************************************************************************/
> +
> +/* main context */
> +static gboolean
> +main_channel_migration_seamless_migration_handshake_cb(gpointer data)
> +{
> +    SpiceMainChannelMigration *self = SPICE_MAIN_CHANNEL_MIGRATION(data);
> +    SpiceChannel *channel = spice_session_lookup_channel(self->priv->target_session, SPICE_CHANNEL_MAIN, 0);
> +
> +    g_return_val_if_fail(spice_channel_get_state(channel) == SPICE_CHANNEL_STATE_MIGRATION_HANDSHAKE, FALSE);
> +
> +    spice_channel_set_state(channel, SPICE_CHANNEL_STATE_MIGRATING);
> +    self->priv->num_migrating_channels++;
> +
> +    spice_debug("migration: channel opened chan:%p (main) - %u/%u in migrating state",
> +                channel, self->priv->num_migrating_channels, self->priv->num_channels);
> +
> +    if (self->priv->num_migrating_channels == self->priv->num_channels) {
> +        coroutine_yieldto(self->priv->source_coroutine, NULL);
> +    }
> +    return FALSE;
> +}
> +
> +/* main context */
> +static bool
> +main_channel_migration_create_and_connect_channel(SpiceMainChannelMigration *self,
> +                                                  int channel_type,
> +                                                  int channel_id)
> +{
> +    spice_debug("migration: creating channel type: %d, id: %d", channel_type, channel_id);
> +
> +    SpiceChannel *channel = spice_channel_new(self->priv->target_session, channel_type, channel_id);
> +    bool success = (channel != NULL && spice_channel_connect(channel));
> +    if (!success) {
> +        spice_debug("migration: failed to create channel");
> +        return false;
> +    }
> +    self->priv->num_channels++;
> +    return true;
> +}
> +
> +/* main context */
> +static void
> +main_channel_migration_on_channel_event(SpiceChannel *channel,
> +                                        SpiceChannelEvent event,
> +                                        gpointer data)
> +{
> +    SpiceMainChannelMigration *self = SPICE_MAIN_CHANNEL_MIGRATION(data);
> +
> +    g_signal_handlers_disconnect_by_func(channel, main_channel_migration_on_channel_event, data);
> +    /* Keeping the security check. Should always be positive here, the main-channel at least */
> +    g_return_if_fail(self->priv->num_channels > 0);
> +
> +    switch (event) {
> +    case SPICE_CHANNEL_OPENED:
> +    {
> +        if (spice_channel_get_channel_type(channel) != SPICE_CHANNEL_MAIN) {
> +            spice_channel_set_state(channel, SPICE_CHANNEL_STATE_MIGRATING);
> +            self->priv->num_migrating_channels++;
> +            break;
> +        }
> +
> +        if (self->priv->seamless_migration) {
> +            /* Now that channel is open and seamless-migration is possible, we wait
> +             * for the seamless-migration-handshake ack/nack from target host */
> +            spice_channel_set_state(channel, SPICE_CHANNEL_STATE_MIGRATION_HANDSHAKE);
> +        } else {
> +            spice_channel_set_state(channel, SPICE_CHANNEL_STATE_MIGRATING);
> +            self->priv->num_migrating_channels++;
> +        }
> +
> +        /* now connect the rest of the channels */
> +        GList *it, *channels = spice_session_get_channels(self->priv->source_session);
> +        for (it = channels; it != NULL; it = it->next) {
> +            SpiceChannel *it_channel = SPICE_CHANNEL(it->data);
> +            if (spice_channel_get_channel_type(it_channel) != SPICE_CHANNEL_MAIN) {
> +                main_channel_migration_create_and_connect_channel(self,
> +                                        spice_channel_get_channel_type(it_channel),
> +                                        spice_channel_get_channel_id(it_channel));
> +            }
> +        }
> +        g_list_free(channels);
> +        break;
> +    }
> +    default:
> +        CHANNEL_DEBUG(channel, "error or unhandled channel event during migration: %u", event);
> +        /* go back to main channel to report error */
> +        coroutine_yieldto(self->priv->source_coroutine, NULL);
> +        return;
> +    }
> +
> +    spice_debug("migration: channel opened chan:%p - %u/%u in migrating state",
> +                channel, self->priv->num_migrating_channels, self->priv->num_channels);
> +
> +    if (self->priv->num_migrating_channels == self->priv->num_channels) {
> +        coroutine_yieldto(self->priv->source_coroutine, NULL);
> +    }
> +}
> +
> +/* main context */
> +static void
> +main_channel_migration_on_channel_new(SpiceSession *target_session,
> +                                      SpiceChannel *channel,
> +                                      gpointer data)
> +{
> +    g_signal_connect(channel, "channel-event",
> +                     G_CALLBACK(main_channel_migration_on_channel_event), data);
> +}
> +
> +/* main context */
> +static gboolean
> +main_channel_migration_run_cb(gpointer data)
> +{
> +    SpiceMainChannelMigration *self = SPICE_MAIN_CHANNEL_MIGRATION(data);
> +
> +    spice_session_set_migration_state(self->priv->target_session,
> +                                      SPICE_SESSION_MIGRATION_CONNECTING);
> +    g_signal_connect(self->priv->target_session, "channel-new",
> +                     G_CALLBACK(main_channel_migration_on_channel_new), self);
> +    /* the migration process is in 2 steps, first the main channel and
> +       then the rest of the channels */
> +    main_channel_migration_create_and_connect_channel(self, SPICE_CHANNEL_MAIN, 0);
> +
> +    return FALSE;
> +}
> +
> +/*******************************************************************************
> + * Internal API
> + ******************************************************************************/
> +
> +/* coroutine context */
> +G_GNUC_INTERNAL
> +bool spice_main_channel_migration_init_migration(SpiceMainChannelMigration *self)
> +{
> +    /* On SpiceMainChannelMigration creation, we check both source_session and
> +     * target_session and initialization would fail if minimum parameters were
> +     * lacking. The guard below is enough for minimal check */
> +    g_return_val_if_fail(SPICE_IS_MAIN_CHANNEL_MIGRATION(self), FALSE);
> +
> +    /* no need to track idle, call is sync for this coroutine */
> +    g_idle_add(main_channel_migration_run_cb, self);
> +
> +    /* switch to main loop and wait for connections */
> +    coroutine_yield(NULL);
> +
> +    return self->priv->num_channels == self->priv->num_migrating_channels;
> +}
> +
> +/* coroutine context */
> +G_GNUC_INTERNAL void
> +spice_main_channel_migration_seamless_handshake_done(SpiceMainChannelMigration *self,
> +                                                     gboolean seamless_migration)
> +{
> +    self->priv->seamless_migration = seamless_migration;
> +    /* move to main context */
> +    g_idle_add(main_channel_migration_seamless_migration_handshake_cb, self);
> +}
> +
> +G_GNUC_INTERNAL
> +guint spice_main_channel_migration_get_source_host_version(SpiceMainChannelMigration *self)
> +{
> +    return self->priv->source_host_version;
> +}
> +
> +G_GNUC_INTERNAL
> +bool spice_main_channel_migration_is_seamless(SpiceMainChannelMigration *self)
> +{
> +    return self->priv->seamless_migration;
> +}
> +
> +/*******************************************************************************
> + * GObject
> + ******************************************************************************/
> +
> +static void
> +spice_main_channel_migration_init(SpiceMainChannelMigration *self)
> +{
> +     self->priv = spice_main_channel_migration_get_instance_private(self);
> +}
> +
> +static void
> +spice_main_channel_migration_dispose(GObject *gobject)
> +{
> +    SpiceMainChannelMigration *self = SPICE_MAIN_CHANNEL_MIGRATION(gobject);
> +
> +    g_clear_object(&self->priv->target_session);
> +    g_clear_object(&self->priv->source_session);
> +
> +    if (G_OBJECT_CLASS(spice_main_channel_migration_parent_class)->dispose) {
> +        G_OBJECT_CLASS(spice_main_channel_migration_parent_class)->dispose(gobject);
> +    }
> +}
> +
> +static void
> +spice_main_channel_migration_get_property(GObject *object,
> +                                          guint property_id,
> +                                          GValue *value,
> +                                          GParamSpec *pspec)
> +{
> +    SpiceMainChannelMigration *self = SPICE_MAIN_CHANNEL_MIGRATION(object);
> +
> +    switch (property_id) {
> +    case PROP_COROUTINE_CONTEXT:
> +        g_value_set_pointer(value, self->priv->source_coroutine);
> +        break;
> +
> +    case PROP_SEAMLESS_MIGRATION:
> +        g_value_set_boolean(value, self->priv->seamless_migration);
> +        break;
> +
> +    case PROP_SOURCE_HOST_VERSION:
> +        g_value_set_uint(value, self->priv->source_host_version);
> +        break;
> +
> +    case PROP_SOURCE_SESSION:
> +        g_value_set_object(value, self->priv->source_session);
> +        break;
> +
> +    case PROP_TARGET_SESSION:
> +        g_value_set_object(value, self->priv->target_session);
> +        break;
> +
> +    default:
> +        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
> +        break;
> +    }
> +}
> +
> +static void
> +spice_main_channel_migration_set_property(GObject *object,
> +                                          guint property_id,
> +                                          const GValue *value,
> +                                          GParamSpec *pspec)
> +{
> +    SpiceMainChannelMigration *self = SPICE_MAIN_CHANNEL_MIGRATION(object);
> +
> +    switch (property_id) {
> +    case PROP_COROUTINE_CONTEXT:
> +        self->priv->source_coroutine = g_value_get_pointer(value);
> +        break;
> +
> +    case PROP_SEAMLESS_MIGRATION:
> +        self->priv->seamless_migration = g_value_get_boolean(value);
> +        break;
> +
> +    case PROP_SOURCE_HOST_VERSION:
> +        self->priv->source_host_version = g_value_get_uint(value);
> +        break;
> +
> +    case PROP_SOURCE_SESSION:
> +        g_clear_object(&self->priv->source_session);
> +        self->priv->source_session = g_value_dup_object(value);
> +        break;
> +
> +    case PROP_TARGET_SESSION:
> +        g_clear_object(&self->priv->target_session);
> +        self->priv->target_session = g_value_dup_object(value);
> +        break;
> +
> +    default:
> +        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
> +        break;
> +    }
> +}
> +
> +static void
> +spice_main_channel_migration_constructed(GObject *gobject)
> +{
> +    SpiceMainChannelMigration *self = SPICE_MAIN_CHANNEL_MIGRATION(gobject);
> +
> +    g_return_if_fail(SPICE_IS_SESSION(self->priv->source_session));
> +    g_return_if_fail(SPICE_IS_SESSION(self->priv->target_session));
> +    g_return_if_fail(spice_session_get_main_channel_migration(self->priv->source_session) == NULL);
> +    g_return_if_fail(spice_session_get_main_channel_migration(self->priv->target_session) == NULL);
> +
> +    spice_session_set_main_channel_migration(self->priv->source_session, self);
> +    spice_session_set_main_channel_migration(self->priv->target_session, self);
> +
> +    if (G_OBJECT_CLASS(spice_main_channel_migration_parent_class)->constructed) {
> +        G_OBJECT_CLASS(spice_main_channel_migration_parent_class)->constructed(gobject);
> +    }
> +}
> +
> +static void
> +spice_main_channel_migration_class_init(SpiceMainChannelMigrationClass *klass)
> +{
> +    GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
> +
> +    gobject_class->dispose = spice_main_channel_migration_dispose;
> +    gobject_class->get_property = spice_main_channel_migration_get_property;
> +    gobject_class->set_property = spice_main_channel_migration_set_property;
> +    gobject_class->constructed = spice_main_channel_migration_constructed;
> +
> +    g_properties[PROP_COROUTINE_CONTEXT] =
> +        g_param_spec_pointer("coroutine-context",
> +                             "Coroutine context",
> +                             "Coroutine context to yield back on failure",
> +                              G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
> +
> +
> +    g_properties[PROP_SEAMLESS_MIGRATION] =
> +        g_param_spec_boolean("seamless-migration",
> +                             "Seamless migration",
> +                             "To enable seamless migration",
> +                             FALSE,
> +                             G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
> +
> +    g_properties[PROP_SOURCE_HOST_VERSION] =
> +        g_param_spec_uint("source-host-version",
> +                          "Source Session",
> +                          "Current session that we are migrating from",
> +                          0, G_MAXUINT, 0,
> +                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
> +
> +    g_properties[PROP_SOURCE_SESSION] =
> +        g_param_spec_object("source-session",
> +                            "Source Session",
> +                            "Current session that we are migrating from",
> +                            SPICE_TYPE_SESSION,
> +                            G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
> +
> +    g_properties[PROP_TARGET_SESSION] =
> +        g_param_spec_object("target-session",
> +                            "Target Session",
> +                            "Target session we are connecting to",
> +                            SPICE_TYPE_SESSION,
> +                            G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
> +
> +    g_object_class_install_properties(gobject_class, PROP_LAST, g_properties);
> +}
> +
> diff --git a/src/channel-main-migration.h b/src/channel-main-migration.h
> new file mode 100644
> index 0000000..15a7e37
> --- /dev/null
> +++ b/src/channel-main-migration.h
> @@ -0,0 +1,63 @@
> +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
> +/*
> +   Copyright (C) 2019 Red Hat, Inc.
> +
> +   This library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   This library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with this library; if not, see <http://www.gnu.org/licenses/>.
> +*/
> +#ifndef __SPICE_CLIENT_MAIN_CHANNEL_MIGRATION_H__
> +#define __SPICE_CLIENT_MAIN_CHANNEL_MIGRATION_H__
> +
> +#if !defined(__SPICE_CLIENT_H_INSIDE__) && !defined(SPICE_COMPILATION)
> +#warning "Only <spice-client.h> can be included directly"
> +#endif
> +
> +#include "spice-client.h"
> +
> +#include <stdbool.h>
> +
> +G_BEGIN_DECLS
> +
> +#define SPICE_TYPE_MAIN_CHANNEL_MIGRATION   (spice_main_channel_migration_get_type())
> +#define SPICE_MAIN_CHANNEL_MIGRATION(obj)   (G_TYPE_CHECK_INSTANCE_CAST((obj), \
> +                                             SPICE_TYPE_MAIN_CHANNEL_MIGRATION, \
> +                                             SpiceMainChannelMigration))
> +#define SPICE_MAIN_CHANNEL_MIGRATION_CLASS(klass)   (G_TYPE_CHECK_CLASS_CAST((klass), \
> +                                                     SPICE_TYPE_MAIN_CHANNEL_MIGRATION, \
> +                                                     SpiceMainChannelMigrationClass))
> +#define SPICE_IS_MAIN_CHANNEL_MIGRATION(obj)    (G_TYPE_CHECK_INSTANCE_TYPE((obj), \
> +                                                 SPICE_TYPE_MAIN_CHANNEL_MIGRATION))
> +#define SPICE_IS_MAIN_CHANNEL_MIGRATION_CLASS(klass)    (G_TYPE_CHECK_CLASS_TYPE((klass), \
> +                                                         SPICE_TYPE_MAIN_CHANNEL_MIGRATION))
> +#define SPICE_MAIN_CHANNEL_MIGRATION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), \
> +                                                     SPICE_TYPE_MAIN_CHANNEL_MIGRATION, \
> +                                                     SpiceMainChannelMigrationClass))
> +
> +/**
> + * SpiceMainChannelMigration
> + *
> + * Opaque data structure.
> + **/
> +typedef struct _SpiceMainChannelMigration SpiceMainChannelMigration;
> +typedef struct _SpiceMainChannelMigrationClass SpiceMainChannelMigrationClass;
> +
> +GType spice_main_channel_migration_get_type(void) G_GNUC_CONST;
> +bool spice_main_channel_migration_init_migration(SpiceMainChannelMigration *self);
> +void spice_main_channel_migration_seamless_handshake_done(SpiceMainChannelMigration *self,
> +                                                          gboolean seamless_migration);
> +guint spice_main_channel_migration_get_source_host_version(SpiceMainChannelMigration *self);
> +bool spice_main_channel_migration_is_seamless(SpiceMainChannelMigration *self);
> +
> +G_END_DECLS
> +
> +#endif /* __SPICE_CLIENT_MAIN_CHANNEL_MIGRATION_H__ */
> diff --git a/src/channel-main.c b/src/channel-main.c
> index 3dba399..3bd91b8 100644
> --- a/src/channel-main.c
> +++ b/src/channel-main.c
> @@ -31,6 +31,7 @@
>  #include "spice-session-priv.h"
>  #include "spice-audio-priv.h"
>  #include "spice-file-transfer-task-priv.h"
> +#include "channel-main-migration.h"
>  
>  /**
>   * SECTION:channel-main
> @@ -50,8 +51,6 @@
>  
>  #define MAX_DISPLAY 16 /* Note must fit in a guint32, see monitors_align */
>  
> -typedef struct spice_migrate spice_migrate;
> -
>  typedef enum {
>      DISPLAY_UNDEFINED,
>      DISPLAY_DISABLED,
> @@ -109,7 +108,6 @@ struct _SpiceMainChannelPrivate  {
>  
>      guint                       switch_host_delayed_id;
>      guint                       migrate_delayed_id;
> -    spice_migrate               *migrate_data;
>      int                         max_clipboard;
>  
>      gboolean                    agent_volume_playback_sync;
> @@ -117,21 +115,6 @@ struct _SpiceMainChannelPrivate  {
>      GCancellable                *cancellable_volume_info;
>  };
>  
> -struct spice_migrate {
> -    struct coroutine *from;
> -    SpiceMigrationDstInfo *info;
> -    SpiceSession *session;
> -    guint nchannels;
> -    SpiceChannel *src_channel;
> -    SpiceChannel *dst_channel;
> -    bool do_seamless; /* used as input and output for the seamless migration handshake.
> -                         input: whether to send to the dest SPICE_MSGC_MAIN_MIGRATE_DST_DO_SEAMLESS
> -                         output: whether the dest approved seamless migration
> -                         (SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_ACK/NACK)
> -                       */
> -    uint32_t src_mig_version;
> -};
> -
>  G_DEFINE_TYPE_WITH_PRIVATE(SpiceMainChannel, spice_main_channel, SPICE_TYPE_CHANNEL)
>  
>  /* Properties */
> @@ -172,9 +155,6 @@ static void spice_main_handle_msg(SpiceChannel *channel, SpiceMsgIn *msg);
>  static void channel_set_handlers(SpiceChannelClass *klass);
>  static void agent_send_msg_queue(SpiceMainChannel *channel);
>  static void agent_free_msg_queue(SpiceMainChannel *channel);
> -static void migrate_channel_event_cb(SpiceChannel *channel, SpiceChannelEvent event,
> -                                     gpointer data);
> -static gboolean main_migrate_handshake_done(gpointer data);
>  static void spice_main_channel_send_migration_handshake(SpiceChannel *channel);
>  static void file_xfer_flushed(SpiceMainChannel *channel, gboolean success);
>  static void file_xfer_read_async_cb(GObject *source_object,
> @@ -2161,37 +2141,19 @@ static void main_handle_agent_token(SpiceChannel *channel, SpiceMsgIn *in)
>      agent_send_msg_queue(SPICE_MAIN_CHANNEL(channel));
>  }
>  
> -/* main context */
> -static void migrate_channel_new_cb(SpiceSession *s, SpiceChannel *channel, gpointer data)
> -{
> -    g_signal_connect(channel, "channel-event",
> -                     G_CALLBACK(migrate_channel_event_cb), data);
> -}
> -
> -static void
> -migrate_channel_connect(spice_migrate *mig, int type, int id)
> -{
> -    SPICE_DEBUG("migrate_channel_connect %d:%d", type, id);
> -
> -    SpiceChannel *newc = spice_channel_new(mig->session, type, id);
> -    if (newc != NULL && spice_channel_connect(newc)) {
> -        mig->nchannels++;
> -    }
> -}
> -
>  /* coroutine context */
>  static void spice_main_channel_send_migration_handshake(SpiceChannel *channel)
>  {
> -    SpiceMainChannelPrivate *c = SPICE_MAIN_CHANNEL(channel)->priv;
> +    SpiceSession *session = spice_channel_get_session(channel);
> +    SpiceMainChannelMigration *migration = spice_session_get_main_channel_migration(session);
>  
>      if (!spice_channel_test_capability(channel, SPICE_MAIN_CAP_SEAMLESS_MIGRATE)) {
> -        c->migrate_data->do_seamless = false;
> -        g_idle_add(main_migrate_handshake_done, c->migrate_data);
> +        spice_main_channel_migration_seamless_handshake_done(migration, FALSE);
>      } else {
>          SpiceMsgcMainMigrateDstDoSeamless msg_data;
>          SpiceMsgOut *msg_out;
>  
> -        msg_data.src_version = c->migrate_data->src_mig_version;
> +        msg_data.src_version = spice_main_channel_migration_get_source_host_version(migration);
>  
>          msg_out = spice_msg_out_new(channel, SPICE_MSGC_MAIN_MIGRATE_DST_DO_SEAMLESS);
>          msg_out->marshallers->msgc_main_migrate_dst_do_seamless(msg_out->marshaller, &msg_data);
> @@ -2199,76 +2161,6 @@ static void spice_main_channel_send_migration_handshake(SpiceChannel *channel)
>      }
>  }
>  
> -/* main context */
> -static void migrate_channel_event_cb(SpiceChannel *channel, SpiceChannelEvent event,
> -                                     gpointer data)
> -{
> -    spice_migrate *mig = data;
> -
> -    g_return_if_fail(mig->nchannels > 0);
> -    g_signal_handlers_disconnect_by_func(channel, migrate_channel_event_cb, data);
> -
> -    switch (event) {
> -    case SPICE_CHANNEL_OPENED:
> -        if (spice_channel_get_channel_type(channel) == SPICE_CHANNEL_MAIN) {
> -            SpiceSession *session = spice_channel_get_session(mig->src_channel);
> -            if (mig->do_seamless) {
> -                SpiceMainChannelPrivate *main_priv = SPICE_MAIN_CHANNEL(channel)->priv;
> -
> -                spice_channel_set_state(channel, SPICE_CHANNEL_STATE_MIGRATION_HANDSHAKE);
> -                mig->dst_channel = channel;
> -                main_priv->migrate_data = mig;
> -            } else {
> -                spice_channel_set_state(channel, SPICE_CHANNEL_STATE_MIGRATING);
> -                mig->nchannels--;
> -            }
> -            /* now connect the rest of the channels */
> -            GList *channels, *l;
> -            l = channels = spice_session_get_channels(session);
> -            while (l != NULL) {
> -                SpiceChannel *it = SPICE_CHANNEL(l->data);
> -
> -                l = l->next;
> -                if (spice_channel_get_channel_type(it) == SPICE_CHANNEL_MAIN) {
> -                    continue;
> -                }
> -                migrate_channel_connect(mig,
> -                                        spice_channel_get_channel_type(it),
> -                                        spice_channel_get_channel_id(it));
> -            }
> -            g_list_free(channels);
> -        } else {
> -            spice_channel_set_state(channel, SPICE_CHANNEL_STATE_MIGRATING);
> -            mig->nchannels--;
> -        }
> -
> -        SPICE_DEBUG("migration: channel opened chan:%p, left %u", channel, mig->nchannels);
> -        if (mig->nchannels == 0)
> -            coroutine_yieldto(mig->from, NULL);
> -        break;
> -    default:
> -        CHANNEL_DEBUG(channel, "error or unhandled channel event during migration: %u", event);
> -        /* go back to main channel to report error */
> -        coroutine_yieldto(mig->from, NULL);
> -    }
> -}
> -
> -/* main context */
> -static gboolean main_migrate_handshake_done(gpointer data)
> -{
> -    spice_migrate *mig = data;
> -    SpiceChannel *channel = SPICE_CHANNEL(mig->dst_channel);
> -
> -    g_return_val_if_fail(spice_channel_get_channel_type(channel) == SPICE_CHANNEL_MAIN, FALSE);
> -    g_return_val_if_fail(spice_channel_get_state(channel) == SPICE_CHANNEL_STATE_MIGRATION_HANDSHAKE, FALSE);
> -
> -    spice_channel_set_state(channel, SPICE_CHANNEL_STATE_MIGRATING);
> -    mig->nchannels--;
> -    if (mig->nchannels == 0)
> -        coroutine_yieldto(mig->from, NULL);
> -    return FALSE;
> -}
> -
>  #ifdef __GNUC__
>  typedef struct __attribute__ ((__packed__)) OldRedMigrationBegin {
>  #else
> @@ -2279,38 +2171,6 @@ typedef struct __declspec(align(1)) OldRedMigrationBegin {
>      char host[0];
>  } OldRedMigrationBegin;
>  
> -/* main context */
> -static gboolean migrate_connect(gpointer data)
> -{
> -    spice_migrate *mig = data;
> -    SpiceChannelPrivate  *c;
> -
> -    g_return_val_if_fail(mig != NULL, FALSE);
> -    g_return_val_if_fail(mig->info != NULL, FALSE);
> -    g_return_val_if_fail(mig->nchannels == 0, FALSE);
> -    c = SPICE_CHANNEL(mig->src_channel)->priv;
> -    g_return_val_if_fail(c != NULL, FALSE);
> -    g_return_val_if_fail(mig->session != NULL, FALSE);
> -
> -    spice_session_set_migration_state(mig->session, SPICE_SESSION_MIGRATION_CONNECTING);
> -
> -    SpiceMigrationDstInfo *info = mig->info;
> -    SPICE_DEBUG("migrate_begin %u %s %d %d",
> -                info->host_size, info->host_data, info->port, info->sport);
> -
> -    g_signal_connect(mig->session, "channel-new",
> -                     G_CALLBACK(migrate_channel_new_cb), mig);
> -
> -    g_signal_emit(mig->src_channel, signals[SPICE_MIGRATION_STARTED], 0,
> -                  mig->session);
> -
> -    /* the migration process is in 2 steps, first the main channel and
> -       then the rest of the channels */
> -    migrate_channel_connect(mig, SPICE_CHANNEL_MAIN, 0);
> -
> -    return FALSE;
> -}
> -
>  static void
>  migration_session_set_destination_info(SpiceSession *target_session,
>                                         SpiceMigrationDstInfo *info)
> @@ -2348,50 +2208,51 @@ static void main_migrate_connect(SpiceChannel *channel,
>                                   bool do_seamless,
>                                   uint32_t src_mig_version)
>  {
> -    SpiceMainChannelPrivate *main_priv = SPICE_MAIN_CHANNEL(channel)->priv;
>      int reply_type = SPICE_MSGC_MAIN_MIGRATE_CONNECT_ERROR;
> -    spice_migrate mig = { 0, };
>      SpiceMsgOut *out;
> -    SpiceSession *session;
> -
> -    mig.src_channel = channel;
> -    mig.info = dst_info;
> -    mig.from = coroutine_self();
> -    mig.do_seamless = do_seamless;
> -    mig.src_mig_version = src_mig_version;
> +    SpiceSession *session, *target_session;
> +    SpiceMainChannelMigration *migration = NULL;
>  
>      CHANNEL_DEBUG(channel, "migrate connect");
>      session = spice_channel_get_session(channel);
> -    mig.session = spice_session_new_from_session(session);
> -    if (!spice_session_set_migration_session(session, mig.session)) {
> +    target_session = spice_session_new_from_session(session);
> +    if (!spice_session_set_migration_session(session, target_session)) {
>          goto end;
>      }
>  
> -    migration_session_set_destination_info(mig.session, dst_info);
> -
> -    main_priv->migrate_data = &mig;
> +    migration_session_set_destination_info(target_session, dst_info);
> +    migration = g_object_new(SPICE_TYPE_MAIN_CHANNEL_MIGRATION,
> +                             "source-session", session,
> +                             "target-session", target_session,
> +                             "coroutine-context", coroutine_self(),
> +                             "seamless-migration", do_seamless,
> +                             "source-host-version", src_mig_version,
> +                             NULL);
>  
> -    /* no need to track idle, call is sync for this coroutine */
> -    g_idle_add(migrate_connect, &mig);
> +    /* Allow application to track new-channel events on target session */
> +    g_signal_emit(channel, signals[SPICE_MIGRATION_STARTED], 0, target_session);
>  
> -    /* switch to main loop and wait for connections */
> -    coroutine_yield(NULL);
> +    /* Runs sync from coroutine perspective */
> +    bool success = spice_main_channel_migration_init_migration(migration);
>  
> -    if (mig.nchannels != 0) {
> +    if (!success) {
>          CHANNEL_DEBUG(channel, "migrate failed: some channels failed to connect");
>          spice_session_abort_migration(session);
> +        goto end;
> +    }
> +
> +    if (spice_main_channel_migration_is_seamless(migration)) {
> +        SPICE_DEBUG("migration (seamless): connections all ok");
> +        reply_type = SPICE_MSGC_MAIN_MIGRATE_CONNECTED_SEAMLESS;
>      } else {
> -        if (mig.do_seamless) {
> -            SPICE_DEBUG("migration (seamless): connections all ok");
> -            reply_type = SPICE_MSGC_MAIN_MIGRATE_CONNECTED_SEAMLESS;
> -        } else {
> -            SPICE_DEBUG("migration (semi-seamless): connections all ok");
> -            reply_type = SPICE_MSGC_MAIN_MIGRATE_CONNECTED;
> -        }
> -        spice_session_start_migrating(session, mig.do_seamless);
> +        SPICE_DEBUG("migration (semi-seamless): connections all ok");
> +        reply_type = SPICE_MSGC_MAIN_MIGRATE_CONNECTED;
>      }
>  
> +    spice_session_start_migrating(session, spice_main_channel_migration_is_seamless(migration));
> +
>  end:
> +    g_clear_object(&migration);
>      CHANNEL_DEBUG(channel, "migrate connect reply %d", reply_type);
>      out = spice_msg_out_new(channel, reply_type);
>      spice_msg_out_send(out);
> @@ -2415,20 +2276,20 @@ static void main_handle_migrate_begin_seamless(SpiceChannel *channel, SpiceMsgIn
>  
>  static void main_handle_migrate_dst_seamless_ack(SpiceChannel *channel, SpiceMsgIn *in)
>  {
> -    SpiceMainChannelPrivate *main_priv = SPICE_MAIN_CHANNEL(channel)->priv;
> +    SpiceSession *session = spice_channel_get_session(channel);
> +    SpiceMainChannelMigration *migration = spice_session_get_main_channel_migration(session);
>  
>      g_return_if_fail(spice_channel_get_state(channel) == SPICE_CHANNEL_STATE_MIGRATION_HANDSHAKE);
> -    main_priv->migrate_data->do_seamless = true;
> -    g_idle_add(main_migrate_handshake_done, main_priv->migrate_data);
> +    spice_main_channel_migration_seamless_handshake_done(migration, TRUE);
>  }
>  
>  static void main_handle_migrate_dst_seamless_nack(SpiceChannel *channel, SpiceMsgIn *in)
>  {
> -    SpiceMainChannelPrivate *main_priv = SPICE_MAIN_CHANNEL(channel)->priv;
> +    SpiceSession *session = spice_channel_get_session(channel);
> +    SpiceMainChannelMigration *migration = spice_session_get_main_channel_migration(session);
>  
>      g_return_if_fail(spice_channel_get_state(channel) == SPICE_CHANNEL_STATE_MIGRATION_HANDSHAKE);
> -    main_priv->migrate_data->do_seamless = false;
> -    g_idle_add(main_migrate_handshake_done, main_priv->migrate_data);
> +    spice_main_channel_migration_seamless_handshake_done(migration, FALSE);
>  }
>  
>  /* main context */
> diff --git a/src/meson.build b/src/meson.build
> index 0461dea..84ee9be 100644
> --- a/src/meson.build
> +++ b/src/meson.build
> @@ -90,6 +90,8 @@ spice_client_glib_sources = [
>    'channel-base.c',
>    'channel-display-gst.c',
>    'channel-display-priv.h',
> +  'channel-main-migration.c',
> +  'channel-main-migration.h',
>    'channel-playback-priv.h',
>    'channel-usbredir-priv.h',
>    'client_sw_canvas.c',
> diff --git a/src/spice-session-priv.h b/src/spice-session-priv.h
> index 62cebc5..b453f92 100644
> --- a/src/spice-session-priv.h
> +++ b/src/spice-session-priv.h
> @@ -28,6 +28,7 @@
>  typedef struct _PhodavServer PhodavServer;
>  #endif
>  
> +#include "channel-main-migration.h"
>  #include "desktop-integration.h"
>  #include "spice-session.h"
>  #include "spice-gtk-session.h"
> @@ -95,6 +96,9 @@ PhodavServer *spice_session_get_webdav_server(SpiceSession *session);
>  PhodavServer* channel_webdav_server_new(SpiceSession *session);
>  guint spice_session_get_n_display_channels(SpiceSession *session);
>  gboolean spice_session_set_migration_session(SpiceSession *session, SpiceSession *mig_session);
> +SpiceMainChannelMigration * spice_session_get_main_channel_migration(SpiceSession *self);
> +void spice_session_set_main_channel_migration(SpiceSession *self,
> +                                              SpiceMainChannelMigration *mc_migration);
>  SpiceAudio *spice_audio_get(SpiceSession *session, GMainContext *context);
>  const gchar* spice_audio_data_mode_to_string(gint mode);
>  G_END_DECLS
> diff --git a/src/spice-session.c b/src/spice-session.c
> index a770c92..e6bbb4f 100644
> --- a/src/spice-session.c
> +++ b/src/spice-session.c
> @@ -110,6 +110,9 @@ struct _SpiceSessionPrivate {
>      gboolean          migrate_wait_init;
>      guint             after_main_init;
>      gboolean          for_migration;
> +    
> +    /* FIXME: Can possibly remove lots of the above */
> +    SpiceMainChannelMigration *mc_migration;
>  
>      display_cache     *images;
>      display_cache     *palettes;
> @@ -344,6 +347,7 @@ spice_session_dispose(GObject *gobject)
>      g_clear_object(&s->usb_manager);
>      g_clear_object(&s->proxy);
>      g_clear_object(&s->webdav);
> +    g_clear_object(&s->mc_migration);
>  
>      /* Chain up to the parent class */
>      if (G_OBJECT_CLASS(spice_session_parent_class)->dispose)
> @@ -1752,6 +1756,7 @@ void spice_session_start_migrating(SpiceSession *session,
>                                     gboolean full_migration)
>  {
>      g_return_if_fail(SPICE_IS_SESSION(session));
> +    g_return_if_fail(SPICE_IS_MAIN_CHANNEL_MIGRATION(session->priv->mc_migration));
>  
>      SpiceSessionPrivate *s = session->priv;
>      SpiceSessionPrivate *m;
> @@ -1834,6 +1839,7 @@ end:
>      g_clear_pointer(&s->migration_left, g_list_free);
>      session_disconnect(s->migration, FALSE);
>      g_clear_pointer(&s->migration, g_object_unref);
> +    g_clear_object(&s->mc_migration);
>  
>      s->migrate_wait_init = FALSE;
>      if (s->after_main_init) {
> @@ -1874,6 +1880,7 @@ void spice_session_channel_migrate(SpiceSession *session, SpiceChannel *channel)
>          session_disconnect(s->migration, FALSE);
>          g_clear_pointer(&s->migration, g_object_unref);
>          spice_session_set_migration_state(session, SPICE_SESSION_MIGRATION_NONE);
> +        g_clear_object(&s->mc_migration);
>      }
>  }
>  
> @@ -2864,3 +2871,22 @@ gboolean spice_session_set_migration_session(SpiceSession *session, SpiceSession
>  
>      return TRUE;
>  }
> +
> +G_GNUC_INTERNAL void
> +spice_session_set_main_channel_migration(SpiceSession *self,
> +                                         SpiceMainChannelMigration *mc_migration)
> +{
> +    g_return_if_fail(SPICE_IS_SESSION(self));
> +    g_return_if_fail(SPICE_IS_MAIN_CHANNEL_MIGRATION(mc_migration));
> +    g_return_if_fail(self->priv->mc_migration == NULL);
> +
> +    self->priv->mc_migration = g_object_ref(mc_migration);
> +}
> +
> +G_GNUC_INTERNAL SpiceMainChannelMigration *
> +spice_session_get_main_channel_migration(SpiceSession *self)
> +{
> +    g_return_val_if_fail(SPICE_IS_SESSION(self), FALSE);
> +
> +    return self->priv->mc_migration;
> +}
> -- 
> 2.21.0
> 
> _______________________________________________
> Spice-devel mailing list
> Spice-devel at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/spice-devel
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 833 bytes
Desc: not available
URL: <https://lists.freedesktop.org/archives/spice-devel/attachments/20190924/860200d7/attachment-0001.sig>


More information about the Spice-devel mailing list