@samitouri / QOSamiQemu / commits / 54a814cc09

migration: Fix calculation of expected_downtime to take VFIO info

QEMU will provide an expected downtime for the whole system during migration, by remembering the total dirty RAM that we synced the last time, divides the estimated switchover bandwidth. That was flawed when VFIO is taking into account: consider there is a VFIO GPU device that contains GBs of data to migrate during stop phase. Those will not be accounted in this math. Fix it by updating dirty_bytes_last_sync properly only when we go to the next iteration, rather than hide this update in the RAM code. Meanwhile, fetch the total (rather than RAM-only) portion of dirty bytes, so as to include GPU device states too. Update the comment of the field to reflect its new meaning. Now after this change, the expected-downtime to be read from query-migrate should be very accurate even with VFIO devices involved. Tested-by: Cédric Le Goater <clg@redhat.com> Reviewed-by: Juraj Marcin <jmarcin@redhat.com> Link: https://lore.kernel.org/r/20260421202110.306051-13-peterx@redhat.com Signed-off-by: Peter Xu <peterx@redhat.com>

Peter Xu committed Apr 21, 2026 at 16:21 UTC 54a814cc09479b3b60f9afad4f4afe17d541d53c
3 files changed +11 -9
migration/migration-stats.h
+3 -5
@@ -31,11 +31,9 @@
31 */
32 typedef struct {
33 /*
34 - * Number of bytes that were dirty last time that we synced with
35 - * the guest memory. We use that to calculate the downtime. As
36 - * the remaining dirty amounts to what we know that is still dirty
37 - * since last iteration, not counting what the guest has dirtied
38 - * since we synchronized bitmaps.
34 + * Number of bytes that were reported dirty after the latest
35 + * system-wise synchronization of dirty information. It is used to do
36 + * best-effort estimation on expected downtime.
37 */
38 uint64_t dirty_bytes_last_sync;
39 /*
migration/migration.c
+8 -3
@@ -3244,18 +3244,23 @@ static void migration_iteration_go_next(MigPendingData *pending)
3244 */
3245 qemu_savevm_query_pending(pending, true);
3246
3247 + /*
3248 + * Update the dirty information for the whole system for this
3249 + * iteration. This value is used to calculate expected downtime.
3250 + */
3251 + qatomic_set(&mig_stats.dirty_bytes_last_sync, pending->total_bytes);
3252 +
3253 /*
3254 * Boost dirty sync count to reflect we finished one iteration.
3255 *
3256 * NOTE: we need to make sure when this happens (together with the
3257 * event sent below) all modules have slow-synced the pending data
3252 - * above. That means a write mem barrier, but qatomic_add() should be
3253 - * enough.
3258 + * above and updated corresponding fields (e.g. dirty_bytes_last_sync).
3259 *
3260 * It's because a mgmt could wait on the iteration event to query again
3261 * on pending data for policy changes (e.g. downtime adjustments). The
3262 * ordering will make sure the query will fetch the latest results from
3258 - * all the modules.
3263 + * all the modules on everything.
3264 */
3265 qatomic_add(&mig_stats.dirty_sync_count, 1);
3266
migration/ram.c
-1
@@ -1148,7 +1148,6 @@ static void migration_bitmap_sync(RAMState *rs, bool last_stage)
1148 RAMBLOCK_FOREACH_NOT_IGNORED(block) {
1149 ramblock_sync_dirty_bitmap(rs, block);
1150 }
1151 - qatomic_set(&mig_stats.dirty_bytes_last_sync, ram_bytes_remaining());
1151 }
1152 }
1153