[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