[Spice-devel] [spice PATCH 10/55] seamless migration: pre migration phase on the src side

Yonit Halperin yhalperi at redhat.com
Wed Aug 15 00:55:50 PDT 2012


sending SPICE_MSG_MAIN_MIGRATE_BEGIN_SEAMLESS and handling
SPICE_MSGC_MAIN_MIGRATE_CONNECTED_SEAMLESS

The src side signals the client to establish a connection
to the destination.
In seamless migration, the client is also used to perform
a sort of handshake with the destination, for verifying
if seamless migration can be supported.

see spice-protocol for more details:
commit 1ad5d259cb4b695ec3106de7ccd082e031e7ae11
---
 server/main_channel.c |  133 +++++++++++++++++++++++++++++++++++++++++--------
 server/main_channel.h |    6 ++-
 server/reds.c         |    8 ++-
 server/reds.h         |    4 +-
 4 files changed, 124 insertions(+), 27 deletions(-)

diff --git a/server/main_channel.c b/server/main_channel.c
index 25eaf18..2d7cb02 100644
--- a/server/main_channel.c
+++ b/server/main_channel.c
@@ -45,6 +45,7 @@
 #include "red_channel.h"
 #include "red_common.h"
 #include "reds.h"
+#include "migration_protocol.h"
 
 #define ZERO_BUF_SIZE 4096
 
@@ -135,6 +136,7 @@ struct MainChannelClient {
     int mig_wait_connect;
     int mig_connect_ok;
     int mig_wait_prev_complete;
+    int mig_wait_prev_try_seamless;
     int init_sent;
 };
 
@@ -576,26 +578,45 @@ static void main_channel_marshall_notify(SpiceMarshaller *m, NotifyPipeItem *ite
     spice_marshaller_add(m, item->mess, item->mess_len + 1);
 }
 
+static void main_channel_fill_migrate_dst_info(MainChannel *main_channel,
+                                               SpiceMigrationDstInfo *dst_info)
+{
+    RedsMigSpice *mig_dst = &main_channel->mig_target;
+    dst_info->port = mig_dst->port;
+    dst_info->sport = mig_dst->sport;
+    dst_info->host_size = strlen(mig_dst->host) + 1;
+    dst_info->host_data = (uint8_t *)mig_dst->host;
+    if (mig_dst->cert_subject) {
+        dst_info->cert_subject_size = strlen(mig_dst->cert_subject) + 1;
+        dst_info->cert_subject_data = (uint8_t *)mig_dst->cert_subject;
+    } else {
+        dst_info->cert_subject_size = 0;
+        dst_info->cert_subject_data = NULL;
+    }
+}
+
 static void main_channel_marshall_migrate_begin(SpiceMarshaller *m, RedChannelClient *rcc)
 {
     SpiceMsgMainMigrationBegin migrate;
     MainChannel *main_ch;
 
     main_ch = SPICE_CONTAINEROF(rcc->channel, MainChannel, base);
-    migrate.dst_info.port = main_ch->mig_target.port;
-    migrate.dst_info.sport = main_ch->mig_target.sport;
-    migrate.dst_info.host_size = strlen(main_ch->mig_target.host) + 1;
-    migrate.dst_info.host_data = (uint8_t *)main_ch->mig_target.host;
-    if (main_ch->mig_target.cert_subject) {
-        migrate.dst_info.cert_subject_size = strlen(main_ch->mig_target.cert_subject) + 1;
-        migrate.dst_info.cert_subject_data = (uint8_t *)main_ch->mig_target.cert_subject;
-    } else {
-        migrate.dst_info.cert_subject_size = 0;
-        migrate.dst_info.cert_subject_data = NULL;
-    }
+    main_channel_fill_migrate_dst_info(main_ch, &migrate.dst_info);
     spice_marshall_msg_main_migrate_begin(m, &migrate);
 }
 
