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

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


- handle SPICE_MSGC_MAIN_MIGRATE_DST_DO_SEAMLESS
- reply with SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_ACK/NACK
- prepare the channels for migration according to the migration
   type (semi/seamless)

see spice-protocol for more details:
commit 1ad5d259cb4b695ec3106de7ccd082e031e7ae11
---
 server/main_channel.c |   23 +++++++++++++--
 server/reds.c         |   75 ++++++++++++++++++++++++++++++++++++++++--------
 server/reds.h         |    3 ++
 3 files changed, 85 insertions(+), 16 deletions(-)

diff --git a/server/main_channel.c b/server/main_channel.c
index 2d7cb02..6123068 100644
--- a/server/main_channel.c
+++ b/server/main_channel.c
@@ -790,9 +790,9 @@ static void main_channel_release_pipe_item(RedChannelClient *rcc,
     free(base);
 }
 
-void main_channel_client_handle_migrate_connected(MainChannelClient *mcc,
-                                                  int success,
-                                                  int seamless)
+static void main_channel_client_handle_migrate_connected(MainChannelClient *mcc,
+                                                         int success,
+                                                         int seamless)
 {
     spice_printerr("client %p connected: %d seamless %d", mcc->base.client, success, seamless);
     if (mcc->mig_wait_connect) {
@@ -813,6 +813,18 @@ void main_channel_client_handle_migrate_connected(MainChannelClient *mcc,
     }
 }
 
+void main_channel_client_handle_migrate_dst_do_seamless(MainChannelClient *mcc,
+                                                        uint32_t src_version)
+{
+    if (reds_on_migrate_dst_set_seamless(mcc, src_version)) {
+        red_channel_client_pipe_add_type(&mcc->base,
+                                         SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_ACK);
+    } else {
+        red_channel_client_pipe_add_type(&mcc->base,
+                                         SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_NACK);
+    }
+}
+
 void main_channel_client_handle_migrate_end(MainChannelClient *mcc)
 {
     if (!red_client_during_migrate_at_target(mcc->base.client)) {
@@ -882,6 +894,10 @@ static int main_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, uint
     case SPICE_MSGC_MAIN_MIGRATE_CONNECT_ERROR:
         main_channel_client_handle_migrate_connected(mcc, FALSE, FALSE);
         break;
+    case SPICE_MSGC_MAIN_MIGRATE_DST_DO_SEAMLESS:
+        main_channel_client_handle_migrate_dst_do_seamless(mcc,
+            ((SpiceMsgcMainMigrateDstDoSeamless *)message)->src_version);
+        break;
     case SPICE_MSGC_MAIN_MOUSE_MODE_REQUEST:
         reds_on_main_mouse_mode_request(message, size);
         break;
@@ -1125,6 +1141,7 @@ MainChannel* main_channel_init(void)
                                         &channel_cbs);
     spice_assert(channel);
     red_channel_set_cap(channel, SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE);
+
     return (MainChannel *)channel;
 }
 
diff --git a/server/reds.c b/server/reds.c
index a7de520..c494c84 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -67,6 +67,7 @@
 #include "stat.h"
 #include "demarshallers.h"
 #include "char_device.h"
+#include "migration_protocol.h"
 #ifdef USE_TUNNEL
 #include "red_tunnel_worker.h"
 #endif
@@ -242,6 +243,8 @@ typedef struct RedsState {
     int expect_migrate;
     int src_do_seamless_migrate; /* per migration. Updated after the migration handshake
                                     between the 2 servers */
+    int dst_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;
@@ -256,7 +259,7 @@ typedef struct RedsState {
 
     int vm_running;
     Ring char_devs_states; /* list of SpiceCharDeviceStateItem */
-    int seamless_migration_enabled;
+    int seamless_migration_enabled; /* command line arg */
 
     SSL_CTX *ctx;
 
@@ -1548,26 +1551,28 @@ static void reds_channel_do_link(RedChannel *channel, RedClient *client,
                                 caps + link_msg->num_common_caps : NULL);
 }
 
-void reds_on_client_migrate_complete(RedClient *client)
+/*
+ * migration target side:
+ * In semi-seamless migration, we activate the channels only
+ * after migration is completed.
+ * In seamless migration, in order to keep the continuousness, and
+ * not lose any data, we activate the target channels before
+ * migration completes, as soon as we receive SPICE_MSGC_MAIN_MIGRATE_DST_DO_SEAMLESS
+ */
+static int reds_link_mig_target_channels(RedClient *client)
 {
     RedsMigTargetClient *mig_client;
-    MainChannelClient *mcc;
     RingItem *item;
 
     spice_info("%p", client);
-    mcc = red_client_get_main(client);
     mig_client = reds_mig_target_client_find(client);
     if (!mig_client) {
-        spice_warning("mig target client was not found");
-        return;
+        spice_info("Error: mig target client was not found");
+        return FALSE;
     }
 
-    // TODO: not doing net test. consider doing it on client_migrate_info
-    main_channel_push_init(mcc, red_dispatcher_count(),
-                           reds->mouse_mode, reds->is_client_mouse_allowed,
-                           reds_get_mm_time() - MM_TIME_DELTA,
-                           red_dispatcher_qxl_ram_size());
-
+    /* Each channel should check if we are during migration, and
+     * act accordingly. */
     RING_FOREACH(item, &mig_client->pending_links) {
         RedsMigPendingLink *mig_link;
         RedChannel *channel;
@@ -1586,6 +1591,40 @@ void reds_on_client_migrate_complete(RedClient *client)
     }
 
     reds_mig_target_client_free(mig_client);
+
+    return TRUE;
+}
+
+int reds_on_migrate_dst_set_seamless(MainChannelClient *mcc, uint32_t src_version)
+{
+    /* seamless migration is not supported with multiple clients*/
+    if (reds->allow_multiple_clients  || src_version > SPICE_MIGRATION_PROTOCOL_VERSION) {
+        reds->dst_do_seamless_migrate = FALSE;
+    } else {
+        RedClient *client;
+
+        client = main_channel_client_get_base(mcc)->client;
+        reds->dst_do_seamless_migrate = reds_link_mig_target_channels(client);
+        /* linking all the channels that have been connected before migration handshake */
+        reds->dst_do_seamless_migrate = reds_link_mig_target_channels(client);
+    }
+    return reds->dst_do_seamless_migrate;
+}
+
+/* semi seamless */
+void reds_on_client_migrate_complete(RedClient *client)
+{
+    MainChannelClient *mcc;
+
+    spice_info("%p", client);
+    mcc = red_client_get_main(client);
+
+    // TODO: not doing net test. consider doing it on client_migrate_info
+    main_channel_push_init(mcc, red_dispatcher_count(),
+                           reds->mouse_mode, reds->is_client_mouse_allowed,
+                           reds_get_mm_time() - MM_TIME_DELTA,
+                           red_dispatcher_qxl_ram_size());
+    reds_link_mig_target_channels(client);
 }
 
 static void reds_handle_other_links(RedLinkInfo *link)
@@ -1623,7 +1662,17 @@ static void reds_handle_other_links(RedLinkInfo *link)
     reds_stream_remove_watch(link->stream);
 
     mig_client = reds_mig_target_client_find(client);
-    if (red_client_during_migrate_at_target(client)) {
+    /*
+     * In semi-seamless migration, we activate the channels only
+     * after migration is completed. Since, the session starts almost from
+     * scratch we don't mind if we skip some messages in between the src session end and
+     * dst session start.
+     * In seamless migration, in order to keep the continuousness of the session, and
+     * in order not to lose any data, we activate the target channels before
+     * migration completes, as soon as we receive SPICE_MSGC_MAIN_MIGRATE_DST_DO_SEAMLESS.
+     * If a channel connects before receiving SPICE_MSGC_MAIN_MIGRATE_DST_DO_SEAMLESS,
+     * reds_on_migrate_dst_set_seamless will take care of activating it */
+    if (red_client_during_migrate_at_target(client) && !reds->dst_do_seamless_migrate) {
         spice_assert(mig_client);
         reds_mig_target_client_add_pending_link(mig_client, link_mess, link->stream);
     } else {
diff --git a/server/reds.h b/server/reds.h
index b3a3155..71a0173 100644
--- a/server/reds.h
+++ b/server/reds.h
@@ -154,6 +154,9 @@ void reds_on_main_migrate_connected(int seamless); //should be called when all t
                                                    // 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);
+/* migration dest side: returns whether it can support seamless migration
+ * with the given src migration protocol version */
+int reds_on_migrate_dst_set_seamless(MainChannelClient *mcc, uint32_t src_version);
 void reds_on_client_migrate_complete(RedClient *client);
 void reds_on_char_device_state_destroy(SpiceCharDeviceState *dev);
 
-- 
1.7.7.6



More information about the Spice-devel mailing list