From: Denis V. Lunev <[email protected]> The postcopy listen thread waits for main_thread_load_event before it reports COMPLETED. That event is set as soon as qemu_loadvm_state_main() returns in the main thread, while the run state is changed later, in loadvm_postcopy_handle_run_bh(). The listen thread can therefore report COMPLETED while the VM is still in RUN_STATE_INMIGRATE, easily so in a bitmap-only postcopy where it has little to load.
Management reacting to COMPLETED then finds the VM still incoming: a blockdev-add creates an inactive node, since bds_tree_init() forces BDRV_O_INACTIVE in RUN_STATE_INMIGRATE, and attaching a device to it fails with "Block node is read-only". Precopy gets this right: process_incoming_migration_bh() changes the run state before it reports COMPLETED. Set an event at the end of the run BH and wait for it in the listen thread, unless loading failed before CMD_POSTCOPY_RUN and the BH was never scheduled. Signed-off-by: Denis V. Lunev <[email protected]> Cc: Peter Xu <[email protected]> Cc: Fabiano Rosas <[email protected]> Cc: Juraj Marcin <[email protected]> --- migration/migration.c | 2 ++ migration/migration.h | 2 ++ migration/postcopy-ram.c | 4 ++++ migration/savevm.c | 1 + 4 files changed, 9 insertions(+) diff --git a/migration/migration.c b/migration/migration.c index fab7949fe1..8944acf3cd 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -324,6 +324,7 @@ void migration_object_init(void) qemu_mutex_init(¤t_incoming->rp_mutex); qemu_mutex_init(¤t_incoming->postcopy_prio_thread_mutex); qemu_event_init(¤t_incoming->main_thread_load_event, false); + qemu_event_init(¤t_incoming->postcopy_run_event, false); qemu_sem_init(¤t_incoming->postcopy_pause_sem_dst, 0); qemu_sem_init(¤t_incoming->postcopy_pause_sem_fault, 0); qemu_sem_init(¤t_incoming->postcopy_pause_sem_fast_load, 0); @@ -493,6 +494,7 @@ void migration_incoming_state_destroy(void) migration_incoming_transport_cleanup(mis); qemu_event_reset(&mis->main_thread_load_event); + qemu_event_reset(&mis->postcopy_run_event); if (mis->page_requested) { g_tree_destroy(mis->page_requested); diff --git a/migration/migration.h b/migration/migration.h index 683bc7bdd5..c65212ff63 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -108,6 +108,8 @@ struct MigrationIncomingState { * loading state. */ QemuEvent main_thread_load_event; + /* Set once the postcopy run BH has changed the run state */ + QemuEvent postcopy_run_event; /* For network announces */ AnnounceTimer announce_timer; diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index 5509cbdcb8..fd2c139cfe 100644 --- a/migration/postcopy-ram.c +++ b/migration/postcopy-ram.c @@ -2443,6 +2443,10 @@ static void *postcopy_listen_thread(void *opaque) * state yet; wait for the end of the main thread. */ qemu_event_wait(&mis->main_thread_load_event); + /* The VM run state is changed later, in loadvm_postcopy_handle_run_bh() */ + if (postcopy_state_get() == POSTCOPY_INCOMING_RUNNING) { + qemu_event_wait(&mis->postcopy_run_event); + } /* * Device load in the main thread has finished, we should be in diff --git a/migration/savevm.c b/migration/savevm.c index 4b590ea672..5ec09d4889 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -2263,6 +2263,7 @@ static void loadvm_postcopy_handle_run_bh(void *opaque) } trace_vmstate_downtime_checkpoint("dst-postcopy-bh-vm-started"); + qemu_event_set(&mis->postcopy_run_event); } /* After all discards we can start running and asking for pages */ base-commit: f7ada39edacaa5c26b30e98b94017b0b2ccbcf94 -- 2.53.0
