diff mbox

[v7,23/42] MIGRATION_STATUS_POSTCOPY_ACTIVE: Add new migration state

Message ID 1434450415-11339-24-git-send-email-dgilbert@redhat.com
State New
Headers show

Commit Message

Dr. David Alan Gilbert June 16, 2015, 10:26 a.m. UTC
From: "Dr. David Alan Gilbert" <dgilbert@redhat.com>

'MIGRATION_STATUS_POSTCOPY_ACTIVE' is entered after migrate_start_postcopy

'migration_postcopy_phase' is provided for other sections to know if
they're in postcopy.

Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
Reviewed-by: David Gibson <david@gibson.dropbear.id.au>
Reviewed-by: Eric Blake <eblake@redhat.com>
---
 include/migration/migration.h |  2 ++
 migration/migration.c         | 56 ++++++++++++++++++++++++++++++++++++-------
 qapi-schema.json              |  4 +++-
 trace-events                  |  1 +
 4 files changed, 54 insertions(+), 9 deletions(-)

Comments

Juan Quintela July 13, 2015, 11:27 a.m. UTC | #1
"Dr. David Alan Gilbert (git)" <dgilbert@redhat.com> wrote:
> From: "Dr. David Alan Gilbert" <dgilbert@redhat.com>
>
> 'MIGRATION_STATUS_POSTCOPY_ACTIVE' is entered after migrate_start_postcopy
>
> 'migration_postcopy_phase' is provided for other sections to know if
> they're in postcopy.
>
> Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
> Reviewed-by: David Gibson <david@gibson.dropbear.id.au>
> Reviewed-by: Eric Blake <eblake@redhat.com>

Reviewed-by: Juan Quintela <quintela@redhat.com>


But (there is always a but....)


> @@ -358,6 +359,39 @@ MigrationInfo *qmp_query_migrate(Error **errp)
>  
>          get_xbzrle_cache_stats(info);
>          break;
> +    case MIGRATION_STATUS_POSTCOPY_ACTIVE:
> +        /* Mostly the same as active; TODO add some postcopy stats */
> +        info->has_status = true;
> +        info->has_total_time = true;
> +        info->total_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME)
> +            - s->total_time;
> +        info->has_expected_downtime = true;
> +        info->expected_downtime = s->expected_downtime;
> +        info->has_setup_time = true;
> +        info->setup_time = s->setup_time;
> +
> +        info->has_ram = true;
> +        info->ram = g_malloc0(sizeof(*info->ram));
> +        info->ram->transferred = ram_bytes_transferred();
> +        info->ram->remaining = ram_bytes_remaining();
> +        info->ram->total = ram_bytes_total();
> +        info->ram->duplicate = dup_mig_pages_transferred();
> +        info->ram->skipped = skipped_mig_pages_transferred();
> +        info->ram->normal = norm_mig_pages_transferred();
> +        info->ram->normal_bytes = norm_mig_bytes_transferred();
> +        info->ram->dirty_pages_rate = s->dirty_pages_rate;
> +        info->ram->mbps = s->mbps;
> +
> +        if (blk_mig_active()) {
> +            info->has_disk = true;
> +            info->disk = g_malloc0(sizeof(*info->disk));
> +            info->disk->transferred = blk_mig_bytes_transferred();
> +            info->disk->remaining = blk_mig_bytes_remaining();
> +            info->disk->total = blk_mig_bytes_total();
> +        }

Can we have block migration active with postcopy?  I would assume that
this would get disk corruption, no?  Or if you preffer the other
question, what protects us from disk corruption?

Once here, I guess we can get the migrate_already_active() bit without
problem?

