[Qemu-devel] [RFC v2 8/8] migration: add incoming mgmt lock

Peter Xu posted 8 patches 8 years, 5 months ago
[Qemu-devel] [RFC v2 8/8] migration: add incoming mgmt lock
Posted by Peter Xu 8 years, 5 months ago
Now at least migrate_incoming can be run in parallel.  Let's provide a
migration lock to protect it.

Signed-off-by: Peter Xu <peterx@redhat.com>
---
 migration/migration.c | 6 ++++++
 migration/migration.h | 3 +++
 2 files changed, 9 insertions(+)

diff --git a/migration/migration.c b/migration/migration.c
index c3fe0ed..32058f7 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -145,6 +145,7 @@ MigrationIncomingState *migration_incoming_get_current(void)
         mis_current.state = MIGRATION_STATUS_NONE;
         memset(&mis_current, 0, sizeof(MigrationIncomingState));
         qemu_mutex_init(&mis_current.rp_mutex);
+        qemu_mutex_init(&mis_current.mgmt_mutex);
         qemu_event_init(&mis_current.main_thread_load_event, false);
         once = true;
     }
@@ -1171,6 +1172,7 @@ void qmp_migrate_incoming(const char *uri, Error **errp)
 {
     Error *local_err = NULL;
     static bool once = true;
+    MigrationIncomingState *mis = migration_incoming_get_current();
 
     if (!deferred_incoming) {
         error_setg(errp, "For use with '-incoming defer'");
@@ -1180,8 +1182,12 @@ void qmp_migrate_incoming(const char *uri, Error **errp)
         error_setg(errp, "The incoming migration has already been started");
     }
 
+    qemu_mutex_lock(&mis->mgmt_mutex);
+
     qemu_start_incoming_migration(uri, &local_err);
 
+    qemu_mutex_unlock(&mis->mgmt_mutex);
+
     if (local_err) {
         error_propagate(errp, local_err);
         return;
diff --git a/migration/migration.h b/migration/migration.h
index 148c9fa..95f077b 100644
--- a/migration/migration.h
+++ b/migration/migration.h
@@ -58,6 +58,9 @@ struct MigrationIncomingState {
     /* The coroutine we should enter (back) after failover */
     Coroutine *migration_incoming_co;
     QemuSemaphore colo_incoming_sem;
+
+    /* Migration incoming management lock */
+    QemuMutex mgmt_mutex;
 };
 
 MigrationIncomingState *migration_incoming_get_current(void);
-- 
2.7.4


Re: [Qemu-devel] [RFC v2 8/8] migration: add incoming mgmt lock
Posted by Dr. David Alan Gilbert 8 years, 5 months ago
* Peter Xu (peterx@redhat.com) wrote:
> Now at least migrate_incoming can be run in parallel.  Let's provide a
> migration lock to protect it.
> 
> Signed-off-by: Peter Xu <peterx@redhat.com>
> ---
>  migration/migration.c | 6 ++++++
>  migration/migration.h | 3 +++
>  2 files changed, 9 insertions(+)
> 
> diff --git a/migration/migration.c b/migration/migration.c
> index c3fe0ed..32058f7 100644
> --- a/migration/migration.c
> +++ b/migration/migration.c
> @@ -145,6 +145,7 @@ MigrationIncomingState *migration_incoming_get_current(void)
>          mis_current.state = MIGRATION_STATUS_NONE;
>          memset(&mis_current, 0, sizeof(MigrationIncomingState));
>          qemu_mutex_init(&mis_current.rp_mutex);
> +        qemu_mutex_init(&mis_current.mgmt_mutex);
>          qemu_event_init(&mis_current.main_thread_load_event, false);
>          once = true;
>      }
> @@ -1171,6 +1172,7 @@ void qmp_migrate_incoming(const char *uri, Error **errp)
>  {
>      Error *local_err = NULL;
>      static bool once = true;
> +    MigrationIncomingState *mis = migration_incoming_get_current();

migration_incoming_get_current isn't actually thread-safe itself unless
you can guarantee the initial allocation has happened - otherwise both
threads can race and do the 'once' code at the same time.

Similarly, these locks - they don't protect our 'once' - so a second
thread could come in here and both get past the !once check.

Dave

>  
>      if (!deferred_incoming) {
>          error_setg(errp, "For use with '-incoming defer'");
> @@ -1180,8 +1182,12 @@ void qmp_migrate_incoming(const char *uri, Error **errp)
>          error_setg(errp, "The incoming migration has already been started");
>      }
>  
> +    qemu_mutex_lock(&mis->mgmt_mutex);
> +
>      qemu_start_incoming_migration(uri, &local_err);
>  
> +    qemu_mutex_unlock(&mis->mgmt_mutex);
> +
>      if (local_err) {
>          error_propagate(errp, local_err);
>          return;
> diff --git a/migration/migration.h b/migration/migration.h
> index 148c9fa..95f077b 100644
> --- a/migration/migration.h
> +++ b/migration/migration.h
> @@ -58,6 +58,9 @@ struct MigrationIncomingState {
>      /* The coroutine we should enter (back) after failover */
>      Coroutine *migration_incoming_co;
>      QemuSemaphore colo_incoming_sem;
> +
> +    /* Migration incoming management lock */
> +    QemuMutex mgmt_mutex;
>  };
>  
>  MigrationIncomingState *migration_incoming_get_current(void);
> -- 
> 2.7.4
> 
--
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK

Re: [Qemu-devel] [RFC v2 8/8] migration: add incoming mgmt lock
Posted by Peter Xu 8 years, 5 months ago
On Wed, Aug 23, 2017 at 07:01:35PM +0100, Dr. David Alan Gilbert wrote:
> * Peter Xu (peterx@redhat.com) wrote:
> > Now at least migrate_incoming can be run in parallel.  Let's provide a
> > migration lock to protect it.
> > 
> > Signed-off-by: Peter Xu <peterx@redhat.com>
> > ---
> >  migration/migration.c | 6 ++++++
> >  migration/migration.h | 3 +++
> >  2 files changed, 9 insertions(+)
> > 
> > diff --git a/migration/migration.c b/migration/migration.c
> > index c3fe0ed..32058f7 100644
> > --- a/migration/migration.c
> > +++ b/migration/migration.c
> > @@ -145,6 +145,7 @@ MigrationIncomingState *migration_incoming_get_current(void)
> >          mis_current.state = MIGRATION_STATUS_NONE;
> >          memset(&mis_current, 0, sizeof(MigrationIncomingState));
> >          qemu_mutex_init(&mis_current.rp_mutex);
> > +        qemu_mutex_init(&mis_current.mgmt_mutex);
> >          qemu_event_init(&mis_current.main_thread_load_event, false);
> >          once = true;
> >      }
> > @@ -1171,6 +1172,7 @@ void qmp_migrate_incoming(const char *uri, Error **errp)
> >  {
> >      Error *local_err = NULL;
> >      static bool once = true;
> > +    MigrationIncomingState *mis = migration_incoming_get_current();
> 
> migration_incoming_get_current isn't actually thread-safe itself unless
> you can guarantee the initial allocation has happened - otherwise both
> threads can race and do the 'once' code at the same time.

How about I init the incoming object as well in
migration_object_init()?

> 
> Similarly, these locks - they don't protect our 'once' - so a second
> thread could come in here and both get past the !once check.

Oh I missed this one since actually I am removing that "once" variable
in postcopy recovery series. :)

I can put the last two patches into postcopy recovery series, then
it'll be fine.

-- 
Peter Xu

Re: [Qemu-devel] [RFC v2 8/8] migration: add incoming mgmt lock
Posted by Dr. David Alan Gilbert 8 years, 5 months ago
* Peter Xu (peterx@redhat.com) wrote:
> On Wed, Aug 23, 2017 at 07:01:35PM +0100, Dr. David Alan Gilbert wrote:
> > * Peter Xu (peterx@redhat.com) wrote:
> > > Now at least migrate_incoming can be run in parallel.  Let's provide a
> > > migration lock to protect it.
> > > 
> > > Signed-off-by: Peter Xu <peterx@redhat.com>
> > > ---
> > >  migration/migration.c | 6 ++++++
> > >  migration/migration.h | 3 +++
> > >  2 files changed, 9 insertions(+)
> > > 
> > > diff --git a/migration/migration.c b/migration/migration.c
> > > index c3fe0ed..32058f7 100644
> > > --- a/migration/migration.c
> > > +++ b/migration/migration.c
> > > @@ -145,6 +145,7 @@ MigrationIncomingState *migration_incoming_get_current(void)
> > >          mis_current.state = MIGRATION_STATUS_NONE;
> > >          memset(&mis_current, 0, sizeof(MigrationIncomingState));
> > >          qemu_mutex_init(&mis_current.rp_mutex);
> > > +        qemu_mutex_init(&mis_current.mgmt_mutex);
> > >          qemu_event_init(&mis_current.main_thread_load_event, false);
> > >          once = true;
> > >      }
> > > @@ -1171,6 +1172,7 @@ void qmp_migrate_incoming(const char *uri, Error **errp)
> > >  {
> > >      Error *local_err = NULL;
> > >      static bool once = true;
> > > +    MigrationIncomingState *mis = migration_incoming_get_current();
> > 
> > migration_incoming_get_current isn't actually thread-safe itself unless
> > you can guarantee the initial allocation has happened - otherwise both
> > threads can race and do the 'once' code at the same time.
> 
> How about I init the incoming object as well in
> migration_object_init()?

Yes I think that might work.

> > 
> > Similarly, these locks - they don't protect our 'once' - so a second
> > thread could come in here and both get past the !once check.
> 
> Oh I missed this one since actually I am removing that "once" variable
> in postcopy recovery series. :)
> 
> I can put the last two patches into postcopy recovery series, then
> it'll be fine.

OK; these thigns just emphasise how hard it is to make a function really
lock free.

Dave

> -- 
> Peter Xu
--
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK

Re: [Qemu-devel] [RFC v2 8/8] migration: add incoming mgmt lock
Posted by Peter Xu 8 years, 5 months ago
On Fri, Aug 25, 2017 at 10:34:56AM +0100, Dr. David Alan Gilbert wrote:
> * Peter Xu (peterx@redhat.com) wrote:
> > On Wed, Aug 23, 2017 at 07:01:35PM +0100, Dr. David Alan Gilbert wrote:
> > > * Peter Xu (peterx@redhat.com) wrote:
> > > > Now at least migrate_incoming can be run in parallel.  Let's provide a
> > > > migration lock to protect it.
> > > > 
> > > > Signed-off-by: Peter Xu <peterx@redhat.com>
> > > > ---
> > > >  migration/migration.c | 6 ++++++
> > > >  migration/migration.h | 3 +++
> > > >  2 files changed, 9 insertions(+)
> > > > 
> > > > diff --git a/migration/migration.c b/migration/migration.c
> > > > index c3fe0ed..32058f7 100644
> > > > --- a/migration/migration.c
> > > > +++ b/migration/migration.c
> > > > @@ -145,6 +145,7 @@ MigrationIncomingState *migration_incoming_get_current(void)
> > > >          mis_current.state = MIGRATION_STATUS_NONE;
> > > >          memset(&mis_current, 0, sizeof(MigrationIncomingState));
> > > >          qemu_mutex_init(&mis_current.rp_mutex);
> > > > +        qemu_mutex_init(&mis_current.mgmt_mutex);
> > > >          qemu_event_init(&mis_current.main_thread_load_event, false);
> > > >          once = true;
> > > >      }
> > > > @@ -1171,6 +1172,7 @@ void qmp_migrate_incoming(const char *uri, Error **errp)
> > > >  {
> > > >      Error *local_err = NULL;
> > > >      static bool once = true;
> > > > +    MigrationIncomingState *mis = migration_incoming_get_current();
> > > 
> > > migration_incoming_get_current isn't actually thread-safe itself unless
> > > you can guarantee the initial allocation has happened - otherwise both
> > > threads can race and do the 'once' code at the same time.
> > 
> > How about I init the incoming object as well in
> > migration_object_init()?
> 
> Yes I think that might work.

This change would suite better for the postcopy recovery series.  Will
add one more patch for it.

> 
> > > 
> > > Similarly, these locks - they don't protect our 'once' - so a second
> > > thread could come in here and both get past the !once check.
> > 
> > Oh I missed this one since actually I am removing that "once" variable
> > in postcopy recovery series. :)
> > 
> > I can put the last two patches into postcopy recovery series, then
> > it'll be fine.
> 
> OK; these thigns just emphasise how hard it is to make a function really
> lock free.

Agreed.

-- 
Peter Xu