[Spice-devel] [spice PATCH 15/55] seamless migration: migration completion on the destination side
Yonit Halperin
yhalperi at redhat.com
Wed Aug 15 00:55:55 PDT 2012
Tracking the channels that wait for migration data. If there
is a new migration process pending, when all the channels have
restored their state, we begin the new migration.
---
server/main_channel.c | 11 +++-
server/main_channel.h | 3 +-
server/main_dispatcher.c | 32 ++++++++++++++
server/main_dispatcher.h | 1 +
server/red_channel.c | 107 ++++++++++++++++++++++++++++++++++++++++++----
server/red_channel.h | 28 +++++++++++-
server/reds.c | 42 ++++++++++++++----
server/reds.h | 3 +-
8 files changed, 202 insertions(+), 25 deletions(-)
diff --git a/server/main_channel.c b/server/main_channel.c
index 6398965..e06bb05 100644
--- a/server/main_channel.c
+++ b/server/main_channel.c
@@ -837,10 +837,14 @@ void main_channel_client_handle_migrate_end(MainChannelClient *mcc)
"client does not support semi-seamless migration");
return;
}
- red_client_migrate_complete(mcc->base.client);
- if (mcc->mig_wait_prev_complete) {
+ red_client_semi_seamless_migrate_complete(mcc->base.client);
+}
+void main_channel_migrate_dst_complete(MainChannelClient *mcc)
+{
+ if (mcc->mig_wait_prev_complete) {
if (mcc->mig_wait_prev_try_seamless) {
+ spice_assert(mcc->base.channel->clients_num == 1);
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);
@@ -849,6 +853,7 @@ void main_channel_client_handle_migrate_end(MainChannelClient *mcc)
mcc->mig_wait_prev_complete = FALSE;
}
}
+
static int main_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, uint16_t type,
void *message)
{
@@ -1248,7 +1253,7 @@ void main_channel_migrate_cancel_wait(MainChannel *main_chan)
main_chan->num_clients_mig_wait = 0;
}
-int main_channel_migrate_complete(MainChannel *main_chan, int success)
+int main_channel_migrate_src_complete(MainChannel *main_chan, int success)
{
RingItem *client_link;
int semi_seamless_count = 0;
diff --git a/server/main_channel.h b/server/main_channel.h
index 40d2215..8cfe62b 100644
--- a/server/main_channel.h
+++ b/server/main_channel.h
@@ -104,7 +104,8 @@ int main_channel_migrate_connect(MainChannel *main_channel, RedsMigSpice *mig_ta
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);
+int main_channel_migrate_src_complete(MainChannel *main_chan, int success);
+void main_channel_migrate_dst_complete(MainChannelClient *mcc);
void main_channel_push_name(MainChannelClient *mcc, const char *name);
void main_channel_push_uuid(MainChannelClient *mcc, const uint8_t uuid[16]);
diff --git a/server/main_dispatcher.c b/server/main_dispatcher.c
index f5b8b4c..1126ec0 100644
--- a/server/main_dispatcher.c
+++ b/server/main_dispatcher.c
@@ -7,6 +7,8 @@
#include "red_common.h"
#include "dispatcher.h"
#include "main_dispatcher.h"
+#include "red_channel.h"
+#include "reds.h"
/*
* Main Dispatcher
@@ -37,6 +39,7 @@ MainDispatcher main_dispatcher;
enum {
MAIN_DISPATCHER_CHANNEL_EVENT = 0,
+ MAIN_DISPATCHER_MIGRATE_SEAMLESS_DST_COMPLETE,
MAIN_DISPATCHER_NUM_MESSAGES
};
@@ -46,6 +49,10 @@ typedef struct MainDispatcherChannelEventMessage {
SpiceChannelEventInfo *info;
} MainDispatcherChannelEventMessage;
+typedef struct MainDispatcherMigrateSeamlessDstCompleteMessage {
+ RedClient *client;
+} MainDispatcherMigrateSeamlessDstCompleteMessage;
+
/* channel_event - calls core->channel_event, must be done in main thread */
static void main_dispatcher_self_handle_channel_event(
int event,
@@ -80,6 +87,28 @@ void main_dispatcher_channel_event(int event, SpiceChannelEventInfo *info)
&msg);
}
+
+static void main_dispatcher_handle_migrate_complete(void *opaque,
+ void *payload)
+{
+ MainDispatcherMigrateSeamlessDstCompleteMessage *mig_complete = payload;
+
+ reds_on_client_seamless_migrate_complete(mig_complete->client);
+}
+
+void main_dispatcher_seamless_migrate_dst_complete(RedClient *client)
+{
+ MainDispatcherMigrateSeamlessDstCompleteMessage msg;
+
+ if (pthread_self() == main_dispatcher.base.self) {
+ reds_on_client_seamless_migrate_complete(client);
+ return;
+ }
+
+ msg.client = client;
+ dispatcher_send_message(&main_dispatcher.base, MAIN_DISPATCHER_MIGRATE_SEAMLESS_DST_COMPLETE,
+ &msg);
+}
static void dispatcher_handle_read(int fd, int event, void *opaque)
{
Dispatcher *dispatcher = opaque;
@@ -97,4 +126,7 @@ void main_dispatcher_init(SpiceCoreInterface *core)
dispatcher_register_handler(&main_dispatcher.base, MAIN_DISPATCHER_CHANNEL_EVENT,
main_dispatcher_handle_channel_event,
sizeof(MainDispatcherChannelEventMessage), 0 /* no ack */);
+ dispatcher_register_handler(&main_dispatcher.base, MAIN_DISPATCHER_MIGRATE_SEAMLESS_DST_COMPLETE,
+ main_dispatcher_handle_migrate_complete,
+ sizeof(MainDispatcherMigrateSeamlessDstCompleteMessage), 0 /* no ack */);
}
diff --git a/server/main_dispatcher.h b/server/main_dispatcher.h
index 2c201c7..ec4a6b4 100644
--- a/server/main_dispatcher.h
+++ b/server/main_dispatcher.h
@@ -4,6 +4,7 @@
#include <spice.h>
void main_dispatcher_channel_event(int event, SpiceChannelEventInfo *info);
+void main_dispatcher_seamless_migrate_dst_complete(RedClient *client);
void main_dispatcher_init(SpiceCoreInterface *core);
#endif //MAIN_DISPATCHER_H
diff --git a/server/red_channel.c b/server/red_channel.c
index d715ce8..65ef2da 100644
--- a/server/red_channel.c
+++ b/server/red_channel.c
@@ -36,6 +36,7 @@
#include "stat.h"
#include "red_channel.h"
#include "reds.h"
+#include "main_dispatcher.h"
static void red_channel_client_event(int fd, int event, void *data);
static void red_client_add_channel(RedClient *client, RedChannelClient *rcc);
@@ -688,6 +689,45 @@ error:
return NULL;
}
+static void red_channel_client_seamless_migration_done(RedChannelClient *rcc)
+{
+ rcc->wait_migrate_data = FALSE;
+
+ pthread_mutex_lock(&rcc->client->lock);
+ rcc->client->num_migrated_channels--;
+
+ /* we assume we always have at least one channel who has migration data transfer,
+ * otherwise, this flag will never be set back to FALSE*/
+ if (!rcc->client->num_migrated_channels) {
+ rcc->client->during_target_migrate = FALSE;
+ rcc->client->seamless_migrate = FALSE;
+ /* migration completion might have been triggered from a different thread
+ * than the main thread */
+ main_dispatcher_seamless_migrate_dst_complete(rcc->client);
+ }
+ pthread_mutex_unlock(&rcc->client->lock);
+}
+
+int red_channel_client_waits_for_migrate_data(RedChannelClient *rcc)
+{
+ return rcc->wait_migrate_data;
+}
+
+int red_channel_waits_for_migrate_data(RedChannel *channel)
+{
+ RedChannelClient *rcc;
+ if (!red_channel_is_connected(channel)) {
+ return FALSE;
+ }
+
+ if (channel->clients_num > 1) {
+ return FALSE;
+ }
+ spice_assert(channel->clients_num == 1);
+ rcc = SPICE_CONTAINEROF(ring_get_head(&channel->clients), RedChannelClient, channel_link);
+ return red_channel_client_waits_for_migrate_data(rcc);
+}
+
static void red_channel_client_default_connect(RedChannel *channel, RedClient *client,
RedsStream *stream,
int migration,
@@ -1085,13 +1125,21 @@ static void red_channel_handle_migrate_flush_mark(RedChannelClient *rcc)
// So need to make all the handlers work with per channel/client data (what data exactly?)
static void red_channel_handle_migrate_data(RedChannelClient *rcc, uint32_t size, void *message)
{
+ spice_debug("channel type %d id %d rcc %p size %u",
+ rcc->channel->type, rcc->channel->id, rcc, size);
if (!rcc->channel->channel_cbs.handle_migrate_data) {
return;
}
- spice_assert(red_channel_client_get_message_serial(rcc) == 0);
- red_channel_client_set_message_serial(rcc,
- rcc->channel->channel_cbs.handle_migrate_data_get_serial(rcc, size, message));
+ if (!red_channel_client_waits_for_migrate_data(rcc)) {
+ spice_error("unexcpected");
+ return;
+ }
+ if (rcc->channel->channel_cbs.handle_migrate_data_get_serial) {
+ red_channel_client_set_message_serial(rcc,
+ rcc->channel->channel_cbs.handle_migrate_data_get_serial(rcc, size, message));
+ }
rcc->channel->channel_cbs.handle_migrate_data(rcc, size, message);
+ red_channel_client_seamless_migration_done(rcc);
}
int red_channel_client_handle_message(RedChannelClient *rcc, uint32_t size,
@@ -1555,11 +1603,38 @@ RedClient *red_client_new(int migrated)
ring_init(&client->channels);
pthread_mutex_init(&client->lock, NULL);
client->thread_id = pthread_self();
- client->migrated = migrated;
+ client->during_target_migrate = migrated;
return client;
}
+/* client mutex should be locked before this call */
+static void red_channel_client_set_migration_seamless(RedChannelClient *rcc)
+{
+ spice_assert(rcc->client->during_target_migrate && rcc->client->seamless_migrate);
+
+ if (rcc->channel->migration_flags & SPICE_MIGRATE_NEED_DATA_TRANSFER) {
+ rcc->wait_migrate_data = TRUE;
+ rcc->client->num_migrated_channels++;
+ }
+ spice_debug("channel type %d id %d rcc %p wait data %d", rcc->channel->type, rcc->channel->id, rcc,
+ rcc->wait_migrate_data);
+}
+
+void red_client_set_migration_seamless(RedClient *client) // dest
+{
+ RingItem *link;
+ pthread_mutex_lock(&client->lock);
+ client->seamless_migrate = TRUE;
+ /* update channel clients that got connected before the migration
+ * type was set. red_client_add_channel will handle newer channel clients */
+ RING_FOREACH(link, &client->channels) {
+ RedChannelClient *rcc = SPICE_CONTAINEROF(link, RedChannelClient, client_link);
+ red_channel_client_set_migration_seamless(rcc);
+ }
+ pthread_mutex_unlock(&client->lock);
+}
+
void red_client_migrate(RedClient *client)
{
RingItem *link, *next;
@@ -1625,6 +1700,9 @@ static void red_client_add_channel(RedClient *client, RedChannelClient *rcc)
{
spice_assert(rcc && client);
ring_add(&client->channels, &rcc->client_link);
+ if (client->during_target_migrate && client->seamless_migrate) {
+ red_channel_client_set_migration_seamless(rcc);
+ }
client->channels_num++;
}
@@ -1636,16 +1714,27 @@ void red_client_set_main(RedClient *client, MainChannelClient *mcc) {
client->mcc = mcc;
}
-void red_client_migrate_complete(RedClient *client)
+void red_client_semi_seamless_migrate_complete(RedClient *client)
{
- spice_assert(client->migrated);
- client->migrated = FALSE;
- reds_on_client_migrate_complete(client);
+ pthread_mutex_lock(&client->lock);
+ if (!client->during_target_migrate || client->seamless_migrate) {
+ spice_error("unexpected");
+ pthread_mutex_unlock(&client->lock);
+ return;
+ }
+ client->during_target_migrate = FALSE;
+ pthread_mutex_unlock(&client->lock);
+ reds_on_client_semi_seamless_migrate_complete(client);
}
+/* should be called only from the main thread */
int red_client_during_migrate_at_target(RedClient *client)
{
- return client->migrated;
+ int ret;
+ pthread_mutex_lock(&client->lock);
+ ret = client->during_target_migrate;
+ pthread_mutex_unlock(&client->lock);
+ return ret;
}
/*
diff --git a/server/red_channel.h b/server/red_channel.h
index 35d11a6..8c8e1c8 100644
--- a/server/red_channel.h
+++ b/server/red_channel.h
@@ -267,6 +267,8 @@ struct RedChannelClient {
RedChannelCapabilities remote_caps;
int is_mini_header;
int destroying;
+
+ int wait_migrate_data;
int wait_migrate_flush_mark;
};
@@ -357,6 +359,11 @@ int red_channel_is_connected(RedChannel *channel);
int red_channel_client_is_connected(RedChannelClient *rcc);
void red_channel_client_default_migrate(RedChannelClient *rcc);
+int red_channel_client_waits_for_migrate_data(RedChannelClient *rcc);
+/* seamless migration is supported for only one client. This routine
+ * checks if the only channel client associated with channel is
+ * waiting for migration data */
+int red_channel_waits_for_migrate_data(RedChannel *channel);
/*
* the disconnect callback is called from the channel's thread,
@@ -513,14 +520,31 @@ struct RedClient {
pthread_t thread_id;
int disconnecting;
- int migrated;
+ /* Note that while semi-seamless migration is conducted by the main thread, seamless migration
+ * involves all channels, and thus the related varaibles can be accessed from different
+ * threads */
+ int during_target_migrate; /* if seamless=TRUE, migration_target is turned off when all
+ the clients received their migration data. Otherwise (semi-seamless),
+ it is turned off, when red_client_semi_seamless_migrate_complete
+ is called */
+ int seamless_migrate;
+ int num_migrated_channels; /* for seamless - number of channels that wait for migrate data*/
};
RedClient *red_client_new(int migrated);
+
MainChannelClient *red_client_get_main(RedClient *client);
// main should be set once before all the other channels are created
void red_client_set_main(RedClient *client, MainChannelClient *mcc);
-void red_client_migrate_complete(RedClient *client);
+
+/* called when the migration handshake results in seamless migration (dst side).
+ * By default we assume semi-seamless */
+void red_client_set_migration_seamless(RedClient *client);
+void red_client_semi_seamless_migrate_complete(RedClient *client); /* dst side */
+/* TRUE if the migration is seamless and there are still channels that wait from migration data.
+ * Or, during semi-seamless migration, and the main channel still waits for MIGRATE_END
+ * from the client.
+ * Note: Call it only from the main thread */
int red_client_during_migrate_at_target(RedClient *client);
void red_client_migrate(RedClient *client);
diff --git a/server/reds.c b/server/reds.c
index fbd5caf..d53b245 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -1440,6 +1440,21 @@ static void reds_mig_target_client_disconnect_all(void)
}
}
+static int reds_find_client(RedClient *client)
+{
+ RingItem *item;
+
+ RING_FOREACH(item, &reds->clients) {
+ RedClient *list_client;
+
+ list_client = SPICE_CONTAINEROF(item, RedClient, link);
+ if (list_client == client) {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
// TODO: now that main is a separate channel this should
// actually be joined with reds_handle_other_links, become reds_handle_link
static void reds_handle_main_link(RedLinkInfo *link)
@@ -1625,18 +1640,26 @@ int reds_on_migrate_dst_set_seamless(MainChannelClient *mcc, uint32_t src_versio
if (reds->allow_multiple_clients || src_version > SPICE_MIGRATION_PROTOCOL_VERSION) {
reds->dst_do_seamless_migrate = FALSE;
} else {
- RedClient *client;
+ RedChannelClient *rcc = main_channel_client_get_base(mcc);
- client = main_channel_client_get_base(mcc)->client;
- reds->dst_do_seamless_migrate = reds_link_mig_target_channels(client);
+ red_client_set_migration_seamless(rcc->client);
/* linking all the channels that have been connected before migration handshake */
- reds->dst_do_seamless_migrate = reds_link_mig_target_channels(client);
+ reds->dst_do_seamless_migrate = reds_link_mig_target_channels(rcc->client);
}
return reds->dst_do_seamless_migrate;
}
-/* semi seamless */
-void reds_on_client_migrate_complete(RedClient *client)
+void reds_on_client_seamless_migrate_complete(RedClient *client)
+{
+ spice_debug(NULL);
+ if (!reds_find_client(client)) {
+ spice_info("client no longer exists");
+ return;
+ }
+ main_channel_migrate_dst_complete(red_client_get_main(client));
+}
+
+void reds_on_client_semi_seamless_migrate_complete(RedClient *client)
{
MainChannelClient *mcc;
@@ -1649,6 +1672,7 @@ void reds_on_client_migrate_complete(RedClient *client)
reds_get_mm_time() - MM_TIME_DELTA,
red_dispatcher_qxl_ram_size());
reds_link_mig_target_channels(client);
+ main_channel_migrate_dst_complete(mcc);
}
static void reds_handle_other_links(RedLinkInfo *link)
@@ -3202,7 +3226,7 @@ static void reds_mig_finished(int completed)
if (reds->src_do_seamless_migrate && completed) {
reds_migrate_channels_seamless();
} else {
- main_channel_migrate_complete(reds->main_channel, completed);
+ main_channel_migrate_src_complete(reds->main_channel, completed);
}
if (completed) {
@@ -4067,8 +4091,8 @@ SPICE_GNUC_VISIBLE int spice_server_migrate_connect(SpiceServer *s, const char*
spice_assert(reds == s);
if (reds->expect_migrate) {
- spice_error("consecutive calls without migration. Canceling previous call");
- main_channel_migrate_complete(reds->main_channel, FALSE);
+ spice_info("consecutive calls without migration. Canceling previous call");
+ main_channel_migrate_src_complete(reds->main_channel, FALSE);
}
sif = SPICE_CONTAINEROF(migration_interface->base.sif, SpiceMigrateInterface, base);
diff --git a/server/reds.h b/server/reds.h
index 71a0173..779d0db 100644
--- a/server/reds.h
+++ b/server/reds.h
@@ -157,7 +157,8 @@ 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_client_semi_seamless_migrate_complete(RedClient *client);
+void reds_on_client_seamless_migrate_complete(RedClient *client);
void reds_on_char_device_state_destroy(SpiceCharDeviceState *dev);
#endif
--
1.7.7.6
More information about the Spice-devel
mailing list