Later, Juan.
Dr. David Alan Gilbert July 13, 2015, 3:53 p.m. UTC | #2
* Juan Quintela (quintela@redhat.com) wrote:
> "Dr. David Alan Gilbert (git)" <dgilbert@redhat.com> wrote:
> > From: "Dr. David Alan Gilbert" <dgilbert@redhat.com>
> >
> > 'MIGRATION_STATUS_POSTCOPY_ACTIVE' is entered after migrate_start_postcopy
> >
> > 'migration_postcopy_phase' is provided for other sections to know if
> > they're in postcopy.
> >
> > Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
> > Reviewed-by: David Gibson <david@gibson.dropbear.id.au>
> > Reviewed-by: Eric Blake <eblake@redhat.com>
> 
> Reviewed-by: Juan Quintela <quintela@redhat.com>
> 
> 
> But (there is always a but....)
> 
> 
> > @@ -358,6 +359,39 @@ MigrationInfo *qmp_query_migrate(Error **errp)
> >  
> >          get_xbzrle_cache_stats(info);
> >          break;
> > +    case MIGRATION_STATUS_POSTCOPY_ACTIVE:
> > +        /* Mostly the same as active; TODO add some postcopy stats */
> > +        info->has_status = true;
> > +        info->has_total_time = true;
> > +        info->total_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME)
> > +            - s->total_time;
> > +        info->has_expected_downtime = true;
> > +        info->expected_downtime = s->expected_downtime;
> > +        info->has_setup_time = true;
> > +        info->setup_time = s->setup_time;
> > +
> > +        info->has_ram = true;
> > +        info->ram = g_malloc0(sizeof(*info->ram));
> > +        info->ram->transferred = ram_bytes_transferred();
> > +        info->ram->remaining = ram_bytes_remaining();
> > +        info->ram->total = ram_bytes_total();
> > +        info->ram->duplicate = dup_mig_pages_transferred();
> > +        info->ram->skipped = skipped_mig_pages_transferred();
> > +        info->ram->normal = norm_mig_pages_transferred();
> > +        info->ram->normal_bytes = norm_mig_bytes_transferred();
> > +        info->ram->dirty_pages_rate = s->dirty_pages_rate;
> > +        info->ram->mbps = s->mbps;
> > +
> > +        if (blk_mig_active()) {
> > +            info->has_disk = true;
> > +            info->disk = g_malloc0(sizeof(*info->disk));
> > +            info->disk->transferred = blk_mig_bytes_transferred();
> > +            info->disk->remaining = blk_mig_bytes_remaining();
> > +            info->disk->total = blk_mig_bytes_total();
> > +        }
> 
> Can we have block migration active with postcopy?  I would assume that
> this would get disk corruption, no?  Or if you preffer the other
> question, what protects us from disk corruption?

I think you can, I've not tried it; however I also think it should
be safe.

 migration/block.c's block_save_pending always puts a value in the
non_postcopiable_pending return value (and 0 in the postcopiable_pending);
the migrate thread checks the non_postcopiable_pending size to
decide when it can switch to postcopy, and performs a call to the complete
method on each device before it does.  Thus the block migration should
be finished before we start doing the actual postcopy stage, and thus
before the destination CPU starts running.

A possibly harder question is what happens if block.c did implement
postcopy and you had both block postcopy and ram postcopy active at
the same time; again I think it should work but I'm not sure if one
would starve the other.

> Once here, I guess we can get the migrate_already_active() bit without
> problem?

I'm not sure of the question here; but the idea of migration_already_active()
is just to avoid all of the open-coded checks for each possible state;
now we've added anothe state they were getting messy.

Dave

