@samitouri / QOSamiQemu / commits / 9519d3667a

migration: Move iteration counter out of RAM

It used to hide in RAM dirty sync path. Now with more modules being able to slow sync on dirty information, keeping it there may not be good anymore because it's not RAM's own concept for iterations: all modules should follow. More importantly, mgmt may try to query dirty info (to make policy decisions like adjusting downtime) by listening to iteration count changes via QMP events. So we must make sure the boost of iterations only happens _after_ the dirty sync operations with whatever form (RAM's dirty bitmap sync, or VFIO's different ioctls to fetch latest dirty info from kernel). Move this to core migration path to manage, together with the event generation, so that it can be well ordered with the sync operations for all modules. This brings a good side effect that we should have an old issue regarding to cpu_throttle_dirty_sync_timer_tick() which can randomly boost iteration counts (because it invokes sync ops). Now it won't, which is actually the right behavior. Said that, we have code (not only QEMU, but likely mgmt too) assuming the 1st iteration will always shows dirty count to 1. Make it initialized with 1 this time, because we'll miss the dirty sync for setup() on boosting this counter now. Reviewed-by: Hyman Huang <yong.huang@smartx.com> Reviewed-by: Prasad Pandit <pjp@fedoraproject.org> Reviewed-by: Juraj Marcin <jmarcin@redhat.com> Link: https://lore.kernel.org/r/20260421202110.306051-10-peterx@redhat.com Signed-off-by: Peter Xu <peterx@redhat.com>

Peter Xu committed Apr 21, 2026 at 16:21 UTC 9519d3667a02446d35273fd2a00d6389a2f26691
3 files changed +28 -10
migration/migration-stats.h
+2 -1
@@ -43,7 +43,8 @@ typedef struct {
43 */
44 uint64_t dirty_pages_rate;
45 /*
46 - * Number of times we have synchronized guest bitmaps.
46 + * Number of times we have synchronized guest bitmaps. This always
47 + * starts from 1 for the 1st iteration.
48 */
49 uint64_t dirty_sync_count;
50 /*
migration/migration.c
+26 -3
@@ -1654,10 +1654,15 @@ int migrate_init(MigrationState *s, Error **errp)
1654 s->threshold_size = 0;
1655 s->switchover_acked = false;
1656 s->rdma_migration = false;
1657 +
1658 /*
1658 - * set mig_stats memory to zero for a new migration
1659 + * set mig_stats memory to zero for a new migration.. except the
1660 + * iteration counter, which we want to make sure it returns 1 for the
1661 + * first iteration.
1662 */
1663 memset(&mig_stats, 0, sizeof(mig_stats));
1664 + mig_stats.dirty_sync_count = 1;
1665 +
1666 migration_reset_vfio_bytes_transferred();
1667
1668 s->postcopy_package_loaded = false;
@@ -3234,10 +3239,28 @@ static bool migration_iteration_next_ready(MigrationState *s,
3239 static void migration_iteration_go_next(MigPendingData *pending)
3240 {
3241 /*
3237 - * Do a slow sync will achieve this. TODO: move RAM iteration code
3238 - * into the core layer.
3242 + * Do a slow sync first before boosting the iteration count.
3243 */
3244 qemu_savevm_query_pending(pending, true);
3245 +
3246 + /*
3247 + * Boost dirty sync count to reflect we finished one iteration.
3248 + *
3249 + * NOTE: we need to make sure when this happens (together with the
3250 + * event sent below) all modules have slow-synced the pending data
3251 + * above. That means a write mem barrier, but qatomic_add() should be
3252 + * enough.
3253 + *
3254 + * It's because a mgmt could wait on the iteration event to query again
3255 + * on pending data for policy changes (e.g. downtime adjustments). The
3256 + * ordering will make sure the query will fetch the latest results from
3257 + * all the modules.
3258 + */
3259 + qatomic_add(&mig_stats.dirty_sync_count, 1);
3260 +
3261 + if (migrate_events()) {
3262 + qapi_event_send_migration_pass(mig_stats.dirty_sync_count);
3263 + }
3264 }
3265
3266 static bool postcopy_should_start(MigrationState *s, MigPendingData *pending)
migration/ram.c
-6
@@ -1136,8 +1136,6 @@ static void migration_bitmap_sync(RAMState *rs, bool last_stage)
1136 RAMBlock *block;
1137 int64_t end_time;
1138
1139 - qatomic_add(&mig_stats.dirty_sync_count, 1);
1140 -
1139 if (!rs->time_last_bitmap_sync) {
1140 rs->time_last_bitmap_sync = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
1141 }
@@ -1172,10 +1170,6 @@ static void migration_bitmap_sync(RAMState *rs, bool last_stage)
1170 rs->num_dirty_pages_period = 0;
1171 rs->bytes_xfer_prev = migration_transferred_bytes();
1172 }
1175 - if (migrate_events()) {
1176 - uint64_t generation = qatomic_read(&mig_stats.dirty_sync_count);
1177 - qapi_event_send_migration_pass(generation);
1178 - }
1173 }
1174
1175 void migration_bitmap_sync_precopy(bool last_stage)