Devices register their own callbacks to MIG_EVENT_* notifications and process them accordingly. But some devices, especially during CPR-style migration, might need to be aware and process SETUP event earlier on. One such example is vhost-based devs, as they need to call RESET_OWNER ioctl before the target (in the cpr-transfer case) tries to call SET_OWNER.
Let's move SETUP notification to an earlier place in qmp_migrate(). Before this change, SETUP was firing shortly before migration thread creation, and FAILED notification balancing it was only fired from migration_iteration_finish() (ran in the thread). But now by moving SETUP to earlier we widen the window. Hence let's also add FAILED to migration_connect_error_propagate() and the special CPR-related case in migration_cancel(), to balance things out. This patch is a rework of the change originally proposed by Steve and Ben at [0]. [0] https://lore.kernel.org/qemu-devel/[email protected] Originally-by: Steve Sistare <[email protected]> Originally-by: Ben Chaney <[email protected]> Signed-off-by: Andrey Drobyshev <[email protected]> --- migration/migration.c | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 074d3f2c697..20eff9dbdcb 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -1471,6 +1471,13 @@ void migration_connect_error_propagate(MigrationState *s, Error *error) migrate_error_propagate(s, error); if (!resume) { + /* + * Emit FAILED for every pre-thread failure/cancel that ends up here, + * to balance the SETUP sent in qmp_migrate(). + */ + if (migration_has_failed(s)) { + migration_call_notifiers(MIG_EVENT_FAILED, NULL); + } migration_cleanup(s); } } @@ -1530,6 +1537,12 @@ void migration_cancel(void) if (cpr_transfer_source_active(s)) { assert(migrate_mode() == MIG_MODE_CPR_TRANSFER); assert(setup && !s->to_dst_file); + /* + * This path bypasses both migration_iteration_finish() and + * migration_connect_error_propagate(), so emit FAILED here to balance + * the SETUP sent in qmp_migrate(), before the VM is resumed. + */ + migration_call_notifiers(MIG_EVENT_FAILED, NULL); migration_cleanup(s); /* Now all things should have been released */ assert(!cpr_transfer_source_active(s)); @@ -2117,6 +2130,12 @@ void qmp_migrate(const char *uri, bool has_channels, */ Error *local_err = NULL; + /* Notify before starting migration thread and before starting CPR */ + if (!(has_resume && resume) && + migration_call_notifiers(MIG_EVENT_SETUP, &local_err)) { + goto out; + } + if (!cpr_state_save(cpr_ch, &local_err)) { goto out; } @@ -3893,11 +3912,6 @@ void migration_start_outgoing(MigrationState *s) } else { /* This is a fresh new migration */ rate_limit = migrate_max_bandwidth(); - - /* Notify before starting migration thread */ - if (migration_call_notifiers(MIG_EVENT_SETUP, &local_err)) { - goto fail; - } } migration_rate_set(rate_limit); -- 2.47.1
