[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