> 
> Later, Juan.
--
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK
Juan Quintela July 13, 2015, 4:26 p.m. UTC | #3
"Dr. David Alan Gilbert" <dgilbert@redhat.com> wrote:
> * Juan Quintela (quintela@redhat.com) wrote:
>> "Dr. David Alan Gilbert (git)" <dgilbert@redhat.com> wrote:
>> > From: "Dr. David Alan Gilbert" <dgilbert@redhat.com>
>> >
>> > 'MIGRATION_STATUS_POSTCOPY_ACTIVE' is entered after migrate_start_postcopy
>> >
>> > 'migration_postcopy_phase' is provided for other sections to know if
>> > they're in postcopy.
>> >
>> > Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
>> > Reviewed-by: David Gibson <david@gibson.dropbear.id.au>
>> > Reviewed-by: Eric Blake <eblake@redhat.com>
>> 
>> Reviewed-by: Juan Quintela <quintela@redhat.com>
>> 
>> 
>> But (there is always a but....)
>> 
>> 
>> > @@ -358,6 +359,39 @@ MigrationInfo *qmp_query_migrate(Error **errp)
>> >  
>> >          get_xbzrle_cache_stats(info);
>> >          break;
>> > +    case MIGRATION_STATUS_POSTCOPY_ACTIVE:
>> > +        /* Mostly the same as active; TODO add some postcopy stats */
>> > +        info->has_status = true;
>> > +        info->has_total_time = true;
>> > +        info->total_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME)
>> > +            - s->total_time;
>> > +        info->has_expected_downtime = true;
>> > +        info->expected_downtime = s->expected_downtime;
>> > +        info->has_setup_time = true;
>> > +        info->setup_time = s->setup_time;
>> > +
>> > +        info->has_ram = true;
>> > +        info->ram = g_malloc0(sizeof(*info->ram));
>> > +        info->ram->transferred = ram_bytes_transferred();
>> > +        info->ram->remaining = ram_bytes_remaining();
>> > +        info->ram->total = ram_bytes_total();
>> > +        info->ram->duplicate = dup_mig_pages_transferred();
>> > +        info->ram->skipped = skipped_mig_pages_transferred();
>> > +        info->ram->normal = norm_mig_pages_transferred();
>> > +        info->ram->normal_bytes = norm_mig_bytes_transferred();
>> > +        info->ram->dirty_pages_rate = s->dirty_pages_rate;
>> > +        info->ram->mbps = s->mbps;
>> > +
>> > +        if (blk_mig_active()) {
>> > +            info->has_disk = true;
>> > +            info->disk = g_malloc0(sizeof(*info->disk));
>> > +            info->disk->transferred = blk_mig_bytes_transferred();
>> > +            info->disk->remaining = blk_mig_bytes_remaining();
>> > +            info->disk->total = blk_mig_bytes_total();
>> > +        }
>> 
>> Can we have block migration active with postcopy?  I would assume that
>> this would get disk corruption, no?  Or if you preffer the other
>> question, what protects us from disk corruption?
>
> I think you can, I've not tried it; however I also think it should
> be safe.
>
>  migration/block.c's block_save_pending always puts a value in the
> non_postcopiable_pending return value (and 0 in the postcopiable_pending);
> the migrate thread checks the non_postcopiable_pending size to
> decide when it can switch to postcopy, and performs a call to the complete
> method on each device before it does.  Thus the block migration should
> be finished before we start doing the actual postcopy stage, and thus
> before the destination CPU starts running.

I mean that as it is right now, the info under blk_mig_active() check
would be zero/the same than before entering postcopy.

>
> A possibly harder question is what happens if block.c did implement
> postcopy and you had both block postcopy and ram postcopy active at
> the same time; again I think it should work but I'm not sure if one
> would starve the other.
>
>> Once here, I guess we can get the migrate_already_active() bit without
>> problem?
>
> I'm not sure of the question here; but the idea of migration_already_active()
> is just to avoid all of the open-coded checks for each possible state;
> now we've added anothe state they were getting messy.

Sorry.  I mean that the migrate_already_active() bits can get in without
further ado.  Don't need to wait for postcopy to be integrated.

