On Fri, Oct 02, 2026 at 01:02:55PM +0200, Denis V. Lunev wrote: > 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.
This in general looks reasonable, only some question to impl details, > > 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; Can we just move main_thread_load_event post to be run_bh? AFAIU that's the last thing postcopy does anyway, then the bh does whatever offloaded later. IOW, my read is the new run event should always be posted after the main_thread_load, then we don't need two. > > /* 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) { Why the condition, rather than always waiting for it? If it happened already, the wait() will consume the posted. Thanks, > + 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 > -- Peter Xu
