[Spice-devel] [RFC PATCH spice 0.8 10/19] server: handling semi-seamless migration in the target side

Yonit Halperin yhalperi at redhat.com
Mon Sep 19 02:47:03 PDT 2011


(1) not sending anything to the client till we recieve SPICE_MSGC_MIGRATE_END
(2) start a new migration (handle client_migrate_info) only after SPICE_MSGC_MIGRATE_END
    from the previous migration has been received

Signed-off-by: Yonit Halperin <yhalperi at redhat.com>
---
 server/reds.c |  125 +++++++++++++++++++++++++++++++++++++++++++++++++++------
 1 files changed, 112 insertions(+), 13 deletions(-)

diff --git a/server/reds.c b/server/reds.c
index bd9a874..a2b2820 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -258,6 +258,13 @@ typedef struct RedsStatValue {
 #endif
 
 typedef struct RedsMigSpice RedsMigSpice;
+typedef struct RedsMigPendingLink RedsMigPendingLink;
+
+struct RedsMigPendingLink {
+    RedsMigPendingLink *next;
+    SpiceLinkMess *link_msg;
+    RedsStream *stream;
+};
 
 typedef struct RedsState {
     int listen_socket;
@@ -274,10 +281,13 @@ typedef struct RedsState {
     int client_semi_mig_cap;
     int mig_wait_connect;
     int mig_wait_disconnect;
+    int mig_wait_prev_complete;
     int mig_inprogress;
     int expect_migrate;
     int mig_target;
     RedsMigSpice *mig_spice;
+    RedsMigPendingLink *mig_pending_links;
+    int num_mig_links;
     int num_of_channels;
     IncomingHandler in_handler;
     RedsOutgoingData outgoing;
@@ -382,6 +392,9 @@ static uint8_t zero_page[ZERO_BUF_SIZE] = {0};
 static void reds_push();
 static void reds_out_item_free(RedsOutItem *item);
 static void migrate_timeout(void *opaque);
+static void reds_mig_pending_links_free(void);
+static void reds_handle_client_migrate_complete(void);
+static void reds_mig_started(void);
 
 static ChannelSecurityOptions *channels_security = NULL;
 static int default_channel_security =
@@ -648,7 +661,7 @@ static void reds_shatdown_channels()
 static void reds_mig_cleanup()
 {
     if (reds->mig_inprogress) {
-        if (reds->mig_wait_connect) {
+        if (reds->mig_wait_connect || reds->mig_wait_prev_complete) {
             SpiceMigrateInterface *sif;
             ASSERT(migration_interface);
             sif = SPICE_CONTAINEROF(migration_interface->base.sif, SpiceMigrateInterface, base);
@@ -657,8 +670,13 @@ static void reds_mig_cleanup()
         reds->mig_inprogress = FALSE;
         reds->mig_wait_connect = FALSE;
         reds->mig_wait_disconnect = FALSE;
+        reds->mig_wait_prev_complete = FALSE;
         core->timer_cancel(reds->mig_timer);
     }
+    if (reds->num_mig_links) {
+        ASSERT(reds->mig_target);
+        reds_mig_pending_links_free();
+    }
 }
 
 static void reds_reset_vdp()
@@ -925,6 +943,11 @@ static void reds_send_channels()
     Channel *channel;
     int i;
 
+    if (reds->mig_target) {
+        red_printf("warning: unexpected SPICE_MSGC_MAIN_ATTACH_CHANNELS during migration");
+        return;
+    }
+
     item = new_out_item(SPICE_MSG_MAIN_CHANNELS_LIST);
     channels_info = (SpiceMsgChannels *)spice_malloc(sizeof(SpiceMsgChannels) + reds->num_of_channels * sizeof(SpiceChannelId));
     channels_info->num_of_channels = reds->num_of_channels;
@@ -1008,7 +1031,7 @@ static void reds_send_mouse_mode()
     SpiceMsgMainMouseMode mouse_mode;
     RedsOutItem *item;
 
-    if (!reds->stream) {
+    if (!reds->stream || reds->mig_target) {
         return;
     }
 
@@ -1077,6 +1100,7 @@ static void reds_agent_remove()
     SpiceCharDeviceInstance *sin = vdagent;
     SpiceCharDeviceInterface *sif;
 
+    // TODO: is this cond needed
     if (!reds->mig_target) {
         reds_reset_vdp();
     }
@@ -1103,7 +1127,7 @@ static void reds_send_tokens()
     SpiceMsgMainAgentTokens tokens;
     RedsOutItem *item;
 
-    if (!reds->stream) {
+    if (!reds->stream || reds->mig_target) {
         return;
     }
 
@@ -1798,6 +1822,18 @@ static void reds_main_handle_message(void *opaque, size_t size, uint32_t type, v
         break;
     case SPICE_MSGC_DISCONNECTING:
         break;
+    case SPICE_MSGC_MAIN_MIGRATE_END:
+        if (!reds->mig_target) {
+            red_printf("unexpected SPICE_MSGC_MIGRATE_END, not target");
+            return;
+        }
+        if (!reds->client_semi_mig_cap) {
+            red_printf("unexpected SPICE_MSGC_MIGRATE_END"
+                       ",client does not support semi-seamless migration");
+            return;
+        }
+        reds_handle_client_migrate_complete();
+        break;
     default:
         red_printf("unexpected type %d", type);
     }
@@ -2125,11 +2161,8 @@ static void reds_handle_main_link(RedLinkInfo *link)
         memcpy(&(reds->taTicket), &taTicket, sizeof(reds->taTicket));
         reds->mig_target = FALSE;
     } else {
-        if (link_mess->connection_id != reds->link_id) {
-            reds_send_link_result(link, SPICE_LINK_ERR_BAD_CONNECTION_ID);
-            reds_link_free(link);
-            return;
-        }
+        // TODO: make sure link_mess->connection_id is the same
+        // connection id the migration src had (use vmstate to store the connection id)
         reds_send_link_result(link, SPICE_LINK_ERR_OK);
         connection_id = link_mess->connection_id;
         reds->mig_target = TRUE;
@@ -2158,6 +2191,7 @@ static void reds_handle_main_link(RedLinkInfo *link)
     link->stream = NULL;
     link->link_mess = NULL;
     reds_link_free(link);
+    // TODO: should this be moved to be done only after mig completed (reds_main_channel_init)?
     if (vdagent) {
         SpiceCharDeviceInterface *sif;
         sif = SPICE_CONTAINEROF(vdagent->base.sif, SpiceCharDeviceInterface, base);
@@ -2612,6 +2646,32 @@ static void inputs_init()
     reds_register_channel(channel);
 }
 
+static void reds_mig_pending_link_add(SpiceLinkMess *link_msg, RedsStream *stream)
+{
+    RedsMigPendingLink *mig_link;
+
+    ASSERT(reds);
+    mig_link = spice_malloc0(sizeof(RedsMigPendingLink));
+    mig_link->link_msg = link_msg;
+    mig_link->stream = stream;
+
+    mig_link->next = reds->mig_pending_links;
+    reds->mig_pending_links = mig_link;
+    reds->num_mig_links++;
+}
+
+static void reds_mig_pending_links_free(void)
+{
+    red_printf("");
+    while(reds->mig_pending_links) {
+        RedsMigPendingLink *tmp = reds->mig_pending_links;
+        reds->mig_pending_links = tmp->next;
+        free(tmp->link_msg);
+        free(tmp);
+    }
+
+    reds->num_mig_links = 0;
+}
 static void reds_send_input_channel_insecure_warn()
 {
     RedsOutItem *item;
@@ -2650,6 +2710,35 @@ static void reds_channel_do_link(Channel *channel, SpiceLinkMess *link_msg, Reds
                   link_msg->num_channel_caps ? caps + link_msg->num_common_caps : NULL);
 }
 
+static void reds_handle_client_migrate_complete(void)
+{
+    RedsMigPendingLink *cur_link;
+
+    red_printf("");
+    // TODO: not doing net test. consider doing it on client_migrate_info
+    reds_main_channel_init(FALSE);
+
+    for (cur_link = reds->mig_pending_links; cur_link; cur_link = cur_link->next) {
+        Channel *channel = reds_find_channel(cur_link->link_msg->channel_type,
+                                             cur_link->link_msg->channel_id);
+        if (!channel) {
+            red_printf("warning: channel (%d, %d) (type,id) wasn't found",
+                       cur_link->link_msg->channel_type, cur_link->link_msg->channel_id);
+            continue;
+       }
+       reds_channel_do_link(channel, cur_link->link_msg, cur_link->stream);
+    }
+
+    reds_mig_pending_links_free();
+    reds->mig_target = FALSE;
+    if (reds->mig_wait_prev_complete) {
+        reds->mig_wait_prev_complete = FALSE;
+        core->timer_cancel(reds->mig_timer);
+        // starting a pending migrate info command
+        reds_mig_started();
+    }
+}
+
 static void reds_handle_other_links(RedLinkInfo *link)
 {
     Channel *channel;
@@ -2675,8 +2764,12 @@ static void reds_handle_other_links(RedLinkInfo *link)
     reds_show_new_channel(link, reds->link_id);
     reds_stream_remove_watch(link->stream);
 
-    reds_channel_do_link(channel, link->link_mess, link->stream);
-    free(link_mess);
+    if (reds->mig_target) {
+        reds_mig_pending_link_add(link->link_mess, link->stream);
+    } else {
+        reds_channel_do_link(channel, link->link_mess, link->stream);
+        free(link_mess);
+    }
 
     link->stream = NULL;
     link->link_mess = NULL;
@@ -4184,7 +4277,7 @@ static void reds_mig_switch(void)
 static void migrate_timeout(void *opaque)
 {
     red_printf("");
-    ASSERT(reds->mig_wait_connect || reds->mig_wait_disconnect);
+    ASSERT(reds->mig_wait_connect || reds->mig_wait_disconnect || reds->mig_wait_prev_complete);
     reds_mig_disconnect();
 }
 
@@ -4211,7 +4304,7 @@ void reds_enable_mm_timer(void)
     RedsOutItem *item;
 
     core->timer_start(reds->mm_timer, MM_TIMER_GRANULARITY_MS);
-    if (!reds->stream) {
+    if (!reds->stream || reds->mig_target) {
         return;
     }
 
@@ -5005,7 +5098,13 @@ SPICE_GNUC_VISIBLE int spice_server_migrate_info(SpiceServer *s, const char* des
     }
 
     if (migration_interface && reds->client_semi_mig_cap) {
-        reds_mig_started();
+        if (!reds->mig_target) {
+            reds_mig_started();
+        } else {
+            red_printf("previous spice migration hasn't completed yet. Waiting for client");
+            reds->mig_wait_prev_complete = TRUE;
+            core->timer_start(reds->mig_timer, MIGRATE_TIMEOUT);
+        }
     } else if (sif) {
         sif->migrate_info_complete(migration_interface);
     }
-- 
1.7.4.4



More information about the Spice-devel mailing list