[Spice-devel] [spice-gtk PATCH 4/7] seamless migration: src and dest servers handshake
Marc-André Lureau
marcandre.lureau at gmail.com
Tue Aug 21 05:25:16 PDT 2012
On Wed, Aug 15, 2012 at 9:56 AM, Yonit Halperin <yhalperi at redhat.com> wrote:
> Flow:
> (1) *src* main channel coroutine (main_handle_migrate_begin_seamless):
> handles SPICE_MSG_MAIN_MIGRATE_BEGIN_SEAMLESS; yields to the main loop,
> supplying it the destination information needed for connection.
> (2) main context (migrate_connect):
> Establishes a new session for connecting to the destination.
> After all the channels are opened (async), their state, except for
> the one of the main channel, is modified to
> SPICE_CHANNEL_STATE_MIGRATING (see migrate_channel_event_cb);
> no reading is done from the channel during this state.
> The dest main channel's state is changed to SPICE_CHANNEL_STATE_MIGRATION_HANDSHAKE
>
> (3) *dest* main channel coroutine: sends to the dest server SPICE_MSGC_MAIN_MIGRATE_DST_DO_SEAMLESS
> (see spice_channel_recv_auth)
> (4) *dest* main channel coroutine: recevices SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_ACK/NACK.
> Then it emits the signal "migration-handshake-done".
> (5) main context: when all the dest session channels are connected, and the main channel handshake
> is done, we yield to the src main channel coroutine (see
> migrate_channel_event_cb and main_migrate_handshake_done_cb)
> (6) *src* main channel coroutine: sends to the src server
> SPICE_MSGC_MAIN_MIGRATE_(CONNECTED|CONNECTED_SEAMLESS|CONNECT_ERROR)
>
> For more details see spice-protocol. commit
> 1ad5d259cb4b695ec3106de7ccd082e031e7ae11
> ---
> gtk/channel-main.c | 163 +++++++++++++++++++++++++++++++++++++++++++--
> gtk/spice-channel-priv.h | 1 +
> gtk/spice-channel.c | 17 +++++
> gtk/spice-channel.h | 6 ++-
> 4 files changed, 178 insertions(+), 9 deletions(-)
>
> diff --git a/gtk/channel-main.c b/gtk/channel-main.c
> index bb0e361..bdcdbcb 100644
> --- a/gtk/channel-main.c
> +++ b/gtk/channel-main.c
> @@ -80,6 +80,7 @@ struct _SpiceMainChannelPrivate {
>
> guint switch_host_delayed_id;
> guint migrate_delayed_id;
> + guint32 migrate_src_version;
> };
>
> typedef struct spice_migrate spice_migrate;
> @@ -90,6 +91,12 @@ struct spice_migrate {
> SpiceSession *session;
> guint nchannels;
> SpiceChannel *channel;
> + bool do_seamless; /* used as input and output for the seamless migration handshake.
> + input: whether to send to the dest SPICE_MSGC_MAIN_MIGRATE_DST_DO_SEAMLESS
> + output: whether the dest approved seamless migration
> + (SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_ACK/NACK)
> + */
> + uint32_t src_mig_version;
> };
>
> G_DEFINE_TYPE(SpiceMainChannel, spice_main_channel, SPICE_TYPE_CHANNEL)
> @@ -121,6 +128,7 @@ enum {
> SPICE_MAIN_CLIPBOARD_SELECTION_REQUEST,
> SPICE_MAIN_CLIPBOARD_SELECTION_RELEASE,
> SPICE_MIGRATION_STARTED,
> + SPICE_MIGRATION_HANDSHAKE_DONE,
> SPICE_MAIN_LAST_SIGNAL,
> };
>
> @@ -131,6 +139,9 @@ static void agent_send_msg_queue(SpiceMainChannel *channel);
> static void agent_free_msg_queue(SpiceMainChannel *channel);
> static void migrate_channel_event_cb(SpiceChannel *channel, SpiceChannelEvent event,
> gpointer data);
> +static void main_migrate_handshake_done_cb(SpiceChannel *channel, gboolean seamless,
> + gpointer data);
> +static void spice_main_channel_send_migration_handshake(SpiceChannel *channel);
>
> /* ------------------------------------------------------------------ */
>
> @@ -164,6 +175,7 @@ static void spice_main_channel_reset_capabilties(SpiceChannel *channel)
> spice_channel_set_capability(SPICE_CHANNEL(channel), SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE);
> spice_channel_set_capability(SPICE_CHANNEL(channel), SPICE_MAIN_CAP_NAME_AND_UUID);
> spice_channel_set_capability(SPICE_CHANNEL(channel), SPICE_MAIN_CAP_AGENT_CONNECTED_TOKENS);
> + spice_channel_set_capability(SPICE_CHANNEL(channel), SPICE_MAIN_CAP_SEAMLESS_MIGRATE);
> }
>
> static void spice_main_channel_init(SpiceMainChannel *channel)
> @@ -327,6 +339,7 @@ static void spice_main_channel_class_init(SpiceMainChannelClass *klass)
> channel_class->iterate_write = spice_channel_iterate_write;
> channel_class->channel_reset = spice_main_channel_reset;
> channel_class->channel_reset_capabilities = spice_main_channel_reset_capabilties;
> + channel_class->channel_send_migration_handshake = spice_main_channel_send_migration_handshake;
>
> /**
> * SpiceMainChannel:mouse-mode:
> @@ -659,6 +672,27 @@ static void spice_main_channel_class_init(SpiceMainChannelClass *klass)
> G_TYPE_OBJECT);
>
>
> + /**
> + * SpiceMainChannel::migration-handshake-done:
> + * @main: the #SpiceMainChannel that emitted the signal
> + * @session: a migration #SpiceSession
> + *
> + * Inform when seamless migration handshake has completed, and
> + * its result: seamless or semi-seamless migration.
> + *
> + * Since: 0.13
> + **/
> + signals[SPICE_MIGRATION_HANDSHAKE_DONE] =
> + g_signal_new("migration-handshake-done",
> + G_OBJECT_CLASS_TYPE(gobject_class),
> + G_SIGNAL_RUN_LAST,
> + 0,
> + NULL, NULL,
> + g_cclosure_marshal_VOID__BOOLEAN,
> + G_TYPE_NONE,
> + 1,
> + G_TYPE_BOOLEAN);
> +
> g_type_class_add_private(klass, sizeof(SpiceMainChannelPrivate));
> }
>
> @@ -714,6 +748,10 @@ struct SPICE_MAIN_CLIPBOARD_SELECTION_RELEASE {
> guint8 selection;
> };
>
> +struct SPICE_MIGRATION_HANDSHAKE_DONE {
> + gboolean seamless;
> +};
> +
> /* main context */
> static void do_emit_main_context(GObject *object, int signum, gpointer params)
> {
> @@ -766,6 +804,11 @@ static void do_emit_main_context(GObject *object, int signum, gpointer params)
> p->selection);
> break;
> }
> + case SPICE_MIGRATION_HANDSHAKE_DONE: {
> + struct SPICE_MIGRATION_HANDSHAKE_DONE *p = params;
> + g_signal_emit(object, signals[signum], 0,
> + p->seamless);
> + }
> default:
> g_warn_if_reached();
> }
> @@ -1573,6 +1616,25 @@ static SpiceChannel* migrate_channel_connect(spice_migrate *mig, int type, int i
> return newc;
> }
>
> +/* coroutine context */
> +static void spice_main_channel_send_migration_handshake(SpiceChannel *channel)
> +{
> + SpiceMainChannelPrivate *c = SPICE_MAIN_CHANNEL(channel)->priv;
> +
> + if (!spice_channel_test_capability(channel, SPICE_MAIN_CAP_SEAMLESS_MIGRATE)) {
> + emit_main_context(channel, SPICE_MIGRATION_HANDSHAKE_DONE, false);
> + } else {
> + SpiceMsgcMainMigrateDstDoSeamless msg_data;
> + SpiceMsgOut *msg_out;
> +
> + msg_data.src_version = c->migrate_src_version;
> +
> + msg_out = spice_msg_out_new(channel, SPICE_MSGC_MAIN_MIGRATE_DST_DO_SEAMLESS);
> + msg_out->marshallers->msgc_main_migrate_dst_do_seamless(msg_out->marshaller, &msg_data);
> + spice_msg_out_send_internal(msg_out);
> + }
> +}
> +
> /* main context */
> static void migrate_channel_event_cb(SpiceChannel *channel, SpiceChannelEvent event,
> gpointer data)
> @@ -1588,9 +1650,20 @@ static void migrate_channel_event_cb(SpiceChannel *channel, SpiceChannelEvent ev
>
> switch (event) {
> case SPICE_CHANNEL_OPENED:
> - c->state = SPICE_CHANNEL_STATE_MIGRATING;
>
> if (c->channel_type == SPICE_CHANNEL_MAIN) {
> + if (mig->do_seamless) {
> + SpiceMainChannelPrivate *main_priv = SPICE_MAIN_CHANNEL(channel)->priv;
> +
> + c->state = SPICE_CHANNEL_STATE_MIGRATION_HANDSHAKE;
> + main_priv->migrate_src_version = mig->src_mig_version;
> + g_signal_connect(channel, "migration-handshake-done",
> + G_CALLBACK(main_migrate_handshake_done_cb),
> + mig);
We shouldn't add a public signal API for an internal condition wait.
You can either call directly an internal method or use
g_coroutine_condition_wait() depending on the needs.
> + } else {
> + c->state = SPICE_CHANNEL_STATE_MIGRATING;
> + mig->nchannels--;
> + }
> /* now connect the rest of the channels */
> GList *channels, *l;
> l = channels = spice_session_get_channels(session);
> @@ -1602,9 +1675,11 @@ static void migrate_channel_event_cb(SpiceChannel *channel, SpiceChannelEvent ev
> migrate_channel_connect(mig, curc->channel_type, curc->channel_id);
> }
> g_list_free(channels);
> + } else {
> + c->state = SPICE_CHANNEL_STATE_MIGRATING;
> + mig->nchannels--;
> }
>
> - mig->nchannels--;
> SPICE_DEBUG("migration: channel opened chan:%p, left %d", channel, mig->nchannels);
> if (mig->nchannels == 0)
> coroutine_yieldto(mig->from, NULL);
> @@ -1616,6 +1691,25 @@ static void migrate_channel_event_cb(SpiceChannel *channel, SpiceChannelEvent ev
> }
> }
>
> +/* main context */
> +static void main_migrate_handshake_done_cb(SpiceChannel *channel, gboolean seamless,
> + gpointer data)
> +{
> + spice_migrate *mig = data;
> + SpiceChannelPrivate *c = SPICE_CHANNEL(channel)->priv;
> +
> + g_return_if_fail(c->channel_type == SPICE_CHANNEL_MAIN);
> + g_return_if_fail(c->state == SPICE_CHANNEL_STATE_MIGRATION_HANDSHAKE);
> + g_return_if_fail(mig->do_seamless == true);
> +
> + g_signal_handlers_disconnect_by_func(channel, main_migrate_handshake_done_cb, data);
> + mig->do_seamless = seamless;
> + c->state = SPICE_CHANNEL_STATE_MIGRATING;
> + mig->nchannels--;
> + if (mig->nchannels == 0)
> + coroutine_yieldto(mig->from, NULL);
> +}
> +
> #ifdef __GNUC__
> typedef struct __attribute__ ((__packed__)) OldRedMigrationBegin {
> #else
> @@ -1703,16 +1797,19 @@ static gboolean migrate_connect(gpointer data)
> }
>
> /* coroutine context */
> -static void main_handle_migrate_begin(SpiceChannel *channel, SpiceMsgIn *in)
> +static void main_migrate_connect(SpiceChannel *channel,
> + SpiceMigrationDstInfo *dst_info, bool do_seamless,
> + uint32_t src_mig_version)
> {
> - SpiceMsgMainMigrationBegin *msg = spice_msg_in_parsed(in);
> spice_migrate mig = { 0, };
> SpiceMsgOut *out;
> int reply_type;
>
> mig.channel = channel;
> - mig.info = &msg->dst_info;
> + mig.info = dst_info;
> mig.from = coroutine_self();
> + mig.do_seamless = do_seamless;
> + mig.src_mig_version = src_mig_version;
>
> /* no need to track idle, call is sync for this coroutine */
> g_idle_add(migrate_connect, &mig);
> @@ -1725,8 +1822,13 @@ static void main_handle_migrate_begin(SpiceChannel *channel, SpiceMsgIn *in)
> reply_type = SPICE_MSGC_MAIN_MIGRATE_CONNECT_ERROR;
> spice_session_disconnect(mig.session);
> } else {
> - SPICE_DEBUG("migration: connections all ok");
> - reply_type = SPICE_MSGC_MAIN_MIGRATE_CONNECTED;
> + if (mig.do_seamless) {
> + SPICE_DEBUG("migration (seamless): connections all ok");
> + reply_type = SPICE_MSGC_MAIN_MIGRATE_CONNECTED_SEAMLESS;
> + } else {
> + SPICE_DEBUG("migration (semi-seamless): connections all ok");
> + reply_type = SPICE_MSGC_MAIN_MIGRATE_CONNECTED;
> + }
> spice_session_set_migration(spice_channel_get_session(channel), mig.session);
> }
> g_object_unref(mig.session);
> @@ -1735,6 +1837,38 @@ static void main_handle_migrate_begin(SpiceChannel *channel, SpiceMsgIn *in)
> spice_msg_out_send(out);
> }
>
> +/* coroutine context */
> +static void main_handle_migrate_begin(SpiceChannel *channel, SpiceMsgIn *in)
> +{
> + SpiceMsgMainMigrationBegin *msg = spice_msg_in_parsed(in);
> +
> + main_migrate_connect(channel, &msg->dst_info, false, 0);
> +}
> +
> +/* coroutine context */
> +static void main_handle_migrate_begin_seamless(SpiceChannel *channel, SpiceMsgIn *in)
> +{
> + SpiceMsgMainMigrateBeginSeamless *msg = spice_msg_in_parsed(in);
> +
> + main_migrate_connect(channel, &msg->dst_info, true, msg->src_mig_version);
> +}
> +
> +static void main_handle_migrate_dst_seamless_ack(SpiceChannel *channel, SpiceMsgIn *in)
> +{
> + SpiceChannelPrivate *c = SPICE_CHANNEL(channel)->priv;
> +
> + g_return_if_fail(c->state == SPICE_CHANNEL_STATE_MIGRATION_HANDSHAKE);
> + emit_main_context(channel, SPICE_MIGRATION_HANDSHAKE_DONE, true);
> +}
> +
> +static void main_handle_migrate_dst_seamless_nack(SpiceChannel *channel, SpiceMsgIn *in)
> +{
> + SpiceChannelPrivate *c = SPICE_CHANNEL(channel)->priv;
> +
> + g_return_if_fail(c->state == SPICE_CHANNEL_STATE_MIGRATION_HANDSHAKE);
> + emit_main_context(channel, SPICE_MIGRATION_HANDSHAKE_DONE, false);
> +}
> +
> /* main context */
> static gboolean migrate_delayed(gpointer data)
> {
> @@ -1848,7 +1982,10 @@ static const spice_msg_handler main_handlers[] = {
> [ 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,
> - [ SPICE_MSG_MAIN_AGENT_CONNECTED_TOKENS ] = main_handle_agent_connected_tokens,
> + [ SPICE_MSG_MAIN_AGENT_CONNECTED_TOKENS ] = main_handle_agent_connected_tokens,
> + [ SPICE_MSG_MAIN_MIGRATE_BEGIN_SEAMLESS ] = main_handle_migrate_begin_seamless,
> + [ SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_ACK] = main_handle_migrate_dst_seamless_ack,
> + [ SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_NACK] = main_handle_migrate_dst_seamless_nack,
> };
>
> /* coroutine context */
> @@ -1856,11 +1993,21 @@ static void spice_main_handle_msg(SpiceChannel *channel, SpiceMsgIn *msg)
> {
> int type = spice_msg_in_type(msg);
> SpiceChannelClass *parent_class;
> + SpiceChannelPrivate *c = SPICE_CHANNEL(channel)->priv;
>
> g_return_if_fail(type < SPICE_N_ELEMENTS(main_handlers));
>
> parent_class = SPICE_CHANNEL_CLASS(spice_main_channel_parent_class);
>
> + if (c->state == SPICE_CHANNEL_STATE_MIGRATION_HANDSHAKE) {
> + if (type != SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_ACK &&
> + type != SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_NACK) {
> + g_critical("unexpected msg (%d)."
> + "Only MIGRATE_DST_SEAMLESS_ACK/NACK are allowed", type);
> + return;
> + }
> + }
> +
> if (main_handlers[type] != NULL)
> main_handlers[type](channel, msg);
> else if (parent_class->handle_msg)
> diff --git a/gtk/spice-channel-priv.h b/gtk/spice-channel-priv.h
> index 4f094d8..8ed79fa 100644
> --- a/gtk/spice-channel-priv.h
> +++ b/gtk/spice-channel-priv.h
> @@ -71,6 +71,7 @@ enum spice_channel_state {
> SPICE_CHANNEL_STATE_READY,
> SPICE_CHANNEL_STATE_SWITCHING,
> SPICE_CHANNEL_STATE_MIGRATING,
> + SPICE_CHANNEL_STATE_MIGRATION_HANDSHAKE,
> };
>
> struct _SpiceChannelPrivate {
> diff --git a/gtk/spice-channel.c b/gtk/spice-channel.c
> index 54d406d..5f37cbc 100644
> --- a/gtk/spice-channel.c
> +++ b/gtk/spice-channel.c
> @@ -49,6 +49,7 @@ static void spice_channel_send_link(SpiceChannel *channel);
> static void channel_disconnect(SpiceChannel *channel);
> static void channel_reset(SpiceChannel *channel, gboolean migrating);
> static void spice_channel_reset_capabilities(SpiceChannel *channel);
> +static void spice_channel_send_migration_handshake(SpiceChannel *channel);
>
> /**
> * SECTION:spice-channel
> @@ -1080,6 +1081,10 @@ static void spice_channel_recv_auth(SpiceChannel *channel)
>
> emit_main_context(channel, SPICE_CHANNEL_EVENT, SPICE_CHANNEL_OPENED);
>
> + if (c->state == SPICE_CHANNEL_STATE_MIGRATION_HANDSHAKE) {
> + spice_channel_send_migration_handshake(channel);
> + }
> +
> if (c->state != SPICE_CHANNEL_STATE_MIGRATING)
> spice_channel_up(channel);
> }
> @@ -2002,6 +2007,7 @@ static void spice_channel_iterate_read(SpiceChannel *channel)
> spice_channel_recv_auth(channel);
> break;
> case SPICE_CHANNEL_STATE_READY:
> + case SPICE_CHANNEL_STATE_MIGRATION_HANDSHAKE:
> spice_channel_recv_msg(channel,
> (handler_msg_in)SPICE_CHANNEL_GET_CLASS(channel)->handle_msg, NULL);
> break;
> @@ -2654,3 +2660,14 @@ static void spice_channel_reset_capabilities(SpiceChannel *channel)
> SPICE_CHANNEL_GET_CLASS(channel)->channel_reset_capabilities(channel);
> }
> }
> +
> +static void spice_channel_send_migration_handshake(SpiceChannel *channel)
> +{
> + SpiceChannelPrivate *c = SPICE_CHANNEL_GET_PRIVATE(channel);
> +
> + if (SPICE_CHANNEL_GET_CLASS(channel)->channel_send_migration_handshake) {
> + SPICE_CHANNEL_GET_CLASS(channel)->channel_send_migration_handshake(channel);
> + } else {
> + c->state = SPICE_CHANNEL_STATE_MIGRATING;
> + }
> +}
> diff --git a/gtk/spice-channel.h b/gtk/spice-channel.h
> index 30ff1ba..d40b844 100644
> --- a/gtk/spice-channel.h
> +++ b/gtk/spice-channel.h
> @@ -90,11 +90,15 @@ struct _SpiceChannelClass
> void (*channel_reset)(SpiceChannel *channel, gboolean migrating);
> void (*channel_reset_capabilities)(SpiceChannel *channel);
>
> + /*< private >*/
> + /* virtual methods, coroutine context */
> + void (*channel_send_migration_handshake)(SpiceChannel *channel);
> +
> /*
> * If adding fields to this struct, remove corresponding
> * amount of padding to avoid changing overall struct size
> */
> - gchar _spice_reserved[SPICE_RESERVED_PADDING];
> + gchar _spice_reserved[SPICE_RESERVED_PADDING - sizeof(void *)];
> };
>
> GType spice_channel_get_type(void);
> --
> 1.7.7.6
>
> _______________________________________________
> Spice-devel mailing list
> Spice-devel at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/spice-devel
--
Marc-André Lureau
More information about the Spice-devel
mailing list