mirror of
https://gitlab.uni-freiburg.de/opensourcevdi/spice
synced 2026-08-07 08:25:39 +00:00
server: handle spice_server_migrate_end
If the migration has completed successfully:
(1) send MSG_MAIN_MIGRATE_END to the clients that are connected to the target
(2) send MSG_MAIN_SWITCH_HOST to all the other clients
If the migration failed, send MSG_MAIN_MIGRATE_CANCEL to clients that are
connected to the target.
(cherry picked from commit 4b82580fc3 branch 0.8;
Was modified to support multiple clients, and the separation of main_channel from reds)
Conflicts:
server/reds.c
This commit is contained in:
parent
fdb464bb05
commit
e62521ca51
@ -548,11 +548,6 @@ static void main_channel_marshall_migrate(SpiceMarshaller *m)
|
||||
spice_marshall_msg_migrate(m, &migrate);
|
||||
}
|
||||
|
||||
void main_channel_push_migrate_cancel(MainChannel *main_chan)
|
||||
{
|
||||
red_channel_pipes_add_type(&main_chan->base, SPICE_MSG_MAIN_MIGRATE_CANCEL);
|
||||
}
|
||||
|
||||
void main_channel_push_multi_media_time(MainChannel *main_chan, int time)
|
||||
{
|
||||
MultiMediaTimePipeItem info = {
|
||||
@ -563,33 +558,43 @@ void main_channel_push_multi_media_time(MainChannel *main_chan, int time)
|
||||
main_multi_media_time_item_new, &info);
|
||||
}
|
||||
|
||||
static PipeItem *main_migrate_switch_item_new(RedChannelClient *rcc,
|
||||
void *data, int num)
|
||||
static void main_channel_fill_mig_target(MainChannel *main_channel, RedsMigSpice *mig_target)
|
||||
{
|
||||
RefsPipeItem *item = spice_malloc(sizeof(*item));
|
||||
|
||||
item->refs = data;
|
||||
red_channel_pipe_item_init(rcc->channel, &item->base,
|
||||
SPICE_MSG_MAIN_MIGRATE_SWITCH_HOST);
|
||||
return &item->base;
|
||||
ASSERT(mig_target);
|
||||
free(main_channel->mig_target.host);
|
||||
main_channel->mig_target.host = strdup(mig_target->host);
|
||||
free(main_channel->mig_target.cert_subject);
|
||||
if (mig_target->cert_subject) {
|
||||
main_channel->mig_target.cert_subject = strdup(mig_target->cert_subject);
|
||||
}
|
||||
main_channel->mig_target.port = mig_target->port;
|
||||
main_channel->mig_target.sport = mig_target->sport;
|
||||
}
|
||||
|
||||
void main_channel_push_migrate_switch(MainChannel *main_chan)
|
||||
void main_channel_migrate_switch(MainChannel *main_chan, RedsMigSpice *mig_target)
|
||||
{
|
||||
int *refs = spice_malloc0(sizeof(int));
|
||||
|
||||
*refs = main_chan->base.clients_num;
|
||||
red_channel_pipes_new_add_push(&main_chan->base,
|
||||
main_migrate_switch_item_new, (void*)refs);
|
||||
main_channel_fill_mig_target(main_chan, mig_target);
|
||||
red_channel_pipes_add_type(&main_chan->base, SPICE_MSG_MAIN_MIGRATE_SWITCH_HOST);
|
||||
}
|
||||
|
||||
static void main_channel_marshall_migrate_switch(SpiceMarshaller *m)
|
||||
static void main_channel_marshall_migrate_switch(SpiceMarshaller *m, RedChannelClient *rcc)
|
||||
{
|
||||
SpiceMsgMainMigrationSwitchHost migrate;
|
||||
MainChannel *main_ch;
|
||||
|
||||
red_printf("");
|
||||
|
||||
reds_fill_mig_switch(&migrate);
|
||||
main_ch = SPICE_CONTAINEROF(rcc->channel, MainChannel, base);
|
||||
migrate.port = main_ch->mig_target.port;
|
||||
migrate.sport = main_ch->mig_target.sport;
|
||||
migrate.host_size = strlen(main_ch->mig_target.host) + 1;
|
||||
migrate.host_data = (uint8_t *)main_ch->mig_target.host;
|
||||
if (main_ch->mig_target.cert_subject) {
|
||||
migrate.cert_subject_size = strlen(main_ch->mig_target.cert_subject) + 1;
|
||||
migrate.cert_subject_data = (uint8_t *)main_ch->mig_target.cert_subject;
|
||||
} else {
|
||||
migrate.cert_subject_size = 0;
|
||||
migrate.cert_subject_data = NULL;
|
||||
}
|
||||
spice_marshall_msg_main_migrate_switch_host(m, &migrate);
|
||||
}
|
||||
|
||||
@ -659,7 +664,7 @@ static void main_channel_send_item(RedChannelClient *rcc, PipeItem *base)
|
||||
SPICE_CONTAINEROF(base, MultiMediaTimePipeItem, base));
|
||||
break;
|
||||
case SPICE_MSG_MAIN_MIGRATE_SWITCH_HOST:
|
||||
main_channel_marshall_migrate_switch(m);
|
||||
main_channel_marshall_migrate_switch(m, rcc);
|
||||
break;
|
||||
};
|
||||
red_channel_client_begin_send_message(rcc);
|
||||
@ -679,14 +684,6 @@ static void main_channel_release_pipe_item(RedChannelClient *rcc,
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SPICE_MSG_MAIN_MIGRATE_SWITCH_HOST: {
|
||||
RefsPipeItem *data = (RefsPipeItem*)base;
|
||||
if (!--*(data->refs)) {
|
||||
free(data->refs);
|
||||
reds_mig_release();
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -985,16 +982,7 @@ int main_channel_migrate_connect(MainChannel *main_channel, RedsMigSpice *mig_ta
|
||||
{
|
||||
RingItem *client_link;
|
||||
|
||||
ASSERT(mig_target);
|
||||
free(main_channel->mig_target.host);
|
||||
main_channel->mig_target.host = strdup(mig_target->host);
|
||||
free(main_channel->mig_target.cert_subject);
|
||||
if (mig_target->cert_subject) {
|
||||
main_channel->mig_target.cert_subject = strdup(mig_target->cert_subject);
|
||||
}
|
||||
main_channel->mig_target.port = mig_target->port;
|
||||
main_channel->mig_target.sport = mig_target->sport;
|
||||
|
||||
main_channel_fill_mig_target(main_channel, mig_target);
|
||||
main_channel->num_clients_mig_wait = 0;
|
||||
|
||||
RING_FOREACH(client_link, &main_channel->base.clients) {
|
||||
@ -1026,3 +1014,44 @@ 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)
|
||||
{
|
||||
RingItem *client_link;
|
||||
int semi_seamless_count = 0;
|
||||
|
||||
red_printf("");
|
||||
|
||||
if (ring_is_empty(&main_chan->base.clients)) {
|
||||
red_printf("no peer connected");
|
||||
return 0;
|
||||
}
|
||||
|
||||
RING_FOREACH(client_link, &main_chan->base.clients) {
|
||||
MainChannelClient *mcc;
|
||||
int semi_seamless_support;
|
||||
|
||||
mcc = SPICE_CONTAINEROF(client_link, MainChannelClient, base.channel_link);
|
||||
semi_seamless_support = red_channel_client_test_remote_cap(&mcc->base,
|
||||
SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE);
|
||||
if (semi_seamless_support && mcc->mig_connect_ok) {
|
||||
if (success) {
|
||||
red_printf("client %p MIGRATE_END", mcc->base.client);
|
||||
red_channel_client_pipe_add_type(&mcc->base, SPICE_MSG_MAIN_MIGRATE_END);
|
||||
semi_seamless_count++;
|
||||
} else {
|
||||
red_printf("client %p MIGRATE_CANCEL", mcc->base.client);
|
||||
red_channel_client_pipe_add_type(&mcc->base, SPICE_MSG_MAIN_MIGRATE_CANCEL);
|
||||
}
|
||||
} else {
|
||||
if (success) {
|
||||
red_printf("client %p SWITCH_HOST", mcc->base.client);
|
||||
red_channel_client_pipe_add_type(&mcc->base, SPICE_MSG_MAIN_MIGRATE_SWITCH_HOST);
|
||||
}
|
||||
}
|
||||
mcc->mig_connect_ok = FALSE;
|
||||
mcc->mig_wait_connect = FALSE;
|
||||
}
|
||||
return semi_seamless_count;
|
||||
}
|
||||
|
||||
|
||||
@ -83,8 +83,6 @@ void main_channel_push_init(MainChannelClient *mcc, int connection_id, int displ
|
||||
int ram_hint);
|
||||
void main_channel_push_notify(MainChannel *main_chan, uint8_t *mess, const int mess_len);
|
||||
void main_channel_push_migrate(MainChannel *main_chan);
|
||||
void main_channel_push_migrate_switch(MainChannel *main_chan);
|
||||
void main_channel_push_migrate_cancel(MainChannel *main_chan);
|
||||
void main_channel_push_multi_media_time(MainChannel *main_chan, int time);
|
||||
int main_channel_getsockname(MainChannel *main_chan, struct sockaddr *sa, socklen_t *salen);
|
||||
int main_channel_getpeername(MainChannel *main_chan, struct sockaddr *sa, socklen_t *salen);
|
||||
@ -95,10 +93,14 @@ uint64_t main_channel_client_get_bitrate_per_sec(MainChannelClient *mcc);
|
||||
int main_channel_is_connected(MainChannel *main_chan);
|
||||
RedChannelClient* main_channel_client_get_base(MainChannelClient* mcc);
|
||||
|
||||
/* switch host migration */
|
||||
void main_channel_migrate_switch(MainChannel *main_chan, RedsMigSpice *mig_target);
|
||||
|
||||
/* 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);
|
||||
void main_channel_migrate_cancel_wait(MainChannel *main_chan);
|
||||
void main_channel_migrate_complete(MainChannel *main_chan, int success);
|
||||
/* returns the number of clients for which SPICE_MSG_MAIN_MIGRATE_END was sent*/
|
||||
int main_channel_migrate_complete(MainChannel *main_chan, int success);
|
||||
#endif
|
||||
|
||||
104
server/reds.c
104
server/reds.c
@ -205,6 +205,7 @@ typedef struct RedsState {
|
||||
int mig_wait_connect;
|
||||
int mig_wait_disconnect;
|
||||
int mig_inprogress;
|
||||
int expect_migrate;
|
||||
int mig_target;
|
||||
RedsMigSpice *mig_spice;
|
||||
int num_of_channels;
|
||||
@ -2983,7 +2984,7 @@ typedef struct RedsMigCertPubKeyInfo {
|
||||
uint32_t len;
|
||||
} RedsMigCertPubKeyInfo;
|
||||
|
||||
void reds_mig_release(void)
|
||||
static void reds_mig_release(void)
|
||||
{
|
||||
if (reds->mig_spice) {
|
||||
free(reds->mig_spice->cert_subject);
|
||||
@ -2998,29 +2999,14 @@ static void reds_mig_started(void)
|
||||
red_printf("");
|
||||
ASSERT(reds->mig_spice);
|
||||
|
||||
reds->mig_wait_connect = TRUE;
|
||||
reds->mig_inprogress = TRUE;
|
||||
|
||||
if (reds->listen_watch != NULL) {
|
||||
core->watch_update_mask(reds->listen_watch, 0);
|
||||
}
|
||||
|
||||
if (reds->secure_listen_watch != NULL) {
|
||||
core->watch_update_mask(reds->secure_listen_watch, 0);
|
||||
}
|
||||
reds->mig_wait_connect = TRUE;
|
||||
core->timer_start(reds->mig_timer, MIGRATE_TIMEOUT);
|
||||
}
|
||||
|
||||
static void reds_mig_finished(int completed)
|
||||
{
|
||||
red_printf("");
|
||||
if (reds->listen_watch != NULL) {
|
||||
core->watch_update_mask(reds->listen_watch, SPICE_WATCH_EVENT_READ);
|
||||
}
|
||||
|
||||
if (reds->secure_listen_watch != NULL) {
|
||||
core->watch_update_mask(reds->secure_listen_watch, SPICE_WATCH_EVENT_READ);
|
||||
}
|
||||
|
||||
if (!reds_main_channel_connected()) {
|
||||
red_printf("no peer connected");
|
||||
@ -3028,28 +3014,13 @@ static void reds_mig_finished(int completed)
|
||||
}
|
||||
reds->mig_inprogress = TRUE;
|
||||
|
||||
if (completed) {
|
||||
RingItem *link, *next;
|
||||
|
||||
if (main_channel_migrate_complete(reds->main_channel, completed)) {
|
||||
reds->mig_wait_disconnect = TRUE;
|
||||
core->timer_start(reds->mig_timer, MIGRATE_TIMEOUT);
|
||||
|
||||
// TODO: so now that main channel is separate, how exactly does migration of it work?
|
||||
// - it can have an empty migrate - that seems ok
|
||||
// - I can try to fill it's migrate, then move stuff from reds.c there, but a lot of data
|
||||
// is in reds state right now.
|
||||
// currently the migrate callback of main_channel does nothing
|
||||
main_channel_push_migrate(reds->main_channel);
|
||||
|
||||
RING_FOREACH_SAFE(link, next, &reds->clients) {
|
||||
red_client_migrate(SPICE_CONTAINEROF(link, RedClient, link));
|
||||
}
|
||||
} else {
|
||||
main_channel_push_migrate_cancel(reds->main_channel);
|
||||
// TODO: all the seemless migration is broken. Before MC we waited for disconection of one client,
|
||||
// no we need to wait to all the clients (see mig_timer)?
|
||||
reds_mig_cleanup();
|
||||
}
|
||||
reds_mig_release();
|
||||
}
|
||||
|
||||
static void reds_mig_switch(void)
|
||||
@ -3058,30 +3029,8 @@ static void reds_mig_switch(void)
|
||||
red_printf("warning: reds_mig_switch called without migrate_info set");
|
||||
return;
|
||||
}
|
||||
main_channel_push_migrate_switch(reds->main_channel);
|
||||
}
|
||||
|
||||
void reds_fill_mig_switch(SpiceMsgMainMigrationSwitchHost *migrate)
|
||||
{
|
||||
RedsMigSpice *s = reds->mig_spice;
|
||||
|
||||
if (s == NULL) {
|
||||
red_printf(
|
||||
"error: reds_fill_mig_switch called without migrate info set");
|
||||
bzero(migrate, sizeof(*migrate));
|
||||
return;
|
||||
}
|
||||
migrate->port = s->port;
|
||||
migrate->sport = s->sport;
|
||||
migrate->host_size = strlen(s->host) + 1;
|
||||
migrate->host_data = (uint8_t *)s->host;
|
||||
if (s->cert_subject) {
|
||||
migrate->cert_subject_size = strlen(s->cert_subject) + 1;
|
||||
migrate->cert_subject_data = (uint8_t *)s->cert_subject;
|
||||
} else {
|
||||
migrate->cert_subject_size = 0;
|
||||
migrate->cert_subject_data = NULL;
|
||||
}
|
||||
main_channel_migrate_switch(reds->main_channel, reds->mig_spice);
|
||||
reds_mig_release();
|
||||
}
|
||||
|
||||
static void migrate_timeout(void *opaque)
|
||||
@ -3873,6 +3822,11 @@ SPICE_GNUC_VISIBLE int spice_server_migrate_connect(SpiceServer *s, const char*
|
||||
ASSERT(migration_interface);
|
||||
ASSERT(reds == s);
|
||||
|
||||
if (reds->expect_migrate) {
|
||||
red_printf("warning: consecutive calls without migration. Canceling previous call");
|
||||
main_channel_migrate_complete(reds->main_channel, FALSE);
|
||||
}
|
||||
|
||||
sif = SPICE_CONTAINEROF(migration_interface->base.sif, SpiceMigrateInterface, base);
|
||||
|
||||
if (!reds_set_migration_dest_info(dest, port, secure_port, cert_subject)) {
|
||||
@ -3880,10 +3834,16 @@ SPICE_GNUC_VISIBLE int spice_server_migrate_connect(SpiceServer *s, const char*
|
||||
return -1;
|
||||
}
|
||||
|
||||
reds->expect_migrate = TRUE;
|
||||
|
||||
|
||||
if (main_channel_migrate_connect(reds->main_channel, reds->mig_spice)) {
|
||||
reds->mig_wait_connect = TRUE;
|
||||
reds_mig_started();
|
||||
} else {
|
||||
if (reds->num_clients == 0) {
|
||||
reds_mig_release();
|
||||
red_printf("no client connected");
|
||||
}
|
||||
sif->migrate_connect_complete(migration_interface);
|
||||
}
|
||||
|
||||
@ -3894,13 +3854,13 @@ SPICE_GNUC_VISIBLE int spice_server_migrate_info(SpiceServer *s, const char* des
|
||||
int port, int secure_port,
|
||||
const char* cert_subject)
|
||||
{
|
||||
red_printf("");
|
||||
ASSERT(!migration_interface);
|
||||
ASSERT(reds == s);
|
||||
|
||||
if (!reds_set_migration_dest_info(dest, port, secure_port, cert_subject)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -3937,20 +3897,40 @@ SPICE_GNUC_VISIBLE int spice_server_migrate_client_state(SpiceServer *s)
|
||||
SPICE_GNUC_VISIBLE int spice_server_migrate_end(SpiceServer *s, int completed)
|
||||
{
|
||||
SpiceMigrateInterface *sif;
|
||||
int ret = 0;
|
||||
|
||||
red_printf("");
|
||||
|
||||
ASSERT(migration_interface);
|
||||
ASSERT(reds == s);
|
||||
reds_mig_finished(completed);
|
||||
|
||||
sif = SPICE_CONTAINEROF(migration_interface->base.sif, SpiceMigrateInterface, base);
|
||||
if (!reds->expect_migrate && reds->num_clients) {
|
||||
red_printf("spice_server_migrate_info was not called, disconnecting clients");
|
||||
reds_disconnect();
|
||||
ret = -1;
|
||||
goto complete;
|
||||
}
|
||||
|
||||
reds->expect_migrate = FALSE;
|
||||
reds_mig_finished(completed);
|
||||
ret = 0;
|
||||
complete:
|
||||
if (sif->migrate_end_complete) {
|
||||
sif->migrate_end_complete(migration_interface);
|
||||
}
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* interface for switch-host migration */
|
||||
SPICE_GNUC_VISIBLE int spice_server_migrate_switch(SpiceServer *s)
|
||||
{
|
||||
ASSERT(reds == s);
|
||||
red_printf("");
|
||||
if (!reds->num_clients) {
|
||||
return 0;
|
||||
}
|
||||
reds->expect_migrate = FALSE;
|
||||
reds_mig_switch();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -133,8 +133,6 @@ void reds_client_disconnect(RedClient *client);
|
||||
typedef struct MainMigrateData MainMigrateData;
|
||||
void reds_marshall_migrate_data_item(SpiceMarshaller *m, MainMigrateData *data);
|
||||
void reds_fill_channels(SpiceMsgChannels *channels_info);
|
||||
void reds_fill_mig_switch(SpiceMsgMainMigrationSwitchHost *migrate);
|
||||
void reds_mig_release(void);
|
||||
int reds_num_of_channels(void);
|
||||
int reds_num_of_clients(void);
|
||||
#ifdef RED_STATISTICS
|
||||
|
||||
Loading…
Reference in New Issue
Block a user