summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYonit Halperin <yhalperi@redhat.com>2012-08-02 11:19:30 +0300
committerYonit Halperin <yhalperi@redhat.com>2012-08-27 09:12:50 +0300
commit8e2576d5ab2f0c2089a74edbb5e3a9c2bfbfb06a (patch)
treee79ca23a033697a8def4f3b54e703f0ad8efc3f5
parent43e0897da5dd880fa9c2df5dd34d9de1ab13b862 (diff)
downloadspice-8e2576d5ab2f0c2089a74edbb5e3a9c2bfbfb06a.tar.gz
spice-8e2576d5ab2f0c2089a74edbb5e3a9c2bfbfb06a.tar.xz
spice-8e2576d5ab2f0c2089a74edbb5e3a9c2bfbfb06a.zip
seamless migration: pre migration phase on the destination side
- 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 3838ad140a046c4ddf42fef58c9727ecfdc09f9f
-rw-r--r--server/main_channel.c23
-rw-r--r--server/reds.c75
-rw-r--r--server/reds.h3
3 files changed, 85 insertions, 16 deletions
diff --git a/server/main_channel.c b/server/main_channel.c
index 2d7cb025..61230682 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 074a19d8..08836fd8 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 b3a31558..71a0173f 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);