diff mbox series

[V4,02/11] migration: preserve suspended runstate

Message ID 1693333086-392798-3-git-send-email-steven.sistare@oracle.com
State New
Headers show
Series fix migration of suspended runstate | expand

Commit Message

Steve Sistare Aug. 29, 2023, 6:17 p.m. UTC
A guest that is migrated in the suspended state automaticaly wakes and
continues execution.  This is wrong; the guest should end migration in
the same state it started.  The root cause is that the outgoing migration
code automatically wakes the guest, then saves the RUNNING runstate in
global_state_store(), hence the incoming migration code thinks the guest is
running and continues the guest if autostart is true.

On the outgoing side, do not call qemu_system_wakeup_request().

On the incoming side for precopy, prepare to start the vm, but do not
yet start it.  A future system_wakeup will cause the main loop to resume
the VCPUs.

On the incoming side for postcopy, do not wake the guest, and apply the
the same logic as found in precopy: if autostart and the runstate is
RUNNING, then vm_start, else prepare to start the vm.

Signed-off-by: Steve Sistare <steven.sistare@oracle.com>
Reviewed-by: Fabiano Rosas <farosas@suse.de>
---
 migration/migration.c |  4 ++--
 migration/savevm.c    | 15 ++++++++++-----
 2 files changed, 12 insertions(+), 7 deletions(-)

Comments

Peter Xu Aug. 30, 2023, 4:07 p.m. UTC | #1
On Tue, Aug 29, 2023 at 11:17:57AM -0700, Steve Sistare wrote:
> A guest that is migrated in the suspended state automaticaly wakes and
> continues execution.  This is wrong; the guest should end migration in
> the same state it started.  The root cause is that the outgoing migration
> code automatically wakes the guest, then saves the RUNNING runstate in
> global_state_store(), hence the incoming migration code thinks the guest is
> running and continues the guest if autostart is true.
> 
> On the outgoing side, do not call qemu_system_wakeup_request().
> 
> On the incoming side for precopy, prepare to start the vm, but do not
> yet start it.  A future system_wakeup will cause the main loop to resume
> the VCPUs.
> 
> On the incoming side for postcopy, do not wake the guest, and apply the
> the same logic as found in precopy: if autostart and the runstate is
> RUNNING, then vm_start, else prepare to start the vm.
> 
> Signed-off-by: Steve Sistare <steven.sistare@oracle.com>
> Reviewed-by: Fabiano Rosas <farosas@suse.de>

Reviewed-by: Peter Xu <peterx@redhat.com>
diff mbox series

Patch

diff --git a/migration/migration.c b/migration/migration.c
index 5528acb..5bcc761 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -496,6 +496,8 @@  static void process_incoming_migration_bh(void *opaque)
     } else if (migration_incoming_colo_enabled()) {
         migration_incoming_disable_colo();
         vm_start();
+    } else if (global_state_get_runstate() == RUN_STATE_SUSPENDED) {
+        vm_prepare_start(false, global_state_get_runstate());
     } else {
         runstate_set(global_state_get_runstate());
     }
@@ -2109,7 +2111,6 @@  static int postcopy_start(MigrationState *ms, Error **errp)
     qemu_mutex_lock_iothread();
     trace_postcopy_start_set_run();
 
-    qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL);
     global_state_store();
     ret = vm_stop_force_state(RUN_STATE_FINISH_MIGRATE);
     if (ret < 0) {
@@ -2315,7 +2316,6 @@  static void migration_completion(MigrationState *s)
     if (s->state == MIGRATION_STATUS_ACTIVE) {
         qemu_mutex_lock_iothread();
         s->downtime_start = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
-        qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL);
 
         s->vm_old_state = runstate_get();
         global_state_store();
diff --git a/migration/savevm.c b/migration/savevm.c
index a2cb885..bae0a1a 100644
--- a/migration/savevm.c
+++ b/migration/savevm.c
@@ -2070,12 +2070,17 @@  static void loadvm_postcopy_handle_run_bh(void *opaque)
 
     dirty_bitmap_mig_before_vm_start();
 
-    if (autostart) {
-        /* Hold onto your hats, starting the CPU */
-        vm_start();
+    if (!global_state_received() ||
+        global_state_get_runstate() == RUN_STATE_RUNNING) {
+        if (autostart) {
+            vm_start();
+        } else {
+            runstate_set(RUN_STATE_PAUSED);
+        }
+    } else if (global_state_get_runstate() == RUN_STATE_SUSPENDED) {
+        vm_prepare_start(false, RUN_STATE_SUSPENDED);
     } else {
-        /* leave it paused and let management decide when to start the CPU */
-        runstate_set(RUN_STATE_PAUSED);
+        runstate_set(global_state_get_runstate());
     }
 
     qemu_bh_delete(mis->bh);