- 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 _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/spice-devel