* [PATCH 0/2] migration: include timing and RAM stats on destination when query-migrate
@ 2026-04-05 15:26 Trieu Huynh
2026-04-05 15:26 ` [PATCH 1/2] migration: track timing and received pages in MigrationIncomingState Trieu Huynh
` (2 more replies)
0 siblings, 3 replies; 20+ messages in thread
From: Trieu Huynh @ 2026-04-05 15:26 UTC (permalink / raw)
To: qemu-devel; +Cc: Trieu Huynh
From: Trieu Huynh <vikingtc4@gmail.com>
When query-migrate is called on the *destination* QEMU after a precopy
migration completes it returns only {"status": "completed"} — no timing,
no RAM statistics. The source correctly returns total-time, downtime,
setup-time, and full ram stats.
This series fixes the gap in two commits:
* commit 1: Track start/end timestamps and per-page-type counters in
MigrationIncomingState, recorded in the receive path.
* commit 2: Make source-only MigrationStats fields optional in the QAPI
schema so the destination can populate info->ram with only the fields
that are meaningful on the receive side. Fields computed from tracked
counters (transferred, normal, duplicate, mbps, pages-per-second) are
shown; source-only fields (dirty-sync-count, precopy-bytes, etc.) are
absent.
The QAPI change (commit 2) is ABI-compatible: optional fields that are
not set are simply absent from the output. Source-side callers are
unaffected because populate_ram_info() sets all optional fields via
has_* setters, so the source output is identical to before.
On dst, {"execute":"query-migrate"}
* As-is:
{
"return": {
"status": "completed"
}
* To-be:
{
"return": {
"status": "completed",
"total-time": 94,
"ram": {
"total": 554508288,
"pages-per-second": 1440234,
"page-size": 4096,
"remaining": 0,
"mbps": 97.955404255319152,
"transferred": 1150976,
"duplicate": 135101,
"normal-bytes": 1150976,
"normal": 281
}
}
}
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3151
Trieu Huynh (2):
migration: track timing and received pages in MigrationIncomingState
migration: expose RAM stats and timing on destination via
query-migrate
migration/migration.c | 37 +++++++++++++++++++++++++++++++++++++
migration/migration.h | 17 +++++++++++++++++
migration/ram.c | 3 +++
qapi/migration.json | 14 +++++++-------
4 files changed, 64 insertions(+), 7 deletions(-)
--
2.43.0
^ permalink raw reply [flat|nested] 20+ messages in thread* [PATCH 1/2] migration: track timing and received pages in MigrationIncomingState 2026-04-05 15:26 [PATCH 0/2] migration: include timing and RAM stats on destination when query-migrate Trieu Huynh @ 2026-04-05 15:26 ` Trieu Huynh 2026-04-07 12:02 ` Claudio Fontana 2026-04-05 15:26 ` [PATCH 2/2] migration: expose RAM stats and timing on destination via query-migrate Trieu Huynh 2026-04-06 14:02 ` [PATCH 0/2] migration: include timing and RAM stats on destination when query-migrate Fabiano Rosas 2 siblings, 1 reply; 20+ messages in thread From: Trieu Huynh @ 2026-04-05 15:26 UTC (permalink / raw) To: qemu-devel; +Cc: Trieu Huynh, Peter Xu, Fabiano Rosas From: Trieu Huynh <vikingtc4@gmail.com> Add start_time, total_time, received_normal_pages, received_zero_pages, and received_xbzrle_pages to MigrationIncomingState. start_time is recorded when the incoming state transitions to ACTIVE; total_time is recorded when it transitions to COMPLETED. The page counters are incremented in ram_load_precopy() as each page type is received from the source. These fields will be used by a subsequent patch to populate fill_destination_migration_info() for the migration COMPLETED case. Signed-off-by: Trieu Huynh <vikingtc4@gmail.com> --- migration/migration.c | 2 ++ migration/migration.h | 17 +++++++++++++++++ migration/ram.c | 3 +++ 3 files changed, 22 insertions(+) diff --git a/migration/migration.c b/migration/migration.c index 5c9aaa6e58..17c9a8b344 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -726,6 +726,7 @@ static void process_incoming_migration_bh(void *opaque) */ migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE, MIGRATION_STATUS_COMPLETED); + mis->total_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) - mis->start_time; migration_incoming_state_destroy(); } @@ -758,6 +759,7 @@ process_incoming_migration_co(void *opaque) migrate_set_state(&mis->state, MIGRATION_STATUS_SETUP, MIGRATION_STATUS_ACTIVE); + mis->start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); mis->loadvm_co = qemu_coroutine_self(); ret = qemu_loadvm_state(mis->from_src_file, &local_err); mis->loadvm_co = NULL; diff --git a/migration/migration.h b/migration/migration.h index b6888daced..cd51ae452c 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -176,6 +176,23 @@ struct MigrationIncomingState { /* PostCopyFD's for external userfaultfds & handlers of shared memory */ GArray *postcopy_remote_fds; + /* + * Timestamps for reporting migration duration via query-migrate on the + * destination. start_time is recorded when the state moves to ACTIVE; + * total_time is recorded when it moves to COMPLETED. + */ + int64_t start_time; + int64_t total_time; + + /* + * Page counters for reporting RAM statistics via query-migrate on the + * destination. Incremented in ram_load_precopy() as each page type + * is received. + */ + uint64_t received_normal_pages; + uint64_t received_zero_pages; + uint64_t received_xbzrle_pages; + MigrationStatus state; /* diff --git a/migration/ram.c b/migration/ram.c index 979751f61b..6646e5daad 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -4417,10 +4417,12 @@ static int ram_load_precopy(QEMUFile *f) break; } ram_handle_zero(host, TARGET_PAGE_SIZE); + mis->received_zero_pages++; break; case RAM_SAVE_FLAG_PAGE: qemu_get_buffer(f, host, TARGET_PAGE_SIZE); + mis->received_normal_pages++; break; case RAM_SAVE_FLAG_XBZRLE: @@ -4430,6 +4432,7 @@ static int ram_load_precopy(QEMUFile *f) ret = -EINVAL; break; } + mis->received_xbzrle_pages++; break; case RAM_SAVE_FLAG_MULTIFD_FLUSH: multifd_recv_sync_main(); -- 2.43.0 ^ permalink raw reply related [flat|nested] 20+ messages in thread
* Re: [PATCH 1/2] migration: track timing and received pages in MigrationIncomingState 2026-04-05 15:26 ` [PATCH 1/2] migration: track timing and received pages in MigrationIncomingState Trieu Huynh @ 2026-04-07 12:02 ` Claudio Fontana 2026-04-07 12:11 ` Claudio Fontana 0 siblings, 1 reply; 20+ messages in thread From: Claudio Fontana @ 2026-04-07 12:02 UTC (permalink / raw) To: Trieu Huynh; +Cc: Peter Xu, Fabiano Rosas, qemu-devel Hello, On 4/5/26 17:26, Trieu Huynh wrote: > From: Trieu Huynh <vikingtc4@gmail.com> > > Add start_time, total_time, received_normal_pages, > received_zero_pages, and received_xbzrle_pages to > MigrationIncomingState. > > start_time is recorded when the incoming state transitions to ACTIVE; > total_time is recorded when it transitions to COMPLETED. The page > counters are incremented in ram_load_precopy() as each page type is > received from the source. > > These fields will be used by a subsequent patch to populate > fill_destination_migration_info() for the migration COMPLETED case. > > Signed-off-by: Trieu Huynh <vikingtc4@gmail.com> > --- > migration/migration.c | 2 ++ > migration/migration.h | 17 +++++++++++++++++ > migration/ram.c | 3 +++ > 3 files changed, 22 insertions(+) > > diff --git a/migration/migration.c b/migration/migration.c > index 5c9aaa6e58..17c9a8b344 100644 > --- a/migration/migration.c > +++ b/migration/migration.c > @@ -726,6 +726,7 @@ static void process_incoming_migration_bh(void *opaque) > */ > migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE, > MIGRATION_STATUS_COMPLETED); > + mis->total_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) - mis->start_time; > migration_incoming_state_destroy(); > } > > @@ -758,6 +759,7 @@ process_incoming_migration_co(void *opaque) > migrate_set_state(&mis->state, MIGRATION_STATUS_SETUP, > MIGRATION_STATUS_ACTIVE); > > + mis->start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); > mis->loadvm_co = qemu_coroutine_self(); > ret = qemu_loadvm_state(mis->from_src_file, &local_err); > mis->loadvm_co = NULL; > diff --git a/migration/migration.h b/migration/migration.h > index b6888daced..cd51ae452c 100644 > --- a/migration/migration.h > +++ b/migration/migration.h > @@ -176,6 +176,23 @@ struct MigrationIncomingState { > /* PostCopyFD's for external userfaultfds & handlers of shared memory */ > GArray *postcopy_remote_fds; > > + /* > + * Timestamps for reporting migration duration via query-migrate on the > + * destination. start_time is recorded when the state moves to ACTIVE; > + * total_time is recorded when it moves to COMPLETED. > + */ > + int64_t start_time; > + int64_t total_time; nit: since this is a timestamp and these are directly linked to state changes, why not call these: int64_t migration_active_ts; int64_t migration_completed_ts; or something similar? Thank you, Claudio > + > + /* > + * Page counters for reporting RAM statistics via query-migrate on the > + * destination. Incremented in ram_load_precopy() as each page type > + * is received. > + */ > + uint64_t received_normal_pages; > + uint64_t received_zero_pages; > + uint64_t received_xbzrle_pages; > + > MigrationStatus state; > > /* > diff --git a/migration/ram.c b/migration/ram.c > index 979751f61b..6646e5daad 100644 > --- a/migration/ram.c > +++ b/migration/ram.c > @@ -4417,10 +4417,12 @@ static int ram_load_precopy(QEMUFile *f) > break; > } > ram_handle_zero(host, TARGET_PAGE_SIZE); > + mis->received_zero_pages++; > break; > > case RAM_SAVE_FLAG_PAGE: > qemu_get_buffer(f, host, TARGET_PAGE_SIZE); > + mis->received_normal_pages++; > break; > > case RAM_SAVE_FLAG_XBZRLE: > @@ -4430,6 +4432,7 @@ static int ram_load_precopy(QEMUFile *f) > ret = -EINVAL; > break; > } > + mis->received_xbzrle_pages++; > break; > case RAM_SAVE_FLAG_MULTIFD_FLUSH: > multifd_recv_sync_main(); ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 1/2] migration: track timing and received pages in MigrationIncomingState 2026-04-07 12:02 ` Claudio Fontana @ 2026-04-07 12:11 ` Claudio Fontana 2026-04-07 18:28 ` Trieu Huynh 0 siblings, 1 reply; 20+ messages in thread From: Claudio Fontana @ 2026-04-07 12:11 UTC (permalink / raw) To: Trieu Huynh; +Cc: Peter Xu, Fabiano Rosas, qemu-devel On 4/7/26 14:02, Claudio Fontana wrote: > Hello, > > On 4/5/26 17:26, Trieu Huynh wrote: >> From: Trieu Huynh <vikingtc4@gmail.com> >> >> Add start_time, total_time, received_normal_pages, >> received_zero_pages, and received_xbzrle_pages to >> MigrationIncomingState. >> >> start_time is recorded when the incoming state transitions to ACTIVE; >> total_time is recorded when it transitions to COMPLETED. The page >> counters are incremented in ram_load_precopy() as each page type is >> received from the source. >> >> These fields will be used by a subsequent patch to populate >> fill_destination_migration_info() for the migration COMPLETED case. >> >> Signed-off-by: Trieu Huynh <vikingtc4@gmail.com> >> --- >> migration/migration.c | 2 ++ >> migration/migration.h | 17 +++++++++++++++++ >> migration/ram.c | 3 +++ >> 3 files changed, 22 insertions(+) >> >> diff --git a/migration/migration.c b/migration/migration.c >> index 5c9aaa6e58..17c9a8b344 100644 >> --- a/migration/migration.c >> +++ b/migration/migration.c >> @@ -726,6 +726,7 @@ static void process_incoming_migration_bh(void *opaque) >> */ >> migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE, >> MIGRATION_STATUS_COMPLETED); >> + mis->total_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) - mis->start_time; >> migration_incoming_state_destroy(); >> } >> >> @@ -758,6 +759,7 @@ process_incoming_migration_co(void *opaque) >> migrate_set_state(&mis->state, MIGRATION_STATUS_SETUP, >> MIGRATION_STATUS_ACTIVE); >> >> + mis->start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); >> mis->loadvm_co = qemu_coroutine_self(); >> ret = qemu_loadvm_state(mis->from_src_file, &local_err); >> mis->loadvm_co = NULL; >> diff --git a/migration/migration.h b/migration/migration.h >> index b6888daced..cd51ae452c 100644 >> --- a/migration/migration.h >> +++ b/migration/migration.h >> @@ -176,6 +176,23 @@ struct MigrationIncomingState { >> /* PostCopyFD's for external userfaultfds & handlers of shared memory */ >> GArray *postcopy_remote_fds; >> >> + /* >> + * Timestamps for reporting migration duration via query-migrate on the >> + * destination. start_time is recorded when the state moves to ACTIVE; >> + * total_time is recorded when it moves to COMPLETED. >> + */ >> + int64_t start_time; >> + int64_t total_time; > > nit: since this is a timestamp and these are directly linked to state changes, > why not call these: > > int64_t migration_active_ts; > int64_t migration_completed_ts; I got the second one wrong, it is not a timestamp, that is indeed a time. So clarity in the name seems to be more than a nit. What about: int64_t migration_active_ts; /* ms */ int64_t migration_completed_time; /* ms */ Thanks, Claudio > >> + >> + /* >> + * Page counters for reporting RAM statistics via query-migrate on the >> + * destination. Incremented in ram_load_precopy() as each page type >> + * is received. >> + */ >> + uint64_t received_normal_pages; >> + uint64_t received_zero_pages; >> + uint64_t received_xbzrle_pages; >> + >> MigrationStatus state; >> >> /* >> diff --git a/migration/ram.c b/migration/ram.c >> index 979751f61b..6646e5daad 100644 >> --- a/migration/ram.c >> +++ b/migration/ram.c >> @@ -4417,10 +4417,12 @@ static int ram_load_precopy(QEMUFile *f) >> break; >> } >> ram_handle_zero(host, TARGET_PAGE_SIZE); >> + mis->received_zero_pages++; >> break; >> >> case RAM_SAVE_FLAG_PAGE: >> qemu_get_buffer(f, host, TARGET_PAGE_SIZE); >> + mis->received_normal_pages++; >> break; >> >> case RAM_SAVE_FLAG_XBZRLE: >> @@ -4430,6 +4432,7 @@ static int ram_load_precopy(QEMUFile *f) >> ret = -EINVAL; >> break; >> } >> + mis->received_xbzrle_pages++; >> break; >> case RAM_SAVE_FLAG_MULTIFD_FLUSH: >> multifd_recv_sync_main(); > ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 1/2] migration: track timing and received pages in MigrationIncomingState 2026-04-07 12:11 ` Claudio Fontana @ 2026-04-07 18:28 ` Trieu Huynh 0 siblings, 0 replies; 20+ messages in thread From: Trieu Huynh @ 2026-04-07 18:28 UTC (permalink / raw) To: Claudio Fontana; +Cc: Peter Xu, Fabiano Rosas, qemu-devel On Tue, Apr 07, 2026 at 02:11:54PM +0200, Claudio Fontana wrote: > On 4/7/26 14:02, Claudio Fontana wrote: > > Hello, > > > > On 4/5/26 17:26, Trieu Huynh wrote: > >> From: Trieu Huynh <vikingtc4@gmail.com> > >> > >> Add start_time, total_time, received_normal_pages, > >> received_zero_pages, and received_xbzrle_pages to > >> MigrationIncomingState. > >> > >> start_time is recorded when the incoming state transitions to ACTIVE; > >> total_time is recorded when it transitions to COMPLETED. The page > >> counters are incremented in ram_load_precopy() as each page type is > >> received from the source. > >> > >> These fields will be used by a subsequent patch to populate > >> fill_destination_migration_info() for the migration COMPLETED case. > >> > >> Signed-off-by: Trieu Huynh <vikingtc4@gmail.com> > >> --- > >> migration/migration.c | 2 ++ > >> migration/migration.h | 17 +++++++++++++++++ > >> migration/ram.c | 3 +++ > >> 3 files changed, 22 insertions(+) > >> > >> diff --git a/migration/migration.c b/migration/migration.c > >> index 5c9aaa6e58..17c9a8b344 100644 > >> --- a/migration/migration.c > >> +++ b/migration/migration.c > >> @@ -726,6 +726,7 @@ static void process_incoming_migration_bh(void *opaque) > >> */ > >> migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE, > >> MIGRATION_STATUS_COMPLETED); > >> + mis->total_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) - mis->start_time; > >> migration_incoming_state_destroy(); > >> } > >> > >> @@ -758,6 +759,7 @@ process_incoming_migration_co(void *opaque) > >> migrate_set_state(&mis->state, MIGRATION_STATUS_SETUP, > >> MIGRATION_STATUS_ACTIVE); > >> > >> + mis->start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); > >> mis->loadvm_co = qemu_coroutine_self(); > >> ret = qemu_loadvm_state(mis->from_src_file, &local_err); > >> mis->loadvm_co = NULL; > >> diff --git a/migration/migration.h b/migration/migration.h > >> index b6888daced..cd51ae452c 100644 > >> --- a/migration/migration.h > >> +++ b/migration/migration.h > >> @@ -176,6 +176,23 @@ struct MigrationIncomingState { > >> /* PostCopyFD's for external userfaultfds & handlers of shared memory */ > >> GArray *postcopy_remote_fds; > >> > >> + /* > >> + * Timestamps for reporting migration duration via query-migrate on the > >> + * destination. start_time is recorded when the state moves to ACTIVE; > >> + * total_time is recorded when it moves to COMPLETED. > >> + */ > >> + int64_t start_time; > >> + int64_t total_time; > > > > nit: since this is a timestamp and these are directly linked to state changes, > > why not call these: > > > > int64_t migration_active_ts; > > int64_t migration_completed_ts; > > I got the second one wrong, it is not a timestamp, that is indeed a time. > So clarity in the name seems to be more than a nit. What about: > > int64_t migration_active_ts; /* ms */ > int64_t migration_completed_time; /* ms */ > make sense to me. will update in v2. thank you. > Thanks, > > Claudio > > > > >> + > >> + /* > >> + * Page counters for reporting RAM statistics via query-migrate on the > >> + * destination. Incremented in ram_load_precopy() as each page type > >> + * is received. > >> + */ > >> + uint64_t received_normal_pages; > >> + uint64_t received_zero_pages; > >> + uint64_t received_xbzrle_pages; > >> + > >> MigrationStatus state; > >> > >> /* > >> diff --git a/migration/ram.c b/migration/ram.c > >> index 979751f61b..6646e5daad 100644 > >> --- a/migration/ram.c > >> +++ b/migration/ram.c > >> @@ -4417,10 +4417,12 @@ static int ram_load_precopy(QEMUFile *f) > >> break; > >> } > >> ram_handle_zero(host, TARGET_PAGE_SIZE); > >> + mis->received_zero_pages++; > >> break; > >> > >> case RAM_SAVE_FLAG_PAGE: > >> qemu_get_buffer(f, host, TARGET_PAGE_SIZE); > >> + mis->received_normal_pages++; > >> break; > >> > >> case RAM_SAVE_FLAG_XBZRLE: > >> @@ -4430,6 +4432,7 @@ static int ram_load_precopy(QEMUFile *f) > >> ret = -EINVAL; > >> break; > >> } > >> + mis->received_xbzrle_pages++; > >> break; > >> case RAM_SAVE_FLAG_MULTIFD_FLUSH: > >> multifd_recv_sync_main(); > > > BRs, Trieu Huynh ^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH 2/2] migration: expose RAM stats and timing on destination via query-migrate 2026-04-05 15:26 [PATCH 0/2] migration: include timing and RAM stats on destination when query-migrate Trieu Huynh 2026-04-05 15:26 ` [PATCH 1/2] migration: track timing and received pages in MigrationIncomingState Trieu Huynh @ 2026-04-05 15:26 ` Trieu Huynh 2026-04-08 16:14 ` Claudio Fontana 2026-04-06 14:02 ` [PATCH 0/2] migration: include timing and RAM stats on destination when query-migrate Fabiano Rosas 2 siblings, 1 reply; 20+ messages in thread From: Trieu Huynh @ 2026-04-05 15:26 UTC (permalink / raw) To: qemu-devel Cc: Trieu Huynh, Peter Xu, Fabiano Rosas, Eric Blake, Markus Armbruster From: Trieu Huynh <vikingtc4@gmail.com> MigrationStats had all-required QAPI fields, so partially populating info->ram on the destination would force display of source-only metrics (dirty-sync-count, precopy-bytes, etc.) as misleading zeros. Make those source-only fields optional with '*' in the QAPI schema. Update populate_ram_info() to set them via has_* setters so source output is unchanged. As per schema's changes, fill_destination_migration_info() can now populate info->ram for the COMPLETED case with the fields that are meaningful on the receive side: * total, remaining, page-size (RAM layout) * transferred, normal, normal-bytes (bytes/pages received) * duplicate (zero pages received) * mbps (throughput: transferred*8 /time_ms/1000) * pages-per-second (total pages / duration in seconds) Others optional fields (dirty-sync-count, precopy-bytes, downtime-bytes, postcopy-bytes, multifd-bytes, postcopy-requests, dirty-sync-missed-zero-copy, dirty-pages-rate) are left unset and are absent from the destination output. On dst, {"execute":"query-migrate"} * As-is: { "return": { "status": "completed" } * To-be: { "return": { "status": "completed", "total-time": 94, "ram": { "total": 554508288, "pages-per-second": 1440234, "page-size": 4096, "remaining": 0, "mbps": 97.955404255319152, "transferred": 1150976, "duplicate": 135101, "normal-bytes": 1150976, "normal": 281 } } } Signed-off-by: Trieu Huynh <vikingtc4@gmail.com> --- migration/migration.c | 35 +++++++++++++++++++++++++++++++++++ qapi/migration.json | 14 +++++++------- 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 17c9a8b344..925d29890d 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -1063,17 +1063,24 @@ static void populate_ram_info(MigrationInfo *info, MigrationState *s) info->ram->normal = qatomic_read(&mig_stats.normal_pages); info->ram->normal_bytes = info->ram->normal * page_size; info->ram->mbps = s->mbps; + info->ram->has_dirty_sync_count = true; info->ram->dirty_sync_count = qatomic_read(&mig_stats.dirty_sync_count); + info->ram->has_dirty_sync_missed_zero_copy = true; info->ram->dirty_sync_missed_zero_copy = qatomic_read(&mig_stats.dirty_sync_missed_zero_copy); + info->ram->has_postcopy_requests = true; info->ram->postcopy_requests = qatomic_read(&mig_stats.postcopy_requests); info->ram->page_size = page_size; + info->ram->has_multifd_bytes = true; info->ram->multifd_bytes = qatomic_read(&mig_stats.multifd_bytes); info->ram->pages_per_second = s->pages_per_second; + info->ram->has_precopy_bytes = true; info->ram->precopy_bytes = qatomic_read(&mig_stats.precopy_bytes); + info->ram->has_downtime_bytes = true; info->ram->downtime_bytes = qatomic_read(&mig_stats.downtime_bytes); + info->ram->has_postcopy_bytes = true; info->ram->postcopy_bytes = qatomic_read(&mig_stats.postcopy_bytes); if (migrate_xbzrle()) { @@ -1094,6 +1101,7 @@ static void populate_ram_info(MigrationInfo *info, MigrationState *s) if (s->state != MIGRATION_STATUS_COMPLETED) { info->ram->remaining = ram_bytes_remaining(); + info->ram->has_dirty_pages_rate = true; info->ram->dirty_pages_rate = qatomic_read(&mig_stats.dirty_pages_rate); } @@ -1209,6 +1217,33 @@ static void fill_destination_migration_info(MigrationInfo *info) case MIGRATION_STATUS_COMPLETED: info->has_status = true; fill_destination_postcopy_migration_info(info); + if (mis->total_time > 0) { + info->has_total_time = true; + info->total_time = mis->total_time; + } + { + size_t page_size = qemu_target_page_size(); + uint64_t normal = mis->received_normal_pages; + uint64_t zero = mis->received_zero_pages; + uint64_t xbzrle = mis->received_xbzrle_pages; + + info->ram = g_malloc0(sizeof(*info->ram)); + info->ram->total = ram_bytes_total(); + info->ram->remaining = 0; + info->ram->normal = normal + xbzrle; + info->ram->normal_bytes = (normal + xbzrle) * page_size; + info->ram->duplicate = zero; + info->ram->transferred = (normal + xbzrle) * page_size; + info->ram->page_size = page_size; + + if (info->has_total_time) { + info->ram->mbps = info->ram->transferred * 8.0 + / info->total_time / 1000.0; + info->ram->pages_per_second = (normal + xbzrle + zero) + * 1000.0 / info->total_time; + } + /* source-only optional omitted from output */ + } break; default: return; diff --git a/qapi/migration.json b/qapi/migration.json index 7134d4ce47..a695d04a22 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -68,13 +68,13 @@ 'data': {'transferred': 'int', 'remaining': 'int', 'total': 'int' , 'duplicate': 'int', 'normal': 'int', - 'normal-bytes': 'int', 'dirty-pages-rate': 'int', - 'mbps': 'number', 'dirty-sync-count': 'int', - 'postcopy-requests': 'int', 'page-size': 'int', - 'multifd-bytes': 'uint64', 'pages-per-second': 'uint64', - 'precopy-bytes': 'uint64', 'downtime-bytes': 'uint64', - 'postcopy-bytes': 'uint64', - 'dirty-sync-missed-zero-copy': 'uint64' } } + 'normal-bytes': 'int', '*dirty-pages-rate': 'int', + 'mbps': 'number', '*dirty-sync-count': 'int', + '*postcopy-requests': 'int', 'page-size': 'int', + '*multifd-bytes': 'uint64', 'pages-per-second': 'uint64', + '*precopy-bytes': 'uint64', '*downtime-bytes': 'uint64', + '*postcopy-bytes': 'uint64', + '*dirty-sync-missed-zero-copy': 'uint64' } } ## # @XBZRLECacheStats: -- 2.43.0 ^ permalink raw reply related [flat|nested] 20+ messages in thread
* Re: [PATCH 2/2] migration: expose RAM stats and timing on destination via query-migrate 2026-04-05 15:26 ` [PATCH 2/2] migration: expose RAM stats and timing on destination via query-migrate Trieu Huynh @ 2026-04-08 16:14 ` Claudio Fontana 2026-04-08 19:38 ` Trieu Huynh 0 siblings, 1 reply; 20+ messages in thread From: Claudio Fontana @ 2026-04-08 16:14 UTC (permalink / raw) To: Trieu Huynh Cc: Peter Xu, Fabiano Rosas, Eric Blake, Markus Armbruster, qemu-devel, Daniel P . Berrangé Hello Trieu, makes sense to me in general, but maintainers can give you a more proper review on this, I can only point out a few sources of confusion from my side, which likely stem from lack of knowledge from my side: On 4/5/26 17:26, Trieu Huynh wrote: > From: Trieu Huynh <vikingtc4@gmail.com> > > MigrationStats had all-required QAPI fields, so partially populating > info->ram on the destination would force display of source-only metrics > (dirty-sync-count, precopy-bytes, etc.) as misleading zeros. > > Make those source-only fields optional with '*' in the QAPI schema. > Update populate_ram_info() to set them via has_* setters so source > output is unchanged. > > As per schema's changes, fill_destination_migration_info() can now > populate info->ram for the COMPLETED case with the fields that are > meaningful on the receive side: > > * total, remaining, page-size (RAM layout) Does 'remaining' make sense if the migration is completed? Don't we just assume 0? But maybe it makes sense to keep it for simplicity/consistency. > * transferred, normal, normal-bytes (bytes/pages received) > * duplicate (zero pages received) it is interesting that zero pages are called "duplicate". > * mbps (throughput: transferred*8 > /time_ms/1000) > * pages-per-second (total pages / duration in > seconds) > > Others optional fields (dirty-sync-count, precopy-bytes, > downtime-bytes, postcopy-bytes, multifd-bytes, postcopy-requests, > dirty-sync-missed-zero-copy, dirty-pages-rate) are left unset and > are absent from the destination output. > > On dst, {"execute":"query-migrate"} > * As-is: > { > "return": { > "status": "completed" > } > * To-be: > { > "return": { > "status": "completed", > "total-time": 94, > "ram": { > "total": 554508288, > "pages-per-second": 1440234, > "page-size": 4096, > "remaining": 0, > "mbps": 97.955404255319152, > "transferred": 1150976, > "duplicate": 135101, > "normal-bytes": 1150976, > "normal": 281 > } > } > } > > Signed-off-by: Trieu Huynh <vikingtc4@gmail.com> > --- > migration/migration.c | 35 +++++++++++++++++++++++++++++++++++ > qapi/migration.json | 14 +++++++------- > 2 files changed, 42 insertions(+), 7 deletions(-) > > diff --git a/migration/migration.c b/migration/migration.c > index 17c9a8b344..925d29890d 100644 > --- a/migration/migration.c > +++ b/migration/migration.c > @@ -1063,17 +1063,24 @@ static void populate_ram_info(MigrationInfo *info, MigrationState *s) > info->ram->normal = qatomic_read(&mig_stats.normal_pages); > info->ram->normal_bytes = info->ram->normal * page_size; > info->ram->mbps = s->mbps; > + info->ram->has_dirty_sync_count = true; > info->ram->dirty_sync_count = > qatomic_read(&mig_stats.dirty_sync_count); > + info->ram->has_dirty_sync_missed_zero_copy = true; > info->ram->dirty_sync_missed_zero_copy = > qatomic_read(&mig_stats.dirty_sync_missed_zero_copy); > + info->ram->has_postcopy_requests = true; > info->ram->postcopy_requests = > qatomic_read(&mig_stats.postcopy_requests); > info->ram->page_size = page_size; > + info->ram->has_multifd_bytes = true; > info->ram->multifd_bytes = qatomic_read(&mig_stats.multifd_bytes); > info->ram->pages_per_second = s->pages_per_second; > + info->ram->has_precopy_bytes = true; > info->ram->precopy_bytes = qatomic_read(&mig_stats.precopy_bytes); > + info->ram->has_downtime_bytes = true; > info->ram->downtime_bytes = qatomic_read(&mig_stats.downtime_bytes); > + info->ram->has_postcopy_bytes = true; > info->ram->postcopy_bytes = qatomic_read(&mig_stats.postcopy_bytes); > > if (migrate_xbzrle()) { > @@ -1094,6 +1101,7 @@ static void populate_ram_info(MigrationInfo *info, MigrationState *s) > > if (s->state != MIGRATION_STATUS_COMPLETED) { > info->ram->remaining = ram_bytes_remaining(); > + info->ram->has_dirty_pages_rate = true; > info->ram->dirty_pages_rate = > qatomic_read(&mig_stats.dirty_pages_rate); > } > @@ -1209,6 +1217,33 @@ static void fill_destination_migration_info(MigrationInfo *info) > case MIGRATION_STATUS_COMPLETED: > info->has_status = true; > fill_destination_postcopy_migration_info(info); > + if (mis->total_time > 0) { > + info->has_total_time = true; > + info->total_time = mis->total_time; > + } > + { > + size_t page_size = qemu_target_page_size(); > + uint64_t normal = mis->received_normal_pages; > + uint64_t zero = mis->received_zero_pages; > + uint64_t xbzrle = mis->received_xbzrle_pages; > + > + info->ram = g_malloc0(sizeof(*info->ram)); > + info->ram->total = ram_bytes_total(); > + info->ram->remaining = 0; > + info->ram->normal = normal + xbzrle; > + info->ram->normal_bytes = (normal + xbzrle) * page_size; > + info->ram->duplicate = zero; > + info->ram->transferred = (normal + xbzrle) * page_size; > + info->ram->page_size = page_size; > + > + if (info->has_total_time) { > + info->ram->mbps = info->ram->transferred * 8.0 > + / info->total_time / 1000.0; > + info->ram->pages_per_second = (normal + xbzrle + zero) > + * 1000.0 / info->total_time; > + } > + /* source-only optional omitted from output */ > + } > break; > default: > return; > diff --git a/qapi/migration.json b/qapi/migration.json > index 7134d4ce47..a695d04a22 100644 > --- a/qapi/migration.json > +++ b/qapi/migration.json > @@ -68,13 +68,13 @@ > 'data': {'transferred': 'int', 'remaining': 'int', 'total': 'int' , > 'duplicate': 'int', > 'normal': 'int', > - 'normal-bytes': 'int', 'dirty-pages-rate': 'int', > - 'mbps': 'number', 'dirty-sync-count': 'int', > - 'postcopy-requests': 'int', 'page-size': 'int', > - 'multifd-bytes': 'uint64', 'pages-per-second': 'uint64', > - 'precopy-bytes': 'uint64', 'downtime-bytes': 'uint64', > - 'postcopy-bytes': 'uint64', > - 'dirty-sync-missed-zero-copy': 'uint64' } } > + 'normal-bytes': 'int', '*dirty-pages-rate': 'int', > + 'mbps': 'number', '*dirty-sync-count': 'int', > + '*postcopy-requests': 'int', 'page-size': 'int', > + '*multifd-bytes': 'uint64', 'pages-per-second': 'uint64', > + '*precopy-bytes': 'uint64', '*downtime-bytes': 'uint64', > + '*postcopy-bytes': 'uint64', > + '*dirty-sync-missed-zero-copy': 'uint64' } } > > ## > # @XBZRLECacheStats: This looks good and makes sense to me, but looking forward to more authoritative voices. Does this work build ok on all archs? Any tests that could be extended to cover these new stats, maybe tests/qtest/migration/ ? Thank you! Claudio ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 2/2] migration: expose RAM stats and timing on destination via query-migrate 2026-04-08 16:14 ` Claudio Fontana @ 2026-04-08 19:38 ` Trieu Huynh 0 siblings, 0 replies; 20+ messages in thread From: Trieu Huynh @ 2026-04-08 19:38 UTC (permalink / raw) To: Claudio Fontana Cc: Peter Xu, Fabiano Rosas, Eric Blake, Markus Armbruster, qemu-devel, Daniel P . Berrangé On Wed, Apr 08, 2026 at 06:14:41PM +0200, Claudio Fontana wrote: > Hello Trieu, > > makes sense to me in general, but maintainers can give you a more proper review on this, > I can only point out a few sources of confusion from my side, which likely stem from lack of knowledge from my side: > > On 4/5/26 17:26, Trieu Huynh wrote: > > From: Trieu Huynh <vikingtc4@gmail.com> > > > > MigrationStats had all-required QAPI fields, so partially populating > > info->ram on the destination would force display of source-only metrics > > (dirty-sync-count, precopy-bytes, etc.) as misleading zeros. > > > > Make those source-only fields optional with '*' in the QAPI schema. > > Update populate_ram_info() to set them via has_* setters so source > > output is unchanged. > > > > As per schema's changes, fill_destination_migration_info() can now > > populate info->ram for the COMPLETED case with the fields that are > > meaningful on the receive side: > > > > * total, remaining, page-size (RAM layout) > > Does 'remaining' make sense if the migration is completed? Don't we just assume 0? > But maybe it makes sense to keep it for simplicity/consistency. > Indeed, it set to 0 when migration status is completed, just to be consistent. > > * transferred, normal, normal-bytes (bytes/pages received) > > * duplicate (zero pages received) > > it is interesting that zero pages are called "duplicate". > Yes, it is. IIUC, in migration stats, duplicate = "pages we didn't need to send because dest's memory is zero-initialized on startup, so any all-zero src page is already "duplicate" of what the dst has. > > * mbps (throughput: transferred*8 > > /time_ms/1000) > > * pages-per-second (total pages / duration in > > seconds) > > > > Others optional fields (dirty-sync-count, precopy-bytes, > > downtime-bytes, postcopy-bytes, multifd-bytes, postcopy-requests, > > dirty-sync-missed-zero-copy, dirty-pages-rate) are left unset and > > are absent from the destination output. > > > > On dst, {"execute":"query-migrate"} > > * As-is: > > { > > "return": { > > "status": "completed" > > } > > * To-be: > > { > > "return": { > > "status": "completed", > > "total-time": 94, > > "ram": { > > "total": 554508288, > > "pages-per-second": 1440234, > > "page-size": 4096, > > "remaining": 0, > > "mbps": 97.955404255319152, > > "transferred": 1150976, > > "duplicate": 135101, > > "normal-bytes": 1150976, > > "normal": 281 > > } > > } > > } > > > > Signed-off-by: Trieu Huynh <vikingtc4@gmail.com> > > --- > > migration/migration.c | 35 +++++++++++++++++++++++++++++++++++ > > qapi/migration.json | 14 +++++++------- > > 2 files changed, 42 insertions(+), 7 deletions(-) > > > > diff --git a/migration/migration.c b/migration/migration.c > > index 17c9a8b344..925d29890d 100644 > > --- a/migration/migration.c > > +++ b/migration/migration.c > > @@ -1063,17 +1063,24 @@ static void populate_ram_info(MigrationInfo *info, MigrationState *s) > > info->ram->normal = qatomic_read(&mig_stats.normal_pages); > > info->ram->normal_bytes = info->ram->normal * page_size; > > info->ram->mbps = s->mbps; > > + info->ram->has_dirty_sync_count = true; > > info->ram->dirty_sync_count = > > qatomic_read(&mig_stats.dirty_sync_count); > > + info->ram->has_dirty_sync_missed_zero_copy = true; > > info->ram->dirty_sync_missed_zero_copy = > > qatomic_read(&mig_stats.dirty_sync_missed_zero_copy); > > + info->ram->has_postcopy_requests = true; > > info->ram->postcopy_requests = > > qatomic_read(&mig_stats.postcopy_requests); > > info->ram->page_size = page_size; > > + info->ram->has_multifd_bytes = true; > > info->ram->multifd_bytes = qatomic_read(&mig_stats.multifd_bytes); > > info->ram->pages_per_second = s->pages_per_second; > > + info->ram->has_precopy_bytes = true; > > info->ram->precopy_bytes = qatomic_read(&mig_stats.precopy_bytes); > > + info->ram->has_downtime_bytes = true; > > info->ram->downtime_bytes = qatomic_read(&mig_stats.downtime_bytes); > > + info->ram->has_postcopy_bytes = true; > > info->ram->postcopy_bytes = qatomic_read(&mig_stats.postcopy_bytes); > > > > if (migrate_xbzrle()) { > > @@ -1094,6 +1101,7 @@ static void populate_ram_info(MigrationInfo *info, MigrationState *s) > > > > if (s->state != MIGRATION_STATUS_COMPLETED) { > > info->ram->remaining = ram_bytes_remaining(); > > + info->ram->has_dirty_pages_rate = true; > > info->ram->dirty_pages_rate = > > qatomic_read(&mig_stats.dirty_pages_rate); > > } > > @@ -1209,6 +1217,33 @@ static void fill_destination_migration_info(MigrationInfo *info) > > case MIGRATION_STATUS_COMPLETED: > > info->has_status = true; > > fill_destination_postcopy_migration_info(info); > > + if (mis->total_time > 0) { > > + info->has_total_time = true; > > + info->total_time = mis->total_time; > > + } > > + { > > + size_t page_size = qemu_target_page_size(); > > + uint64_t normal = mis->received_normal_pages; > > + uint64_t zero = mis->received_zero_pages; > > + uint64_t xbzrle = mis->received_xbzrle_pages; > > + > > + info->ram = g_malloc0(sizeof(*info->ram)); > > + info->ram->total = ram_bytes_total(); > > + info->ram->remaining = 0; > > + info->ram->normal = normal + xbzrle; > > + info->ram->normal_bytes = (normal + xbzrle) * page_size; > > + info->ram->duplicate = zero; > > + info->ram->transferred = (normal + xbzrle) * page_size; > > + info->ram->page_size = page_size; > > + > > + if (info->has_total_time) { > > + info->ram->mbps = info->ram->transferred * 8.0 > > + / info->total_time / 1000.0; > > + info->ram->pages_per_second = (normal + xbzrle + zero) > > + * 1000.0 / info->total_time; > > + } > > + /* source-only optional omitted from output */ > > + } > > break; > > default: > > return; > > diff --git a/qapi/migration.json b/qapi/migration.json > > index 7134d4ce47..a695d04a22 100644 > > --- a/qapi/migration.json > > +++ b/qapi/migration.json > > @@ -68,13 +68,13 @@ > > 'data': {'transferred': 'int', 'remaining': 'int', 'total': 'int' , > > 'duplicate': 'int', > > 'normal': 'int', > > - 'normal-bytes': 'int', 'dirty-pages-rate': 'int', > > - 'mbps': 'number', 'dirty-sync-count': 'int', > > - 'postcopy-requests': 'int', 'page-size': 'int', > > - 'multifd-bytes': 'uint64', 'pages-per-second': 'uint64', > > - 'precopy-bytes': 'uint64', 'downtime-bytes': 'uint64', > > - 'postcopy-bytes': 'uint64', > > - 'dirty-sync-missed-zero-copy': 'uint64' } } > > + 'normal-bytes': 'int', '*dirty-pages-rate': 'int', > > + 'mbps': 'number', '*dirty-sync-count': 'int', > > + '*postcopy-requests': 'int', 'page-size': 'int', > > + '*multifd-bytes': 'uint64', 'pages-per-second': 'uint64', > > + '*precopy-bytes': 'uint64', '*downtime-bytes': 'uint64', > > + '*postcopy-bytes': 'uint64', > > + '*dirty-sync-missed-zero-copy': 'uint64' } } > > > > ## > > # @XBZRLECacheStats: > > This looks good and makes sense to me, > but looking forward to more authoritative voices. > Thanks, let's waiting. > Does this work build ok on all archs? Any tests that could be extended to cover these new stats, Actually, I have tested x86_64 but not other archs. The patch are arch-independent IIUC, so I expected it will work without impact any existing callers on other archs. Anw, I will check it. > maybe tests/qtest/migration/ ? > Thanks for the point. I think can reuse existing test_precopy_tcp_plain. Will try to add it in v2. > Thank you! > > Claudio > BRs, Trieu Huynh ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 0/2] migration: include timing and RAM stats on destination when query-migrate 2026-04-05 15:26 [PATCH 0/2] migration: include timing and RAM stats on destination when query-migrate Trieu Huynh 2026-04-05 15:26 ` [PATCH 1/2] migration: track timing and received pages in MigrationIncomingState Trieu Huynh 2026-04-05 15:26 ` [PATCH 2/2] migration: expose RAM stats and timing on destination via query-migrate Trieu Huynh @ 2026-04-06 14:02 ` Fabiano Rosas 2026-04-08 20:04 ` Peter Xu 2 siblings, 1 reply; 20+ messages in thread From: Fabiano Rosas @ 2026-04-06 14:02 UTC (permalink / raw) To: Trieu Huynh, qemu-devel Cc: Trieu Huynh, Claudio Fontana, Peter Xu, Daniel P. Berrangé Trieu Huynh <vikingtc4@gmail.com> writes: > From: Trieu Huynh <vikingtc4@gmail.com> > > When query-migrate is called on the *destination* QEMU after a precopy > migration completes it returns only {"status": "completed"} — no timing, > no RAM statistics. The source correctly returns total-time, downtime, > setup-time, and full ram stats. > > This series fixes the gap in two commits: > > * commit 1: Track start/end timestamps and per-page-type counters in > MigrationIncomingState, recorded in the receive path. > > * commit 2: Make source-only MigrationStats fields optional in the QAPI > schema so the destination can populate info->ram with only the fields > that are meaningful on the receive side. Fields computed from tracked > counters (transferred, normal, duplicate, mbps, pages-per-second) are > shown; source-only fields (dirty-sync-count, precopy-bytes, etc.) are > absent. > > The QAPI change (commit 2) is ABI-compatible: optional fields that are > not set are simply absent from the output. Source-side callers are > unaffected because populate_ram_info() sets all optional fields via > has_* setters, so the source output is identical to before. > > On dst, {"execute":"query-migrate"} > * As-is: > { > "return": { > "status": "completed" > } > * To-be: > { > "return": { > "status": "completed", > "total-time": 94, > "ram": { > "total": 554508288, > "pages-per-second": 1440234, > "page-size": 4096, > "remaining": 0, > "mbps": 97.955404255319152, > "transferred": 1150976, > "duplicate": 135101, > "normal-bytes": 1150976, > "normal": 281 > } > } > } > > Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3151 > > Trieu Huynh (2): > migration: track timing and received pages in MigrationIncomingState > migration: expose RAM stats and timing on destination via > query-migrate > > migration/migration.c | 37 +++++++++++++++++++++++++++++++++++++ > migration/migration.h | 17 +++++++++++++++++ > migration/ram.c | 3 +++ > qapi/migration.json | 14 +++++++------- > 4 files changed, 64 insertions(+), 7 deletions(-) Hi, remember to copy the interested people in your series. Using get_maintainers is correct, but when there's already a discussion about the topic it's good to add some CCs manually. +CC Daniel and Claudio ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 0/2] migration: include timing and RAM stats on destination when query-migrate 2026-04-06 14:02 ` [PATCH 0/2] migration: include timing and RAM stats on destination when query-migrate Fabiano Rosas @ 2026-04-08 20:04 ` Peter Xu 2026-04-09 10:08 ` Claudio Fontana 0 siblings, 1 reply; 20+ messages in thread From: Peter Xu @ 2026-04-08 20:04 UTC (permalink / raw) To: Fabiano Rosas Cc: Trieu Huynh, qemu-devel, Claudio Fontana, Daniel P. Berrangé On Mon, Apr 06, 2026 at 11:02:56AM -0300, Fabiano Rosas wrote: > Trieu Huynh <vikingtc4@gmail.com> writes: > > > From: Trieu Huynh <vikingtc4@gmail.com> > > > > When query-migrate is called on the *destination* QEMU after a precopy > > migration completes it returns only {"status": "completed"} — no timing, > > no RAM statistics. The source correctly returns total-time, downtime, > > setup-time, and full ram stats. > > > > This series fixes the gap in two commits: > > > > * commit 1: Track start/end timestamps and per-page-type counters in > > MigrationIncomingState, recorded in the receive path. > > > > * commit 2: Make source-only MigrationStats fields optional in the QAPI > > schema so the destination can populate info->ram with only the fields > > that are meaningful on the receive side. Fields computed from tracked > > counters (transferred, normal, duplicate, mbps, pages-per-second) are > > shown; source-only fields (dirty-sync-count, precopy-bytes, etc.) are > > absent. > > > > The QAPI change (commit 2) is ABI-compatible: optional fields that are > > not set are simply absent from the output. Source-side callers are > > unaffected because populate_ram_info() sets all optional fields via > > has_* setters, so the source output is identical to before. > > > > On dst, {"execute":"query-migrate"} > > * As-is: > > { > > "return": { > > "status": "completed" > > } > > * To-be: > > { > > "return": { > > "status": "completed", > > "total-time": 94, > > "ram": { > > "total": 554508288, > > "pages-per-second": 1440234, > > "page-size": 4096, > > "remaining": 0, > > "mbps": 97.955404255319152, > > "transferred": 1150976, > > "duplicate": 135101, > > "normal-bytes": 1150976, > > "normal": 281 > > } > > } > > } > > > > Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3151 > > > > Trieu Huynh (2): > > migration: track timing and received pages in MigrationIncomingState > > migration: expose RAM stats and timing on destination via > > query-migrate > > > > migration/migration.c | 37 +++++++++++++++++++++++++++++++++++++ > > migration/migration.h | 17 +++++++++++++++++ > > migration/ram.c | 3 +++ > > qapi/migration.json | 14 +++++++------- > > 4 files changed, 64 insertions(+), 7 deletions(-) > > Hi, remember to copy the interested people in your series. Using > get_maintainers is correct, but when there's already a discussion about > the topic it's good to add some CCs manually. > > +CC Daniel and Claudio The request is a valid one. Said that, redo accounting on both sides seem to be a duplicated work and overkill to me, if src has everything. Is there a way we can make Libvirt always query the results before src dies and report it somewhere? I recall these info were captured somewhere, do we at least dump them into logs? Thanks, -- Peter Xu ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 0/2] migration: include timing and RAM stats on destination when query-migrate 2026-04-08 20:04 ` Peter Xu @ 2026-04-09 10:08 ` Claudio Fontana 2026-04-09 13:08 ` Fabian Grünbichler 2026-04-13 21:07 ` Peter Xu 0 siblings, 2 replies; 20+ messages in thread From: Claudio Fontana @ 2026-04-09 10:08 UTC (permalink / raw) To: Peter Xu, Fabiano Rosas Cc: Trieu Huynh, qemu-devel, Jim Fehlig, Daniel P. Berrangé On 4/8/26 22:04, Peter Xu wrote: > On Mon, Apr 06, 2026 at 11:02:56AM -0300, Fabiano Rosas wrote: >> Trieu Huynh <vikingtc4@gmail.com> writes: >> >>> From: Trieu Huynh <vikingtc4@gmail.com> >>> >>> When query-migrate is called on the *destination* QEMU after a precopy >>> migration completes it returns only {"status": "completed"} — no timing, >>> no RAM statistics. The source correctly returns total-time, downtime, >>> setup-time, and full ram stats. >>> >>> This series fixes the gap in two commits: >>> >>> * commit 1: Track start/end timestamps and per-page-type counters in >>> MigrationIncomingState, recorded in the receive path. >>> >>> * commit 2: Make source-only MigrationStats fields optional in the QAPI >>> schema so the destination can populate info->ram with only the fields >>> that are meaningful on the receive side. Fields computed from tracked >>> counters (transferred, normal, duplicate, mbps, pages-per-second) are >>> shown; source-only fields (dirty-sync-count, precopy-bytes, etc.) are >>> absent. >>> >>> The QAPI change (commit 2) is ABI-compatible: optional fields that are >>> not set are simply absent from the output. Source-side callers are >>> unaffected because populate_ram_info() sets all optional fields via >>> has_* setters, so the source output is identical to before. >>> >>> On dst, {"execute":"query-migrate"} >>> * As-is: >>> { >>> "return": { >>> "status": "completed" >>> } >>> * To-be: >>> { >>> "return": { >>> "status": "completed", >>> "total-time": 94, >>> "ram": { >>> "total": 554508288, >>> "pages-per-second": 1440234, >>> "page-size": 4096, >>> "remaining": 0, >>> "mbps": 97.955404255319152, >>> "transferred": 1150976, >>> "duplicate": 135101, >>> "normal-bytes": 1150976, >>> "normal": 281 >>> } >>> } >>> } >>> >>> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3151 >>> >>> Trieu Huynh (2): >>> migration: track timing and received pages in MigrationIncomingState >>> migration: expose RAM stats and timing on destination via >>> query-migrate >>> >>> migration/migration.c | 37 +++++++++++++++++++++++++++++++++++++ >>> migration/migration.h | 17 +++++++++++++++++ >>> migration/ram.c | 3 +++ >>> qapi/migration.json | 14 +++++++------- >>> 4 files changed, 64 insertions(+), 7 deletions(-) >> >> Hi, remember to copy the interested people in your series. Using >> get_maintainers is correct, but when there's already a discussion about >> the topic it's good to add some CCs manually. >> >> +CC Daniel and Claudio > > The request is a valid one. Said that, redo accounting on both sides seem > to be a duplicated work and overkill to me, if src has everything. > > Is there a way we can make Libvirt always query the results before src dies > and report it somewhere? I recall these info were captured somewhere, do > we at least dump them into logs? > > Thanks, > I am interested in the outcome, but I think the approach shown here is fine, the amount of duplication seems minimal to me. That said if we _have_ to keep libvirt in the picture, I think I see a possible alternative, but then the feature is not available to anyone that does not use libvirt. At the same time, if libvirt is not used, one is free to keep source QEMU alive and query the stats, like I think Proxmox does. So with libvirt, I think we need a way to tell libvirt to keep the source QEMU alive before the actual switchover and the source QEMU is destroyed, so that we can do everything that needs to be done from the platform layer before the source QEMU is lost. It might go beyond querying for stats using QEMU and libvirt. Something similar to (and potentially combined with) virDomainMigrateFlags VIR_MIGRATE_PAUSE, for example: VIR_MIGRATE_NO_SOURCE_SHUTOFF So one might say: flags = VIR_MIGRATE_LIVE | VIR_MIGRATE_PERSIST_DEST | VIR_MIGRATE_UNDEFINE_SOURCE | VIR_MIGRATE_PAUSE | VIR_MIGRATE_NO_SOURCE_SHUTOFF new_domain = virDomainMigrate3(old_domain, conn, params, nparams, flags) and then we could do everything that needs to be done from the platform layer, then issue a virDomainResume to start the domain on destination, and maybe a virDomainDestroyFlags on the original domain to terminate it on the source. But I would still favor the simplicity and the overall applicability of this series from Trieu Huynh. Thanks, Claudio ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 0/2] migration: include timing and RAM stats on destination when query-migrate 2026-04-09 10:08 ` Claudio Fontana @ 2026-04-09 13:08 ` Fabian Grünbichler 2026-04-09 13:17 ` Claudio Fontana 2026-04-13 21:07 ` Peter Xu 1 sibling, 1 reply; 20+ messages in thread From: Fabian Grünbichler @ 2026-04-09 13:08 UTC (permalink / raw) To: Claudio Fontana, Fabiano Rosas, Peter Xu Cc: Daniel P. Berrangé, Jim Fehlig, qemu-devel, Trieu Huynh, Fiona Ebner On April 9, 2026 12:08 pm, Claudio Fontana wrote: > On 4/8/26 22:04, Peter Xu wrote: >> On Mon, Apr 06, 2026 at 11:02:56AM -0300, Fabiano Rosas wrote: >>> Trieu Huynh <vikingtc4@gmail.com> writes: >>> >>>> From: Trieu Huynh <vikingtc4@gmail.com> >>>> >>>> When query-migrate is called on the *destination* QEMU after a precopy >>>> migration completes it returns only {"status": "completed"} — no timing, >>>> no RAM statistics. The source correctly returns total-time, downtime, >>>> setup-time, and full ram stats. >>>> [..] >>>> >>> >>> Hi, remember to copy the interested people in your series. Using >>> get_maintainers is correct, but when there's already a discussion about >>> the topic it's good to add some CCs manually. >>> >>> +CC Daniel and Claudio >> >> The request is a valid one. Said that, redo accounting on both sides seem >> to be a duplicated work and overkill to me, if src has everything. >> >> Is there a way we can make Libvirt always query the results before src dies >> and report it somewhere? I recall these info were captured somewhere, do >> we at least dump them into logs? >> >> Thanks, >> > > I am interested in the outcome, but I think the approach shown here is fine, the amount of duplication seems minimal to me. > > That said if we _have_ to keep libvirt in the picture, I think I see a possible alternative, but then the feature is not available to anyone that does not use libvirt. At the same time, if libvirt is not used, one is free to keep source QEMU alive and query the stats, like I think Proxmox does. that is correct, we call query-migrate in a loop while migrating both to log progress and to manage migration parameters, and manually switch execution and ownership over to the target node once the migration has converged: https://git.proxmox.com/?p=qemu-server.git;a=blob;f=src/PVE/QemuMigrate.pm;h=f7ec322770a47dcc8b89109ecfa6f15ea140f30f;hb=HEAD#l1423 > So with libvirt, I think we need a way to tell libvirt to keep the source QEMU alive before the actual switchover and the source QEMU is destroyed, > so that we can do everything that needs to be done from the platform layer before the source QEMU is lost. > It might go beyond querying for stats using QEMU and libvirt. > > Something similar to (and potentially combined with) virDomainMigrateFlags VIR_MIGRATE_PAUSE, for example: > > VIR_MIGRATE_NO_SOURCE_SHUTOFF > > So one might say: > > flags = VIR_MIGRATE_LIVE | VIR_MIGRATE_PERSIST_DEST | VIR_MIGRATE_UNDEFINE_SOURCE | VIR_MIGRATE_PAUSE | VIR_MIGRATE_NO_SOURCE_SHUTOFF > new_domain = virDomainMigrate3(old_domain, conn, params, nparams, flags) > > and then we could do everything that needs to be done from the platform layer, > then issue a virDomainResume to start the domain on destination, > and maybe a virDomainDestroyFlags on the original domain to terminate it on the source. > > But I would still favor the simplicity and the overall applicability of this series from Trieu Huynh. > > Thanks, > > Claudio > > > ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 0/2] migration: include timing and RAM stats on destination when query-migrate 2026-04-09 13:08 ` Fabian Grünbichler @ 2026-04-09 13:17 ` Claudio Fontana 2026-04-09 13:29 ` Fabian Grünbichler 0 siblings, 1 reply; 20+ messages in thread From: Claudio Fontana @ 2026-04-09 13:17 UTC (permalink / raw) To: Fabian Grünbichler, Fabiano Rosas, Peter Xu Cc: Daniel P. Berrangé, Jim Fehlig, qemu-devel, Trieu Huynh, Fiona Ebner On 4/9/26 15:08, Fabian Grünbichler wrote: > On April 9, 2026 12:08 pm, Claudio Fontana wrote: >> On 4/8/26 22:04, Peter Xu wrote: >>> On Mon, Apr 06, 2026 at 11:02:56AM -0300, Fabiano Rosas wrote: >>>> Trieu Huynh <vikingtc4@gmail.com> writes: >>>> >>>>> From: Trieu Huynh <vikingtc4@gmail.com> >>>>> >>>>> When query-migrate is called on the *destination* QEMU after a precopy >>>>> migration completes it returns only {"status": "completed"} — no timing, >>>>> no RAM statistics. The source correctly returns total-time, downtime, >>>>> setup-time, and full ram stats. > >>>>> [..] >>>>> >>>> >>>> Hi, remember to copy the interested people in your series. Using >>>> get_maintainers is correct, but when there's already a discussion about >>>> the topic it's good to add some CCs manually. >>>> >>>> +CC Daniel and Claudio >>> >>> The request is a valid one. Said that, redo accounting on both sides seem >>> to be a duplicated work and overkill to me, if src has everything. >>> >>> Is there a way we can make Libvirt always query the results before src dies >>> and report it somewhere? I recall these info were captured somewhere, do >>> we at least dump them into logs? >>> >>> Thanks, >>> >> >> I am interested in the outcome, but I think the approach shown here is fine, the amount of duplication seems minimal to me. >> >> That said if we _have_ to keep libvirt in the picture, I think I see a possible alternative, but then the feature is not available to anyone that does not use libvirt. At the same time, if libvirt is not used, one is free to keep source QEMU alive and query the stats, like I think Proxmox does. > > that is correct, we call query-migrate in a loop while migrating both to > log progress and to manage migration parameters, and manually switch > execution and ownership over to the target node once the migration has > converged: > > https://git.proxmox.com/?p=qemu-server.git;a=blob;f=src/PVE/QemuMigrate.pm;h=f7ec322770a47dcc8b89109ecfa6f15ea140f30f;hb=HEAD#l1423 Hello Fabian, thanks for the confirmation, would it simplify your code if you had the stats already carried over by QEMU to the new QEMU instance? I suspect you don't want to touch that code without a very compelling reason, but at least in principle it should make things easier? > >> So with libvirt, I think we need a way to tell libvirt to keep the source QEMU alive before the actual switchover and the source QEMU is destroyed, >> so that we can do everything that needs to be done from the platform layer before the source QEMU is lost. >> It might go beyond querying for stats using QEMU and libvirt. >> >> Something similar to (and potentially combined with) virDomainMigrateFlags VIR_MIGRATE_PAUSE, for example: >> >> VIR_MIGRATE_NO_SOURCE_SHUTOFF >> >> So one might say: >> >> flags = VIR_MIGRATE_LIVE | VIR_MIGRATE_PERSIST_DEST | VIR_MIGRATE_UNDEFINE_SOURCE | VIR_MIGRATE_PAUSE | VIR_MIGRATE_NO_SOURCE_SHUTOFF >> new_domain = virDomainMigrate3(old_domain, conn, params, nparams, flags) >> >> and then we could do everything that needs to be done from the platform layer, >> then issue a virDomainResume to start the domain on destination, >> and maybe a virDomainDestroyFlags on the original domain to terminate it on the source. >> >> But I would still favor the simplicity and the overall applicability of this series from Trieu Huynh. >> >> Thanks, >> >> Claudio >> >> >> > ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 0/2] migration: include timing and RAM stats on destination when query-migrate 2026-04-09 13:17 ` Claudio Fontana @ 2026-04-09 13:29 ` Fabian Grünbichler 0 siblings, 0 replies; 20+ messages in thread From: Fabian Grünbichler @ 2026-04-09 13:29 UTC (permalink / raw) To: Claudio Fontana, Fabiano Rosas, Peter Xu Cc: Daniel P. Berrangé, Fiona Ebner, Jim Fehlig, qemu-devel, Trieu Huynh On April 9, 2026 3:17 pm, Claudio Fontana wrote: > On 4/9/26 15:08, Fabian Grünbichler wrote: >> On April 9, 2026 12:08 pm, Claudio Fontana wrote: >>> On 4/8/26 22:04, Peter Xu wrote: >>>> On Mon, Apr 06, 2026 at 11:02:56AM -0300, Fabiano Rosas wrote: >>>>> Trieu Huynh <vikingtc4@gmail.com> writes: >>>>> >>>>>> From: Trieu Huynh <vikingtc4@gmail.com> >>>>>> >>>>>> When query-migrate is called on the *destination* QEMU after a precopy >>>>>> migration completes it returns only {"status": "completed"} — no timing, >>>>>> no RAM statistics. The source correctly returns total-time, downtime, >>>>>> setup-time, and full ram stats. >> >>>>>> [..] >>>>>> >>>>> >>>>> Hi, remember to copy the interested people in your series. Using >>>>> get_maintainers is correct, but when there's already a discussion about >>>>> the topic it's good to add some CCs manually. >>>>> >>>>> +CC Daniel and Claudio >>>> >>>> The request is a valid one. Said that, redo accounting on both sides seem >>>> to be a duplicated work and overkill to me, if src has everything. >>>> >>>> Is there a way we can make Libvirt always query the results before src dies >>>> and report it somewhere? I recall these info were captured somewhere, do >>>> we at least dump them into logs? >>>> >>>> Thanks, >>>> >>> >>> I am interested in the outcome, but I think the approach shown here is fine, the amount of duplication seems minimal to me. >>> >>> That said if we _have_ to keep libvirt in the picture, I think I see a possible alternative, but then the feature is not available to anyone that does not use libvirt. At the same time, if libvirt is not used, one is free to keep source QEMU alive and query the stats, like I think Proxmox does. >> >> that is correct, we call query-migrate in a loop while migrating both to >> log progress and to manage migration parameters, and manually switch >> execution and ownership over to the target node once the migration has >> converged: >> >> https://git.proxmox.com/?p=qemu-server.git;a=blob;f=src/PVE/QemuMigrate.pm;h=f7ec322770a47dcc8b89109ecfa6f15ea140f30f;hb=HEAD#l1423 > > Hello Fabian, thanks for the confirmation, > would it simplify your code if you had the stats already carried over by QEMU to the new QEMU instance? not really - the source node is driving the migration anyway, so it can also do the polling + logging. there is no management task running on the target node/side, after the initial spawning of the VM process. > I suspect you don't want to touch that code without a very compelling reason, but at least in principle it should make things easier? ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 0/2] migration: include timing and RAM stats on destination when query-migrate 2026-04-09 10:08 ` Claudio Fontana 2026-04-09 13:08 ` Fabian Grünbichler @ 2026-04-13 21:07 ` Peter Xu 2026-04-16 16:53 ` Trieu Huynh 2026-04-22 22:46 ` Fabiano Rosas 1 sibling, 2 replies; 20+ messages in thread From: Peter Xu @ 2026-04-13 21:07 UTC (permalink / raw) To: Claudio Fontana Cc: Fabiano Rosas, Trieu Huynh, qemu-devel, Jim Fehlig, Daniel P. Berrangé On Thu, Apr 09, 2026 at 12:08:34PM +0200, Claudio Fontana wrote: > On 4/8/26 22:04, Peter Xu wrote: > > On Mon, Apr 06, 2026 at 11:02:56AM -0300, Fabiano Rosas wrote: > >> Trieu Huynh <vikingtc4@gmail.com> writes: > >> > >>> From: Trieu Huynh <vikingtc4@gmail.com> > >>> > >>> When query-migrate is called on the *destination* QEMU after a precopy > >>> migration completes it returns only {"status": "completed"} — no timing, > >>> no RAM statistics. The source correctly returns total-time, downtime, > >>> setup-time, and full ram stats. > >>> > >>> This series fixes the gap in two commits: > >>> > >>> * commit 1: Track start/end timestamps and per-page-type counters in > >>> MigrationIncomingState, recorded in the receive path. > >>> > >>> * commit 2: Make source-only MigrationStats fields optional in the QAPI > >>> schema so the destination can populate info->ram with only the fields > >>> that are meaningful on the receive side. Fields computed from tracked > >>> counters (transferred, normal, duplicate, mbps, pages-per-second) are > >>> shown; source-only fields (dirty-sync-count, precopy-bytes, etc.) are > >>> absent. > >>> > >>> The QAPI change (commit 2) is ABI-compatible: optional fields that are > >>> not set are simply absent from the output. Source-side callers are > >>> unaffected because populate_ram_info() sets all optional fields via > >>> has_* setters, so the source output is identical to before. > >>> > >>> On dst, {"execute":"query-migrate"} > >>> * As-is: > >>> { > >>> "return": { > >>> "status": "completed" > >>> } > >>> * To-be: > >>> { > >>> "return": { > >>> "status": "completed", > >>> "total-time": 94, > >>> "ram": { > >>> "total": 554508288, > >>> "pages-per-second": 1440234, > >>> "page-size": 4096, > >>> "remaining": 0, > >>> "mbps": 97.955404255319152, > >>> "transferred": 1150976, > >>> "duplicate": 135101, > >>> "normal-bytes": 1150976, > >>> "normal": 281 > >>> } > >>> } > >>> } > >>> > >>> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3151 > >>> > >>> Trieu Huynh (2): > >>> migration: track timing and received pages in MigrationIncomingState > >>> migration: expose RAM stats and timing on destination via > >>> query-migrate > >>> > >>> migration/migration.c | 37 +++++++++++++++++++++++++++++++++++++ > >>> migration/migration.h | 17 +++++++++++++++++ > >>> migration/ram.c | 3 +++ > >>> qapi/migration.json | 14 +++++++------- > >>> 4 files changed, 64 insertions(+), 7 deletions(-) > >> > >> Hi, remember to copy the interested people in your series. Using > >> get_maintainers is correct, but when there's already a discussion about > >> the topic it's good to add some CCs manually. > >> > >> +CC Daniel and Claudio > > > > The request is a valid one. Said that, redo accounting on both sides seem > > to be a duplicated work and overkill to me, if src has everything. > > > > Is there a way we can make Libvirt always query the results before src dies > > and report it somewhere? I recall these info were captured somewhere, do > > we at least dump them into logs? > > > > Thanks, > > > > I am interested in the outcome, but I think the approach shown here is fine, the amount of duplication seems minimal to me. > > That said if we _have_ to keep libvirt in the picture, I think I see a possible alternative, but then the feature is not available to anyone that does not use libvirt. At the same time, if libvirt is not used, one is free to keep source QEMU alive and query the stats, like I think Proxmox does. The problem is here from QEMU's perspective we have all the information, and "not quit" is the default behavior for src QEMU. It means by default all info is available.. this is true for no matter what upper mgmt is in use. > > So with libvirt, I think we need a way to tell libvirt to keep the source QEMU alive before the actual switchover and the source QEMU is destroyed, > so that we can do everything that needs to be done from the platform layer before the source QEMU is lost. > It might go beyond querying for stats using QEMU and libvirt. > > Something similar to (and potentially combined with) virDomainMigrateFlags VIR_MIGRATE_PAUSE, for example: > > VIR_MIGRATE_NO_SOURCE_SHUTOFF > > So one might say: > > flags = VIR_MIGRATE_LIVE | VIR_MIGRATE_PERSIST_DEST | VIR_MIGRATE_UNDEFINE_SOURCE | VIR_MIGRATE_PAUSE | VIR_MIGRATE_NO_SOURCE_SHUTOFF > new_domain = virDomainMigrate3(old_domain, conn, params, nparams, flags) > > and then we could do everything that needs to be done from the platform layer, > then issue a virDomainResume to start the domain on destination, > and maybe a virDomainDestroyFlags on the original domain to terminate it on the source. > > But I would still favor the simplicity and the overall applicability of this series from Trieu Huynh. I still think it'll be great we only do accounting once, and above sounds like a libvirt question that I don't know the answer. Adding Jiri and libvirt list in case we can collect some inputs. If you're looking for this feature, IMHO it'll be great if you can have a look at least from libvirt side to see if there's any blocker for collecting this info from src, then we can evaluate the rest. E.g. there's at least another option to forward statistic data from src to dest. Then we don't worry on mismatched accounting, or when fixing / add new accounting we don't need to do it both sides, which will be awkward. Thanks, > > Thanks, > > Claudio > -- Peter Xu ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 0/2] migration: include timing and RAM stats on destination when query-migrate 2026-04-13 21:07 ` Peter Xu @ 2026-04-16 16:53 ` Trieu Huynh 2026-04-16 18:50 ` Peter Xu 2026-04-22 22:46 ` Fabiano Rosas 1 sibling, 1 reply; 20+ messages in thread From: Trieu Huynh @ 2026-04-16 16:53 UTC (permalink / raw) To: Peter Xu Cc: Claudio Fontana, Fabiano Rosas, qemu-devel, Jim Fehlig, Daniel P. Berrangé On Mon, Apr 13, 2026 at 05:07:00PM -0400, Peter Xu wrote: > On Thu, Apr 09, 2026 at 12:08:34PM +0200, Claudio Fontana wrote: > > On 4/8/26 22:04, Peter Xu wrote: > > > On Mon, Apr 06, 2026 at 11:02:56AM -0300, Fabiano Rosas wrote: > > >> Trieu Huynh <vikingtc4@gmail.com> writes: > > >> > > >>> From: Trieu Huynh <vikingtc4@gmail.com> > > >>> > > >>> When query-migrate is called on the *destination* QEMU after a precopy > > >>> migration completes it returns only {"status": "completed"} — no timing, > > >>> no RAM statistics. The source correctly returns total-time, downtime, > > >>> setup-time, and full ram stats. > > >>> > > >>> This series fixes the gap in two commits: > > >>> > > >>> * commit 1: Track start/end timestamps and per-page-type counters in > > >>> MigrationIncomingState, recorded in the receive path. > > >>> > > >>> * commit 2: Make source-only MigrationStats fields optional in the QAPI > > >>> schema so the destination can populate info->ram with only the fields > > >>> that are meaningful on the receive side. Fields computed from tracked > > >>> counters (transferred, normal, duplicate, mbps, pages-per-second) are > > >>> shown; source-only fields (dirty-sync-count, precopy-bytes, etc.) are > > >>> absent. > > >>> > > >>> The QAPI change (commit 2) is ABI-compatible: optional fields that are > > >>> not set are simply absent from the output. Source-side callers are > > >>> unaffected because populate_ram_info() sets all optional fields via > > >>> has_* setters, so the source output is identical to before. > > >>> > > >>> On dst, {"execute":"query-migrate"} > > >>> * As-is: > > >>> { > > >>> "return": { > > >>> "status": "completed" > > >>> } > > >>> * To-be: > > >>> { > > >>> "return": { > > >>> "status": "completed", > > >>> "total-time": 94, > > >>> "ram": { > > >>> "total": 554508288, > > >>> "pages-per-second": 1440234, > > >>> "page-size": 4096, > > >>> "remaining": 0, > > >>> "mbps": 97.955404255319152, > > >>> "transferred": 1150976, > > >>> "duplicate": 135101, > > >>> "normal-bytes": 1150976, > > >>> "normal": 281 > > >>> } > > >>> } > > >>> } > > >>> > > >>> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3151 > > >>> > > >>> Trieu Huynh (2): > > >>> migration: track timing and received pages in MigrationIncomingState > > >>> migration: expose RAM stats and timing on destination via > > >>> query-migrate > > >>> > > >>> migration/migration.c | 37 +++++++++++++++++++++++++++++++++++++ > > >>> migration/migration.h | 17 +++++++++++++++++ > > >>> migration/ram.c | 3 +++ > > >>> qapi/migration.json | 14 +++++++------- > > >>> 4 files changed, 64 insertions(+), 7 deletions(-) > > >> > > >> Hi, remember to copy the interested people in your series. Using > > >> get_maintainers is correct, but when there's already a discussion about > > >> the topic it's good to add some CCs manually. > > >> > > >> +CC Daniel and Claudio > > > > > > The request is a valid one. Said that, redo accounting on both sides seem > > > to be a duplicated work and overkill to me, if src has everything. > > > > > > Is there a way we can make Libvirt always query the results before src dies > > > and report it somewhere? I recall these info were captured somewhere, do > > > we at least dump them into logs? > > > > > > Thanks, > > > > > > > I am interested in the outcome, but I think the approach shown here is fine, the amount of duplication seems minimal to me. > > > > That said if we _have_ to keep libvirt in the picture, I think I see a possible alternative, but then the feature is not available to anyone that does not use libvirt. At the same time, if libvirt is not used, one is free to keep source QEMU alive and query the stats, like I think Proxmox does. > > The problem is here from QEMU's perspective we have all the information, > and "not quit" is the default behavior for src QEMU. It means by default > all info is available.. this is true for no matter what upper mgmt is in > use. > > > > > So with libvirt, I think we need a way to tell libvirt to keep the source QEMU alive before the actual switchover and the source QEMU is destroyed, > > so that we can do everything that needs to be done from the platform layer before the source QEMU is lost. > > It might go beyond querying for stats using QEMU and libvirt. > > > > Something similar to (and potentially combined with) virDomainMigrateFlags VIR_MIGRATE_PAUSE, for example: > > > > VIR_MIGRATE_NO_SOURCE_SHUTOFF > > > > So one might say: > > > > flags = VIR_MIGRATE_LIVE | VIR_MIGRATE_PERSIST_DEST | VIR_MIGRATE_UNDEFINE_SOURCE | VIR_MIGRATE_PAUSE | VIR_MIGRATE_NO_SOURCE_SHUTOFF > > new_domain = virDomainMigrate3(old_domain, conn, params, nparams, flags) > > > > and then we could do everything that needs to be done from the platform layer, > > then issue a virDomainResume to start the domain on destination, > > and maybe a virDomainDestroyFlags on the original domain to terminate it on the source. > > > > But I would still favor the simplicity and the overall applicability of this series from Trieu Huynh. > > I still think it'll be great we only do accounting once, and above sounds > like a libvirt question that I don't know the answer. Adding Jiri and > libvirt list in case we can collect some inputs. > > If you're looking for this feature, IMHO it'll be great if you can have a > look at least from libvirt side to see if there's any blocker for > collecting this info from src, then we can evaluate the rest. E.g. there's > at least another option to forward statistic data from src to dest. Then > we don't worry on mismatched accounting, or when fixing / add new > accounting we don't need to do it both sides, which will be awkward. Hello Peter, One specific use case where the source is completely unavailable is loading from a migration snapshot (non-live). In this scenario, there is no active source QEMU to query_for_stats. The dst (IMHO) is the only entity that can report how long the loading took and how much data was actually processed from the snapshot file. This use case is a little bit niche but still a valid one though, isn't it? > > Thanks, > > > > > Thanks, > > > > Claudio > > > > -- > Peter Xu > BRs, Trieu Huynh ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 0/2] migration: include timing and RAM stats on destination when query-migrate 2026-04-16 16:53 ` Trieu Huynh @ 2026-04-16 18:50 ` Peter Xu 2026-04-17 10:01 ` Trieu Huynh 0 siblings, 1 reply; 20+ messages in thread From: Peter Xu @ 2026-04-16 18:50 UTC (permalink / raw) To: Trieu Huynh Cc: Claudio Fontana, Fabiano Rosas, qemu-devel, Jim Fehlig, Daniel P. Berrangé On Thu, Apr 16, 2026 at 11:53:55PM +0700, Trieu Huynh wrote: > Hello Peter, Hi, Trieu, > One specific use case where the source is completely unavailable is loading > from a migration snapshot (non-live). > In this scenario, there is no active source QEMU to query_for_stats. The > dst (IMHO) is the only entity that can report how long the loading took > and how much data was actually processed from the snapshot file. > This use case is a little bit niche but still a valid one though, isn't it? Yes it's in general valid, but it also depends. There's a reason that QEMU supported load snapshot over the years and nobody was asking for it. I believe it's because people simply don't need it. Load snapshot, unlike generic form of live migration, doesn't have much uncertainty on its own. It should either succeed after a while, or fail with an error. People should not expect to need to query anything. It will likely, and should, hold true even if with async load snapshot in the future. It is only valid in that it might be useful for developers who works on snapshot, especially tuning the performance. But then it also means we shouldn't expose it to an user API (QMP is in this case). For dev only reportings, we should rely on trace points unless we have good reason to make it visible to users. In general, we don't need to add unnecessary API maint burden to ourselves when not needed. Thanks, -- Peter Xu ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 0/2] migration: include timing and RAM stats on destination when query-migrate 2026-04-16 18:50 ` Peter Xu @ 2026-04-17 10:01 ` Trieu Huynh 0 siblings, 0 replies; 20+ messages in thread From: Trieu Huynh @ 2026-04-17 10:01 UTC (permalink / raw) To: Peter Xu Cc: Claudio Fontana, Fabiano Rosas, qemu-devel, Jim Fehlig, Daniel P. Berrangé On Thu, Apr 16, 2026 at 02:50:29PM -0400, Peter Xu wrote: > On Thu, Apr 16, 2026 at 11:53:55PM +0700, Trieu Huynh wrote: > > Hello Peter, > > Hi, Trieu, > > > One specific use case where the source is completely unavailable is loading > > from a migration snapshot (non-live). > > In this scenario, there is no active source QEMU to query_for_stats. The > > dst (IMHO) is the only entity that can report how long the loading took > > and how much data was actually processed from the snapshot file. > > This use case is a little bit niche but still a valid one though, isn't it? > > Yes it's in general valid, but it also depends. There's a reason that QEMU > supported load snapshot over the years and nobody was asking for it. I > believe it's because people simply don't need it. > > Load snapshot, unlike generic form of live migration, doesn't have much > uncertainty on its own. It should either succeed after a while, or fail > with an error. People should not expect to need to query anything. > > It will likely, and should, hold true even if with async load snapshot in > the future. > > It is only valid in that it might be useful for developers who works on > snapshot, especially tuning the performance. But then it also means we > shouldn't expose it to an user API (QMP is in this case). For dev only > reportings, we should rely on trace points unless we have good reason to > make it visible to users. > > In general, we don't need to add unnecessary API maint burden to ourselves > when not needed. Thanks for your detailed feedback, I appreciate it. Taking your comments from the other thread into account, I might drop this series for now. As I don't have a deep understanding of libvirt or similar management tools, the specific use case remains a bit ambiguous to me. I’ll leave this series/patches here for now in case Claudio and other experts want to chime in or if further concerns arise. Thanks again! > > Thanks, > > -- > Peter Xu > Brs, Trieu Huynh ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 0/2] migration: include timing and RAM stats on destination when query-migrate 2026-04-13 21:07 ` Peter Xu 2026-04-16 16:53 ` Trieu Huynh @ 2026-04-22 22:46 ` Fabiano Rosas 2026-04-23 13:22 ` Peter Xu 1 sibling, 1 reply; 20+ messages in thread From: Fabiano Rosas @ 2026-04-22 22:46 UTC (permalink / raw) To: Peter Xu, Claudio Fontana Cc: Trieu Huynh, qemu-devel, Jim Fehlig, Daniel P. Berrangé Peter Xu <peterx@redhat.com> writes: > On Thu, Apr 09, 2026 at 12:08:34PM +0200, Claudio Fontana wrote: >> On 4/8/26 22:04, Peter Xu wrote: >> > On Mon, Apr 06, 2026 at 11:02:56AM -0300, Fabiano Rosas wrote: >> >> Trieu Huynh <vikingtc4@gmail.com> writes: >> >> >> >>> From: Trieu Huynh <vikingtc4@gmail.com> >> >>> >> >>> When query-migrate is called on the *destination* QEMU after a precopy >> >>> migration completes it returns only {"status": "completed"} — no timing, >> >>> no RAM statistics. The source correctly returns total-time, downtime, >> >>> setup-time, and full ram stats. >> >>> >> >>> This series fixes the gap in two commits: >> >>> >> >>> * commit 1: Track start/end timestamps and per-page-type counters in >> >>> MigrationIncomingState, recorded in the receive path. >> >>> >> >>> * commit 2: Make source-only MigrationStats fields optional in the QAPI >> >>> schema so the destination can populate info->ram with only the fields >> >>> that are meaningful on the receive side. Fields computed from tracked >> >>> counters (transferred, normal, duplicate, mbps, pages-per-second) are >> >>> shown; source-only fields (dirty-sync-count, precopy-bytes, etc.) are >> >>> absent. >> >>> >> >>> The QAPI change (commit 2) is ABI-compatible: optional fields that are >> >>> not set are simply absent from the output. Source-side callers are >> >>> unaffected because populate_ram_info() sets all optional fields via >> >>> has_* setters, so the source output is identical to before. >> >>> >> >>> On dst, {"execute":"query-migrate"} >> >>> * As-is: >> >>> { >> >>> "return": { >> >>> "status": "completed" >> >>> } >> >>> * To-be: >> >>> { >> >>> "return": { >> >>> "status": "completed", >> >>> "total-time": 94, >> >>> "ram": { >> >>> "total": 554508288, >> >>> "pages-per-second": 1440234, >> >>> "page-size": 4096, >> >>> "remaining": 0, >> >>> "mbps": 97.955404255319152, >> >>> "transferred": 1150976, >> >>> "duplicate": 135101, >> >>> "normal-bytes": 1150976, >> >>> "normal": 281 >> >>> } >> >>> } >> >>> } >> >>> >> >>> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3151 >> >>> >> >>> Trieu Huynh (2): >> >>> migration: track timing and received pages in MigrationIncomingState >> >>> migration: expose RAM stats and timing on destination via >> >>> query-migrate >> >>> >> >>> migration/migration.c | 37 +++++++++++++++++++++++++++++++++++++ >> >>> migration/migration.h | 17 +++++++++++++++++ >> >>> migration/ram.c | 3 +++ >> >>> qapi/migration.json | 14 +++++++------- >> >>> 4 files changed, 64 insertions(+), 7 deletions(-) >> >> >> >> Hi, remember to copy the interested people in your series. Using >> >> get_maintainers is correct, but when there's already a discussion about >> >> the topic it's good to add some CCs manually. >> >> >> >> +CC Daniel and Claudio >> > >> > The request is a valid one. Said that, redo accounting on both sides seem >> > to be a duplicated work and overkill to me, if src has everything. >> > >> > Is there a way we can make Libvirt always query the results before src dies >> > and report it somewhere? I recall these info were captured somewhere, do >> > we at least dump them into logs? >> > >> > Thanks, >> > >> >> I am interested in the outcome, but I think the approach shown here is fine, the amount of duplication seems minimal to me. >> >> That said if we _have_ to keep libvirt in the picture, I think I see a possible alternative, but then the feature is not available to anyone that does not use libvirt. At the same time, if libvirt is not used, one is free to keep source QEMU alive and query the stats, like I think Proxmox does. > > The problem is here from QEMU's perspective we have all the information, > and "not quit" is the default behavior for src QEMU. It means by default > all info is available.. this is true for no matter what upper mgmt is in > use. > >> >> So with libvirt, I think we need a way to tell libvirt to keep the source QEMU alive before the actual switchover and the source QEMU is destroyed, >> so that we can do everything that needs to be done from the platform layer before the source QEMU is lost. >> It might go beyond querying for stats using QEMU and libvirt. >> >> Something similar to (and potentially combined with) virDomainMigrateFlags VIR_MIGRATE_PAUSE, for example: >> >> VIR_MIGRATE_NO_SOURCE_SHUTOFF >> >> So one might say: >> >> flags = VIR_MIGRATE_LIVE | VIR_MIGRATE_PERSIST_DEST | VIR_MIGRATE_UNDEFINE_SOURCE | VIR_MIGRATE_PAUSE | VIR_MIGRATE_NO_SOURCE_SHUTOFF >> new_domain = virDomainMigrate3(old_domain, conn, params, nparams, flags) >> >> and then we could do everything that needs to be done from the platform layer, >> then issue a virDomainResume to start the domain on destination, >> and maybe a virDomainDestroyFlags on the original domain to terminate it on the source. >> >> But I would still favor the simplicity and the overall applicability of this series from Trieu Huynh. > > I still think it'll be great we only do accounting once, and above sounds > like a libvirt question that I don't know the answer. Adding Jiri and > libvirt list in case we can collect some inputs. > > If you're looking for this feature, IMHO it'll be great if you can have a > look at least from libvirt side to see if there's any blocker for > collecting this info from src, then we can evaluate the rest. E.g. there's > at least another option to forward statistic data from src to dest. Then > we don't worry on mismatched accounting, or when fixing / add new > accounting we don't need to do it both sides, which will be awkward. I came to the thread to suggest this, but I see you already mentioned it. I'm not sure what you mean by "forward", but I was actually thinking "migrate". Do we have precedent for migrating a QAPI object? Do we want to have it? =) Put that MigrationInfo at the end of the stream and we'd have a nice symmetric interface for query-migrate in both src and dst. (we have plans to deal with QEMU_VM_VMDESCRIPTION at some point anyway, we could maybe rework that end section into something useful) > > Thanks, > >> >> Thanks, >> >> Claudio >> ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 0/2] migration: include timing and RAM stats on destination when query-migrate 2026-04-22 22:46 ` Fabiano Rosas @ 2026-04-23 13:22 ` Peter Xu 0 siblings, 0 replies; 20+ messages in thread From: Peter Xu @ 2026-04-23 13:22 UTC (permalink / raw) To: Fabiano Rosas Cc: Claudio Fontana, Trieu Huynh, qemu-devel, Jim Fehlig, Daniel P. Berrangé On Wed, Apr 22, 2026 at 07:46:45PM -0300, Fabiano Rosas wrote: > I came to the thread to suggest this, but I see you already mentioned > it. I'm not sure what you mean by "forward", but I was actually thinking > "migrate". Do we have precedent for migrating a QAPI object? Do we want > to have it? =) Put that MigrationInfo at the end of the stream and we'd > have a nice symmetric interface for query-migrate in both src and dst. Do we need to "migrate" it, say, using VMSD data structures? We could, but I'm not yet sure if it's a good idea, especially if we should make that as part of ABI. It may add some unnecessary burden to us. > > (we have plans to deal with QEMU_VM_VMDESCRIPTION at some point anyway, > we could maybe rework that end section into something useful) That might still be a bit early phase to contain all the stats info. For example, the real downtime needs to be calculated until src receiving the ACK, that should be after dest receive QEMU_VM_VMDESCRIPTION, IIRC. So if we want to at least forward the downtime info, that might be too early. In all cases, I still think we should evaluate libvirt solution before any other explorations.. because so far we still don't know why it can't be done, and it's the simplest afaiu. -- Peter Xu ^ permalink raw reply [flat|nested] 20+ messages in thread
end of thread, other threads:[~2026-04-23 13:23 UTC | newest] Thread overview: 20+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-04-05 15:26 [PATCH 0/2] migration: include timing and RAM stats on destination when query-migrate Trieu Huynh 2026-04-05 15:26 ` [PATCH 1/2] migration: track timing and received pages in MigrationIncomingState Trieu Huynh 2026-04-07 12:02 ` Claudio Fontana 2026-04-07 12:11 ` Claudio Fontana 2026-04-07 18:28 ` Trieu Huynh 2026-04-05 15:26 ` [PATCH 2/2] migration: expose RAM stats and timing on destination via query-migrate Trieu Huynh 2026-04-08 16:14 ` Claudio Fontana 2026-04-08 19:38 ` Trieu Huynh 2026-04-06 14:02 ` [PATCH 0/2] migration: include timing and RAM stats on destination when query-migrate Fabiano Rosas 2026-04-08 20:04 ` Peter Xu 2026-04-09 10:08 ` Claudio Fontana 2026-04-09 13:08 ` Fabian Grünbichler 2026-04-09 13:17 ` Claudio Fontana 2026-04-09 13:29 ` Fabian Grünbichler 2026-04-13 21:07 ` Peter Xu 2026-04-16 16:53 ` Trieu Huynh 2026-04-16 18:50 ` Peter Xu 2026-04-17 10:01 ` Trieu Huynh 2026-04-22 22:46 ` Fabiano Rosas 2026-04-23 13:22 ` Peter Xu
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.