[Spice-devel] [spice-gtk PATCH 4/7] seamless migration: src and dest servers handshake
Yonit Halperin
yhalperi at redhat.com
Wed Aug 15 00:56:39 PDT 2012
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.
Then it emits the signal "migration-handshake-done".
(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_cb)
(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 | 163 +++++++++++++++++++++++++++++++++++++++++++--
gtk/spice-channel-priv.h | 1 +
gtk/spice-channel.c | 17 +++++
gtk/spice-channel.h | 6 ++-
4 files changed, 178 insertions(+), 9 deletions(-)
diff --git a/gtk/channel-main.c b/gtk/channel-main.c
index bb0e361..bdcdbcb 100644
--- a/gtk/channel-main.c
+++ b/gtk/channel-main.c
@@ -80,6 +80,7 @@ struct _SpiceMainChannelPrivate {
guint switch_host_delayed_id;
guint migrate_delayed_id;
+ guint32 migrate_src_version;
};
typedef struct spice_migrate spice_migrate;
@@ -90,6 +91,12 @@ struct spice_migrate {
SpiceSession *session;
guint nchannels;
SpiceChannel *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)
@@ -121,6 +128,7 @@ enum {
SPICE_MAIN_CLIPBOARD_SELECTION_REQUEST,
SPICE_MAIN_CLIPBOARD_SELECTION_RELEASE,
SPICE_MIGRATION_STARTED,
+ SPICE_MIGRATION_HANDSHAKE_DONE,
SPICE_MAIN_LAST_SIGNAL,
};
@@ -131,6 +139,9 @@ 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 void main_migrate_handshake_done_cb(SpiceChannel *channel, gboolean seamless,
+ gpointer data);
+static void spice_main_channel_send_migration_handshake(SpiceChannel *channel);
/* ------------------------------------------------------------------ */
@@ -164,6 +175,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 +339,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:
@@ -659,6 +672,27 @@ static void spice_main_channel_class_init(SpiceMainChannelClass *klass)
G_TYPE_OBJECT);
+ /**
+ * SpiceMainChannel::migration-handshake-done:
+ * @main: the #SpiceMainChannel that emitted the signal
+ * @session: a migration #SpiceSession
+ *
+ * Inform when seamless migration handshake has completed, and
+ * its result: seamless or semi-seamless migration.
+ *
+ * Since: 0.13
+ **/
+ signals[SPICE_MIGRATION_HANDSHAKE_DONE] =
+ g_signal_new("migration-handshake-done",
+ G_OBJECT_CLASS_TYPE(gobject_class),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL,
+ g_cclosure_marshal_VOID__BOOLEAN,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_BOOLEAN);
+
g_type_class_add_private(klass, sizeof(SpiceMainChannelPrivate));
}
@@ -714,6 +748,10 @@ struct SPICE_MAIN_CLIPBOARD_SELECTION_RELEASE {
guint8 selection;
};
+struct SPICE_MIGRATION_HANDSHAKE_DONE {
+ gboolean seamless;
+};
+
/* main context */
static void do_emit_main_context(GObject *object, int signum, gpointer params)
{
@@ -766,6 +804,11 @@ static void do_emit_main_context(GObject *object, int signum, gpointer params)
p->selection);
break;
}
+ case SPICE_MIGRATION_HANDSHAKE_DONE: {
+ struct SPICE_MIGRATION_HANDSHAKE_DONE *p = params;
+ g_signal_emit(object, signals[signum], 0,
+ p->seamless);
+ }
default:
g_warn_if_reached();
}
@@ -1573,6 +1616,25 @@ 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)) {
+ emit_main_context(channel, SPICE_MIGRATION_HANDSHAKE_DONE, false);
+ } else {
+ SpiceMsgcMainMigrateDstDoSeamless msg_data;
+ SpiceMsgOut *msg_out;
+
+ msg_data.src_version = c->migrate_src_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)
@@ -1588,9 +1650,20 @@ static void migrate_channel_event_cb(SpiceChannel *channel, SpiceChannelEvent ev
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;
+ main_priv->migrate_src_version = mig->src_mig_version;
+ g_signal_connect(channel, "migration-handshake-done",
+ G_CALLBACK(main_migrate_handshake_done_cb),
+ 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 +1675,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 +1691,25 @@ static void migrate_channel_event_cb(SpiceChannel *channel, SpiceChannelEvent ev
}
}
+/* main context */
+static void main_migrate_handshake_done_cb(SpiceChannel *channel, gboolean seamless,
+ gpointer data)
+{
+ spice_migrate *mig = data;
+ SpiceChannelPrivate *c = SPICE_CHANNEL(channel)->priv;
+
+ g_return_if_fail(c->channel_type == SPICE_CHANNEL_MAIN);
+ g_return_if_fail(c->state == SPICE_CHANNEL_STATE_MIGRATION_HANDSHAKE);
+ g_return_if_fail(mig->do_seamless == true);
+
+ g_signal_handlers_disconnect_by_func(channel, main_migrate_handshake_done_cb, data);
+ mig->do_seamless = seamless;
+ c->state = SPICE_CHANNEL_STATE_MIGRATING;
+ mig->nchannels--;
+ if (mig->nchannels == 0)
+ coroutine_yieldto(mig->from, NULL);
+}
+
#ifdef __GNUC__
typedef struct __attribute__ ((__packed__)) OldRedMigrationBegin {
#else
@@ -1703,16 +1797,19 @@ 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);
spice_migrate mig = { 0, };
SpiceMsgOut *out;
int reply_type;
mig.channel = channel;
- mig.info = &msg->dst_info;
+ mig.info = dst_info;
mig.from = coroutine_self();
+ mig.do_seamless = do_seamless;
+ mig.src_mig_version = src_mig_version;
/* no need to track idle, call is sync for this coroutine */
g_idle_add(migrate_connect, &mig);
@@ -1725,8 +1822,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 +1837,38 @@ 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;
+
+ g_return_if_fail(c->state == SPICE_CHANNEL_STATE_MIGRATION_HANDSHAKE);
+ emit_main_context(channel, SPICE_MIGRATION_HANDSHAKE_DONE, true);
+}
+
+static void main_handle_migrate_dst_seamless_nack(SpiceChannel *channel, SpiceMsgIn *in)
+{
+ SpiceChannelPrivate *c = SPICE_CHANNEL(channel)->priv;
+
+ g_return_if_fail(c->state == SPICE_CHANNEL_STATE_MIGRATION_HANDSHAKE);
+ emit_main_context(channel, SPICE_MIGRATION_HANDSHAKE_DONE, false);
+}
+
/* main context */
static gboolean migrate_delayed(gpointer data)
{
@@ -1848,7 +1982,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 +1993,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
More information about the Spice-devel
mailing list