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@xxxxxxxxxxxxxx> > > 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@xxxxxxxxxx> 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@xxxxxxxxxxxxxxxxxxxxx > https://lists.freedesktop.org/mailman/listinfo/spice-devel
Attachment:
signature.asc
Description: PGP signature
_______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/spice-devel