+static void main_channel_marshall_migrate_begin_seamless(SpiceMarshaller *m,
+                                                         RedChannelClient *rcc)
+{
+    SpiceMsgMainMigrateBeginSeamless migrate_seamless;
+    MainChannel *main_ch;
+
+    main_ch = SPICE_CONTAINEROF(rcc->channel, MainChannel, base);
+    main_channel_fill_migrate_dst_info(main_ch, &migrate_seamless.dst_info);
+    migrate_seamless.src_mig_version = SPICE_MIGRATION_PROTOCOL_VERSION;
+    spice_marshall_msg_main_migrate_begin_seamless(m, &migrate_seamless);
+}
+
 void main_channel_push_migrate(MainChannel *main_chan)
 {
     red_channel_pipes_add_type(&main_chan->base, SPICE_MSG_MIGRATE);
@@ -728,6 +749,9 @@ static void main_channel_send_item(RedChannelClient *rcc, PipeItem *base)
         case SPICE_MSG_MAIN_MIGRATE_BEGIN:
             main_channel_marshall_migrate_begin(m, rcc);
             break;
+        case SPICE_MSG_MAIN_MIGRATE_BEGIN_SEAMLESS:
+            main_channel_marshall_migrate_begin_seamless(m, rcc);
+            break;
         case SPICE_MSG_MAIN_MULTI_MEDIA_TIME:
             main_channel_marshall_multi_media_time(m,
                 SPICE_CONTAINEROF(base, MultiMediaTimePipeItem, base));
@@ -766,17 +790,20 @@ static void main_channel_release_pipe_item(RedChannelClient *rcc,
     free(base);
 }
 
-void main_channel_client_handle_migrate_connected(MainChannelClient *mcc, int success)
+void main_channel_client_handle_migrate_connected(MainChannelClient *mcc,
+                                                  int success,
+                                                  int seamless)
 {
-    spice_printerr("client %p connected: %d", mcc->base.client, success);
+    spice_printerr("client %p connected: %d seamless %d", mcc->base.client, success, seamless);
     if (mcc->mig_wait_connect) {
         MainChannel *main_channel = SPICE_CONTAINEROF(mcc->base.channel, MainChannel, base);
 
         mcc->mig_wait_connect = FALSE;
         mcc->mig_connect_ok = success;
         spice_assert(main_channel->num_clients_mig_wait);
+        spice_assert(!seamless || main_channel->num_clients_mig_wait == 1);
         if (!--main_channel->num_clients_mig_wait) {
-            reds_on_main_migrate_connected();
+            reds_on_main_migrate_connected(seamless && success);
         }
     } else {
         if (success) {
@@ -800,7 +827,12 @@ void main_channel_client_handle_migrate_end(MainChannelClient *mcc)
     }
     red_client_migrate_complete(mcc->base.client);
     if (mcc->mig_wait_prev_complete) {
-        red_channel_client_pipe_add_type(&mcc->base, SPICE_MSG_MAIN_MIGRATE_BEGIN);
+
+        if (mcc->mig_wait_prev_try_seamless) {
+            red_channel_client_pipe_add_type(&mcc->base, SPICE_MSG_MAIN_MIGRATE_BEGIN_SEAMLESS);
+        } else {
+            red_channel_client_pipe_add_type(&mcc->base, SPICE_MSG_MAIN_MIGRATE_BEGIN);
+        }
         mcc->mig_wait_connect = TRUE;
         mcc->mig_wait_prev_complete = FALSE;
     }
@@ -838,10 +870,17 @@ static int main_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, uint
         main_channel_push_channels(mcc);
         break;
     case SPICE_MSGC_MAIN_MIGRATE_CONNECTED:
-        main_channel_client_handle_migrate_connected(mcc, TRUE);
+        main_channel_client_handle_migrate_connected(mcc,
+                                                     TRUE /* success */,
+                                                     FALSE /* seamless */);
+        break;
+    case SPICE_MSGC_MAIN_MIGRATE_CONNECTED_SEAMLESS:
+        main_channel_client_handle_migrate_connected(mcc,
+                                                     TRUE /* success */,
+                                                     TRUE /* seamless */);
         break;
     case SPICE_MSGC_MAIN_MIGRATE_CONNECT_ERROR:
-        main_channel_client_handle_migrate_connected(mcc, FALSE);
+        main_channel_client_handle_migrate_connected(mcc, FALSE, FALSE);
         break;
     case SPICE_MSGC_MAIN_MOUSE_MODE_REQUEST:
         reds_on_main_mouse_mode_request(message, size);
@@ -1095,13 +1134,10 @@ RedChannelClient* main_channel_client_get_base(MainChannelClient* mcc)
     return &mcc->base;
 }
 
-int main_channel_migrate_connect(MainChannel *main_channel, RedsMigSpice *mig_target)
+static int main_channel_connect_semi_seamless(MainChannel *main_channel)
 {
     RingItem *client_link;
 
-    main_channel_fill_mig_target(main_channel, mig_target);
-    main_channel->num_clients_mig_wait = 0;
-
     RING_FOREACH(client_link, &main_channel->base.clients) {
         MainChannelClient * mcc = SPICE_CONTAINEROF(client_link, MainChannelClient,
                                                     base.channel_link);
@@ -1110,6 +1146,7 @@ int main_channel_migrate_connect(MainChannel *main_channel, RedsMigSpice *mig_ta
             if (red_client_during_migrate_at_target(mcc->base.client)) {
                 spice_printerr("client %p: wait till previous migration completes", mcc->base.client);
                 mcc->mig_wait_prev_complete = TRUE;
+                mcc->mig_wait_prev_try_seamless = FALSE;
             } else {
                 red_channel_client_pipe_add_type(&mcc->base, SPICE_MSG_MAIN_MIGRATE_BEGIN);
                 mcc->mig_wait_connect = TRUE;
@@ -1121,6 +1158,60 @@ int main_channel_migrate_connect(MainChannel *main_channel, RedsMigSpice *mig_ta
     return main_channel->num_clients_mig_wait;
 }
 
+static int main_channel_connect_seamless(MainChannel *main_channel)
+{
+    RingItem *client_link;
+
+    spice_assert(main_channel->base.clients_num == 1);
+
+    RING_FOREACH(client_link, &main_channel->base.clients) {
+        MainChannelClient * mcc = SPICE_CONTAINEROF(client_link, MainChannelClient,
+                                                    base.channel_link);
+        spice_assert(red_channel_client_test_remote_cap(&mcc->base,
+                                                        SPICE_MAIN_CAP_SEAMLESS_MIGRATE));
+        if (red_client_during_migrate_at_target(mcc->base.client)) {
+           spice_printerr("client %p: wait till previous migration completes", mcc->base.client);
+           mcc->mig_wait_prev_complete = TRUE;
+           mcc->mig_wait_prev_try_seamless = TRUE;
+        } else {
+            red_channel_client_pipe_add_type(&mcc->base, SPICE_MSG_MAIN_MIGRATE_BEGIN_SEAMLESS);
+            mcc->mig_wait_connect = TRUE;
+        }
+        mcc->mig_connect_ok = FALSE;
+        main_channel->num_clients_mig_wait++;
+    }
+    return main_channel->num_clients_mig_wait;
+}
+
+int main_channel_migrate_connect(MainChannel *main_channel, RedsMigSpice *mig_target,
+                                 int try_seamless)
+{
+    main_channel_fill_mig_target(main_channel, mig_target);
+    main_channel->num_clients_mig_wait = 0;
+
+    if (!main_channel_is_connected(main_channel)) {
+        return 0;
+    }
+
+    if (!try_seamless) {
+        return main_channel_connect_semi_seamless(main_channel);
+    } else {
+        RingItem *client_item;
+        MainChannelClient *mcc;
+
+        client_item = ring_get_head(&main_channel->base.clients);
+        mcc = SPICE_CONTAINEROF(client_item, MainChannelClient, base.channel_link);
+
+        if (!red_channel_client_test_remote_cap(&mcc->base,
+                                                SPICE_MAIN_CAP_SEAMLESS_MIGRATE)) {
+            return main_channel_connect_semi_seamless(main_channel);
+        } else {
+            return main_channel_connect_seamless(main_channel);
+        }
+    }
+
+}
+
 void main_channel_migrate_cancel_wait(MainChannel *main_chan)
 {
     RingItem *client_link;
diff --git a/server/main_channel.h b/server/main_channel.h
index b69dcfe..40d2215 100644
--- a/server/main_channel.h
+++ b/server/main_channel.h
@@ -98,8 +98,10 @@ void main_channel_migrate_switch(MainChannel *main_chan, RedsMigSpice *mig_targe
 
 /* semi seamless migration */
 
-/* returns the number of clients that we are waiting for their connection */
-int main_channel_migrate_connect(MainChannel *main_channel, RedsMigSpice *mig_target);
+/* returns the number of clients that we are waiting for their connection.
+ * try_seamless = 'true' when the seamless-migration=on in qemu command line */
+int main_channel_migrate_connect(MainChannel *main_channel, RedsMigSpice *mig_target,
+                                 int try_seamless);
 void main_channel_migrate_cancel_wait(MainChannel *main_chan);
 /* returns the number of clients for which SPICE_MSG_MAIN_MIGRATE_END was sent*/
 int main_channel_migrate_complete(MainChannel *main_chan, int success);
diff --git a/server/reds.c b/server/reds.c
index b053a84..a7de520 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -240,6 +240,8 @@ typedef struct RedsState {
     int mig_wait_disconnect;
     int mig_inprogress;
     int expect_migrate;
+    int src_do_seamless_migrate; /* per migration. Updated after the migration handshake
+                                    between the 2 servers */
     Ring mig_target_clients;
     int num_mig_target_clients;
     RedsMigSpice *mig_spice;
@@ -1163,8 +1165,9 @@ void reds_on_main_agent_data(MainChannelClient *mcc, void *message, size_t size)
     spice_char_device_write_buffer_add(reds->agent_state.base, dev_state->recv_from_client_buf);
 }
 
-void reds_on_main_migrate_connected(void)
+void reds_on_main_migrate_connected(int seamless)
 {
+    reds->src_do_seamless_migrate = seamless;
     if (reds->mig_wait_connect) {
         reds_mig_cleanup();
     }
@@ -3938,7 +3941,8 @@ SPICE_GNUC_VISIBLE int spice_server_migrate_connect(SpiceServer *s, const char*
     reds->expect_migrate = TRUE;
 
     /* main channel will take care of clients that are still during migration (at target)*/
-    if (main_channel_migrate_connect(reds->main_channel, reds->mig_spice)) {
+    if (main_channel_migrate_connect(reds->main_channel, reds->mig_spice,
+                                     reds->seamless_migration_enabled)) {
         reds_mig_started();
     } else {
         if (reds->num_clients == 0) {
diff --git a/server/reds.h b/server/reds.h
index 87125d2..b3a3155 100644
--- a/server/reds.h
+++ b/server/reds.h
@@ -150,8 +150,8 @@ void reds_on_main_agent_tokens(MainChannelClient *mcc, uint32_t num_tokens);
 uint8_t *reds_get_agent_data_buffer(MainChannelClient *mcc, size_t size);
 void reds_release_agent_data_buffer(uint8_t *buf);
 void reds_on_main_agent_data(MainChannelClient *mcc, void *message, size_t size);
-void reds_on_main_migrate_connected(void); //should be called when all the clients
-                                           // are connected to the target
+void reds_on_main_migrate_connected(int seamless); //should be called when all the clients
+                                                   // are connected to the target
 void reds_on_main_receive_migrate_data(MainMigrateData *data, uint8_t *end);
 void reds_on_main_mouse_mode_request(void *message, size_t size);
 void reds_on_client_migrate_complete(RedClient *client);
-- 
1.7.7.6



More information about the Spice-devel mailing list