>
> Dave
>
>> 
>> Later, Juan.
> --
> Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK
Dr. David Alan Gilbert July 13, 2015, 4:48 p.m. UTC | #4
* Juan Quintela (quintela@redhat.com) wrote:
> "Dr. David Alan Gilbert" <dgilbert@redhat.com> wrote:
> > * Juan Quintela (quintela@redhat.com) wrote:
> >> "Dr. David Alan Gilbert (git)" <dgilbert@redhat.com> wrote:
> >> > From: "Dr. David Alan Gilbert" <dgilbert@redhat.com>
> >> >
> >> > 'MIGRATION_STATUS_POSTCOPY_ACTIVE' is entered after migrate_start_postcopy
> >> >
> >> > 'migration_postcopy_phase' is provided for other sections to know if
> >> > they're in postcopy.
> >> >
> >> > Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
> >> > Reviewed-by: David Gibson <david@gibson.dropbear.id.au>
> >> > Reviewed-by: Eric Blake <eblake@redhat.com>
> >> 
> >> Reviewed-by: Juan Quintela <quintela@redhat.com>
> >> 
> >> 
> >> But (there is always a but....)
> >> 
> >> 
> >> > @@ -358,6 +359,39 @@ MigrationInfo *qmp_query_migrate(Error **errp)
> >> >  
> >> >          get_xbzrle_cache_stats(info);
> >> >          break;
> >> > +    case MIGRATION_STATUS_POSTCOPY_ACTIVE:
> >> > +        /* Mostly the same as active; TODO add some postcopy stats */
> >> > +        info->has_status = true;
> >> > +        info->has_total_time = true;
> >> > +        info->total_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME)
> >> > +            - s->total_time;
> >> > +        info->has_expected_downtime = true;
> >> > +        info->expected_downtime = s->expected_downtime;
> >> > +        info->has_setup_time = true;
> >> > +        info->setup_time = s->setup_time;
> >> > +
> >> > +        info->has_ram = true;
> >> > +        info->ram = g_malloc0(sizeof(*info->ram));
> >> > +        info->ram->transferred = ram_bytes_transferred();
> >> > +        info->ram->remaining = ram_bytes_remaining();
> >> > +        info->ram->total = ram_bytes_total();
> >> > +        info->ram->duplicate = dup_mig_pages_transferred();
> >> > +        info->ram->skipped = skipped_mig_pages_transferred();
> >> > +        info->ram->normal = norm_mig_pages_transferred();
> >> > +        info->ram->normal_bytes = norm_mig_bytes_transferred();
> >> > +        info->ram->dirty_pages_rate = s->dirty_pages_rate;
> >> > +        info->ram->mbps = s->mbps;
> >> > +
> >> > +        if (blk_mig_active()) {
> >> > +            info->has_disk = true;
> >> > +            info->disk = g_malloc0(sizeof(*info->disk));
> >> > +            info->disk->transferred = blk_mig_bytes_transferred();
> >> > +            info->disk->remaining = blk_mig_bytes_remaining();
> >> > +            info->disk->total = blk_mig_bytes_total();
> >> > +        }
> >> 
> >> Can we have block migration active with postcopy?  I would assume that
> >> this would get disk corruption, no?  Or if you preffer the other
> >> question, what protects us from disk corruption?
> >
> > I think you can, I've not tried it; however I also think it should
> > be safe.
> >
> >  migration/block.c's block_save_pending always puts a value in the
> > non_postcopiable_pending return value (and 0 in the postcopiable_pending);
> > the migrate thread checks the non_postcopiable_pending size to
> > decide when it can switch to postcopy, and performs a call to the complete
> > method on each device before it does.  Thus the block migration should
> > be finished before we start doing the actual postcopy stage, and thus
> > before the destination CPU starts running.
> 
> I mean that as it is right now, the info under blk_mig_active() check
> would be zero/the same than before entering postcopy.

Ah, yes;  would blk_mig_bytes_total/transferred still have valid values you
would want to display, even at the end of the block migration phase?

> >
> > A possibly harder question is what happens if block.c did implement
> > postcopy and you had both block postcopy and ram postcopy active at
> > the same time; again I think it should work but I'm not sure if one
> > would starve the other.
> >
> >> Once here, I guess we can get the migrate_already_active() bit without
> >> problem?
> >
> > I'm not sure of the question here; but the idea of migration_already_active()
> > is just to avoid all of the open-coded checks for each possible state;
> > now we've added anothe state they were getting messy.
> 
> Sorry.  I mean that the migrate_already_active() bits can get in without
> further ado.  Don't need to wait for postcopy to be integrated.

Yes; do you want it split out?

Dave

