@samitouri / QOSamiQemu / commits / 1ae7d9c30d

migration: Fix blocking in POSTCOPY_DEVICE during package load

The package_loaded event is not set in case MIG_RP_MSG_PONG does not arrive on the source from the destination in the return path thread. The migration thread would then be blocked waiting for package_loaded event indefinitely in POSTCOPY_DEVICE state. Where as, in such a condition the source VM can safely resume as the destination has not yet started. The pong message can get lost in case of a network failure or destination crash before sending the pong. This patch removes the package_loaded event and uses rp_sem, instead of kicking multiple events. The error is detected in case of network failure or destination crash and rp_sem is set in the out path of the return path thread. This will kick the migration thread out from a condition of indefinitely waiting for rp_sem. The migration thread then fails early and breaks from the migration loop to resume the vm on the source side. Fixes: 7b842fe354c6 ("migration: Introduce POSTCOPY_DEVICE state") Signed-off-by: Pranav Tyagi <prtyagi@redhat.com> Reviewed-by: Juraj Marcin <jmarcin@redhat.com> Reviewed-by: Peter Xu <peterx@redhat.com> Link: https://lore.kernel.org/r/20260423094438.43556-1-prtyagi@redhat.com Signed-off-by: Peter Xu <peterx@redhat.com>

Pranav Tyagi committed Apr 23, 2026 at 15:14 UTC 1ae7d9c30df1ceffc00b243ccf55fbe0a1ce1667
2 files changed +31 -18
migration/migration.c
+31 -17
@@ -1661,7 +1661,6 @@ int migrate_init(MigrationState *s, Error **errp)
1661 migration_reset_vfio_bytes_transferred();
1662
1663 s->postcopy_package_loaded = false;
1664 - qemu_event_reset(&s->postcopy_package_loaded_event);
1664
1665 return 0;
1666 }
@@ -2317,7 +2316,7 @@ static void *source_return_path_thread(void *opaque)
2316 if (tmp32 == QEMU_VM_PING_PACKAGED_LOADED) {
2317 trace_source_return_path_thread_postcopy_package_loaded();
2318 ms->postcopy_package_loaded = true;
2320 - qemu_event_set(&ms->postcopy_package_loaded_event);
2319 + migration_rp_kick(ms);
2320 }
2321 break;
2322
@@ -2388,16 +2387,21 @@ out:
2387 trace_source_return_path_thread_bad_end();
2388 }
2389
2391 - if (ms->state == MIGRATION_STATUS_POSTCOPY_RECOVER) {
2390 + if (ms->state == MIGRATION_STATUS_POSTCOPY_RECOVER ||
2391 + ms->state == MIGRATION_STATUS_POSTCOPY_DEVICE) {
2392 /*
2393 - * this will be extremely unlikely: that we got yet another network
2394 - * issue during recovering of the 1st network failure.. during this
2395 - * period the main migration thread can be waiting on rp_sem for
2396 - * this thread to sync with the other side.
2393 + * The migration thread can get stuck waiting for rp_sem if the
2394 + * return path fails to sync with the destination. This handles
2395 + * two specific cases:
2396 *
2398 - * When this happens, explicitly kick the migration thread out of
2399 - * RECOVER stage and back to PAUSED, so the admin can try
2400 - * everything again.
2397 + * POSTCOPY_RECOVER: A failure occurs during a recovery attempt.
2398 + * We kick the migration thread back to PAUSED so the admin can
2399 + * retry.
2400 + *
2401 + * POSTCOPY_DEVICE: The MIG_RP_MSG_PONG is lost due to a
2402 + * network failure or destination crash. We kick the migration
2403 + * thread out of its wait so it can fail the migration and safely
2404 + * resume the VM on the source.
2405 */
2406 migration_rp_kick(ms);
2407 }
@@ -3226,12 +3230,24 @@ static MigIterateState migration_iteration_run(MigrationState *s)
3230 if (s->state == MIGRATION_STATUS_POSTCOPY_DEVICE &&
3231 (s->postcopy_package_loaded || complete_ready)) {
3232 /*
3229 - * If package has been loaded, the event is set and we will
3230 - * immediatelly transition to POSTCOPY_ACTIVE. If we are ready for
3231 - * completion, we need to wait for destination to load the postcopy
3232 - * package before actually completing.
3233 + * We will immediately transition to POSTCOPY_ACTIVE.
3234 + * If we are ready for completion, we need to wait for
3235 + * destination to load the postcopy package before actually
3236 + * completing.
3237 */
3234 - qemu_event_wait(&s->postcopy_package_loaded_event);
3238 + while (!s->postcopy_package_loaded) {
3239 + if (migration_rp_wait(s)) {
3240 + /*
3241 + * Error happened. Migration thread was stuck waiting in
3242 + * POSTCOPY_DEVICE for rp_sem which was never set.
3243 + */
3244 + migrate_set_state(&s->state,
3245 + MIGRATION_STATUS_POSTCOPY_DEVICE,
3246 + MIGRATION_STATUS_FAILING);
3247 + return MIG_ITERATE_BREAK;
3248 + }
3249 + }
3250 + /* Acknowledgement received from the destination */
3251 migrate_set_state(&s->state, MIGRATION_STATUS_POSTCOPY_DEVICE,
3252 MIGRATION_STATUS_POSTCOPY_ACTIVE);
3253 }
@@ -3863,7 +3879,6 @@ static void migration_instance_finalize(Object *obj)
3879 qemu_sem_destroy(&ms->rp_state.rp_pong_acks);
3880 qemu_sem_destroy(&ms->postcopy_qemufile_src_sem);
3881 error_free(ms->error);
3866 - qemu_event_destroy(&ms->postcopy_package_loaded_event);
3882 }
3883
3884 static void migration_instance_init(Object *obj)
@@ -3885,7 +3900,6 @@ static void migration_instance_init(Object *obj)
3900 qemu_sem_init(&ms->wait_unplug_sem, 0);
3901 qemu_sem_init(&ms->postcopy_qemufile_src_sem, 0);
3902 qemu_mutex_init(&ms->qemu_file_lock);
3888 - qemu_event_init(&ms->postcopy_package_loaded_event, 0);
3903 }
3904
3905 /*
migration/migration.h
-1
@@ -512,7 +512,6 @@ struct MigrationState {
512 bool rdma_migration;
513
514 bool postcopy_package_loaded;
515 - QemuEvent postcopy_package_loaded_event;
515
516 GSource *hup_source;
517