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(&current_incoming->rp_mutex);
>      qemu_mutex_init(&current_incoming->postcopy_prio_thread_mutex);
>      qemu_event_init(&current_incoming->main_thread_load_event, false);
> +    qemu_event_init(&current_incoming->postcopy_run_event, false);
>      qemu_sem_init(&current_incoming->postcopy_pause_sem_dst, 0);
>      qemu_sem_init(&current_incoming->postcopy_pause_sem_fault, 0);
>      qemu_sem_init(&current_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


Reply via email to