Flow: (1) *src* main channel coroutine (main_handle_migrate_begin_seamless): handles SPICE_MSG_MAIN_MIGRATE_BEGIN_SEAMLESS; yields to the main loop, supplying it the destination information needed for connection. (2) main context (migrate_connect): Establishes a new session for connecting to the destination. After all the channels are opened (async), their state, except for the one of the main channel, is modified to SPICE_CHANNEL_STATE_MIGRATING (see migrate_channel_event_cb); no reading is done from the channel during this state. The dest main channel's state is changed to SPICE_CHANNEL_STATE_MIGRATION_HANDSHAKE (3) *dest* main channel coroutine: sends to the dest server SPICE_MSGC_MAIN_MIGRATE_DST_DO_SEAMLESS (see spice_channel_recv_auth) (4) *dest* main channel coroutine: recevices SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_ACK/NACK. adds main_migrate_handshake_done to the main loop. (5) main context: when all the dest session channels are connected, and the main channel handshake is done, we yield to the src main channel coroutine (see migrate_channel_event_cb and main_migrate_handshake_done) (6) *src* main channel coroutine: sends to the src server SPICE_MSGC_MAIN_MIGRATE_(CONNECTED|CONNECTED_SEAMLESS|CONNECT_ERROR) For more details see spice-protocol. commit 1ad5d259cb4b695ec3106de7ccd082e031e7ae11 --- gtk/channel-main.c | 152 ++++++++++++++++++++++++++++++++++++++++----- gtk/spice-channel-priv.h | 1 + gtk/spice-channel.c | 17 +++++ gtk/spice-channel.h | 6 ++- 4 files changed, 158 insertions(+), 18 deletions(-) diff --git a/gtk/channel-main.c b/gtk/channel-main.c index bb0e361..0e073a6 100644 --- a/gtk/channel-main.c +++ b/gtk/channel-main.c @@ -49,6 +49,8 @@ #define MAX_DISPLAY 16 +typedef struct spice_migrate spice_migrate; + struct _SpiceMainChannelPrivate { enum SpiceMouseMode mouse_mode; bool agent_connected; @@ -80,16 +82,22 @@ struct _SpiceMainChannelPrivate { guint switch_host_delayed_id; guint migrate_delayed_id; + spice_migrate *migrate_data; }; -typedef struct spice_migrate spice_migrate; - struct spice_migrate { struct coroutine *from; SpiceMigrationDstInfo *info; SpiceSession *session; guint nchannels; - SpiceChannel *channel; + 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(SpiceMainChannel, spice_main_channel, SPICE_TYPE_CHANNEL) @@ -131,6 +139,8 @@ 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); /* ------------------------------------------------------------------ */ @@ -164,6 +174,7 @@ static void spice_main_channel_reset_capabilties(SpiceChannel *channel) spice_channel_set_capability(SPICE_CHANNEL(channel), SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE); spice_channel_set_capability(SPICE_CHANNEL(channel), SPICE_MAIN_CAP_NAME_AND_UUID); spice_channel_set_capability(SPICE_CHANNEL(channel), SPICE_MAIN_CAP_AGENT_CONNECTED_TOKENS); + spice_channel_set_capability(SPICE_CHANNEL(channel), SPICE_MAIN_CAP_SEAMLESS_MIGRATE); } static void spice_main_channel_init(SpiceMainChannel *channel) @@ -327,6 +338,7 @@ static void spice_main_channel_class_init(SpiceMainChannelClass *klass) channel_class->iterate_write = spice_channel_iterate_write; channel_class->channel_reset = spice_main_channel_reset; channel_class->channel_reset_capabilities = spice_main_channel_reset_capabilties; + channel_class->channel_send_migration_handshake = spice_main_channel_send_migration_handshake; /** * SpiceMainChannel:mouse-mode: @@ -658,7 +670,6 @@ static void spice_main_channel_class_init(SpiceMainChannelClass *klass) 1, G_TYPE_OBJECT); - g_type_class_add_private(klass, sizeof(SpiceMainChannelPrivate)); } @@ -1573,6 +1584,26 @@ static SpiceChannel* migrate_channel_connect(spice_migrate *mig, int type, int i return newc; } +/* coroutine context */ +static void spice_main_channel_send_migration_handshake(SpiceChannel *channel) +{ + SpiceMainChannelPrivate *c = SPICE_MAIN_CHANNEL(channel)->priv; + + 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); + } else { + SpiceMsgcMainMigrateDstDoSeamless msg_data; + SpiceMsgOut *msg_out; + + msg_data.src_version = c->migrate_data->src_mig_version; + + 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); + spice_msg_out_send_internal(msg_out); + } +} + /* main context */ static void migrate_channel_event_cb(SpiceChannel *channel, SpiceChannelEvent event, gpointer data) @@ -1584,13 +1615,22 @@ static void migrate_channel_event_cb(SpiceChannel *channel, SpiceChannelEvent ev g_return_if_fail(mig->nchannels > 0); g_signal_handlers_disconnect_by_func(channel, migrate_channel_event_cb, data); - session = spice_channel_get_session(mig->channel); + session = spice_channel_get_session(mig->src_channel); switch (event) { case SPICE_CHANNEL_OPENED: - c->state = SPICE_CHANNEL_STATE_MIGRATING; if (c->channel_type == SPICE_CHANNEL_MAIN) { + if (mig->do_seamless) { + SpiceMainChannelPrivate *main_priv = SPICE_MAIN_CHANNEL(channel)->priv; + + c->state = SPICE_CHANNEL_STATE_MIGRATION_HANDSHAKE; + mig->dst_channel = channel; + main_priv->migrate_data = mig; + } else { + c->state = SPICE_CHANNEL_STATE_MIGRATING; + mig->nchannels--; + } /* now connect the rest of the channels */ GList *channels, *l; l = channels = spice_session_get_channels(session); @@ -1602,9 +1642,11 @@ static void migrate_channel_event_cb(SpiceChannel *channel, SpiceChannelEvent ev migrate_channel_connect(mig, curc->channel_type, curc->channel_id); } g_list_free(channels); + } else { + c->state = SPICE_CHANNEL_STATE_MIGRATING; + mig->nchannels--; } - mig->nchannels--; SPICE_DEBUG("migration: channel opened chan:%p, left %d", channel, mig->nchannels); if (mig->nchannels == 0) coroutine_yieldto(mig->from, NULL); @@ -1616,6 +1658,22 @@ static void migrate_channel_event_cb(SpiceChannel *channel, SpiceChannelEvent ev } } +/* main context */ +static gboolean main_migrate_handshake_done(gpointer data) +{ + spice_migrate *mig = data; + SpiceChannelPrivate *c = SPICE_CHANNEL(mig->dst_channel)->priv; + + g_return_val_if_fail(c->channel_type == SPICE_CHANNEL_MAIN, FALSE); + g_return_val_if_fail(c->state == SPICE_CHANNEL_STATE_MIGRATION_HANDSHAKE, FALSE); + + c->state = 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 @@ -1638,10 +1696,10 @@ static gboolean migrate_connect(gpointer data) 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->channel)->priv; + c = SPICE_CHANNEL(mig->src_channel)->priv; g_return_val_if_fail(c != NULL, FALSE); - session = spice_channel_get_session(mig->channel); + session = spice_channel_get_session(mig->src_channel); mig->session = spice_session_new_from_session(session); if ((c->peer_hdr.major_version == 1) && @@ -1692,7 +1750,7 @@ static gboolean migrate_connect(gpointer data) g_signal_connect(mig->session, "channel-new", G_CALLBACK(migrate_channel_new_cb), mig); - g_signal_emit(mig->channel, signals[SPICE_MIGRATION_STARTED], 0, + 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 @@ -1703,16 +1761,22 @@ static gboolean migrate_connect(gpointer data) } /* coroutine context */ -static void main_handle_migrate_begin(SpiceChannel *channel, SpiceMsgIn *in) +static void main_migrate_connect(SpiceChannel *channel, + SpiceMigrationDstInfo *dst_info, bool do_seamless, + uint32_t src_mig_version) { - SpiceMsgMainMigrationBegin *msg = spice_msg_in_parsed(in); + SpiceMainChannelPrivate *main_priv = SPICE_MAIN_CHANNEL(channel)->priv; spice_migrate mig = { 0, }; SpiceMsgOut *out; int reply_type; - mig.channel = channel; - mig.info = &msg->dst_info; + mig.src_channel = channel; + mig.info = dst_info; mig.from = coroutine_self(); + mig.do_seamless = do_seamless; + mig.src_mig_version = src_mig_version; + + main_priv->migrate_data = &mig; /* no need to track idle, call is sync for this coroutine */ g_idle_add(migrate_connect, &mig); @@ -1725,8 +1789,13 @@ static void main_handle_migrate_begin(SpiceChannel *channel, SpiceMsgIn *in) reply_type = SPICE_MSGC_MAIN_MIGRATE_CONNECT_ERROR; spice_session_disconnect(mig.session); } else { - SPICE_DEBUG("migration: connections all ok"); - reply_type = SPICE_MSGC_MAIN_MIGRATE_CONNECTED; + 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_set_migration(spice_channel_get_session(channel), mig.session); } g_object_unref(mig.session); @@ -1735,6 +1804,42 @@ static void main_handle_migrate_begin(SpiceChannel *channel, SpiceMsgIn *in) spice_msg_out_send(out); } +/* coroutine context */ +static void main_handle_migrate_begin(SpiceChannel *channel, SpiceMsgIn *in) +{ + SpiceMsgMainMigrationBegin *msg = spice_msg_in_parsed(in); + + main_migrate_connect(channel, &msg->dst_info, false, 0); +} + +/* coroutine context */ +static void main_handle_migrate_begin_seamless(SpiceChannel *channel, SpiceMsgIn *in) +{ + SpiceMsgMainMigrateBeginSeamless *msg = spice_msg_in_parsed(in); + + main_migrate_connect(channel, &msg->dst_info, true, msg->src_mig_version); +} + +static void main_handle_migrate_dst_seamless_ack(SpiceChannel *channel, SpiceMsgIn *in) +{ + SpiceChannelPrivate *c = SPICE_CHANNEL(channel)->priv; + SpiceMainChannelPrivate *main_priv = SPICE_MAIN_CHANNEL(channel)->priv; + + g_return_if_fail(c->state == SPICE_CHANNEL_STATE_MIGRATION_HANDSHAKE); + main_priv->migrate_data->do_seamless = true; + g_idle_add(main_migrate_handshake_done, main_priv->migrate_data); +} + +static void main_handle_migrate_dst_seamless_nack(SpiceChannel *channel, SpiceMsgIn *in) +{ + SpiceChannelPrivate *c = SPICE_CHANNEL(channel)->priv; + SpiceMainChannelPrivate *main_priv = SPICE_MAIN_CHANNEL(channel)->priv; + + g_return_if_fail(c->state == SPICE_CHANNEL_STATE_MIGRATION_HANDSHAKE); + main_priv->migrate_data->do_seamless = false; + g_idle_add(main_migrate_handshake_done, main_priv->migrate_data); +} + /* main context */ static gboolean migrate_delayed(gpointer data) { @@ -1848,7 +1953,10 @@ static const spice_msg_handler main_handlers[] = { [ SPICE_MSG_MAIN_MIGRATE_END ] = main_handle_migrate_end, [ SPICE_MSG_MAIN_MIGRATE_CANCEL ] = main_handle_migrate_cancel, [ SPICE_MSG_MAIN_MIGRATE_SWITCH_HOST ] = main_handle_migrate_switch_host, - [ SPICE_MSG_MAIN_AGENT_CONNECTED_TOKENS ] = main_handle_agent_connected_tokens, + [ SPICE_MSG_MAIN_AGENT_CONNECTED_TOKENS ] = main_handle_agent_connected_tokens, + [ SPICE_MSG_MAIN_MIGRATE_BEGIN_SEAMLESS ] = main_handle_migrate_begin_seamless, + [ SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_ACK] = main_handle_migrate_dst_seamless_ack, + [ SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_NACK] = main_handle_migrate_dst_seamless_nack, }; /* coroutine context */ @@ -1856,11 +1964,21 @@ static void spice_main_handle_msg(SpiceChannel *channel, SpiceMsgIn *msg) { int type = spice_msg_in_type(msg); SpiceChannelClass *parent_class; + SpiceChannelPrivate *c = SPICE_CHANNEL(channel)->priv; g_return_if_fail(type < SPICE_N_ELEMENTS(main_handlers)); parent_class = SPICE_CHANNEL_CLASS(spice_main_channel_parent_class); + if (c->state == SPICE_CHANNEL_STATE_MIGRATION_HANDSHAKE) { + if (type != SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_ACK && + type != SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_NACK) { + g_critical("unexpected msg (%d)." + "Only MIGRATE_DST_SEAMLESS_ACK/NACK are allowed", type); + return; + } + } + if (main_handlers[type] != NULL) main_handlers[type](channel, msg); else if (parent_class->handle_msg) diff --git a/gtk/spice-channel-priv.h b/gtk/spice-channel-priv.h index 4f094d8..8ed79fa 100644 --- a/gtk/spice-channel-priv.h +++ b/gtk/spice-channel-priv.h @@ -71,6 +71,7 @@ enum spice_channel_state { SPICE_CHANNEL_STATE_READY, SPICE_CHANNEL_STATE_SWITCHING, SPICE_CHANNEL_STATE_MIGRATING, + SPICE_CHANNEL_STATE_MIGRATION_HANDSHAKE, }; struct _SpiceChannelPrivate { diff --git a/gtk/spice-channel.c b/gtk/spice-channel.c index 54d406d..5f37cbc 100644 --- a/gtk/spice-channel.c +++ b/gtk/spice-channel.c @@ -49,6 +49,7 @@ static void spice_channel_send_link(SpiceChannel *channel); static void channel_disconnect(SpiceChannel *channel); static void channel_reset(SpiceChannel *channel, gboolean migrating); static void spice_channel_reset_capabilities(SpiceChannel *channel); +static void spice_channel_send_migration_handshake(SpiceChannel *channel); /** * SECTION:spice-channel @@ -1080,6 +1081,10 @@ static void spice_channel_recv_auth(SpiceChannel *channel) emit_main_context(channel, SPICE_CHANNEL_EVENT, SPICE_CHANNEL_OPENED); + if (c->state == SPICE_CHANNEL_STATE_MIGRATION_HANDSHAKE) { + spice_channel_send_migration_handshake(channel); + } + if (c->state != SPICE_CHANNEL_STATE_MIGRATING) spice_channel_up(channel); } @@ -2002,6 +2007,7 @@ static void spice_channel_iterate_read(SpiceChannel *channel) spice_channel_recv_auth(channel); break; case SPICE_CHANNEL_STATE_READY: + case SPICE_CHANNEL_STATE_MIGRATION_HANDSHAKE: spice_channel_recv_msg(channel, (handler_msg_in)SPICE_CHANNEL_GET_CLASS(channel)->handle_msg, NULL); break; @@ -2654,3 +2660,14 @@ static void spice_channel_reset_capabilities(SpiceChannel *channel) SPICE_CHANNEL_GET_CLASS(channel)->channel_reset_capabilities(channel); } } + +static void spice_channel_send_migration_handshake(SpiceChannel *channel) +{ + SpiceChannelPrivate *c = SPICE_CHANNEL_GET_PRIVATE(channel); + + if (SPICE_CHANNEL_GET_CLASS(channel)->channel_send_migration_handshake) { + SPICE_CHANNEL_GET_CLASS(channel)->channel_send_migration_handshake(channel); + } else { + c->state = SPICE_CHANNEL_STATE_MIGRATING; + } +} diff --git a/gtk/spice-channel.h b/gtk/spice-channel.h index 30ff1ba..d40b844 100644 --- a/gtk/spice-channel.h +++ b/gtk/spice-channel.h @@ -90,11 +90,15 @@ struct _SpiceChannelClass void (*channel_reset)(SpiceChannel *channel, gboolean migrating); void (*channel_reset_capabilities)(SpiceChannel *channel); + /*< private >*/ + /* virtual methods, coroutine context */ + void (*channel_send_migration_handshake)(SpiceChannel *channel); + /* * If adding fields to this struct, remove corresponding * amount of padding to avoid changing overall struct size */ - gchar _spice_reserved[SPICE_RESERVED_PADDING]; + gchar _spice_reserved[SPICE_RESERVED_PADDING - sizeof(void *)]; }; GType spice_channel_get_type(void); -- 1.7.7.6 _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/spice-devel