summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--server/main_channel.c133
-rw-r--r--server/main_channel.h6
-rw-r--r--server/reds.c8
-rw-r--r--server/reds.h4
4 files changed, 124 insertions, 27 deletions
diff --git a/server/main_channel.c b/server/main_channel.c
index 25eaf186..2d7cb025 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 b69dcfe9..40d2215a 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 6397b41a..074a19d8 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 87125d25..b3a31558 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);