migration: Implement switchover ack logic

Implement switchover ack logic. This prevents the source from stopping
the VM and completing the migration until an ACK is received from the
destination that it's OK to do so.

To achieve this, a new SaveVMHandlers handler switchover_ack_needed()
and a new return path message MIG_RP_MSG_SWITCHOVER_ACK are added.

The switchover_ack_needed() handler is called during migration setup in
the destination to check if switchover ack is used by the migrated
device.

When switchover is approved by all migrated devices in the destination
that support this capability, the MIG_RP_MSG_SWITCHOVER_ACK return path
message is sent to the source to notify it that it's OK to do
switchover.

Signed-off-by: Avihai Horon <avihaih@nvidia.com>
Reviewed-by: Peter Xu <peterx@redhat.com>
Tested-by: YangHang Liu <yanghliu@redhat.com>
Acked-by: Alex Williamson <alex.williamson@redhat.com>
Signed-off-by: Cédric Le Goater <clg@redhat.com>
This commit is contained in:
Avihai Horon 2023-06-21 14:11:55 +03:00 committed by Cédric Le Goater
parent 6574232fff
commit 1b4adb10f8
6 changed files with 104 additions and 2 deletions

View file

@ -2360,6 +2360,21 @@ static int loadvm_process_command(QEMUFile *f)
error_report("CMD_OPEN_RETURN_PATH failed");
return -1;
}
/*
* Switchover ack is enabled but no device uses it, so send an ACK to
* source that it's OK to switchover. Do it here, after return path has
* been created.
*/
if (migrate_switchover_ack() && !mis->switchover_ack_pending_num) {
int ret = migrate_send_rp_switchover_ack(mis);
if (ret) {
error_report(
"Could not send switchover ack RP MSG, err %d (%s)", ret,
strerror(-ret));
return ret;
}
}
break;
case MIG_CMD_PING:
@ -2586,6 +2601,23 @@ static int qemu_loadvm_state_header(QEMUFile *f)
return 0;
}
static void qemu_loadvm_state_switchover_ack_needed(MigrationIncomingState *mis)
{
SaveStateEntry *se;
QTAILQ_FOREACH(se, &savevm_state.handlers, entry) {
if (!se->ops || !se->ops->switchover_ack_needed) {
continue;
}
if (se->ops->switchover_ack_needed(se->opaque)) {
mis->switchover_ack_pending_num++;
}
}
trace_loadvm_state_switchover_ack_needed(mis->switchover_ack_pending_num);
}
static int qemu_loadvm_state_setup(QEMUFile *f)
{
SaveStateEntry *se;
@ -2789,6 +2821,10 @@ int qemu_loadvm_state(QEMUFile *f)
return -EINVAL;
}
if (migrate_switchover_ack()) {
qemu_loadvm_state_switchover_ack_needed(mis);
}
cpu_synchronize_all_pre_loadvm();
ret = qemu_loadvm_state_main(f, mis);
@ -2862,6 +2898,24 @@ int qemu_load_device_state(QEMUFile *f)
return 0;
}
int qemu_loadvm_approve_switchover(void)
{
MigrationIncomingState *mis = migration_incoming_get_current();
if (!mis->switchover_ack_pending_num) {
return -EINVAL;
}
mis->switchover_ack_pending_num--;
trace_loadvm_approve_switchover(mis->switchover_ack_pending_num);
if (mis->switchover_ack_pending_num) {
return 0;
}
return migrate_send_rp_switchover_ack(mis);
}
bool save_snapshot(const char *name, bool overwrite, const char *vmstate,
bool has_devices, strList *devices, Error **errp)
{