seamless migration: pre migration phase on the src side

sending SPICE_MSG_MAIN_MIGRATE_BEGIN_SEAMLESS and handling
SPICE_MSGC_MAIN_MIGRATE_CONNECTED_SEAMLESS

The src side signals the client to establish a connection
to the destination.
In seamless migration, the client is also used to perform
a sort of handshake with the destination, for verifying
if seamless migration can be supported.

see spice-protocol for more details:
commit 3838ad140a046c4ddf42fef58c9727ecfdc09f9f
This commit is contained in:
Yonit Halperin 2012-07-04 16:23:58 +03:00
parent 35cf65a45e
commit 43e0897da5
4 changed files with 124 additions and 27 deletions

View File

@ -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;

View File

@ -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);

View File

@ -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) {

View File

@ -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);