> 
> >
> > Dave
> >
> >> 
> >> Later, Juan.
> > --
> > Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK
--
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK
Juan Quintela July 13, 2015, 6:05 p.m. UTC | #5
"Dr. David Alan Gilbert" <dgilbert@redhat.com> wrote:
> * Juan Quintela (quintela@redhat.com) wrote:
>> "Dr. David Alan Gilbert" <dgilbert@redhat.com> wrote:
>> > * Juan Quintela (quintela@redhat.com) wrote:
>> >> "Dr. David Alan Gilbert (git)" <dgilbert@redhat.com> wrote:
>> >> > From: "Dr. David Alan Gilbert" <dgilbert@redhat.com>
>> >> >
>> >> > 'MIGRATION_STATUS_POSTCOPY_ACTIVE' is entered after migrate_start_postcopy
>> >> >
>> >> > 'migration_postcopy_phase' is provided for other sections to know if
>> >> > they're in postcopy.
>> >> >
>> >> > Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
>> >> > Reviewed-by: David Gibson <david@gibson.dropbear.id.au>
>> >> > Reviewed-by: Eric Blake <eblake@redhat.com>
>> >> 
>> >> Reviewed-by: Juan Quintela <quintela@redhat.com>
>> >> 
>> >> 
>> >> But (there is always a but....)
>> >> 
>> >> 
>> >> > @@ -358,6 +359,39 @@ MigrationInfo *qmp_query_migrate(Error **errp)
>> >> >  
>> >> >          get_xbzrle_cache_stats(info);
>> >> >          break;
>> >> > +    case MIGRATION_STATUS_POSTCOPY_ACTIVE:
>> >> > +        /* Mostly the same as active; TODO add some postcopy stats */
>> >> > +        info->has_status = true;
>> >> > +        info->has_total_time = true;
>> >> > +        info->total_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME)
>> >> > +            - s->total_time;
>> >> > +        info->has_expected_downtime = true;
>> >> > +        info->expected_downtime = s->expected_downtime;
>> >> > +        info->has_setup_time = true;
>> >> > +        info->setup_time = s->setup_time;
>> >> > +
>> >> > +        info->has_ram = true;
>> >> > +        info->ram = g_malloc0(sizeof(*info->ram));
>> >> > +        info->ram->transferred = ram_bytes_transferred();
>> >> > +        info->ram->remaining = ram_bytes_remaining();
>> >> > +        info->ram->total = ram_bytes_total();
>> >> > +        info->ram->duplicate = dup_mig_pages_transferred();
>> >> > +        info->ram->skipped = skipped_mig_pages_transferred();
>> >> > +        info->ram->normal = norm_mig_pages_transferred();
>> >> > +        info->ram->normal_bytes = norm_mig_bytes_transferred();
>> >> > +        info->ram->dirty_pages_rate = s->dirty_pages_rate;
>> >> > +        info->ram->mbps = s->mbps;
>> >> > +
>> >> > +        if (blk_mig_active()) {
>> >> > +            info->has_disk = true;
>> >> > +            info->disk = g_malloc0(sizeof(*info->disk));
>> >> > +            info->disk->transferred = blk_mig_bytes_transferred();
>> >> > +            info->disk->remaining = blk_mig_bytes_remaining();
>> >> > +            info->disk->total = blk_mig_bytes_total();
>> >> > +        }
>> >> 
>> >> Can we have block migration active with postcopy?  I would assume that
>> >> this would get disk corruption, no?  Or if you preffer the other
>> >> question, what protects us from disk corruption?
>> >
>> > I think you can, I've not tried it; however I also think it should
>> > be safe.
>> >
>> >  migration/block.c's block_save_pending always puts a value in the
>> > non_postcopiable_pending return value (and 0 in the postcopiable_pending);
>> > the migrate thread checks the non_postcopiable_pending size to
>> > decide when it can switch to postcopy, and performs a call to the complete
>> > method on each device before it does.  Thus the block migration should
>> > be finished before we start doing the actual postcopy stage, and thus
>> > before the destination CPU starts running.
>> 
>> I mean that as it is right now, the info under blk_mig_active() check
>> would be zero/the same than before entering postcopy.
>
> Ah, yes;  would blk_mig_bytes_total/transferred still have valid values you
> would want to display, even at the end of the block migration phase?
>
>> >
>> > A possibly harder question is what happens if block.c did implement
>> > postcopy and you had both block postcopy and ram postcopy active at
>> > the same time; again I think it should work but I'm not sure if one
>> > would starve the other.
>> >
>> >> Once here, I guess we can get the migrate_already_active() bit without
>> >> problem?
>> >
>> > I'm not sure of the question here; but the idea of migration_already_active()
>> > is just to avoid all of the open-coded checks for each possible state;
>> > now we've added anothe state they were getting messy.
>> 
>> Sorry.  I mean that the migrate_already_active() bits can get in without
>> further ado.  Don't need to wait for postcopy to be integrated.
>
> Yes; do you want it split out?

Whatever is easier for you.  I mean that it can be integrated
independently of postcopy.

so, it is up to you.

Juan.

>
> Dave
>
>> 
>> >
>> > Dave
>> >
>> >> 
>> >> Later, Juan.
>> > --
>> > Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK
> --
> Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK
Amit Shah July 21, 2015, 10:33 a.m. UTC | #6
On (Tue) 16 Jun 2015 [11:26:36], Dr. David Alan Gilbert (git) wrote:

