[Spice-devel] [PATCH migration 09/19] server: handling semi-seamless migration in the target side
Yonit Halperin
yhalperi at redhat.com
Wed Nov 2 01:18:06 PDT 2011
On 11/01/2011 11:34 PM, Alon Levy wrote:
> On Wed, Oct 12, 2011 at 12:38:59PM +0200, Yonit Halperin wrote:
>> (1) not sending anything to a migrated client till we recieve SPICE_MSGC_MIGRATE_END
>> (2) start a new client migration (handle client_migrate_info) only after SPICE_MSGC_MIGRATE_END
>> from the previous migration was received for this client
>> (3) use the correct ticket
>>
>
> ACK. Some trivial comments. Note that the whole series requires a simple
> rebase because of the main_dispatcher patch. I've done that, so when I
> complete the review if I don't find anything else I can push this for
> you.
>
>> Note: we assume the same channles are linked before and ater migration. i.e.,
>> SPICE_MSGC_MAIN_ATTACH_CHANNELS is not sent from the clients.
>>
>> Signed-off-by: Yonit Halperin<yhalperi at redhat.com>
>> ---
>> server/main_channel.c | 53 +++++++++++-
>> server/main_channel.h | 2 +-
>> server/red_channel.c | 15 +++-
>> server/red_channel.h | 6 +-
>> server/reds.c | 215 ++++++++++++++++++++++++++++++++++++++++---------
>> server/reds.h | 2 +-
>> 6 files changed, 247 insertions(+), 46 deletions(-)
>>
>> diff --git a/server/main_channel.c b/server/main_channel.c
>> index ffc593d..9b19756 100644
>> --- a/server/main_channel.c
>> +++ b/server/main_channel.c
>> @@ -129,6 +129,8 @@ struct MainChannelClient {
>> #endif
>> int mig_wait_connect;
>> int mig_connect_ok;
>> + int mig_wait_prev_complete;
>> + int init_sent;
>> };
>>
>> enum NetTestStage {
>> @@ -138,6 +140,9 @@ enum NetTestStage {
>> NET_TEST_STAGE_RATE,
>> };
>>
>> +static void main_channel_release_pipe_item(RedChannelClient *rcc,
>> + PipeItem *base, int item_pushed);
>> +
>> int main_channel_is_connected(MainChannel *main_chan)
>> {
>> return red_channel_is_connected(&main_chan->base);
>> @@ -289,6 +294,10 @@ static PipeItem *main_multi_media_time_item_new(
>>
>> static void main_channel_push_channels(MainChannelClient *mcc)
>> {
>> + if (red_client_during_migrate_at_target(mcc->base.client)) {
>> + red_printf("warning: unexpected SPICE_MSGC_MAIN_ATTACH_CHANNELS during migration");
>
> s/unexpected/ignoring unexpected/ ?
>
>> + return;
>> + }
>> red_channel_client_pipe_add_type(&mcc->base, SPICE_MSG_MAIN_CHANNELS_LIST);
>> }
>>
>> @@ -451,7 +460,7 @@ static uint64_t main_channel_handle_migrate_data(RedChannelClient *base,
>> return TRUE;
>> }
>>
>> -void main_channel_push_init(MainChannelClient *mcc, int connection_id,
>> +void main_channel_push_init(MainChannelClient *mcc,
>> int display_channels_hint, int current_mouse_mode,
>> int is_client_mouse_allowed, int multi_media_time,
>> int ram_hint)
>> @@ -459,7 +468,7 @@ void main_channel_push_init(MainChannelClient *mcc, int connection_id,
>> PipeItem *item;
>>
>> item = main_init_item_new(mcc,
>> - connection_id, display_channels_hint, current_mouse_mode,
>> + mcc->connection_id, display_channels_hint, current_mouse_mode,
>> is_client_mouse_allowed, multi_media_time, ram_hint);
>> red_channel_client_pipe_add_push(&mcc->base, item);
>> }
>> @@ -612,6 +621,13 @@ static void main_channel_send_item(RedChannelClient *rcc, PipeItem *base)
>> MainChannelClient *mcc = SPICE_CONTAINEROF(rcc, MainChannelClient, base);
>> SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
>>
>> + if (!mcc->init_sent&& base->type != SPICE_MSG_MAIN_INIT) {
>> + red_printf("Init msg for client %p was not sent yet "
>> + "(client is probably during migration). Ignoring msg type %d",
>> + rcc->client, base->type);
>
> Does this happen in practice? Sounds like a bug.
Yes it does happen. It is not a bug. When we supported only one client,
we avoided pathways that might send msgs to a client didn't complete the
migration by testing reds->mig_target. Since I don't like adding a test
for each possible pathway in reds that will check if all the clients
that are currently connected are mig_targets or whether we also have
"fresh" clients, I thought a better solution would be, to just catch
outgoing messages that are designated to a client that didn't completed
the migration yet (didn't send MIGRATE_END to the target).
>
>> + main_channel_release_pipe_item(rcc, base, FALSE);
>> + return;
>> + }
>> red_channel_client_init_send_data(rcc, base->type, base);
>> switch (base->type) {
>> case SPICE_MSG_MAIN_CHANNELS_LIST:
>> @@ -646,6 +662,7 @@ static void main_channel_send_item(RedChannelClient *rcc, PipeItem *base)
>> mcc->ping_id);
>> break;
>> case SPICE_MSG_MAIN_INIT:
>> + mcc->init_sent = TRUE;
>> main_channel_marshall_init(m,
>> SPICE_CONTAINEROF(base, InitPipeItem, base));
>> break;
>> @@ -710,6 +727,25 @@ void main_channel_client_handle_migrate_connected(MainChannelClient *mcc, int su
>> }
>> }
>>
>> +void main_channel_client_handlle_migrate_end(MainChannelClient *mcc)
> typo - handle
>
>> +{
>> + if (!red_client_during_migrate_at_target(mcc->base.client)) {
>> + red_printf("unexpected SPICE_MSGC_MIGRATE_END");
>> + return;
>> + }
>> + if (!red_channel_client_test_remote_cap(&mcc->base,
>> + SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE)) {
>> + red_printf("unexpected SPICE_MSGC_MIGRATE_END, "
>> + "client does not support semi-seamless migration");
>> + return;
>> + }
>> + 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);
>> + mcc->mig_wait_connect = TRUE;
>> + mcc->mig_wait_prev_complete = FALSE;
>> + }
>> +}
>> static int main_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, uint16_t type, void *message)
>> {
>> MainChannel *main_chan = SPICE_CONTAINEROF(rcc->channel, MainChannel, base);
>> @@ -797,6 +833,9 @@ static int main_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, uint
>> }
>> case SPICE_MSGC_DISCONNECTING:
>> break;
>> + case SPICE_MSGC_MAIN_MIGRATE_END:
>> + main_channel_client_handlle_migrate_end(mcc);
>> + break;
>> default:
>> red_printf("unexpected type %d", type);
>> }
>> @@ -989,8 +1028,13 @@ int main_channel_migrate_connect(MainChannel *main_channel, RedsMigSpice *mig_ta
>> MainChannelClient * mcc = SPICE_CONTAINEROF(client_link, MainChannelClient, base.channel_link);
>> if (red_channel_client_test_remote_cap(&mcc->base,
>> SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE)) {
>> - red_channel_client_pipe_add_type(&mcc->base, SPICE_MSG_MAIN_MIGRATE_BEGIN);
>> - mcc->mig_wait_connect = TRUE;
>> + if (red_client_during_migrate_at_target(mcc->base.client)) {
>> + red_printf("client %p: wait till previous migration completes", mcc->base.client);
>> + mcc->mig_wait_prev_complete = TRUE;
>> + } else {
>> + red_channel_client_pipe_add_type(&mcc->base, SPICE_MSG_MAIN_MIGRATE_BEGIN);
>> + mcc->mig_wait_connect = TRUE;
>> + }
>> mcc->mig_connect_ok = FALSE;
>> main_channel->num_clients_mig_wait++;
>> }
>> @@ -1011,6 +1055,7 @@ void main_channel_migrate_cancel_wait(MainChannel *main_chan)
>> mcc->mig_wait_connect = FALSE;
>> mcc->mig_connect_ok = FALSE;
>> }
>> + mcc->mig_wait_prev_complete = FALSE;
>> }
>> main_chan->num_clients_mig_wait = 0;
>> }
>> diff --git a/server/main_channel.h b/server/main_channel.h
>> index d97857d..c5d407e 100644
>> --- a/server/main_channel.h
>> +++ b/server/main_channel.h
>> @@ -78,7 +78,7 @@ void main_channel_push_agent_data(MainChannel *main_chan, uint8_t* data, size_t
>> void main_channel_client_start_net_test(MainChannelClient *mcc);
>> // TODO: huge. Consider making a reds_* interface for these functions
>> // and calling from main.
>> -void main_channel_push_init(MainChannelClient *mcc, int connection_id, int display_channels_hint,
>> +void main_channel_push_init(MainChannelClient *mcc, int display_channels_hint,
>> int current_mouse_mode, int is_client_mouse_allowed, int multi_media_time,
>> int ram_hint);
>> void main_channel_push_notify(MainChannel *main_chan, uint8_t *mess, const int mess_len);
>> diff --git a/server/red_channel.c b/server/red_channel.c
>> index 51415cb..2ce0094 100644
>> --- a/server/red_channel.c
>> +++ b/server/red_channel.c
>> @@ -1215,7 +1215,7 @@ void red_channel_client_pipe_remove_and_release(RedChannelClient *rcc,
>> * pretty tied together.
>> */
>>
>> -RedClient *red_client_new()
>> +RedClient *red_client_new(int migrated)
>> {
>> RedClient *client;
>>
>> @@ -1223,6 +1223,7 @@ RedClient *red_client_new()
>> ring_init(&client->channels);
>> pthread_mutex_init(&client->lock, NULL);
>> client->thread_id = pthread_self();
>> + client->migrated = migrated;
>>
>> return client;
>> }
>> @@ -1286,6 +1287,18 @@ void red_client_set_main(RedClient *client, MainChannelClient *mcc) {
>> client->mcc = mcc;
>> }
>>
>> +void red_client_migrate_complete(RedClient *client)
>> +{
>> + ASSERT(client->migrated);
>> + client->migrated = FALSE;
>> + reds_on_client_migrate_complete(client);
>> +}
>> +
>> +int red_client_during_migrate_at_target(RedClient *client)
>> +{
>> + return client->migrated;
>> +}
>> +
>> /*
>> * Functions to push the same item to multiple pipes.
>> */
>> diff --git a/server/red_channel.h b/server/red_channel.h
>> index e30401c..cce6965 100644
>> --- a/server/red_channel.h
>> +++ b/server/red_channel.h
>> @@ -450,13 +450,15 @@ struct RedClient {
>> pthread_t thread_id;
>>
>> int disconnecting;
>> -
>> + int migrated;
>> };
>>
>> -RedClient *red_client_new(void);
>> +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);
>> +int red_client_during_migrate_at_target(RedClient *client);
>>
>> void red_client_migrate(RedClient *client);
>> // disconnects all the client's channels (should be called from the client's thread)
>> diff --git a/server/reds.c b/server/reds.c
>> index 2deed4c..c885a4b 100644
>> --- a/server/reds.c
>> +++ b/server/reds.c
>> @@ -190,6 +190,18 @@ typedef struct RedsStatValue {
>>
>> #endif
>>
>> +typedef struct RedsMigPendingLink {
>> + RingItem ring_link; // list of links that belongs to the same client
>> + SpiceLinkMess *link_msg;
>> + RedsStream *stream;
>> +} RedsMigPendingLink;
>> +
>> +typedef struct RedsMigTargetClient {
>> + RingItem link;
>> + RedClient *client;
>> + Ring pending_links;
>> +} RedsMigTargetClient;
>> +
>> typedef struct RedsState {
>> int listen_socket;
>> int secure_listen_socket;
>> @@ -205,7 +217,8 @@ typedef struct RedsState {
>> int mig_wait_disconnect;
>> int mig_inprogress;
>> int expect_migrate;
>> - int mig_target;
>> + Ring mig_target_clients;
>> + int num_mig_target_clients;
>> RedsMigSpice *mig_spice;
>> int num_of_channels;
>> Ring channels;
>> @@ -218,7 +231,6 @@ typedef struct RedsState {
>> SpiceTimer *vdi_port_write_timer;
>> int vdi_port_write_timer_started;
>>
>> - TicketAuthentication taTicket;
>> SSL_CTX *ctx;
>>
>> #ifdef RED_STATISTICS
>> @@ -294,6 +306,8 @@ struct ChannelSecurityOptions {
>> };
>>
>> static void migrate_timeout(void *opaque);
>> +static RedsMigTargetClient* reds_mig_target_client_find(RedClient *client);
>> +static void reds_mig_target_client_free(RedsMigTargetClient *mig_client);
>>
>> static ChannelSecurityOptions *channels_security = NULL;
>> static int default_channel_security =
>> @@ -592,6 +606,8 @@ static int reds_main_channel_connected(void)
>>
>> void reds_client_disconnect(RedClient *client)
>> {
>> + RedsMigTargetClient *mig_client;
>> +
>> if (!client || client->disconnecting) {
>> return;
>> }
>> @@ -606,6 +622,10 @@ void reds_client_disconnect(RedClient *client)
>> // TODO: we need to handle agent properly for all clients!!!! (e.g., cut and paste, how?)
>> // We shouldn't initialize the agent when there are still clients connected
>>
>> + mig_client = reds_mig_target_client_find(client);
>> + if (mig_client) {
>> + reds_mig_target_client_free(mig_client);
>> + }
>> ring_remove(&client->link);
>> reds->num_clients--;
>> red_client_destroy(client);
>> @@ -692,14 +712,14 @@ static void reds_update_mouse_mode(void)
>>
>> static void reds_agent_remove(void)
>> {
>> - if (!reds->mig_target) {
>> - reds_reset_vdp();
>> - }
>> + // TODO: agent is broken with multiple clients. also need to figure out what to do when
>> + // part of the clients are during target migration.
>> + reds_reset_vdp();
>>
>> vdagent = NULL;
>> reds_update_mouse_mode();
>>
>> - if (reds_main_channel_connected()&& !reds->mig_target) {
>> + if (reds_main_channel_connected()) {
>> main_channel_push_agent_disconnected(reds->main_channel);
>> }
>> }
>> @@ -739,7 +759,7 @@ static int write_to_vdi_port(void)
>> int total = 0;
>> int n;
>>
>> - if (!vdagent || reds->mig_target) {
>> + if (!vdagent) {
>> return 0;
>> }
>>
>> @@ -843,8 +863,8 @@ static int read_from_vdi_port(void)
>> }
>> inside_call = 1;
>>
>> - if (reds->mig_target || !vdagent) {
>> - // discard data only if we are migrating or vdagent has not been
>> + if (!vdagent) {
>> + // discard data only if we are migrating (?) or vdagent has not been
>> // initialized.
>> inside_call = 0;
>> return 0;
>> @@ -930,7 +950,7 @@ void reds_handle_agent_mouse_event(const VDAgentMouseState *mouse_state)
>> if (!inputs_inited()) {
>> return;
>> }
>> - if (reds->mig_target || !(ring_item = ring_get_head(&reds->agent_state.internal_bufs))) {
>> + if (!(ring_item = ring_get_head(&reds->agent_state.internal_bufs))) {
>> reds->pending_mouse_event = TRUE;
>> vdi_port_write_timer_start();
>> return;
>> @@ -1345,7 +1365,6 @@ void reds_on_main_receive_migrate_data(MainMigrateData *data, uint8_t *end)
>> reds_main_channel_restore_vdi_wqueue(data, pos, end);
>> ASSERT(state->num_client_tokens + state->num_tokens == REDS_AGENT_WINDOW_SIZE);
>>
>> - reds->mig_target = FALSE;
>> while (write_to_vdi_port() || read_from_vdi_port());
>> }
>>
>> @@ -1485,6 +1504,75 @@ int reds_expects_link_id(uint32_t connection_id)
>> return 1;
>> }
>>
>> +static void reds_mig_target_client_add(RedClient *client)
>> +{
>> + RedsMigTargetClient *mig_client;
>> +
>> + ASSERT(reds);
>> + red_printf("");
>> + mig_client = spice_malloc0(sizeof(RedsMigTargetClient));
>> + mig_client->client = client;
>> + ring_init(&mig_client->pending_links);
>> + ring_add(&reds->mig_target_clients,&mig_client->link);
>> + reds->num_mig_target_clients++;
>> +
>> +}
>> +
>> +static RedsMigTargetClient* reds_mig_target_client_find(RedClient *client)
>> +{
>> + RingItem *item;
>> +
>> + RING_FOREACH(item,&reds->mig_target_clients) {
>> + RedsMigTargetClient *mig_client;
>> +
>> + mig_client = SPICE_CONTAINEROF(item, RedsMigTargetClient, link);
>> + if (mig_client->client == client) {
>> + return mig_client;
>> + }
>> + }
>> + return NULL;
>> +}
>> +
>> +static void reds_mig_target_client_add_pending_link(RedsMigTargetClient *client,
>> + SpiceLinkMess *link_msg,
>> + RedsStream *stream)
>> +{
>> + RedsMigPendingLink *mig_link;
>> +
>> + ASSERT(reds);
>> + ASSERT(client);
>> + mig_link = spice_malloc0(sizeof(RedsMigPendingLink));
>> + mig_link->link_msg = link_msg;
>> + mig_link->stream = stream;
>> +
>> + ring_add(&client->pending_links,&mig_link->ring_link);
>> +}
>> +
>> +static void reds_mig_target_client_free(RedsMigTargetClient *mig_client)
>> +{
>> + RingItem *now, *next;
>> +
>> + ring_remove(&mig_client->link);
>> + reds->num_mig_target_clients--;
>> +
>> + RING_FOREACH_SAFE(now, next,&mig_client->pending_links) {
>> + RedsMigPendingLink *mig_link = SPICE_CONTAINEROF(now, RedsMigPendingLink, ring_link);
>> + ring_remove(now);
>> + free(mig_link);
>> + }
>> + free(mig_client);
>> +}
>> +
>> +static void reds_mig_target_client_disconnect_all()
>> +{
>> + RingItem *now, *next;
>> +
>> + RING_FOREACH_SAFE(now, next,&reds->mig_target_clients) {
>> + RedsMigTargetClient *mig_client = SPICE_CONTAINEROF(now, RedsMigTargetClient, link);
>> + reds_client_disconnect(mig_client->client);
>> + }
>> +}
>> +
>> // 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)
>> @@ -1495,6 +1583,7 @@ static void reds_handle_main_link(RedLinkInfo *link)
>> uint32_t *caps;
>> uint32_t connection_id;
>> MainChannelClient *mcc;
>> + int mig_target = FALSE;
>>
>> red_printf("");
>> ASSERT(reds->main_channel);
>> @@ -1508,18 +1597,13 @@ static void reds_handle_main_link(RedLinkInfo *link)
>> reds_send_link_result(link, SPICE_LINK_ERR_OK);
>> while((connection_id = rand()) == 0);
>> reds->agent_state.num_tokens = 0;
>> - memcpy(&(reds->taTicket),&taTicket, sizeof(reds->taTicket));
>> - reds->mig_target = FALSE;
>> + mig_target = FALSE;
>> } else {
>> - // migration - check if this is one of the expected connection_id's
>> - if (!reds_expects_link_id(link_mess->connection_id)) {
>> - reds_send_link_result(link, SPICE_LINK_ERR_BAD_CONNECTION_ID);
>> - reds_link_free(link);
>> - return;
>> - }
>> + // TODO: make sure link_mess->connection_id is the same
>> + // connection id the migration src had (use vmstate to store the connection id)
>> reds_send_link_result(link, SPICE_LINK_ERR_OK);
>> connection_id = link_mess->connection_id;
>> - reds->mig_target = TRUE;
>> + mig_target = TRUE;
>> }
>>
>> reds->mig_inprogress = FALSE;
>> @@ -1533,11 +1617,11 @@ static void reds_handle_main_link(RedLinkInfo *link)
>> link->link_mess = NULL;
>> reds_link_free(link);
>> caps = (uint32_t *)((uint8_t *)link_mess + link_mess->caps_offset);
>> - client = red_client_new();
>> + client = red_client_new(mig_target);
>> ring_add(&reds->clients,&client->link);
>> reds->num_clients++;
>> mcc = main_channel_link(reds->main_channel, client,
>> - stream, connection_id, reds->mig_target,
>> + stream, connection_id, mig_target,
>> link_mess->num_common_caps,
>> link_mess->num_common_caps ? caps : NULL, link_mess->num_channel_caps,
>> link_mess->num_channel_caps ? caps + link_mess->num_common_caps : NULL);
>> @@ -1550,9 +1634,10 @@ static void reds_handle_main_link(RedLinkInfo *link)
>> reds->agent_state.plug_generation++;
>> }
>>
>> - if (!reds->mig_target) {
>> - reds->agent_state.num_client_tokens = REDS_AGENT_WINDOW_SIZE;
>> - main_channel_push_init(mcc, connection_id, red_dispatcher_count(),
>> + reds->agent_state.num_client_tokens = REDS_AGENT_WINDOW_SIZE;
>> +
>> + if (!mig_target) {
>> + 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());
>> @@ -1560,6 +1645,8 @@ static void reds_handle_main_link(RedLinkInfo *link)
>> main_channel_client_start_net_test(mcc);
>> /* Now that we have a client, forward any pending agent data */
>> while (read_from_vdi_port());
>> + } else {
>> + reds_mig_target_client_add(client);
>> }
>> }
>>
>> @@ -1613,7 +1700,8 @@ static void reds_channel_do_link(RedChannel *channel, RedClient *client,
>> }
>>
>> caps = (uint32_t *)((uint8_t *)link_msg + link_msg->caps_offset);
>> - channel->client_cbs.connect(channel, client, stream, reds->mig_target,
>> + channel->client_cbs.connect(channel, client, stream,
>> + red_client_during_migrate_at_target(client),
>> link_msg->num_common_caps,
>> link_msg->num_common_caps ? caps : NULL,
>> link_msg->num_channel_caps,
>> @@ -1621,11 +1709,55 @@ 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)
>> +{
>> + RedsMigTargetClient *mig_client;
>> + MainChannelClient *mcc;
>> + RingItem *item;
>> +
>> + red_printf("%p", client);
>> + mcc = red_client_get_main(client);
>> + mig_client = reds_mig_target_client_find(client);
>> + if (!mig_client) {
>> + red_printf("Error: mig target client was not found");
>> + return;
>> + }
>> +
>> + // 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());
>> +
>> + RING_FOREACH(item,&mig_client->pending_links) {
>> + RedsMigPendingLink *mig_link;
>> + RedChannel *channel;
>> +
>> + mig_link = SPICE_CONTAINEROF(item, RedsMigPendingLink, ring_link);
>> + channel = reds_find_channel(mig_link->link_msg->channel_type,
>> + mig_link->link_msg->channel_id);
>> + if (!channel) {
>> + red_printf("warning: client %p channel (%d, %d) (type, id) wasn't found",
>> + client,
>> + mig_link->link_msg->channel_type,
>> + mig_link->link_msg->channel_id);
>> + continue;
>> + }
>> + reds_channel_do_link(channel, client, mig_link->link_msg, mig_link->stream);
>> + }
>> +
>> + reds_mig_target_client_free(mig_client);
>> +
>> + /* Now that we have a client, forward any pending agent data */
>> + while (read_from_vdi_port());
>> +}
>> +
>> static void reds_handle_other_links(RedLinkInfo *link)
>> {
>> RedChannel *channel;
>> RedClient *client = NULL;
>> SpiceLinkMess *link_mess;
>> + RedsMigTargetClient *mig_client;
>>
>> link_mess = link->link_mess;
>> if (reds->main_channel) {
>> @@ -1653,8 +1785,16 @@ static void reds_handle_other_links(RedLinkInfo *link)
>> reds_send_link_result(link, SPICE_LINK_ERR_OK);
>> reds_show_new_channel(link, link_mess->connection_id);
>> reds_stream_remove_watch(link->stream);
>> - reds_channel_do_link(channel, client, link_mess, link->stream);
>> - free(link_mess);
>> +
>> + mig_client = reds_mig_target_client_find(client);
>> + if (red_client_during_migrate_at_target(client)) {
>> + ASSERT(mig_client);
>> + reds_mig_target_client_add_pending_link(mig_client, link_mess, link->stream);
>> + } else {
>> + ASSERT(!mig_client);
>> + reds_channel_do_link(channel, client, link_mess, link->stream);
>> + free(link_mess);
>> + }
>> link->stream = NULL;
>> link->link_mess = NULL;
>> reds_link_free(link);
>> @@ -1682,10 +1822,9 @@ static void reds_handle_ticket(void *opaque)
>> (unsigned char *)password, link->tiTicketing.rsa, RSA_PKCS1_OAEP_PADDING);
>>
>> if (ticketing_enabled) {
>> - int expired = !link->link_mess->connection_id&& taTicket.expiration_time< ltime;
>> - char *actual_sever_pass = link->link_mess->connection_id ? reds->taTicket.password :
>> - taTicket.password;
>> - if (strlen(actual_sever_pass) == 0) {
>> + int expired = taTicket.expiration_time< ltime;
>> +
>> + if (strlen(taTicket.password) == 0) {
>> reds_send_link_result(link, SPICE_LINK_ERR_PERMISSION_DENIED);
>> red_printf("Ticketing is enabled, but no password is set. "
>> "please set a ticket first");
>> @@ -1693,7 +1832,7 @@ static void reds_handle_ticket(void *opaque)
>> return;
>> }
>>
>> - if (expired || strncmp(password, actual_sever_pass, SPICE_MAX_PASSWORD_LENGTH) != 0) {
>> + if (expired || strncmp(password, taTicket.password, SPICE_MAX_PASSWORD_LENGTH) != 0) {
>> reds_send_link_result(link, SPICE_LINK_ERR_PERMISSION_DENIED);
>> reds_link_free(link);
>> return;
>> @@ -3048,7 +3187,10 @@ static void migrate_timeout(void *opaque)
>> red_printf("");
>> ASSERT(reds->mig_wait_connect || reds->mig_wait_disconnect);
>> if (reds->mig_wait_connect) {
>> + /* we will fall back to the switch host scheme when migration completes */
>> main_channel_migrate_cancel_wait(reds->main_channel);
>> + /* in case part of the client haven't yet completed the previous migration, disconnect them */
>> + reds_mig_target_client_disconnect_all();
>> reds_mig_cleanup();
>> } else {
>> reds_mig_disconnect();
>> @@ -3107,9 +3249,7 @@ static void attach_to_red_agent(SpiceCharDeviceInstance *sin)
>> state->read_filter.discard_all = FALSE;
>> reds->agent_state.plug_generation++;
>>
>> - if (!reds->mig_target) {
>> - main_channel_push_agent_connected(reds->main_channel);
>> - }
>> + main_channel_push_agent_connected(reds->main_channel);
>> }
>>
>> SPICE_GNUC_VISIBLE void spice_server_char_device_wakeup(SpiceCharDeviceInstance* sin)
>> @@ -3411,6 +3551,7 @@ static int do_spice_init(SpiceCoreInterface *core_interface)
>> ring_init(&reds->clients);
>> reds->num_clients = 0;
>> ring_init(&reds->channels);
>> + ring_init(&reds->mig_target_clients);
>>
>> if (!(reds->mig_timer = core->timer_add(migrate_timeout, NULL))) {
>> red_error("migration timer create failed");
>> @@ -3845,7 +3986,7 @@ 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)) {
>> reds_mig_started();
>> } else {
>> diff --git a/server/reds.h b/server/reds.h
>> index 9feb9ab..450825d 100644
>> --- a/server/reds.h
>> +++ b/server/reds.h
>> @@ -147,6 +147,6 @@ void reds_on_main_migrate_connected(void); //should be called when all the clien
>> // 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);
>> #endif
>>
>> --
>> 1.7.6.4
>>
More information about the Spice-devel
mailing list