[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