> -    if (s->state == MIGRATION_STATUS_ACTIVE ||
> -        s->state == MIGRATION_STATUS_SETUP) {
> +    if (migration_already_active(s)) {

(I know, not introduced here, but:)

A better name is migration_is_active()

> +bool migration_postcopy_phase(MigrationState *s)
> +{
> +    return (s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE);
> +}

And this is better named migration_in_postcopy()

otherwise,

Reviewed-by: Amit Shah <amit.shah@redhat.com>



		Amit
Dr. David Alan Gilbert Sept. 23, 2015, 5:04 p.m. UTC | #7
* Amit Shah (amit.shah@redhat.com) wrote:
> On (Tue) 16 Jun 2015 [11:26:36], Dr. David Alan Gilbert (git) wrote:
> 
> > -    if (s->state == MIGRATION_STATUS_ACTIVE ||
> > -        s->state == MIGRATION_STATUS_SETUP) {
> > +    if (migration_already_active(s)) {
> 
> (I know, not introduced here, but:)
> 
> A better name is migration_is_active()

Done.

> 
> > +bool migration_postcopy_phase(MigrationState *s)
> > +{
> > +    return (s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE);
> > +}
> 
> And this is better named migration_in_postcopy()

Done

> 
> otherwise,
> 
> Reviewed-by: Amit Shah <amit.shah@redhat.com>
> 
> 
> 
> 		Amit
--
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK
diff mbox

Patch

diff --git a/include/migration/migration.h b/include/migration/migration.h
index e973490..2a22381 100644
--- a/include/migration/migration.h
+++ b/include/migration/migration.h
@@ -154,6 +154,8 @@  MigrationState *migrate_init(const MigrationParams *params);
 bool migration_in_setup(MigrationState *);
 bool migration_has_finished(MigrationState *);
 bool migration_has_failed(MigrationState *);
+/* True if outgoing migration has entered postcopy phase */
+bool migration_postcopy_phase(MigrationState *);
 MigrationState *migrate_get_current(void);
 
 void migrate_compress_threads_create(void);
diff --git a/migration/migration.c b/migration/migration.c
index 6fc47f9..22be23e 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -288,6 +288,7 @@  static bool migration_already_active(MigrationState *ms)
 {
     switch (ms->state) {
     case MIGRATION_STATUS_ACTIVE:
+    case MIGRATION_STATUS_POSTCOPY_ACTIVE:
     case MIGRATION_STATUS_SETUP:
         return true;
 
@@ -358,6 +359,39 @@  MigrationInfo *qmp_query_migrate(Error **errp)
 
         get_xbzrle_cache_stats(info);
         break;
+    case MIGRATION_STATUS_POSTCOPY_ACTIVE:
+        /* Mostly the same as active; TODO add some postcopy stats */
+        info->has_status = true;
+        info->has_total_time = true;
+        info->total_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME)
+            - s->total_time;
+        info->has_expected_downtime = true;
+        info->expected_downtime = s->expected_downtime;
+        info->has_setup_time = true;
+        info->setup_time = s->setup_time;
+
+        info->has_ram = true;
+        info->ram = g_malloc0(sizeof(*info->ram));
+        info->ram->transferred = ram_bytes_transferred();
+        info->ram->remaining = ram_bytes_remaining();
+        info->ram->total = ram_bytes_total();
+        info->ram->duplicate = dup_mig_pages_transferred();
+        info->ram->skipped = skipped_mig_pages_transferred();
+        info->ram->normal = norm_mig_pages_transferred();
+        info->ram->normal_bytes = norm_mig_bytes_transferred();
+        info->ram->dirty_pages_rate = s->dirty_pages_rate;
+        info->ram->mbps = s->mbps;
+
+        if (blk_mig_active()) {
+            info->has_disk = true;
+            info->disk = g_malloc0(sizeof(*info->disk));
+            info->disk->transferred = blk_mig_bytes_transferred();
+            info->disk->remaining = blk_mig_bytes_remaining();
+            info->disk->total = blk_mig_bytes_total();
+        }
+
+        get_xbzrle_cache_stats(info);
+        break;
     case MIGRATION_STATUS_COMPLETED:
         get_xbzrle_cache_stats(info);
 
@@ -399,8 +433,7 @@  void qmp_migrate_set_capabilities(MigrationCapabilityStatusList *params,
     MigrationState *s = migrate_get_current();
     MigrationCapabilityStatusList *cap;
 
-    if (s->state == MIGRATION_STATUS_ACTIVE ||
-        s->state == MIGRATION_STATUS_SETUP) {
+    if (migration_already_active(s)) {
         error_set(errp, QERR_MIGRATION_ACTIVE);
         return;
     }
@@ -531,7 +564,8 @@  static void migrate_fd_cleanup(void *opaque)
         s->file = NULL;
     }
 
-    assert(s->state != MIGRATION_STATUS_ACTIVE);
+    assert((s->state != MIGRATION_STATUS_ACTIVE) &&
+           (s->state != MIGRATION_STATUS_POSTCOPY_ACTIVE));
 
     if (s->state != MIGRATION_STATUS_COMPLETED) {
         qemu_savevm_state_cancel();
@@ -566,8 +600,7 @@  static void migrate_fd_cancel(MigrationState *s)
 
     do {
         old_state = s->state;
-        if (old_state != MIGRATION_STATUS_SETUP &&
-            old_state != MIGRATION_STATUS_ACTIVE) {
+        if (!migration_already_active(s)) {
             break;
         }
         migrate_set_state(s, old_state, MIGRATION_STATUS_CANCELLING);
@@ -611,6 +644,11 @@  bool migration_has_failed(MigrationState *s)
             s->state == MIGRATION_STATUS_FAILED);
 }
 
+bool migration_postcopy_phase(MigrationState *s)
+{
+    return (s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE);
+}
+
 MigrationState *migrate_init(const MigrationParams *params)
 {
     MigrationState *s = migrate_get_current();
@@ -692,8 +730,7 @@  void qmp_migrate(const char *uri, bool has_blk, bool blk,
     params.blk = has_blk && blk;
     params.shared = has_inc && inc;
 
-    if (s->state == MIGRATION_STATUS_ACTIVE ||
-        s->state == MIGRATION_STATUS_SETUP ||
+    if (migration_already_active(s) ||
         s->state == MIGRATION_STATUS_CANCELLING) {
         error_set(errp, QERR_MIGRATION_ACTIVE);
         return;
@@ -1041,7 +1078,10 @@  static void *migration_thread(void *opaque)
     s->setup_time = qemu_clock_get_ms(QEMU_CLOCK_HOST) - setup_start;
     migrate_set_state(s, MIGRATION_STATUS_SETUP, MIGRATION_STATUS_ACTIVE);
 
-    while (s->state == MIGRATION_STATUS_ACTIVE) {
+    trace_migration_thread_setup_complete();
+
+    while (s->state == MIGRATION_STATUS_ACTIVE ||
+           s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE) {
         int64_t current_time;
         uint64_t pending_size;
 
diff --git a/qapi-schema.json b/qapi-schema.json
index b0177bb..5e2f487 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -424,6 +424,8 @@ 
 #
 # @active: in the process of doing migration.
 #
+# @postcopy-active: like active, but now in postcopy mode. (since 2.4)
+#
 # @completed: migration is finished.
 #
 # @failed: some error occurred during migration process.
@@ -433,7 +435,7 @@ 
 ##
 { 'enum': 'MigrationStatus',
   'data': [ 'none', 'setup', 'cancelling', 'cancelled',
-            'active', 'completed', 'failed' ] }
+            'active', 'postcopy-active', 'completed', 'failed' ] }
 
 ##
 # @MigrationInfo
diff --git a/trace-events b/trace-events
index 339eb71..72e9889 100644
--- a/trace-events
+++ b/trace-events
@@ -1421,6 +1421,7 @@  migrate_fd_error(void) ""
 migrate_fd_cancel(void) ""
 migrate_pending(uint64_t size, uint64_t max, uint64_t post, uint64_t nonpost) "pending size %" PRIu64 " max %" PRIu64 " (post=%" PRIu64 " nonpost=%" PRIu64 ")"
 migrate_send_rp_message(int msg_type, uint16_t len) "%d: len %d"
+migration_thread_setup_complete(void) ""
 open_return_path_on_source(void) ""
 open_return_path_on_source_continue(void) ""
 source_return_path_thread_bad_end(void) ""