[Spice-devel] [PATCH spice-gtk 6/6] Support semi-seamless migration
Alon Levy
alevy at redhat.com
Sun Dec 18 10:23:30 PST 2011
On Sun, Dec 18, 2011 at 06:27:25PM +0100, Marc-André Lureau wrote:
> Yonit Halperin described the flow, that I followed:
>
> (1) when client_migrate_info is called SPICE_MSG_MAIN_MIGRATE_BEGIN is
> sent to the client.
>
> Then, the client should link to the target server (SpiceLinkMess),
> i.e., connect all the channels, but it shouldn't poll from the target,
> only from the source. You can refer to RedClient::Migrate class. The
> connection id in the link message should be the id of the connection
> to the source server.
>
> (2) The client sends to the source server
> SPICE_MSGC_MAIN_MIGRATE_(CONNECTED|CONNECT_ERROR)
>
> (3) When migration completes SPICE_MSG_MAIN_MIGRATE_(END|CANCEL) is
> sent to the client.
>
> (3.1) In case of SPICE_MSG_MAIN_MIGRATE_CANCEL, the client closes the
> connections to the target.
>
> (3.2) In case of SPICE_MSG_MAIN_MIGRATE_END, the client should reset
> all the data that is related to the connection to the source and
> complete the transition to the target server (without sending
> ATTACH_CHANNELS, and without guest display initialization via agent).
>
> Then, the client sends SPICE_MSG_MAIN_MIGRATE_END to the target.
MSGC ?
> ---
> gtk/channel-main.c | 56 ++++++++++++++++++++++++++----
> gtk/spice-channel.c | 30 ++++++++++++----
> gtk/spice-session-priv.h | 4 ++
> gtk/spice-session.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++
> 4 files changed, 158 insertions(+), 16 deletions(-)
>
> diff --git a/gtk/channel-main.c b/gtk/channel-main.c
> index 38a2c5d..42e4494 100644
> --- a/gtk/channel-main.c
> +++ b/gtk/channel-main.c
> @@ -75,6 +75,7 @@ struct _SpiceMainChannelPrivate {
> GQueue *agent_msg_queue;
>
> guint switch_host_delayed_id;
> + guint migrate_delayed_id;
> };
>
> typedef struct spice_migrate spice_migrate;
> @@ -158,6 +159,8 @@ static void spice_main_channel_init(SpiceMainChannel *channel)
>
> c = channel->priv = SPICE_MAIN_CHANNEL_GET_PRIVATE(channel);
> c->agent_msg_queue = g_queue_new();
> +
> + spice_channel_set_capability(SPICE_CHANNEL(channel), SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE);
> }
>
> static void spice_main_get_property(GObject *object,
> @@ -242,6 +245,11 @@ static void spice_main_channel_dispose(GObject *obj)
> c->switch_host_delayed_id = 0;
> }
>
> + if (c->migrate_delayed_id) {
> + g_source_remove(c->migrate_delayed_id);
> + c->migrate_delayed_id = 0;
> + }
> +
> if (G_OBJECT_CLASS(spice_main_channel_parent_class)->dispose)
> G_OBJECT_CLASS(spice_main_channel_parent_class)->dispose(obj);
> }
> @@ -1116,18 +1124,20 @@ static void main_handle_init(SpiceChannel *channel, SpiceMsgIn *in)
> session = spice_channel_get_session(channel);
> spice_session_set_connection_id(session, init->session_id);
>
> - out = spice_msg_out_new(SPICE_CHANNEL(channel), SPICE_MSGC_MAIN_ATTACH_CHANNELS);
> - spice_msg_out_send_internal(out);
> -
> set_mouse_mode(SPICE_MAIN_CHANNEL(channel), init->supported_mouse_modes,
> init->current_mouse_mode);
>
> + spice_session_set_mm_time(session, init->multi_media_time);
> +
> c->agent_tokens = init->agent_tokens;
> - if (init->agent_connected) {
> + if (init->agent_connected)
> agent_start(SPICE_MAIN_CHANNEL(channel));
> - }
>
> - spice_session_set_mm_time(session, init->multi_media_time);
> + if (spice_session_migrate_after_main_init(session))
> + return;
> +
> + out = spice_msg_out_new(SPICE_CHANNEL(channel), SPICE_MSGC_MAIN_ATTACH_CHANNELS);
> + spice_msg_out_send_internal(out);
> }
>
> /* coroutine context */
> @@ -1389,13 +1399,15 @@ static void migrate_channel_new_cb(SpiceSession *s, SpiceChannel *channel, gpoin
> G_CALLBACK(migrate_channel_event_cb), data);
> }
>
> -static void migrate_channel_connect(spice_migrate *mig, int type, int id)
> +static SpiceChannel* migrate_channel_connect(spice_migrate *mig, int type, int id)
> {
> SPICE_DEBUG("migrate_channel_connect %d:%d", type, id);
>
> SpiceChannel *newc = spice_channel_new(mig->session, type, id);
> spice_channel_connect(newc);
> mig->nchannels++;
> +
> + return newc;
> }
>
> /* main context */
> @@ -1522,7 +1534,7 @@ static gboolean migrate_connect(gpointer data)
>
> /* the migration process is in 2 steps, first the main channel and
> then the rest of the channels */
> - migrate_channel_connect(mig, SPICE_CHANNEL_MAIN, 0);
> + mig->session->priv->cmain = migrate_channel_connect(mig, SPICE_CHANNEL_MAIN, 0);
>
> return FALSE;
> }
> @@ -1561,6 +1573,33 @@ static void main_handle_migrate_begin(SpiceChannel *channel, SpiceMsgIn *in)
> }
>
> /* main context */
> +static gboolean migrate_delayed(gpointer data)
> +{
> + SpiceChannel *channel = data;
> + SpiceMainChannelPrivate *c = SPICE_MAIN_CHANNEL(channel)->priv;
> +
> + g_warn_if_fail(c->migrate_delayed_id != 0);
> + c->migrate_delayed_id = 0;
> +
> + spice_session_migrate_end(channel->priv->session);
> +
> + return FALSE;
> +}
> +
> +/* coroutine context */
> +static void main_handle_migrate_end(SpiceChannel *channel, SpiceMsgIn *in)
> +{
> + SpiceMainChannelPrivate *c = SPICE_MAIN_CHANNEL(channel)->priv;
> +
> + SPICE_DEBUG("migrate end");
> +
> + g_return_if_fail(c->migrate_delayed_id == 0);
> + g_return_if_fail(spice_channel_test_capability(channel, SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE));
> +
> + c->migrate_delayed_id = g_idle_add(migrate_delayed, channel);
> +}
> +
> +/* main context */
> static gboolean switch_host_delayed(gpointer data)
> {
> SpiceChannel *channel = data;
> @@ -1638,6 +1677,7 @@ static const spice_msg_handler main_handlers[] = {
> [ SPICE_MSG_MAIN_AGENT_TOKEN ] = main_handle_agent_token,
>
> [ SPICE_MSG_MAIN_MIGRATE_BEGIN ] = main_handle_migrate_begin,
> + [ SPICE_MSG_MAIN_MIGRATE_END ] = main_handle_migrate_end,
> [ SPICE_MSG_MAIN_MIGRATE_CANCEL ] = main_handle_migrate_cancel,
> [ SPICE_MSG_MAIN_MIGRATE_SWITCH_HOST ] = main_handle_migrate_switch_host,
> };
> diff --git a/gtk/spice-channel.c b/gtk/spice-channel.c
> index 6420c53..cbd89e1 100644
> --- a/gtk/spice-channel.c
> +++ b/gtk/spice-channel.c
> @@ -1008,6 +1008,7 @@ static void spice_channel_recv_link_hdr(SpiceChannel *channel)
> goto error;
> }
>
> + SPICE_DEBUG("Peer version: %d:%d", c->peer_hdr.major_version, c->peer_hdr.minor_version);
> if (c->peer_hdr.major_version != c->link_hdr.major_version) {
> if (c->peer_hdr.major_version == 1) {
> /* enter spice 0.4 mode */
> @@ -1810,11 +1811,26 @@ static void spice_channel_iterate_read(SpiceChannel *channel)
> spice_channel_recv_msg(channel,
> (handler_msg_in)SPICE_CHANNEL_GET_CLASS(channel)->handle_msg, NULL);
> break;
> + case SPICE_CHANNEL_STATE_MIGRATING:
> + return;
> default:
> g_critical("unknown state %d", c->state);
> }
> }
>
> +static gboolean wait_migration(gpointer data)
> +{
> + SpiceChannel *channel = SPICE_CHANNEL(data);
> + SpiceChannelPrivate *c = channel->priv;
> +
> + if (c->state != SPICE_CHANNEL_STATE_MIGRATING) {
> + SPICE_DEBUG("unfreeze channel %s", c->name);
> + return TRUE;
> + }
> +
> + return FALSE;
> +}
> +
> /* coroutine context */
> static gboolean spice_channel_iterate(SpiceChannel *channel)
> {
> @@ -1822,11 +1838,9 @@ static gboolean spice_channel_iterate(SpiceChannel *channel)
> GIOCondition ret;
>
> do {
> - while (c->state == SPICE_CHANNEL_STATE_MIGRATING) {
> - /* freeze coroutine */
> - coroutine_yield(NULL);
> - g_return_val_if_fail(c->state != SPICE_CHANNEL_STATE_MIGRATING, FALSE);
> - }
> + /* freeze coroutine */
> + if (c->state == SPICE_CHANNEL_STATE_MIGRATING)
> + g_condition_wait(wait_migration, channel);
>
> if (c->has_error) {
> SPICE_DEBUG("channel has error, breaking loop");
> @@ -1835,6 +1849,7 @@ static gboolean spice_channel_iterate(SpiceChannel *channel)
>
> SPICE_CHANNEL_GET_CLASS(channel)->iterate_write(channel);
> ret = g_io_wait_interruptible(&c->wait, c->sock, G_IO_IN);
> +
> #ifdef WIN32
> /* FIXME: windows gsocket is buggy, it doesn't return correct condition... */
> ret = g_socket_condition_check(c->sock, G_IO_IN);
> @@ -2211,6 +2226,7 @@ void spice_channel_disconnect(SpiceChannel *channel, SpiceChannelEvent reason)
> {
> SpiceChannelPrivate *c = SPICE_CHANNEL_GET_PRIVATE(channel);
>
> + SPICE_DEBUG("channel disconnect %d", reason);
> g_return_if_fail(c != NULL);
>
> if (c->state == SPICE_CHANNEL_STATE_UNCONNECTED)
> @@ -2223,13 +2239,11 @@ void spice_channel_disconnect(SpiceChannel *channel, SpiceChannelEvent reason)
>
> if (c->state == SPICE_CHANNEL_STATE_MIGRATING) {
> c->state = SPICE_CHANNEL_STATE_READY;
> - coroutine_yieldto(&c->coroutine, NULL);
This looks strange - is this removal related to this patch?
> } else
> spice_channel_wakeup(channel);
>
> - if (reason != SPICE_CHANNEL_NONE) {
> + if (reason != SPICE_CHANNEL_NONE)
> g_signal_emit(G_OBJECT(channel), signals[SPICE_CHANNEL_EVENT], 0, reason);
> - }
> }
>
> static gboolean test_capability(GArray *caps, guint32 cap)
> diff --git a/gtk/spice-session-priv.h b/gtk/spice-session-priv.h
> index 0851c39..4d52254 100644
> --- a/gtk/spice-session-priv.h
> +++ b/gtk/spice-session-priv.h
> @@ -78,6 +78,8 @@ struct _SpiceSessionPrivate {
> GList *migration_left;
> SpiceSessionMigration migration_state;
> gboolean disconnecting;
> + gboolean migrate_wait_init;
> + guint after_main_init;
>
> display_cache images;
> display_cache palettes;
> @@ -123,6 +125,8 @@ void spice_session_get_caches(SpiceSession *session,
> SpiceGlzDecoderWindow **glz_window);
> void spice_session_palettes_clear(SpiceSession *session);
> void spice_session_images_clear(SpiceSession *session);
> +void spice_session_migrate_end(SpiceSession *session);
> +gboolean spice_session_migrate_after_main_init(SpiceSession *session);
>
> G_END_DECLS
>
> diff --git a/gtk/spice-session.c b/gtk/spice-session.c
> index 2b10635..9432073 100644
> --- a/gtk/spice-session.c
> +++ b/gtk/spice-session.c
> @@ -146,6 +146,11 @@ spice_session_dispose(GObject *gobject)
> s->migration_left = NULL;
> }
>
> + if (s->after_main_init) {
> + g_source_remove(s->after_main_init);
> + s->after_main_init = 0;
> + }
> +
> g_clear_object(&s->audio_manager);
> g_clear_object(&s->gtk_session);
> g_clear_object(&s->usb_manager);
> @@ -1146,6 +1151,85 @@ void spice_session_channel_migrate(SpiceSession *session, SpiceChannel *channel)
> }
> }
>
> +/* main context */
> +static gboolean after_main_init(gpointer data)
> +{
> + SpiceSession *self = data;
> + SpiceSessionPrivate *s = self->priv;
> + GList *l;
> +
> + for (l = s->migration_left; l != NULL; ) {
> + SpiceChannel *channel = l->data;
> + l = l->next;
> +
> + spice_session_channel_migrate(self, channel);
> + channel->priv->state = SPICE_CHANNEL_STATE_READY;
> + spice_channel_up(channel);
> + }
> +
> + s->after_main_init = 0;
> + return FALSE;
> +}
> +
> +/* coroutine context */
> +G_GNUC_INTERNAL
> +gboolean spice_session_migrate_after_main_init(SpiceSession *self)
> +{
> + SpiceSessionPrivate *s = self->priv;
> +
> + if (!s->migrate_wait_init)
> + return FALSE;
> +
> + g_return_val_if_fail(g_list_length(s->migration_left) != 0, FALSE);
> + g_return_val_if_fail(s->after_main_init == 0, FALSE);
> +
> + s->migrate_wait_init = FALSE;
> + s->after_main_init = g_idle_add(after_main_init, self);
> +
> + return TRUE;
> +}
> +
> +/* main context */
> +G_GNUC_INTERNAL
> +void spice_session_migrate_end(SpiceSession *self)
> +{
> + SpiceSessionPrivate *s = self->priv;
> + SpiceMsgOut *out;
> + GList *l;
> +
> + g_return_if_fail(s->migration);
> + g_return_if_fail(s->migration->priv->cmain);
> + g_return_if_fail(g_list_length(s->migration_left) != 0);
> +
> + /* disconnect and reset all channels */
> + for (l = s->migration_left; l != NULL; ) {
> + SpiceChannel *channel = l->data;
> + l = l->next;
> +
> + /* reset for migration, disconnect */
> + spice_channel_reset(channel, TRUE);
> +
> + if (SPICE_IS_MAIN_CHANNEL(channel)) {
> + /* migrate main to target, so we can start talking */
> + spice_session_channel_migrate(self, channel);
> + } else {
> + /* freeze other channels */
> + channel->priv->state = SPICE_CHANNEL_STATE_MIGRATING;
> + }
> + }
> +
> + spice_session_palettes_clear(self);
> + spice_session_images_clear(self);
> + glz_decoder_window_clear(s->glz_window);
> +
> + /* send MIGRATE_END to target */
> + out = spice_msg_out_new(s->cmain, SPICE_MSGC_MAIN_MIGRATE_END);
> + spice_msg_out_send(out);
> +
> + /* now wait after main init for the rest of channels migration */
> + s->migrate_wait_init = TRUE;
> +}
> +
> /**
> * spice_session_get_read_only:
> * @session: a #SpiceSession
> --
> 1.7.7.4
>
> _______________________________________________
> Spice-devel mailing list
> Spice-devel at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/spice-devel
More information about the Spice-devel
mailing list