vfio/migration: Check VFIO_PRECOPY_INFO_REINIT during switchover

VFIO_REPCOPY_INFO_REINIT is checked only during precopy, before the
switchover decision. However, the switchover decision and guest stop are
not atomic, so a VFIO device may want to set VFIO_PRECOPY_INFO_REINIT
and request another switchover ACK in the gap after switchover decision
has been made but before the guest is stopped. This would be missed and
may increase downtime.

Solve this by checking if VFIO_PRECOPY_INFO_REINIT was set during that
gap, and request a new switchover-ack in the final save_state_pending
call. Query precopy info after vCPUs are stopped but before
transitioning from PRE_COPY state, when its valid to call the ioctl.

Acked-by: Peter Xu <peterx@redhat.com>
Signed-off-by: Avihai Horon <avihaih@nvidia.com>
Link: https://lore.kernel.org/qemu-devel/20260706085211.13905-14-avihaih@nvidia.com
Signed-off-by: Cédric Le Goater <clg@redhat.com>
This commit is contained in:
Avihai Horon
2026-07-06 11:52:08 +03:00
committed by Cédric Le Goater
parent 5fcadd7d14
commit 941f269923

View File

@@ -685,6 +685,9 @@ static void vfio_state_pending(void *opaque, MigPendingData *pending,
* The final pending query runs during switchover downtime. VFIO does not
* need a fresh device pending-data query then to get the latest dirty
* data, so avoid the extra work and report the cached counters below.
* On the other hand, precopy sync is needed to check if switchover ACK was
* requested, but that's already done during guest stop when device is in
* PRE_COPY state.
*/
if (exact && !final) {
vfio_state_pending_sync(vbasedev);
@@ -964,6 +967,26 @@ static const SaveVMHandlers savevm_vfio_handlers = {
/* ---------------------------------------------------------------------- */
static void vfio_final_precopy_reinit_check(VFIODevice *vbasedev)
{
VFIOMigration *migration = vbasedev->migration;
int ret;
if (!migration->precopy_info_v2_used || !migrate_switchover_ack() ||
migrate_switchover_ack_legacy()) {
return;
}
ret = vfio_query_precopy_size(migration);
if (ret) {
error_report("%s: Final precopy reinit check failed (err: %d)",
vbasedev->name, ret);
/* If query failed, assume reinit and request switchover-ack */
migration->request_switchover_ack = true;
migration->initial_data_sent = false;
}
}
static void vfio_vmstate_change_prepare(void *opaque, bool running,
RunState state)
{
@@ -977,6 +1000,15 @@ static void vfio_vmstate_change_prepare(void *opaque, bool running,
VFIO_DEVICE_STATE_PRE_COPY_P2P :
VFIO_DEVICE_STATE_RUNNING_P2P;
if (migration->device_state == VFIO_DEVICE_STATE_PRE_COPY) {
/*
* Now that vCPUs are stopped, check if new init_bytes are available
* since switchover decision, to be reported in the final
* save_query_pending.
*/
vfio_final_precopy_reinit_check(vbasedev);
}
ret = vfio_migration_set_state_or_reset(vbasedev, new_state, &local_err);
if (ret) {
/*