[Spice-devel] [spice-gtk PATCH v2 4/8] seamless migration: src and dest servers handshake
Yonit Halperin
yhalperi at redhat.com
Sat Aug 25 13:20:20 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.
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
More information about the Spice-devel
mailing list