All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/18] migration: MigrationParameters changes
@ 2026-09-02 22:15 Fabiano Rosas
  2026-09-02 22:15 ` [PATCH 01/18] checkpatch: Fix checking of newlines in error messages Fabiano Rosas
                   ` (17 more replies)
  0 siblings, 18 replies; 50+ messages in thread
From: Fabiano Rosas @ 2026-09-02 22:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: Peter Xu

Hi, this contains the pending work from my previous two series to
reduce duplication in the migration parameters handling and unify
migration parameters and capabilities.

Supersedes these series:
- [PATCH v3 00/51] migration: Unify capabilities and parameters
https://lore.kernel.org/r/20251215220041.12657-1-farosas@suse.de

- [PATCH v2 0/9] qapi: Use visitors for migration parameters handling
https://lore.kernel.org/r/20260202224101.20568-1-farosas@suse.de

I held on to the "pass full config to migration commands" changes for
now, let's put this^ scaffolding in first.

Changes from previous versions:

1) hacky qapi dealloc visitor replaced with a simple merge using
QDict.

  Suggested as one of the alternatives by Markus in:
  https://lore.kernel.org/r/871pio3d3h.fsf@pond.sub.org

This^ made me realise we've been doing manual checking in a lot of
places where a QAPI->QDict serialization would do the work
automatically, so:

2) New qtest to validate migration HMP commands.

  Good to have in general, but also this series touches that code
  heavily.

3) Rewrite of hmp_migrate_set_parameters, hmp_info_migrate_parameters
and hmp_migrate_set_parameter_completion.

  I want to remove all manual handling of migration parameters. These
  commands were the last heavy users of the "if params->has_foo,
  handle params->foo" pattern. We lose some user-friendliness here,
  but hopefully it's ok.

4) Remove MigrationParameter (singular).

  With this, migration.json now has only one place to define and
  document migration parameters, the MigrationParameters (plural).

5) New routine to validate has_* fields.

  Due to the above change, we lose MIGRATION_PARAMETER__MAX. Add a
  routine that ensures s->parameters has all has_ fields set to
  true. This is required for all the QAPI cloning and merging, etc.

CI run: https://gitlab.com/farosas/qemu/-/pipelines/2814719051

Fabiano Rosas (18):
  checkpatch: Fix checking of newlines in error messages
  migration/options.c: Don't export migrate_tls_opts_free
  migration: Rename variables in qmp_migrate_set_parameters
  migration: Use QAPI_CLONE_MEMBERS in migrate_params_apply
  migration: Merge parameter structs instead of assigning one by one
  migration: Open code migrate_params_apply
  migration: Stop freeing s->parameters members individually
  migration: Use migrate_params_free during finalize
  tests/qtest/migration: Add a test for HMP
  migration: Validate that all params are set for query
  migration: Use keyval input visitor in HMP set command
  migration: Change HMP 'info migrate_parameters' output
  migration: Use output visitor in info command
  migration: Rewrite migrate_set_parameter_completion using QDict
  qapi/migration: Remove MigrationParameter
  migration: Add capabilities into MigrationParameters
  migration: Remove s->capabilities
  qapi/migration: Deprecate capabilities commands

 docs/about/deprecated.rst          |  13 +
 migration/migration-hmp-cmds.c     | 465 ++++++----------
 migration/migration.c              |  17 +-
 migration/migration.h              |   2 +-
 migration/options.c                | 848 ++++++++++++++---------------
 migration/options.h                |  26 +-
 migration/savevm.c                 |   8 +-
 qapi/migration.json                | 170 ++++--
 scripts/checkpatch.pl              |  11 +-
 tests/qemu-iotests/300             |  20 +-
 tests/qtest/migration/misc-tests.c | 460 ++++++++++++++++
 11 files changed, 1218 insertions(+), 822 deletions(-)

-- 
2.53.0



^ permalink raw reply	[flat|nested] 50+ messages in thread

* [PATCH 01/18] checkpatch: Fix checking of newlines in error messages
  2026-09-02 22:15 [PATCH 00/18] migration: MigrationParameters changes Fabiano Rosas
@ 2026-09-02 22:15 ` Fabiano Rosas
  2026-09-03 17:37   ` Peter Xu
  2026-09-04 14:00   ` Markus Armbruster
  2026-09-02 22:15 ` [PATCH 02/18] migration/options.c: Don't export migrate_tls_opts_free Fabiano Rosas
                   ` (16 subsequent siblings)
  17 siblings, 2 replies; 50+ messages in thread
From: Fabiano Rosas @ 2026-09-02 22:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: Peter Xu, Chao Liu

Using newlines in the g_test_message is fine. It automatically adds
the '#' required by the TAP protocol to the start of each line.

Relax the regex for this function, but still forbid a trailing newline
because it's added automatically and usually not what the user wants.

Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
 scripts/checkpatch.pl | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl
index 03f35e75012..fd4534b3a1e 100755
--- a/scripts/checkpatch.pl
+++ b/scripts/checkpatch.pl
@@ -3303,13 +3303,20 @@ sub process {
 					 info_vreport|
 					 error_report|
 					 warn_report|
-					 info_report|
-					 g_test_message}x;
+					 info_report}x;
 
 		if ($rawline =~ /\b(?:$qemu_error_funcs)\s*\(.*\".*\\n/) {
 			ERROR("Error messages should not contain newlines\n" . $herecurr);
 		}
 
+		# No newlines at the end
+		my $trail_newline_error_funcs = qr{g_test_message}x;
+
+		if ($rawline =~ /\b(?:$trail_newline_error_funcs)\(.*\".*\\n\"/) {
+		    ERROR("Error messages should not contain trailing " .
+			  "newlines\n" . $herecurr);
+		}
+
 		# Continue checking for error messages that contains newlines.
 		# This check handles cases where string literals are spread
 		# over multiple lines.
-- 
2.53.0



^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 02/18] migration/options.c: Don't export migrate_tls_opts_free
  2026-09-02 22:15 [PATCH 00/18] migration: MigrationParameters changes Fabiano Rosas
  2026-09-02 22:15 ` [PATCH 01/18] checkpatch: Fix checking of newlines in error messages Fabiano Rosas
@ 2026-09-02 22:15 ` Fabiano Rosas
  2026-09-02 22:15 ` [PATCH 03/18] migration: Rename variables in qmp_migrate_set_parameters Fabiano Rosas
                   ` (15 subsequent siblings)
  17 siblings, 0 replies; 50+ messages in thread
From: Fabiano Rosas @ 2026-09-02 22:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: Peter Xu

The migrate_tls_opts_free function was never used outside
options.c. Make it static.

Reviewed-by: Peter Xu <peterx@redhat.com>
Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
 migration/options.c | 2 +-
 migration/options.h | 1 -
 2 files changed, 1 insertion(+), 2 deletions(-)

diff --git a/migration/options.c b/migration/options.c
index d2575edb6cb..79a61ac60d6 100644
--- a/migration/options.c
+++ b/migration/options.c
@@ -1055,7 +1055,7 @@ AnnounceParameters *migrate_announce_params(void)
     return &ap;
 }
 
-void migrate_tls_opts_free(MigrationParameters *params)
+static void migrate_tls_opts_free(MigrationParameters *params)
 {
     qapi_free_StrOrNull(params->tls_creds);
     qapi_free_StrOrNull(params->tls_hostname);
diff --git a/migration/options.h b/migration/options.h
index b46221998a0..c272eb62084 100644
--- a/migration/options.h
+++ b/migration/options.h
@@ -93,5 +93,4 @@ uint64_t migrate_rdma_chunk_size(void);
 
 bool migrate_params_check(MigrationParameters *params, Error **errp);
 void migrate_params_init(MigrationParameters *params);
-void migrate_tls_opts_free(MigrationParameters *params);
 #endif
-- 
2.53.0



^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 03/18] migration: Rename variables in qmp_migrate_set_parameters
  2026-09-02 22:15 [PATCH 00/18] migration: MigrationParameters changes Fabiano Rosas
  2026-09-02 22:15 ` [PATCH 01/18] checkpatch: Fix checking of newlines in error messages Fabiano Rosas
  2026-09-02 22:15 ` [PATCH 02/18] migration/options.c: Don't export migrate_tls_opts_free Fabiano Rosas
@ 2026-09-02 22:15 ` Fabiano Rosas
  2026-09-03 17:44   ` Peter Xu
  2026-09-02 22:15 ` [PATCH 04/18] migration: Use QAPI_CLONE_MEMBERS in migrate_params_apply Fabiano Rosas
                   ` (14 subsequent siblings)
  17 siblings, 1 reply; 50+ messages in thread
From: Fabiano Rosas @ 2026-09-02 22:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: Peter Xu

Give the variables in qmp_migrate_set_parameters more semantic
names.

s/params/input/
this is the user input from qapi

s/tmp/new/
this is the combination of the current parameters and the input

Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
 migration/options.c | 24 ++++++++++++------------
 1 file changed, 12 insertions(+), 12 deletions(-)

diff --git a/migration/options.c b/migration/options.c
index 79a61ac60d6..388cb07dd0f 100644
--- a/migration/options.c
+++ b/migration/options.c
@@ -1583,9 +1583,9 @@ static void migrate_params_apply(MigrationParameters *params)
     }
 }
 
-void qmp_migrate_set_parameters(MigrationParameters *params, Error **errp)
+void qmp_migrate_set_parameters(MigrationParameters *input, Error **errp)
 {
-    MigrationParameters tmp;
+    MigrationParameters new;
 
     /*
      * Convert QTYPE_QNULL and NULL to the empty string (""). Even
@@ -1595,18 +1595,18 @@ void qmp_migrate_set_parameters(MigrationParameters *params, Error **errp)
      * the options to the rest of the migration code already use
      * return NULL when the empty string is found.
      */
-    tls_opt_to_str(params->tls_creds);
-    tls_opt_to_str(params->tls_hostname);
-    tls_opt_to_str(params->tls_authz);
+    tls_opt_to_str(input->tls_creds);
+    tls_opt_to_str(input->tls_hostname);
+    tls_opt_to_str(input->tls_authz);
 
-    migrate_params_test_apply(params, &tmp);
+    migrate_params_test_apply(input, &new);
 
-    if (migrate_params_check(&tmp, errp)) {
-        migrate_params_apply(params);
-        migrate_post_update_params(params, errp);
+    if (migrate_params_check(&new, errp)) {
+        migrate_params_apply(input);
+        migrate_post_update_params(input, errp);
     }
 
-    migrate_tls_opts_free(&tmp);
-    qapi_free_BitmapMigrationNodeAliasList(tmp.block_bitmap_mapping);
-    qapi_free_strList(tmp.cpr_exec_command);
+    migrate_tls_opts_free(&new);
+    qapi_free_BitmapMigrationNodeAliasList(new.block_bitmap_mapping);
+    qapi_free_strList(new.cpr_exec_command);
 }
-- 
2.53.0



^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 04/18] migration: Use QAPI_CLONE_MEMBERS in migrate_params_apply
  2026-09-02 22:15 [PATCH 00/18] migration: MigrationParameters changes Fabiano Rosas
                   ` (2 preceding siblings ...)
  2026-09-02 22:15 ` [PATCH 03/18] migration: Rename variables in qmp_migrate_set_parameters Fabiano Rosas
@ 2026-09-02 22:15 ` Fabiano Rosas
  2026-09-02 22:15 ` [PATCH 05/18] migration: Merge parameter structs instead of assigning one by one Fabiano Rosas
                   ` (13 subsequent siblings)
  17 siblings, 0 replies; 50+ messages in thread
From: Fabiano Rosas @ 2026-09-02 22:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: Peter Xu, Prasad Pandit

Instead of setting parameters one by one, use the temporary object,
which already contains the current migration parameters plus the new
ones and was just validated by migration_params_check(). Use cloning
to overwrite it.

This avoids the need to alter this function every time a new parameter
is added.

Since parameters are not individually checked anymore, the setting of
s->has_block_bitmap_mapping moves into migrate_post_update_params().

Reviewed-by: Peter Xu <peterx@redhat.com>
Reviewed-by: Prasad Pandit <pjp@fedoraproject.org>
Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
 migration/options.c | 141 +++++---------------------------------------
 1 file changed, 14 insertions(+), 127 deletions(-)

diff --git a/migration/options.c b/migration/options.c
index 388cb07dd0f..6b787950808 100644
--- a/migration/options.c
+++ b/migration/options.c
@@ -1160,6 +1160,10 @@ static void migrate_post_update_params(MigrationParameters *new, Error **errp)
             migration_rate_set(new->max_postcopy_bandwidth);
         }
     }
+
+    if (new->has_block_bitmap_mapping) {
+        s->has_block_bitmap_mapping = true;
+    }
 }
 
 /*
@@ -1451,136 +1455,19 @@ static void migrate_params_test_apply(MigrationParameters *params,
     }
 }
 
+/*
+ * Caller must ensure the has_* fields of @params are true so they all
+ * get copied and the pointer members don't dangle.
+ */
 static void migrate_params_apply(MigrationParameters *params)
 {
     MigrationState *s = migrate_get_current();
+    MigrationParameters *cur = &s->parameters;
 
-    /* TODO use QAPI_CLONE() instead of duplicating it inline */
-
-    if (params->has_throttle_trigger_threshold) {
-        s->parameters.throttle_trigger_threshold = params->throttle_trigger_threshold;
-    }
-
-    if (params->has_cpu_throttle_initial) {
-        s->parameters.cpu_throttle_initial = params->cpu_throttle_initial;
-    }
-
-    if (params->has_cpu_throttle_increment) {
-        s->parameters.cpu_throttle_increment = params->cpu_throttle_increment;
-    }
-
-    if (params->has_cpu_throttle_tailslow) {
-        s->parameters.cpu_throttle_tailslow = params->cpu_throttle_tailslow;
-    }
-
-    if (params->tls_creds) {
-        qapi_free_StrOrNull(s->parameters.tls_creds);
-        s->parameters.tls_creds = QAPI_CLONE(StrOrNull, params->tls_creds);
-    }
-
-    if (params->tls_hostname) {
-        qapi_free_StrOrNull(s->parameters.tls_hostname);
-        s->parameters.tls_hostname = QAPI_CLONE(StrOrNull,
-                                                params->tls_hostname);
-    }
-
-    if (params->tls_authz) {
-        qapi_free_StrOrNull(s->parameters.tls_authz);
-        s->parameters.tls_authz = QAPI_CLONE(StrOrNull, params->tls_authz);
-    }
-
-    if (params->has_max_bandwidth) {
-        s->parameters.max_bandwidth = params->max_bandwidth;
-    }
-
-    if (params->has_avail_switchover_bandwidth) {
-        s->parameters.avail_switchover_bandwidth = params->avail_switchover_bandwidth;
-    }
-
-    if (params->has_downtime_limit) {
-        s->parameters.downtime_limit = params->downtime_limit;
-    }
-
-    if (params->has_x_checkpoint_delay) {
-        s->parameters.x_checkpoint_delay = params->x_checkpoint_delay;
-    }
-
-    if (params->has_multifd_channels) {
-        s->parameters.multifd_channels = params->multifd_channels;
-    }
-    if (params->has_multifd_compression) {
-        s->parameters.multifd_compression = params->multifd_compression;
-    }
-    if (params->has_multifd_qatzip_level) {
-        s->parameters.multifd_qatzip_level = params->multifd_qatzip_level;
-    }
-    if (params->has_multifd_zlib_level) {
-        s->parameters.multifd_zlib_level = params->multifd_zlib_level;
-    }
-    if (params->has_multifd_zstd_level) {
-        s->parameters.multifd_zstd_level = params->multifd_zstd_level;
-    }
-    if (params->has_xbzrle_cache_size) {
-        s->parameters.xbzrle_cache_size = params->xbzrle_cache_size;
-    }
-    if (params->has_max_postcopy_bandwidth) {
-        s->parameters.max_postcopy_bandwidth = params->max_postcopy_bandwidth;
-    }
-    if (params->has_max_cpu_throttle) {
-        s->parameters.max_cpu_throttle = params->max_cpu_throttle;
-    }
-    if (params->has_announce_initial) {
-        s->parameters.announce_initial = params->announce_initial;
-    }
-    if (params->has_announce_max) {
-        s->parameters.announce_max = params->announce_max;
-    }
-    if (params->has_announce_rounds) {
-        s->parameters.announce_rounds = params->announce_rounds;
-    }
-    if (params->has_announce_step) {
-        s->parameters.announce_step = params->announce_step;
-    }
-
-    if (params->has_block_bitmap_mapping) {
-        qapi_free_BitmapMigrationNodeAliasList(
-            s->parameters.block_bitmap_mapping);
-
-        s->has_block_bitmap_mapping = true;
-        s->parameters.block_bitmap_mapping =
-            QAPI_CLONE(BitmapMigrationNodeAliasList,
-                       params->block_bitmap_mapping);
-    }
-
-    if (params->has_x_vcpu_dirty_limit_period) {
-        s->parameters.x_vcpu_dirty_limit_period =
-            params->x_vcpu_dirty_limit_period;
-    }
-    if (params->has_vcpu_dirty_limit) {
-        s->parameters.vcpu_dirty_limit = params->vcpu_dirty_limit;
-    }
-
-    if (params->has_mode) {
-        s->parameters.mode = params->mode;
-    }
-
-    if (params->has_zero_page_detection) {
-        s->parameters.zero_page_detection = params->zero_page_detection;
-    }
-
-    if (params->has_direct_io) {
-        s->parameters.direct_io = params->direct_io;
-    }
-
-    if (params->has_x_rdma_chunk_size) {
-        s->parameters.x_rdma_chunk_size = params->x_rdma_chunk_size;
-    }
-
-    if (params->has_cpr_exec_command) {
-        qapi_free_strList(s->parameters.cpr_exec_command);
-        s->parameters.cpr_exec_command =
-            QAPI_CLONE(strList, params->cpr_exec_command);
-    }
+    migrate_tls_opts_free(cur);
+    qapi_free_BitmapMigrationNodeAliasList(cur->block_bitmap_mapping);
+    qapi_free_strList(cur->cpr_exec_command);
+    QAPI_CLONE_MEMBERS(MigrationParameters, cur, params);
 }
 
 void qmp_migrate_set_parameters(MigrationParameters *input, Error **errp)
@@ -1602,7 +1489,7 @@ void qmp_migrate_set_parameters(MigrationParameters *input, Error **errp)
     migrate_params_test_apply(input, &new);
 
     if (migrate_params_check(&new, errp)) {
-        migrate_params_apply(input);
+        migrate_params_apply(&new);
         migrate_post_update_params(input, errp);
     }
 
-- 
2.53.0



^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 05/18] migration: Merge parameter structs instead of assigning one by one
  2026-09-02 22:15 [PATCH 00/18] migration: MigrationParameters changes Fabiano Rosas
                   ` (3 preceding siblings ...)
  2026-09-02 22:15 ` [PATCH 04/18] migration: Use QAPI_CLONE_MEMBERS in migrate_params_apply Fabiano Rosas
@ 2026-09-02 22:15 ` Fabiano Rosas
  2026-09-03 18:20   ` Peter Xu
  2026-09-02 22:15 ` [PATCH 06/18] migration: Open code migrate_params_apply Fabiano Rosas
                   ` (12 subsequent siblings)
  17 siblings, 1 reply; 50+ messages in thread
From: Fabiano Rosas @ 2026-09-02 22:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: Peter Xu

Convert the code in migrate_params_test_apply() from an open-coded
copy of every migration parameter to a merge operation using QAPI
visitors and QDict.

The purpose of that routine is to update a temporary structure
(pre-populated with the current migration parameters), with the values
received from the user via QAPI. As a result, the temporary structure
will then contain the "to be applied" parameters and it's validated
before being used to overwrite the parameters currently in use.

The update is currently done as follows:

where 'params' is the user input from QAPI,
for each parameter:

  a) check if the option is present
     params->has_<name> == true
     params-><name> != NULL // for strings

  b) if the parameter is a pointer, free the to-be-assigned member and
     allocate memory for the copy from params

  c) assign the user provided value to the temporary structure.

Step (a) is the same in principle as what the QAPI visitors do at
visit_type_MigrationParameters_members().

Steps (b) and (c) are roughly the same as what the QDict
implementation does when qdict_del() and qdict_put_obj() are combined.

Therefore, replace the open-coded function with
migrate_params_merge(), which achieves the same goal, but uses
visitors and QDict. This hides the details of QAPI (has_*) from the
migration code and avoids the need to update
migrate_params_test_apply() every time a new migration parameter is
added.

Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
 migration/options.c | 201 +++++++++++++++-----------------------------
 1 file changed, 66 insertions(+), 135 deletions(-)

diff --git a/migration/options.c b/migration/options.c
index 6b787950808..de091d2eee3 100644
--- a/migration/options.c
+++ b/migration/options.c
@@ -20,6 +20,9 @@
 #include "qapi/qapi-commands-migration.h"
 #include "qapi/qapi-visit-migration.h"
 #include "qapi/qmp/qerror.h"
+#include "qapi/qobject-input-visitor.h"
+#include "qapi/qobject-output-visitor.h"
+#include "qobject/qdict.h"
 #include "qobject/qnull.h"
 #include "system/runstate.h"
 #include "migration/colo.h"
@@ -1074,6 +1077,28 @@ static void tls_opt_to_str(StrOrNull *opt)
     opt->u.s = g_strdup("");
 }
 
+static QDict *migrate_params_to_dict(MigrationParameters *p, Error **errp)
+{
+    QObject *obj = NULL;
+    Visitor *v = qobject_output_visitor_new(&obj);
+
+    if (visit_type_MigrationParameters(v, NULL, &p, errp)) {
+        visit_complete(v, &obj);
+    }
+    visit_free(v);
+    return qobject_to(QDict, obj);
+}
+
+static MigrationParameters *migrate_params_from_dict(QDict *d, Error **errp)
+{
+    Visitor *v = qobject_input_visitor_new(QOBJECT(d));
+    MigrationParameters *tmp = NULL;
+
+    visit_type_MigrationParameters(v, NULL, &tmp, errp);
+    visit_free(v);
+    return tmp;
+}
+
 /*
  * query-migrate-parameters expects all members of MigrationParameters
  * to be present, but we cannot mark them non-optional in QAPI because
@@ -1166,6 +1191,39 @@ static void migrate_post_update_params(MigrationParameters *new, Error **errp)
     }
 }
 
+static bool migrate_params_merge(MigrationParameters *in1,
+                                 MigrationParameters *in2,
+                                 MigrationParameters **out,
+                                 Error **errp)
+{
+    g_autoptr(QDict) d1 = NULL;
+    g_autoptr(QDict) d2 = NULL;
+    const QDictEntry *e;
+
+    d1 = migrate_params_to_dict(in1, errp);
+    if (!d1) {
+        return false;
+    }
+
+    d2 = migrate_params_to_dict(in2, errp);
+    if (!d2) {
+        return false;
+    }
+
+    for (e = qdict_first(d2); e; e = qdict_next(d2, e)) {
+        const char *key = qdict_entry_key(e);
+        QObject *value = qdict_entry_value(e);
+
+        qdict_del(d1, key);
+        qobject_ref(value);
+        qdict_put_obj(d1, key, value);
+    }
+
+    *out = migrate_params_from_dict(d1, errp);
+
+    return !!*out;
+}
+
 /*
  * Check whether the parameters are valid. Error will be put into errp
  * (if provided). Return true if valid, otherwise false.
@@ -1328,133 +1386,6 @@ bool migrate_params_check(MigrationParameters *params, Error **errp)
     return true;
 }
 
-static void migrate_params_test_apply(MigrationParameters *params,
-                                      MigrationParameters *dest)
-{
-    MigrationState *s = migrate_get_current();
-
-    QAPI_CLONE_MEMBERS(MigrationParameters, dest, &s->parameters);
-
-    if (params->has_throttle_trigger_threshold) {
-        dest->throttle_trigger_threshold = params->throttle_trigger_threshold;
-    }
-
-    if (params->has_cpu_throttle_initial) {
-        dest->cpu_throttle_initial = params->cpu_throttle_initial;
-    }
-
-    if (params->has_cpu_throttle_increment) {
-        dest->cpu_throttle_increment = params->cpu_throttle_increment;
-    }
-
-    if (params->has_cpu_throttle_tailslow) {
-        dest->cpu_throttle_tailslow = params->cpu_throttle_tailslow;
-    }
-
-    if (params->tls_creds) {
-        qapi_free_StrOrNull(dest->tls_creds);
-        dest->tls_creds = QAPI_CLONE(StrOrNull, params->tls_creds);
-    }
-
-    if (params->tls_hostname) {
-        qapi_free_StrOrNull(dest->tls_hostname);
-        dest->tls_hostname = QAPI_CLONE(StrOrNull, params->tls_hostname);
-    }
-
-    if (params->tls_authz) {
-        qapi_free_StrOrNull(dest->tls_authz);
-        dest->tls_authz = QAPI_CLONE(StrOrNull, params->tls_authz);
-    }
-
-    if (params->has_max_bandwidth) {
-        dest->max_bandwidth = params->max_bandwidth;
-    }
-
-    if (params->has_avail_switchover_bandwidth) {
-        dest->avail_switchover_bandwidth = params->avail_switchover_bandwidth;
-    }
-
-    if (params->has_downtime_limit) {
-        dest->downtime_limit = params->downtime_limit;
-    }
-
-    if (params->has_x_checkpoint_delay) {
-        dest->x_checkpoint_delay = params->x_checkpoint_delay;
-    }
-
-    if (params->has_multifd_channels) {
-        dest->multifd_channels = params->multifd_channels;
-    }
-    if (params->has_multifd_compression) {
-        dest->multifd_compression = params->multifd_compression;
-    }
-    if (params->has_multifd_qatzip_level) {
-        dest->multifd_qatzip_level = params->multifd_qatzip_level;
-    }
-    if (params->has_multifd_zlib_level) {
-        dest->multifd_zlib_level = params->multifd_zlib_level;
-    }
-    if (params->has_multifd_zstd_level) {
-        dest->multifd_zstd_level = params->multifd_zstd_level;
-    }
-    if (params->has_xbzrle_cache_size) {
-        dest->xbzrle_cache_size = params->xbzrle_cache_size;
-    }
-    if (params->has_max_postcopy_bandwidth) {
-        dest->max_postcopy_bandwidth = params->max_postcopy_bandwidth;
-    }
-    if (params->has_max_cpu_throttle) {
-        dest->max_cpu_throttle = params->max_cpu_throttle;
-    }
-    if (params->has_announce_initial) {
-        dest->announce_initial = params->announce_initial;
-    }
-    if (params->has_announce_max) {
-        dest->announce_max = params->announce_max;
-    }
-    if (params->has_announce_rounds) {
-        dest->announce_rounds = params->announce_rounds;
-    }
-    if (params->has_announce_step) {
-        dest->announce_step = params->announce_step;
-    }
-
-    if (params->has_block_bitmap_mapping) {
-        qapi_free_BitmapMigrationNodeAliasList(dest->block_bitmap_mapping);
-        dest->block_bitmap_mapping = QAPI_CLONE(BitmapMigrationNodeAliasList,
-                                                params->block_bitmap_mapping);
-    }
-
-    if (params->has_x_vcpu_dirty_limit_period) {
-        dest->x_vcpu_dirty_limit_period =
-            params->x_vcpu_dirty_limit_period;
-    }
-    if (params->has_vcpu_dirty_limit) {
-        dest->vcpu_dirty_limit = params->vcpu_dirty_limit;
-    }
-
-    if (params->has_mode) {
-        dest->mode = params->mode;
-    }
-
-    if (params->has_zero_page_detection) {
-        dest->zero_page_detection = params->zero_page_detection;
-    }
-
-    if (params->has_direct_io) {
-        dest->direct_io = params->direct_io;
-    }
-
-    if (params->has_x_rdma_chunk_size) {
-        dest->x_rdma_chunk_size = params->x_rdma_chunk_size;
-    }
-
-    if (params->has_cpr_exec_command) {
-        qapi_free_strList(dest->cpr_exec_command);
-        dest->cpr_exec_command = QAPI_CLONE(strList, params->cpr_exec_command);
-    }
-}
-
 /*
  * Caller must ensure the has_* fields of @params are true so they all
  * get copied and the pointer members don't dangle.
@@ -1472,7 +1403,8 @@ static void migrate_params_apply(MigrationParameters *params)
 
 void qmp_migrate_set_parameters(MigrationParameters *input, Error **errp)
 {
-    MigrationParameters new;
+    MigrationParameters *cur = &migrate_get_current()->parameters;
+    g_autoptr(MigrationParameters) new = NULL;
 
     /*
      * Convert QTYPE_QNULL and NULL to the empty string (""). Even
@@ -1486,14 +1418,13 @@ void qmp_migrate_set_parameters(MigrationParameters *input, Error **errp)
     tls_opt_to_str(input->tls_hostname);
     tls_opt_to_str(input->tls_authz);
 
-    migrate_params_test_apply(input, &new);
+    /* merge input on top of current */
+    if (!migrate_params_merge(cur, input, &new, errp)) {
+        return;
+    }
 
-    if (migrate_params_check(&new, errp)) {
-        migrate_params_apply(&new);
+    if (migrate_params_check(new, errp)) {
+        migrate_params_apply(new);
         migrate_post_update_params(input, errp);
     }
-
-    migrate_tls_opts_free(&new);
-    qapi_free_BitmapMigrationNodeAliasList(new.block_bitmap_mapping);
-    qapi_free_strList(new.cpr_exec_command);
 }
-- 
2.53.0



^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 06/18] migration: Open code migrate_params_apply
  2026-09-02 22:15 [PATCH 00/18] migration: MigrationParameters changes Fabiano Rosas
                   ` (4 preceding siblings ...)
  2026-09-02 22:15 ` [PATCH 05/18] migration: Merge parameter structs instead of assigning one by one Fabiano Rosas
@ 2026-09-02 22:15 ` Fabiano Rosas
  2026-09-02 22:15 ` [PATCH 07/18] migration: Stop freeing s->parameters members individually Fabiano Rosas
                   ` (11 subsequent siblings)
  17 siblings, 0 replies; 50+ messages in thread
From: Fabiano Rosas @ 2026-09-02 22:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: Peter Xu, Prasad Pandit

Remove migrate_params_apply so the logic of setting migration
parameters is all in one spot.

Suggested-by: Prasad Pandit <ppandit@redhat.com>
Reviewed-by: Peter Xu <peterx@redhat.com>
Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
 migration/options.c | 28 ++++++++++------------------
 1 file changed, 10 insertions(+), 18 deletions(-)

diff --git a/migration/options.c b/migration/options.c
index de091d2eee3..f7c154079bd 100644
--- a/migration/options.c
+++ b/migration/options.c
@@ -1386,21 +1386,6 @@ bool migrate_params_check(MigrationParameters *params, Error **errp)
     return true;
 }
 
-/*
- * Caller must ensure the has_* fields of @params are true so they all
- * get copied and the pointer members don't dangle.
- */
-static void migrate_params_apply(MigrationParameters *params)
-{
-    MigrationState *s = migrate_get_current();
-    MigrationParameters *cur = &s->parameters;
-
-    migrate_tls_opts_free(cur);
-    qapi_free_BitmapMigrationNodeAliasList(cur->block_bitmap_mapping);
-    qapi_free_strList(cur->cpr_exec_command);
-    QAPI_CLONE_MEMBERS(MigrationParameters, cur, params);
-}
-
 void qmp_migrate_set_parameters(MigrationParameters *input, Error **errp)
 {
     MigrationParameters *cur = &migrate_get_current()->parameters;
@@ -1423,8 +1408,15 @@ void qmp_migrate_set_parameters(MigrationParameters *input, Error **errp)
         return;
     }
 
-    if (migrate_params_check(new, errp)) {
-        migrate_params_apply(new);
-        migrate_post_update_params(input, errp);
+    if (!migrate_params_check(new, errp)) {
+        return;
     }
+
+    migrate_tls_opts_free(cur);
+    qapi_free_BitmapMigrationNodeAliasList(cur->block_bitmap_mapping);
+    qapi_free_strList(cur->cpr_exec_command);
+
+    QAPI_CLONE_MEMBERS(MigrationParameters, cur, new);
+
+    migrate_post_update_params(input, errp);
 }
-- 
2.53.0



^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 07/18] migration: Stop freeing s->parameters members individually
  2026-09-02 22:15 [PATCH 00/18] migration: MigrationParameters changes Fabiano Rosas
                   ` (5 preceding siblings ...)
  2026-09-02 22:15 ` [PATCH 06/18] migration: Open code migrate_params_apply Fabiano Rosas
@ 2026-09-02 22:15 ` Fabiano Rosas
  2026-09-02 22:15 ` [PATCH 08/18] migration: Use migrate_params_free during finalize Fabiano Rosas
                   ` (10 subsequent siblings)
  17 siblings, 0 replies; 50+ messages in thread
From: Fabiano Rosas @ 2026-09-02 22:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: Peter Xu

Expecting every pointer member from s->parameters to be freed
individually is prone to leave some fields forgotten when the code is
eventually updated. Use a dealloc visitor to free them all at once.

Reviewed-by: Peter Xu <peterx@redhat.com>
Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
 migration/options.c | 25 ++++++++++++++++++-------
 1 file changed, 18 insertions(+), 7 deletions(-)

diff --git a/migration/options.c b/migration/options.c
index f7c154079bd..6d7ef2df986 100644
--- a/migration/options.c
+++ b/migration/options.c
@@ -16,6 +16,7 @@
 #include "qemu/units.h"
 #include "exec/target_page.h"
 #include "qapi/clone-visitor.h"
+#include "qapi/dealloc-visitor.h"
 #include "qapi/error.h"
 #include "qapi/qapi-commands-migration.h"
 #include "qapi/qapi-visit-migration.h"
@@ -1058,11 +1059,21 @@ AnnounceParameters *migrate_announce_params(void)
     return &ap;
 }
 
-static void migrate_tls_opts_free(MigrationParameters *params)
+static bool migrate_params_free(MigrationParameters *params, Error **errp)
 {
-    qapi_free_StrOrNull(params->tls_creds);
-    qapi_free_StrOrNull(params->tls_hostname);
-    qapi_free_StrOrNull(params->tls_authz);
+    Visitor *v = qapi_dealloc_visitor_new();
+    bool ret;
+
+    /*
+     * qapi_free_MigrationParameters can't be used here because
+     * MigrationParameters is embedded in MigrationState due to qdev
+     * needing to access the offset of the migration properties inside
+     * the migration object.
+     */
+    ret = visit_type_MigrationParameters_members(v, params, errp);
+    visit_free(v);
+
+    return ret;
 }
 
 /* normalize QTYPE_QNULL to QTYPE_QSTRING "" */
@@ -1412,9 +1423,9 @@ void qmp_migrate_set_parameters(MigrationParameters *input, Error **errp)
         return;
     }
 
-    migrate_tls_opts_free(cur);
-    qapi_free_BitmapMigrationNodeAliasList(cur->block_bitmap_mapping);
-    qapi_free_strList(cur->cpr_exec_command);
+    if (!migrate_params_free(cur, errp)) {
+        return;
+    }
 
     QAPI_CLONE_MEMBERS(MigrationParameters, cur, new);
 
-- 
2.53.0



^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 08/18] migration: Use migrate_params_free during finalize
  2026-09-02 22:15 [PATCH 00/18] migration: MigrationParameters changes Fabiano Rosas
                   ` (6 preceding siblings ...)
  2026-09-02 22:15 ` [PATCH 07/18] migration: Stop freeing s->parameters members individually Fabiano Rosas
@ 2026-09-02 22:15 ` Fabiano Rosas
  2026-09-02 22:15 ` [PATCH 09/18] tests/qtest/migration: Add a test for HMP Fabiano Rosas
                   ` (9 subsequent siblings)
  17 siblings, 0 replies; 50+ messages in thread
From: Fabiano Rosas @ 2026-09-02 22:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: Peter Xu

Use the recently introduced migrate_params_free routine at
migration_instance_finalize() so that newly added pointers are already
freed by default.

Special case: The TLS options are currently the only pointers that
also have a qdev property implementation, so they will be freed by
qdev using the .release method. Update the method so that a second
invocation of qapi_free_StrOrNull doesn't assert.

Reviewed-by: Peter Xu <peterx@redhat.com>
Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
 migration/migration.c | 3 +--
 migration/options.c   | 7 ++++---
 migration/options.h   | 1 +
 3 files changed, 6 insertions(+), 5 deletions(-)

diff --git a/migration/migration.c b/migration/migration.c
index b413d28622a..dab282dd2f3 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -4039,8 +4039,7 @@ static void migration_instance_finalize(Object *obj)
 {
     MigrationState *ms = MIGRATION(obj);
 
-    qapi_free_BitmapMigrationNodeAliasList(ms->parameters.block_bitmap_mapping);
-    qapi_free_strList(ms->parameters.cpr_exec_command);
+    migrate_params_free(&ms->parameters, NULL);
     qemu_mutex_destroy(&ms->error_mutex);
     qemu_mutex_destroy(&ms->qemu_file_lock);
     qemu_sem_destroy(&ms->wait_unplug_sem);
diff --git a/migration/options.c b/migration/options.c
index 6d7ef2df986..bd7be8f9832 100644
--- a/migration/options.c
+++ b/migration/options.c
@@ -271,8 +271,9 @@ static void set_StrOrNull(Object *obj, Visitor *v, const char *name,
 
 static void release_StrOrNull(Object *obj, const char *name, void *opaque)
 {
-    const Property *prop = opaque;
-    qapi_free_StrOrNull(*(StrOrNull **)object_field_prop_ptr(obj, prop));
+    StrOrNull **ptr = object_field_prop_ptr(obj, opaque);
+
+    g_clear_pointer(ptr, qapi_free_StrOrNull);
 }
 
 static void set_default_value_tls_opt(ObjectProperty *op, const Property *prop)
@@ -1059,7 +1060,7 @@ AnnounceParameters *migrate_announce_params(void)
     return &ap;
 }
 
-static bool migrate_params_free(MigrationParameters *params, Error **errp)
+bool migrate_params_free(MigrationParameters *params, Error **errp)
 {
     Visitor *v = qapi_dealloc_visitor_new();
     bool ret;
diff --git a/migration/options.h b/migration/options.h
index c272eb62084..c7da2d0b5b0 100644
--- a/migration/options.h
+++ b/migration/options.h
@@ -93,4 +93,5 @@ uint64_t migrate_rdma_chunk_size(void);
 
 bool migrate_params_check(MigrationParameters *params, Error **errp);
 void migrate_params_init(MigrationParameters *params);
+bool migrate_params_free(MigrationParameters *params, Error **errp);
 #endif
-- 
2.53.0



^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 09/18] tests/qtest/migration: Add a test for HMP
  2026-09-02 22:15 [PATCH 00/18] migration: MigrationParameters changes Fabiano Rosas
                   ` (7 preceding siblings ...)
  2026-09-02 22:15 ` [PATCH 08/18] migration: Use migrate_params_free during finalize Fabiano Rosas
@ 2026-09-02 22:15 ` Fabiano Rosas
  2026-09-03 20:38   ` Peter Xu
  2026-09-02 22:15 ` [PATCH 10/18] migration: Validate that all params are set for query Fabiano Rosas
                   ` (8 subsequent siblings)
  17 siblings, 1 reply; 50+ messages in thread
From: Fabiano Rosas @ 2026-09-02 22:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: Peter Xu, Laurent Vivier, Paolo Bonzini

The following patches will change how parameters are set and shown in
HMP, including readline completion so add two minimal tests.

- set/info migrate_parameters

The test uses qtest facilities to issue HMP migrate_set_parameters for
each of the existing migration parameters and queries them back with
the info command. A list is kept with the expected strings. A
substring match function inspired by glib's g_str_match_string is
implemented for this test so the test can produce a decent error
output instead of just assert failure. E.g:

 # HMP output mismatch for entry at line 55:
 # expected vs. found:
 #
 # max-bandwidth: 10356305952768 bytes/hour
 # ---
 # max-bandwidth: 10356305952768 bytes/second

(note that line 55 above is the source line where the test case for
max-bandwith is)

- readline completion

The test puts the monitor on a chardev via socket and bypasses qtest
facilities because it needs to emit raw codes to readline. It
therefore requires a couple of new helpers to read/write to the
monitor socket.

Usage:
QTEST_QEMU_BINARY=./qemu-system-x86_64 \
./tests/qtest/migration-test --full -p /x86_64/migration/hmp

Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
 tests/qtest/migration/misc-tests.c | 319 +++++++++++++++++++++++++++++
 1 file changed, 319 insertions(+)

diff --git a/tests/qtest/migration/misc-tests.c b/tests/qtest/migration/misc-tests.c
index 4e0deb7f188..3554900b758 100644
--- a/tests/qtest/migration/misc-tests.c
+++ b/tests/qtest/migration/misc-tests.c
@@ -11,6 +11,7 @@
  */
 
 #include "qemu/osdep.h"
+#include "qemu/sockets.h"
 #include "qapi/error.h"
 #include "qobject/qjson.h"
 #include "libqtest.h"
@@ -21,6 +22,320 @@
 #define ANALYZE_SCRIPT "scripts/analyze-migration.py"
 
 static char *tmpfs;
+static int test_case_line;
+
+#define TEST(i1, i2, e) { i1, i2, e , .line = __LINE__, }
+#define SKIP(i1, i2, e) { i1, i2, e , .skip = true, }
+#define BG_SNAP_MSG ("Error: Background-snapshot is not compatible with " \
+                     "currently set capabilities")
+
+typedef struct HMPTestData {
+    const char *input1;
+    const char *input2;
+    const char *output1;
+    bool skip;
+    int line;
+} HMPTestData;
+
+/*
+ * .input1: string to be used as parameter name
+ * .input2: string to be used as parameter value
+ * .output1: expected output of migrate_set_parameters
+ * E.g:
+ * (qemu) migrate_set_parameters .input1 .input2
+ * .output1
+ */
+HMPTestData test_cases[] = {
+    TEST("", "", "migrate_set_parameter: string expected"),
+    TEST("foo", "", "migrate_set_parameter: string expected"),
+    TEST("foo", "on", "Error: invalid parameter value: foo"),
+
+    /* bool */
+    TEST("cpu-throttle-tailslow", "on", "on"),
+    TEST("direct-io", "on", "on"),
+
+    /* uint64_t */
+    TEST("announce-initial", "60", "60 ms"),
+    TEST("announce-max", "600", "600 ms"),
+    TEST("announce-rounds", "6", "6"),
+    TEST("announce-step", "15", "15 ms"),
+    TEST("downtime-limit", "400", "400 ms"),
+    TEST("avail-switchover-bandwidth", "2097152", "2199023255552 bytes/second"),
+    TEST("max-bandwidth", "9876543", "10356305952768 bytes/second"),
+    TEST("max-postcopy-bandwidth", "1048576", "1048576 bytes/second"),
+    TEST("vcpu-dirty-limit", "20", "20 MB/s"),
+    TEST("x-rdma-chunk-size", "1048576", "1048576 bytes"),
+    TEST("x-vcpu-dirty-limit-period", "750", "750 ms"),
+    TEST("xbzrle-cache-size", "67108864", "67108864 bytes"),
+
+    /* uint32_t */
+    TEST("x-checkpoint-delay", "5000", "5000 ms"),
+
+    /* uint8_t */
+    TEST("cpu-throttle-increment", "15", "15"),
+    TEST("cpu-throttle-initial", "25", "25"),
+    TEST("max-cpu-throttle", "85", "85"),
+    TEST("multifd-channels", "8", "8"),
+    TEST("throttle-trigger-threshold", "65", "65"),
+
+    /* complex types */
+    TEST("mode", "cpr-exec", "cpr-exec"),
+    TEST("multifd-compression", "zlib", "zlib"),
+    TEST("zero-page-detection", "none", "none"),
+    TEST("tls-authz", "my_authz", "'my_authz'"),
+    TEST("tls-creds", "null", "'null'"),
+    TEST("tls-hostname", "localhost", "'localhost'"),
+    TEST("cpr-exec-command", "/bin/true foobar", "/bin/true foobar"),
+
+    /* can be set but are currently missing in the query output */
+    SKIP("multifd-qatzip-level", "5", "5"),
+    SKIP("multifd-zlib-level", "4", "4"),
+    SKIP("multifd-zstd-level", "6", "6"),
+
+    /* cannot be set */
+    TEST("block-bitmap-mapping", "[]",
+         "Error: The block-bitmap-mapping parameter "
+         "can only be set through QMP"),
+};
+
+/*
+ * .input1: partial string with an ending TAB (as if pressed by
+ *          the user).
+ * .input2: common root of the completions, i.e. what the partial
+ *          part of .input1 string completes to.
+ * .expected: full list of completion suggestions for the string
+ *          in .input2.
+ * E.g:
+ * (qemu) .input1
+ * <after TAB>
+ * (qemu) .input2
+ * .output1
+ */
+HMPTestData completion_cases[] = {
+    TEST("migra\t",
+         "migrate",
+         "migrate migrate_cancel migrate_continue migrate_incoming "
+         "migrate_pause migrate_recover migrate_set_capability "
+         "migrate_set_parameter migrate_start_postcopy"),
+
+    /*
+     * Note QEMU doesn't keep 'info' when offering the completions
+     * suggestions.
+     */
+    TEST("info migra\t",
+         "migrate",
+         "migrate migrate_capabilities migrate_parameters"),
+};
+
+/*
+ * Find a contiguous run of tokens in @larger that match the sequence
+ * of tokens in @smaller. If @strip_empty, ignore mismatches due to
+ * sequences of empty tokens.
+ *
+ * Returns whether a match was found.
+ * Updates the indices:
+ *   @last_match: which token in @larger last matched a token in
+ *                @smaller
+ *   @last_tried: which token in @smaller was last compared with a
+ *                token in @larger
+ */
+static bool token_list_is_substr(char **smaller, char **larger,
+                                 int *last_match, int *last_tried,
+                                 bool skip_empty)
+{
+    int i, j, k = 0;
+    bool match = true;
+
+    for (i = 0; smaller[i]; i++) {
+        for (j = k; larger[j]; j++) {
+
+            if (!*larger[j] && skip_empty) {
+                continue;
+            }
+
+            if (!g_str_has_prefix(larger[j], smaller[i])) {
+                continue;
+            }
+
+            match = true;
+            k = j;
+            goto next;
+        }
+
+        match = false;
+        break;
+    next:
+        ;
+    }
+
+    *last_match = k;
+    *last_tried = i;
+
+    return match;
+}
+
+static void assert_hmp_match(const char *str, const char *text, bool per_line)
+{
+    const char *delim = per_line ? ":\n" : " \r\n";
+    g_auto(GStrv) t1 = g_strsplit_set(str, delim, -1);
+    g_auto(GStrv) t2 = g_strsplit_set(text, delim, -1);
+    int match, mismatch;
+
+    if (token_list_is_substr(t1, t2, &match, &mismatch, !per_line)) {
+        return;
+    }
+
+    g_test_message("HMP output mismatch for entry at line %d:", test_case_line);
+
+    if (per_line) {
+        if (mismatch == 0) {
+            g_test_message("'%s' not present in output", t1[mismatch]);
+        } else {
+            g_test_message("expected vs. found:\n\n%s\n---\n%s:%s", str,
+                           t2[match], t2[match + 1]);
+            /*
+             * + 1 above is safe because HMP output has an ending newline
+             * and the glib array ends on two NULL slots.
+             */
+        }
+    } else {
+        g_test_message("expected vs. found (whitespace ignored):"
+                       "\n---\n%s\n---\n%s\n---", str, g_strjoinv(" ", t2));
+    }
+    g_assert_not_reached();
+}
+
+static void assert_hmp_success(const char *str)
+{
+    if (!g_str_equal(str, "")) {
+        g_test_message("HMP command failed:\n\n%s", str);
+        g_assert_not_reached();
+    }
+}
+
+static void test_hmp_migration_parameters(char *name, MigrateCommon *args)
+{
+    QTestState *qts;
+
+    /* force TCG so it can run in all targets */
+    qts = qtest_init("-accel tcg -nodefaults -S");
+
+    for (int i = 0; i < G_N_ELEMENTS(test_cases); i++) {
+        g_autofree char *resp = NULL;
+        g_autofree char *line = NULL;
+        struct HMPTestData *t = &test_cases[i];
+
+        if (t->skip) {
+            continue;
+        }
+
+        test_case_line = t->line;
+
+        resp = qtest_hmp(qts, "migrate_set_parameter %s %s", t->input1,
+                         t->input2);
+
+        if (g_str_has_prefix(t->output1, "Error:") ||
+            g_str_has_prefix(resp, "migrate_set_parameter:")) {
+
+            assert_hmp_match(t->output1, resp, true);
+            continue;
+        }
+        assert_hmp_success(resp);
+        g_free(resp);
+
+        resp = qtest_hmp(qts, "info migrate_parameters");
+
+        line = g_strconcat(t->input1, ": ", t->output1, NULL);
+        assert_hmp_match(line, resp, true);
+    }
+
+    qtest_quit(qts);
+}
+
+static void hmp_sock_write(int fd, const char *buf)
+{
+    size_t sz = strlen(buf);
+
+    assert(fd > 0);
+    assert(write(fd, buf, sz) == sz);
+}
+
+static void hmp_sock_read(int fd, char *buf, size_t buf_sz)
+{
+    char *p = buf;
+    size_t sz = buf_sz - 1;
+
+    assert(fd >= 0);
+    memset(buf, 0, buf_sz);
+
+    while (sz > 0) {
+        ssize_t r = read(fd, p, sz);
+        char *prompt;
+
+        if (!r) {
+            break;
+        } else if (r < 0) {
+            if (errno == EINTR) {
+                continue;
+            }
+            g_assert_not_reached();
+        }
+
+        p += r;
+        sz -= r;
+
+        prompt = strstr(buf, "(qemu) ");
+        if (prompt) {
+            *prompt = '\0';
+            break;
+        }
+    }
+}
+
+static void test_hmp_completion(char *name, MigrateCommon *args)
+{
+    g_autofree char *cmdline;
+    QTestState *qts;
+    char buf[1024];
+    int sockfds[2];
+
+    assert(!qemu_socketpair(AF_UNIX, SOCK_STREAM, 0, sockfds));
+    qemu_clear_cloexec(sockfds[1]);
+
+    cmdline = g_strdup_printf("-chardev socket,id=mon0,fd=%d "
+                              "-mon chardev=mon0,mode=readline -S",
+                              sockfds[1]);
+    qts = qtest_init(cmdline);
+    close(sockfds[1]);
+
+    /* read HMP banner */
+    hmp_sock_read(sockfds[0], buf, sizeof(buf));
+
+    for (int i = 0; i < G_N_ELEMENTS(completion_cases); i++) {
+        const struct HMPTestData *t = &completion_cases[i];
+        char *output;
+
+        hmp_sock_write(sockfds[0], t->input1);
+        hmp_sock_read(sockfds[0], buf, sizeof(buf));
+
+        /*
+         * readline first rewrites the input to the common root of the
+         * completions, then outputs the completion suggestions:
+         *
+         * (qemu) info migr<TAB>
+         * (qemu) migrate migrate_parameters
+         * migrate_capabilities ...
+         */
+        output = strstr(buf, t->input2);
+        assert_hmp_match(t->output1, output, false);
+
+        /* ^U backward kill line */
+        hmp_sock_write(sockfds[0], "\x15");
+    }
+
+    close(sockfds[0]);
+    qtest_quit(qts);
+}
 
 static void test_baddest(char *name, MigrateCommon *args)
 {
@@ -260,4 +575,8 @@ void migration_test_add_misc(MigrationTestEnv *env)
                        test_validate_uri_channels_both_set);
     migration_test_add("/migration/validate_uri/channels/none_set",
                        test_validate_uri_channels_none_set);
+    migration_test_add("/migration/hmp/parameters",
+                       test_hmp_migration_parameters);
+    migration_test_add("/migration/hmp/completion",
+                       test_hmp_completion);
 }
-- 
2.53.0



^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 10/18] migration: Validate that all params are set for query
  2026-09-02 22:15 [PATCH 00/18] migration: MigrationParameters changes Fabiano Rosas
                   ` (8 preceding siblings ...)
  2026-09-02 22:15 ` [PATCH 09/18] tests/qtest/migration: Add a test for HMP Fabiano Rosas
@ 2026-09-02 22:15 ` Fabiano Rosas
  2026-09-03 18:59   ` Peter Xu
  2026-09-02 22:15 ` [PATCH 11/18] migration: Use keyval input visitor in HMP set command Fabiano Rosas
                   ` (7 subsequent siblings)
  17 siblings, 1 reply; 50+ messages in thread
From: Fabiano Rosas @ 2026-09-02 22:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: Peter Xu

There are a couple of situations where all fields of a
MigrationParameters object need to be marked as present: when cloning
an entire object and when creating the transient object in
qmp_query_migrate(). The query-migrate-parameters QMP command contract
requires that all parameters, except block-bitmap-mapping, are present
in the output.

Validate that a given object has all has_* fields set to true.

Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
 migration/options.c | 54 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 54 insertions(+)

diff --git a/migration/options.c b/migration/options.c
index bd7be8f9832..5d17acdd881 100644
--- a/migration/options.c
+++ b/migration/options.c
@@ -12,6 +12,7 @@
  */
 
 #include "qemu/osdep.h"
+#include "qemu/cutils.h"
 #include "qemu/error-report.h"
 #include "qemu/units.h"
 #include "exec/target_page.h"
@@ -23,8 +24,10 @@
 #include "qapi/qmp/qerror.h"
 #include "qapi/qobject-input-visitor.h"
 #include "qapi/qobject-output-visitor.h"
+#include "qobject/qbool.h"
 #include "qobject/qdict.h"
 #include "qobject/qnull.h"
+#include "qobject/qstring.h"
 #include "system/runstate.h"
 #include "migration/colo.h"
 #include "migration/cpr.h"
@@ -1149,12 +1152,63 @@ static void migrate_mark_all_params_present(MigrationParameters *p)
     }
 }
 
+static bool assert_all_params_present(MigrationParameters *params, Error **errp)
+{
+    g_autoptr(QDict) d = migrate_params_to_dict(params, errp);
+    const QDictEntry *e = NULL;
+    int i = 0;
+
+    if (!d) {
+        return false;
+    }
+
+    for (e = qdict_first(d); e; e = qdict_next(d, e), i++) {
+        const char *key = qdict_entry_key(e);
+        const char *p;
+
+        if (strstart(key, "tls-", &p)) {
+            QString *s = qobject_to(QString, qdict_entry_value(e));
+
+            if (!s) {
+                break;
+            }
+        } else if (strstart(key, "has-", &p)) {
+            if (qdict_haskey(d, p)) {
+                QBool *b = qobject_to(QBool, qdict_entry_value(e));
+
+                if (!b || !qbool_get_bool(b)) {
+                    break;
+                }
+            }
+        }
+    }
+
+    if (i && !e) {
+        return true;
+    }
+
+    /*
+     * Should never happen, but avoid asserting becase this is
+     * reachable from QMP.
+     */
+    error_setg(errp, "Missing parameter. Query output will be incomplete.");
+    return false;
+}
+
 MigrationParameters *qmp_query_migrate_parameters(Error **errp)
 {
     MigrationState *s = migrate_get_current();
     MigrationParameters *params = QAPI_CLONE(MigrationParameters,
                                              &s->parameters);
 
+    /*
+     * Validate all parameters have their has_* field set to true as
+     * consequence of the initial migrate_mark_all_params_present().
+     */
+    if (!assert_all_params_present(params, errp)) {
+        return NULL;
+    }
+
     /*
      * The block-bitmap-mapping breaks the expected API of
      * query-migrate-parameters of having all members present. To keep
-- 
2.53.0



^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 11/18] migration: Use keyval input visitor in HMP set command
  2026-09-02 22:15 [PATCH 00/18] migration: MigrationParameters changes Fabiano Rosas
                   ` (9 preceding siblings ...)
  2026-09-02 22:15 ` [PATCH 10/18] migration: Validate that all params are set for query Fabiano Rosas
@ 2026-09-02 22:15 ` Fabiano Rosas
  2026-09-03 19:44   ` Peter Xu
  2026-09-04  9:45   ` Markus Armbruster
  2026-09-02 22:15 ` [PATCH 12/18] migration: Change HMP 'info migrate_parameters' output Fabiano Rosas
                   ` (6 subsequent siblings)
  17 siblings, 2 replies; 50+ messages in thread
From: Fabiano Rosas @ 2026-09-02 22:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: Peter Xu, Laurent Vivier, Paolo Bonzini

Change the hmp_migrate_set_parameter command to use a keyval input
visitor.

Currently a string visitor is used and due to limitations of that
particular visitor's implementation it's necessary to consult the QAPI
type enum (MigrationParameter_lookup) and call each visit_type_*
function individually. Which makes using a visitor pointless.

Since there are other visitors implemented properly and generated code
to iterate the QAPI object, prefer using one of those. The keyval
input visitor is adequate because HMP provides basically one key and
one value for each migrate_set_parameter command.

To switch from string_input_visitor to keyval_input_visitor simply put
the parameter name and value into a dict and invoke
visit_type_MigrationParameters().

Note that it's not necessary to go through any of the keyval_* code
because due to the nature of HMP, there's no parsing to do (no '=', no
',', etc).

With this the migrate_set_parameters HMP commands will be
automatically updated anytime a new migration parameter is added.

The bad part:

Some parameters accept a non-standard data format, I moved them to a
"legacy" suffixed function in this patch as there's only 3 of them:

- "max-bandwidth" and "avail-switchover-bandwidth" take MiB instead of B for
   a size parameter;

- "cpr-exec-command" takes the strList type which needs to be built
  manually.

Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
 migration/migration-hmp-cmds.c     | 203 +++++++++--------------------
 tests/qtest/migration/misc-tests.c |   2 +-
 2 files changed, 59 insertions(+), 146 deletions(-)

diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c
index ad68fa23aa4..220ac28b5e3 100644
--- a/migration/migration-hmp-cmds.c
+++ b/migration/migration-hmp-cmds.c
@@ -24,7 +24,9 @@
 #include "qapi/error.h"
 #include "qapi/qapi-commands-migration.h"
 #include "qapi/qapi-visit-migration.h"
+#include "qapi/qobject-input-visitor.h"
 #include "qobject/qdict.h"
+#include "qobject/qstring.h"
 #include "qapi/string-input-visitor.h"
 #include "qapi/string-output-visitor.h"
 #include "qemu/cutils.h"
@@ -589,59 +591,16 @@ end:
     hmp_handle_error(mon, err);
 }
 
-void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict)
+static void hmp_migrate_set_parameter_legacy(Monitor *mon, const QDict *qdict)
 {
     const char *param = qdict_get_str(qdict, "parameter");
     const char *valuestr = qdict_get_str(qdict, "value");
-    Visitor *v = string_input_visitor_new(valuestr);
     MigrationParameters *p = g_new0(MigrationParameters, 1);
     uint64_t valuebw = 0;
-    uint64_t cache_size;
     Error *err = NULL;
-    int val, ret;
+    int ret;
 
-    val = qapi_enum_parse(&MigrationParameter_lookup, param, -1, &err);
-    if (val < 0) {
-        goto cleanup;
-    }
-
-    switch (val) {
-    case MIGRATION_PARAMETER_THROTTLE_TRIGGER_THRESHOLD:
-        p->has_throttle_trigger_threshold = true;
-        visit_type_uint8(v, param, &p->throttle_trigger_threshold, &err);
-        break;
-    case MIGRATION_PARAMETER_CPU_THROTTLE_INITIAL:
-        p->has_cpu_throttle_initial = true;
-        visit_type_uint8(v, param, &p->cpu_throttle_initial, &err);
-        break;
-    case MIGRATION_PARAMETER_CPU_THROTTLE_INCREMENT:
-        p->has_cpu_throttle_increment = true;
-        visit_type_uint8(v, param, &p->cpu_throttle_increment, &err);
-        break;
-    case MIGRATION_PARAMETER_CPU_THROTTLE_TAILSLOW:
-        p->has_cpu_throttle_tailslow = true;
-        visit_type_bool(v, param, &p->cpu_throttle_tailslow, &err);
-        break;
-    case MIGRATION_PARAMETER_MAX_CPU_THROTTLE:
-        p->has_max_cpu_throttle = true;
-        visit_type_uint8(v, param, &p->max_cpu_throttle, &err);
-        break;
-    case MIGRATION_PARAMETER_TLS_CREDS:
-        p->tls_creds = g_new0(StrOrNull, 1);
-        p->tls_creds->type = QTYPE_QSTRING;
-        visit_type_str(v, param, &p->tls_creds->u.s, &err);
-        break;
-    case MIGRATION_PARAMETER_TLS_HOSTNAME:
-        p->tls_hostname = g_new0(StrOrNull, 1);
-        p->tls_hostname->type = QTYPE_QSTRING;
-        visit_type_str(v, param, &p->tls_hostname->u.s, &err);
-        break;
-    case MIGRATION_PARAMETER_TLS_AUTHZ:
-        p->tls_authz = g_new0(StrOrNull, 1);
-        p->tls_authz->type = QTYPE_QSTRING;
-        visit_type_str(v, param, &p->tls_authz->u.s, &err);
-        break;
-    case MIGRATION_PARAMETER_MAX_BANDWIDTH:
+    if (g_str_equal(param, "max-bandwidth")) {
         p->has_max_bandwidth = true;
         /*
          * Can't use visit_type_size() here, because it
@@ -651,109 +610,21 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict)
         if (ret < 0 || valuebw > INT64_MAX
             || (size_t)valuebw != valuebw) {
             error_setg(&err, "Invalid size %s", valuestr);
-            break;
+            return;
         }
         p->max_bandwidth = valuebw;
-        break;
-    case MIGRATION_PARAMETER_AVAIL_SWITCHOVER_BANDWIDTH:
+
+    } else if (g_str_equal(param, "avail-switchover-bandwidth")) {
         p->has_avail_switchover_bandwidth = true;
         ret = qemu_strtosz_MiB(valuestr, NULL, &valuebw);
         if (ret < 0 || valuebw > INT64_MAX
             || (size_t)valuebw != valuebw) {
             error_setg(&err, "Invalid size %s", valuestr);
-            break;
+            return;
         }
         p->avail_switchover_bandwidth = valuebw;
-        break;
-    case MIGRATION_PARAMETER_DOWNTIME_LIMIT:
-        p->has_downtime_limit = true;
-        visit_type_size(v, param, &p->downtime_limit, &err);
-        break;
-    case MIGRATION_PARAMETER_X_CHECKPOINT_DELAY:
-        p->has_x_checkpoint_delay = true;
-        visit_type_uint32(v, param, &p->x_checkpoint_delay, &err);
-        break;
-    case MIGRATION_PARAMETER_MULTIFD_CHANNELS:
-        p->has_multifd_channels = true;
-        visit_type_uint8(v, param, &p->multifd_channels, &err);
-        break;
-    case MIGRATION_PARAMETER_MULTIFD_COMPRESSION:
-        p->has_multifd_compression = true;
-        visit_type_MultiFDCompression(v, param, &p->multifd_compression,
-                                      &err);
-        break;
-    case MIGRATION_PARAMETER_MULTIFD_ZLIB_LEVEL:
-        p->has_multifd_zlib_level = true;
-        visit_type_uint8(v, param, &p->multifd_zlib_level, &err);
-        break;
-    case MIGRATION_PARAMETER_MULTIFD_QATZIP_LEVEL:
-        p->has_multifd_qatzip_level = true;
-        visit_type_uint8(v, param, &p->multifd_qatzip_level, &err);
-        break;
-    case MIGRATION_PARAMETER_MULTIFD_ZSTD_LEVEL:
-        p->has_multifd_zstd_level = true;
-        visit_type_uint8(v, param, &p->multifd_zstd_level, &err);
-        break;
-    case MIGRATION_PARAMETER_ZERO_PAGE_DETECTION:
-        p->has_zero_page_detection = true;
-        visit_type_ZeroPageDetection(v, param, &p->zero_page_detection, &err);
-        break;
-    case MIGRATION_PARAMETER_XBZRLE_CACHE_SIZE:
-        p->has_xbzrle_cache_size = true;
-        if (!visit_type_size(v, param, &cache_size, &err)) {
-            break;
-        }
-        if (cache_size > INT64_MAX || (size_t)cache_size != cache_size) {
-            error_setg(&err, "Invalid size %s", valuestr);
-            break;
-        }
-        p->xbzrle_cache_size = cache_size;
-        break;
-    case MIGRATION_PARAMETER_MAX_POSTCOPY_BANDWIDTH:
-        p->has_max_postcopy_bandwidth = true;
-        visit_type_size(v, param, &p->max_postcopy_bandwidth, &err);
-        break;
-    case MIGRATION_PARAMETER_ANNOUNCE_INITIAL:
-        p->has_announce_initial = true;
-        visit_type_size(v, param, &p->announce_initial, &err);
-        break;
-    case MIGRATION_PARAMETER_ANNOUNCE_MAX:
-        p->has_announce_max = true;
-        visit_type_size(v, param, &p->announce_max, &err);
-        break;
-    case MIGRATION_PARAMETER_ANNOUNCE_ROUNDS:
-        p->has_announce_rounds = true;
-        visit_type_size(v, param, &p->announce_rounds, &err);
-        break;
-    case MIGRATION_PARAMETER_ANNOUNCE_STEP:
-        p->has_announce_step = true;
-        visit_type_size(v, param, &p->announce_step, &err);
-        break;
-    case MIGRATION_PARAMETER_BLOCK_BITMAP_MAPPING:
-        error_setg(&err, "The block-bitmap-mapping parameter can only be set "
-                   "through QMP");
-        break;
-    case MIGRATION_PARAMETER_X_VCPU_DIRTY_LIMIT_PERIOD:
-        p->has_x_vcpu_dirty_limit_period = true;
-        visit_type_size(v, param, &p->x_vcpu_dirty_limit_period, &err);
-        break;
-    case MIGRATION_PARAMETER_VCPU_DIRTY_LIMIT:
-        p->has_vcpu_dirty_limit = true;
-        visit_type_size(v, param, &p->vcpu_dirty_limit, &err);
-        break;
-    case MIGRATION_PARAMETER_MODE:
-        p->has_mode = true;
-        visit_type_MigMode(v, param, &p->mode, &err);
-        break;
-    case MIGRATION_PARAMETER_DIRECT_IO:
-        p->has_direct_io = true;
-        visit_type_bool(v, param, &p->direct_io, &err);
-        break;
-    case MIGRATION_PARAMETER_X_RDMA_CHUNK_SIZE:
-        p->has_x_rdma_chunk_size = true;
-        visit_type_size(v, param, &p->x_rdma_chunk_size, &err);
-        break;
-    case MIGRATION_PARAMETER_CPR_EXEC_COMMAND: {
+
+    } else if (g_str_equal(param, "cpr-exec-command")) {
         /*
          * NOTE: g_autofree will only auto g_free() the strv array when
          * needed, it will not free the strings within the array. It's
@@ -766,15 +637,14 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict)
 
         if (!g_shell_parse_argv(valuestr, NULL, &strv, &gerr)) {
             error_setg(&err, "%s", gerr->message);
-            break;
+            return;
         }
         for (int i = 0; strv[i]; i++) {
             QAPI_LIST_APPEND(tail, strv[i]);
         }
         p->has_cpr_exec_command = true;
-        break;
-    }
-    default:
+
+    } else {
         g_assert_not_reached();
     }
 
@@ -784,12 +654,55 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict)
 
     qmp_migrate_set_parameters(p, &err);
 
- cleanup:
+cleanup:
     qapi_free_MigrationParameters(p);
+    hmp_handle_error(mon, err);
+}
+
+static void hmp_migrate_set_parameter_qapi(Monitor *mon, const QDict *qdict)
+{
+    const char *param = qdict_get_str(qdict, "parameter");
+    const char *valuestr = qdict_get_str(qdict, "value");
+    g_autoptr(QDict) input = qdict_new();
+    g_autoptr(MigrationParameters) p = NULL;
+    Visitor *v;
+    Error *err = NULL;
+
+    /* the same as keyval_parse(), but here there's no need to parse */
+    qdict_put_obj(input, param, QOBJECT(qstring_from_str(valuestr)));
+
+    v = qobject_input_visitor_new_keyval(QOBJECT(input));
+    if (visit_type_MigrationParameters(v, NULL, &p, &err)) {
+        qmp_migrate_set_parameters(p, &err);
+    }
+
     visit_free(v);
     hmp_handle_error(mon, err);
 }
 
+void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict)
+{
+    const char *param = qdict_get_str(qdict, "parameter");
+
+    if (g_str_equal(param, "block-bitmap-mapping")) {
+        Error *err = NULL;
+
+        error_setg(&err, "The %s parameter can only be set through QMP", param);
+        hmp_handle_error(mon, err);
+        return;
+    }
+
+    /* these have non-standard setters */
+    if (g_str_equal(param, "max-bandwidth") ||
+        g_str_equal(param, "avail-switchover-bandwidth") ||
+        g_str_equal(param, "cpr-exec-command")) {
+
+        return hmp_migrate_set_parameter_legacy(mon, qdict);
+    }
+
+    hmp_migrate_set_parameter_qapi(mon, qdict);
+}
+
 void hmp_migrate_start_postcopy(Monitor *mon, const QDict *qdict)
 {
     Error *err = NULL;
diff --git a/tests/qtest/migration/misc-tests.c b/tests/qtest/migration/misc-tests.c
index 3554900b758..ba8183978b3 100644
--- a/tests/qtest/migration/misc-tests.c
+++ b/tests/qtest/migration/misc-tests.c
@@ -48,7 +48,7 @@ typedef struct HMPTestData {
 HMPTestData test_cases[] = {
     TEST("", "", "migrate_set_parameter: string expected"),
     TEST("foo", "", "migrate_set_parameter: string expected"),
-    TEST("foo", "on", "Error: invalid parameter value: foo"),
+    TEST("foo", "on", "Error: Parameter 'foo' is unexpected"),
 
     /* bool */
     TEST("cpu-throttle-tailslow", "on", "on"),
-- 
2.53.0



^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 12/18] migration: Change HMP 'info migrate_parameters' output
  2026-09-02 22:15 [PATCH 00/18] migration: MigrationParameters changes Fabiano Rosas
                   ` (10 preceding siblings ...)
  2026-09-02 22:15 ` [PATCH 11/18] migration: Use keyval input visitor in HMP set command Fabiano Rosas
@ 2026-09-02 22:15 ` Fabiano Rosas
  2026-09-03 20:25   ` Peter Xu
  2026-09-02 22:15 ` [PATCH 13/18] migration: Use output visitor in info command Fabiano Rosas
                   ` (5 subsequent siblings)
  17 siblings, 1 reply; 50+ messages in thread
From: Fabiano Rosas @ 2026-09-02 22:15 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Xu, Kevin Wolf, Hanna Reitz, Laurent Vivier, Paolo Bonzini

The output of 'info migrate_parameters' includes units of measurement
for a few parameters. This is convenient for a user. It also requires
every parameter to be individually listed in the
hmp_migrate_set_parameter() function, which in turn requires the
MigrationParameter (singular) enum to exist. While the latter is not
bothersome at all, the former is.

From a development and maintenance perspective, having a list of
parameters explicitly written in several parts of the code brings
several annoyances: conflicts during rebase, multiple extra hits when
grepping, requires contributors to search for every location a change
needs to be mirrored to, etc.

Remove the units from the output so we can write this code in a more
convenient way. The HMP output is not part of any ABI.

Also remove quotes from around the TLS options strings as this is
inconsistent with all the other strings.

Change block-bitmap-mapping format to a single line. This requires
updating one of the iotests to match.

Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
 migration/migration-hmp-cmds.c     | 70 +++++++++++++++++-------------
 tests/qemu-iotests/300             | 20 ++++++---
 tests/qtest/migration/misc-tests.c | 30 ++++++-------
 3 files changed, 69 insertions(+), 51 deletions(-)

diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c
index 220ac28b5e3..089c6d4ff46 100644
--- a/migration/migration-hmp-cmds.c
+++ b/migration/migration-hmp-cmds.c
@@ -336,16 +336,16 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict)
     params = qmp_query_migrate_parameters(NULL);
 
     if (params) {
-        monitor_printf(mon, "%s: %" PRIu64 " ms\n",
+        monitor_printf(mon, "%s: %" PRIu64 "\n",
             MigrationParameter_str(MIGRATION_PARAMETER_ANNOUNCE_INITIAL),
             params->announce_initial);
-        monitor_printf(mon, "%s: %" PRIu64 " ms\n",
+        monitor_printf(mon, "%s: %" PRIu64 "\n",
             MigrationParameter_str(MIGRATION_PARAMETER_ANNOUNCE_MAX),
             params->announce_max);
         monitor_printf(mon, "%s: %" PRIu64 "\n",
             MigrationParameter_str(MIGRATION_PARAMETER_ANNOUNCE_ROUNDS),
             params->announce_rounds);
-        monitor_printf(mon, "%s: %" PRIu64 " ms\n",
+        monitor_printf(mon, "%s: %" PRIu64 "\n",
             MigrationParameter_str(MIGRATION_PARAMETER_ANNOUNCE_STEP),
             params->announce_step);
         assert(params->has_throttle_trigger_threshold);
@@ -369,35 +369,35 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict)
             MigrationParameter_str(MIGRATION_PARAMETER_MAX_CPU_THROTTLE),
             params->max_cpu_throttle);
         assert(params->tls_creds);
-        monitor_printf(mon, "%s: '%s'\n",
+        monitor_printf(mon, "%s: %s\n",
             MigrationParameter_str(MIGRATION_PARAMETER_TLS_CREDS),
                        params->tls_creds->u.s);
         assert(params->tls_hostname);
-        monitor_printf(mon, "%s: '%s'\n",
+        monitor_printf(mon, "%s: %s\n",
             MigrationParameter_str(MIGRATION_PARAMETER_TLS_HOSTNAME),
                        params->tls_hostname->u.s);
         assert(params->tls_authz);
-        monitor_printf(mon, "%s: '%s'\n",
+        monitor_printf(mon, "%s: %s\n",
             MigrationParameter_str(MIGRATION_PARAMETER_TLS_AUTHZ),
                        params->tls_authz->u.s);
         assert(params->has_max_bandwidth);
-        monitor_printf(mon, "%s: %" PRIu64 " bytes/second\n",
+        monitor_printf(mon, "%s: %" PRIu64 "\n",
             MigrationParameter_str(MIGRATION_PARAMETER_MAX_BANDWIDTH),
             params->max_bandwidth);
         assert(params->has_avail_switchover_bandwidth);
-        monitor_printf(mon, "%s: %" PRIu64 " bytes/second\n",
+        monitor_printf(mon, "%s: %" PRIu64 "\n",
             MigrationParameter_str(MIGRATION_PARAMETER_AVAIL_SWITCHOVER_BANDWIDTH),
             params->avail_switchover_bandwidth);
         assert(params->has_max_postcopy_bandwidth);
-        monitor_printf(mon, "%s: %" PRIu64 " bytes/second\n",
+        monitor_printf(mon, "%s: %" PRIu64 "\n",
             MigrationParameter_str(MIGRATION_PARAMETER_MAX_POSTCOPY_BANDWIDTH),
             params->max_postcopy_bandwidth);
         assert(params->has_downtime_limit);
-        monitor_printf(mon, "%s: %" PRIu64 " ms\n",
+        monitor_printf(mon, "%s: %" PRIu64 "\n",
             MigrationParameter_str(MIGRATION_PARAMETER_DOWNTIME_LIMIT),
             params->downtime_limit);
         assert(params->has_x_checkpoint_delay);
-        monitor_printf(mon, "%s: %u ms\n",
+        monitor_printf(mon, "%s: %u\n",
             MigrationParameter_str(MIGRATION_PARAMETER_X_CHECKPOINT_DELAY),
             params->x_checkpoint_delay);
         monitor_printf(mon, "%s: %u\n",
@@ -411,41 +411,51 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict)
             MigrationParameter_str(MIGRATION_PARAMETER_ZERO_PAGE_DETECTION),
             qapi_enum_lookup(&ZeroPageDetection_lookup,
                 params->zero_page_detection));
-        monitor_printf(mon, "%s: %" PRIu64 " bytes\n",
+        monitor_printf(mon, "%s: %" PRIu64 "\n",
             MigrationParameter_str(MIGRATION_PARAMETER_XBZRLE_CACHE_SIZE),
             params->xbzrle_cache_size);
 
         if (s->has_block_bitmap_mapping) {
-            const BitmapMigrationNodeAliasList *bmnal;
+            BitmapMigrationNodeAliasList *nal;
+            BitmapMigrationNodeAlias *na;
+            BitmapMigrationBitmapAliasList *bal;
+            BitmapMigrationBitmapAlias *ba;
+            BitmapMigrationBitmapAliasTransform *bat;
 
-            monitor_printf(mon, "%s:\n",
+            monitor_printf(mon, "%s:",
                            MigrationParameter_str(
                                MIGRATION_PARAMETER_BLOCK_BITMAP_MAPPING));
 
-            for (bmnal = params->block_bitmap_mapping;
-                 bmnal;
-                 bmnal = bmnal->next)
+            for (nal = params->block_bitmap_mapping; nal; nal = nal->next)
             {
-                const BitmapMigrationNodeAlias *bmna = bmnal->value;
-                const BitmapMigrationBitmapAliasList *bmbal;
+                na = nal->value;
+                monitor_printf(mon, " bitmaps:");
+                for (bal = na->bitmaps; bal; bal = bal->next) {
+                    ba = bal->value;
+                    bat = ba->transform;
 
-                monitor_printf(mon, "  '%s' -> '%s'\n",
-                               bmna->node_name, bmna->alias);
-
-                for (bmbal = bmna->bitmaps; bmbal; bmbal = bmbal->next) {
-                    const BitmapMigrationBitmapAlias *bmba = bmbal->value;
-
-                    monitor_printf(mon, "    '%s' -> '%s'\n",
-                                   bmba->name, bmba->alias);
+                    monitor_printf(mon, " name: %s", ba->name);
+                    if (bat && bat->has_persistent) {
+                        if (bat->persistent) {
+                            monitor_printf(mon, " persistent: on");
+                        } else {
+                            monitor_printf(mon, " persistent: off");
+                        }
+                    }
+                    monitor_printf(mon, " alias: %s", ba->alias);
                 }
+                monitor_printf(mon, " node-name: %s alias: %s",
+                               na->node_name, na->alias);
             }
+
+            monitor_printf(mon, "\n");
         }
 
-        monitor_printf(mon, "%s: %" PRIu64 " ms\n",
+        monitor_printf(mon, "%s: %" PRIu64 "\n",
         MigrationParameter_str(MIGRATION_PARAMETER_X_VCPU_DIRTY_LIMIT_PERIOD),
         params->x_vcpu_dirty_limit_period);
 
-        monitor_printf(mon, "%s: %" PRIu64 " MB/s\n",
+        monitor_printf(mon, "%s: %" PRIu64 "\n",
             MigrationParameter_str(MIGRATION_PARAMETER_VCPU_DIRTY_LIMIT),
             params->vcpu_dirty_limit);
 
@@ -462,7 +472,7 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict)
         }
 
         if (params->has_x_rdma_chunk_size) {
-            monitor_printf(mon, "%s: %" PRIu64 " bytes\n",
+            monitor_printf(mon, "%s: %" PRIu64 "\n",
                            MigrationParameter_str(
                                MIGRATION_PARAMETER_X_RDMA_CHUNK_SIZE),
                            params->x_rdma_chunk_size);
diff --git a/tests/qemu-iotests/300 b/tests/qemu-iotests/300
index e46616d7b19..03248f4474b 100755
--- a/tests/qemu-iotests/300
+++ b/tests/qemu-iotests/300
@@ -147,8 +147,7 @@ class TestDirtyBitmapMigration(iotests.QMPTestCase):
 
             result = vm.qmp('human-monitor-command',
                             command_line='info migrate_parameters')
-
-            m = re.search(r'^block-bitmap-mapping:\r?(\n  .*)*\n',
+            m = re.search(r'^block-bitmap-mapping:(.*)\r\n',
                           result['return'], flags=re.MULTILINE)
             hmp_mapping = m.group(0).replace('\r', '') if m else None
 
@@ -158,15 +157,24 @@ class TestDirtyBitmapMigration(iotests.QMPTestCase):
 
     @staticmethod
     def to_hmp_mapping(mapping: BlockBitmapMapping) -> str:
-        result = 'block-bitmap-mapping:\n'
+        result = 'block-bitmap-mapping:'
 
         for node in mapping:
-            result += f"  '{node['node-name']}' -> '{node['alias']}'\n"
-
             assert isinstance(node['bitmaps'], list)
+            result += ' bitmaps:'
             for bitmap in node['bitmaps']:
-                result += f"    '{bitmap['name']}' -> '{bitmap['alias']}'\n"
+                result += f" name: {bitmap['name']}"
+                if 'transform' in bitmap:
+                    if 'persistent' in bitmap['transform']:
+                        if bitmap['transform']['persistent']:
+                            result += " persistent: on"
+                        else:
+                            result += " persistent: off"
+                result += f" alias: {bitmap['alias']}"
 
+            result += f" node-name: {node['node-name']} alias: {node['alias']}"
+
+        result += '\n'
         return result
 
 
diff --git a/tests/qtest/migration/misc-tests.c b/tests/qtest/migration/misc-tests.c
index ba8183978b3..34b376562ff 100644
--- a/tests/qtest/migration/misc-tests.c
+++ b/tests/qtest/migration/misc-tests.c
@@ -55,21 +55,21 @@ HMPTestData test_cases[] = {
     TEST("direct-io", "on", "on"),
 
     /* uint64_t */
-    TEST("announce-initial", "60", "60 ms"),
-    TEST("announce-max", "600", "600 ms"),
+    TEST("announce-initial", "60", "60"),
+    TEST("announce-max", "600", "600"),
     TEST("announce-rounds", "6", "6"),
-    TEST("announce-step", "15", "15 ms"),
-    TEST("downtime-limit", "400", "400 ms"),
-    TEST("avail-switchover-bandwidth", "2097152", "2199023255552 bytes/second"),
-    TEST("max-bandwidth", "9876543", "10356305952768 bytes/second"),
-    TEST("max-postcopy-bandwidth", "1048576", "1048576 bytes/second"),
-    TEST("vcpu-dirty-limit", "20", "20 MB/s"),
-    TEST("x-rdma-chunk-size", "1048576", "1048576 bytes"),
-    TEST("x-vcpu-dirty-limit-period", "750", "750 ms"),
-    TEST("xbzrle-cache-size", "67108864", "67108864 bytes"),
+    TEST("announce-step", "15", "15"),
+    TEST("downtime-limit", "400", "400"),
+    TEST("avail-switchover-bandwidth", "2097152", "2199023255552"),
+    TEST("max-bandwidth", "9876543", "10356305952768"),
+    TEST("max-postcopy-bandwidth", "1048576", "1048576"),
+    TEST("vcpu-dirty-limit", "20", "20"),
+    TEST("x-rdma-chunk-size", "1048576", "1048576"),
+    TEST("x-vcpu-dirty-limit-period", "750", "750"),
+    TEST("xbzrle-cache-size", "67108864", "67108864"),
 
     /* uint32_t */
-    TEST("x-checkpoint-delay", "5000", "5000 ms"),
+    TEST("x-checkpoint-delay", "5000", "5000"),
 
     /* uint8_t */
     TEST("cpu-throttle-increment", "15", "15"),
@@ -82,9 +82,9 @@ HMPTestData test_cases[] = {
     TEST("mode", "cpr-exec", "cpr-exec"),
     TEST("multifd-compression", "zlib", "zlib"),
     TEST("zero-page-detection", "none", "none"),
-    TEST("tls-authz", "my_authz", "'my_authz'"),
-    TEST("tls-creds", "null", "'null'"),
-    TEST("tls-hostname", "localhost", "'localhost'"),
+    TEST("tls-authz", "my_authz", "my_authz"),
+    TEST("tls-creds", "null", "null"),
+    TEST("tls-hostname", "localhost", "localhost"),
     TEST("cpr-exec-command", "/bin/true foobar", "/bin/true foobar"),
 
     /* can be set but are currently missing in the query output */
-- 
2.53.0



^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 13/18] migration: Use output visitor in info command
  2026-09-02 22:15 [PATCH 00/18] migration: MigrationParameters changes Fabiano Rosas
                   ` (11 preceding siblings ...)
  2026-09-02 22:15 ` [PATCH 12/18] migration: Change HMP 'info migrate_parameters' output Fabiano Rosas
@ 2026-09-02 22:15 ` Fabiano Rosas
  2026-09-04 12:04   ` Peter Xu
  2026-09-02 22:15 ` [PATCH 14/18] migration: Rewrite migrate_set_parameter_completion using QDict Fabiano Rosas
                   ` (4 subsequent siblings)
  17 siblings, 1 reply; 50+ messages in thread
From: Fabiano Rosas @ 2026-09-02 22:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: Peter Xu

The hmp_info_migrate_parameters function currently open-codes the
mon_printf calls for each migration parameter. As with the set command
in the last patch, this should not be necessary as the QAPI
infrastructure already has generated code that takes type and struct
member names into account, including converting _ from C into the '-'
character as part of parameter names strings.

The current code is also quite painful to rebase if a series has been
carried for a long time while parameters have been added in master.

Replace all of this with a conversion from MigrationParameters to
QDict using an output visitor and a loop over the QDict that prints
per-QAPI-type formatted strings.

Modelled after block/qapi.c:dump_qobject, but with some changes to
keep the migration command output formatting.

Note that this was not a for-free improvement, the HMP command format
output was changed incompatibly in a previous patch. It doesn't output
units anymore.

Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
 migration/migration-hmp-cmds.c | 243 ++++++++++++++-------------------
 1 file changed, 100 insertions(+), 143 deletions(-)

diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c
index 089c6d4ff46..dff69650a0c 100644
--- a/migration/migration-hmp-cmds.c
+++ b/migration/migration-hmp-cmds.c
@@ -25,7 +25,12 @@
 #include "qapi/qapi-commands-migration.h"
 #include "qapi/qapi-visit-migration.h"
 #include "qapi/qobject-input-visitor.h"
+#include "qapi/qobject-output-visitor.h"
+#include "qobject/qbool.h"
 #include "qobject/qdict.h"
+#include "qobject/qjson.h"
+#include "qobject/qlist.h"
+#include "qobject/qnum.h"
 #include "qobject/qstring.h"
 #include "qapi/string-input-visitor.h"
 #include "qapi/string-output-visitor.h"
@@ -316,170 +321,122 @@ void hmp_info_migrate_capabilities(Monitor *mon, const QDict *qdict)
     qapi_free_MigrationCapabilityStatusList(caps);
 }
 
-static void monitor_print_cpr_exec_command(Monitor *mon, strList *args)
+static QDict *migrate_params_to_dict(MigrationParameters *p, Error **errp)
 {
-    monitor_printf(mon, "%s:",
-        MigrationParameter_str(MIGRATION_PARAMETER_CPR_EXEC_COMMAND));
+    QObject *obj = NULL;
+    Visitor *v = qobject_output_visitor_new(&obj);
 
-    while (args) {
-        monitor_printf(mon, " %s", args->value);
-        args = args->next;
+    if (visit_type_MigrationParameters(v, NULL, &p, errp)) {
+        visit_complete(v, &obj);
     }
-    monitor_printf(mon, "\n");
+    visit_free(v);
+    return qobject_to(QDict, obj);
 }
 
-void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict)
+static void hmp_migrate_print_qobject(Monitor *mon, const char *label,
+                                      QObject *obj)
 {
-    MigrationParameters *params;
-    MigrationState *s = migrate_get_current();
+    const char *sep;
 
-    params = qmp_query_migrate_parameters(NULL);
+    if (!obj) {
+        return;
+    }
 
-    if (params) {
-        monitor_printf(mon, "%s: %" PRIu64 "\n",
-            MigrationParameter_str(MIGRATION_PARAMETER_ANNOUNCE_INITIAL),
-            params->announce_initial);
-        monitor_printf(mon, "%s: %" PRIu64 "\n",
-            MigrationParameter_str(MIGRATION_PARAMETER_ANNOUNCE_MAX),
-            params->announce_max);
-        monitor_printf(mon, "%s: %" PRIu64 "\n",
-            MigrationParameter_str(MIGRATION_PARAMETER_ANNOUNCE_ROUNDS),
-            params->announce_rounds);
-        monitor_printf(mon, "%s: %" PRIu64 "\n",
-            MigrationParameter_str(MIGRATION_PARAMETER_ANNOUNCE_STEP),
-            params->announce_step);
-        assert(params->has_throttle_trigger_threshold);
-        monitor_printf(mon, "%s: %u\n",
-            MigrationParameter_str(MIGRATION_PARAMETER_THROTTLE_TRIGGER_THRESHOLD),
-            params->throttle_trigger_threshold);
-        assert(params->has_cpu_throttle_initial);
-        monitor_printf(mon, "%s: %u\n",
-            MigrationParameter_str(MIGRATION_PARAMETER_CPU_THROTTLE_INITIAL),
-            params->cpu_throttle_initial);
-        assert(params->has_cpu_throttle_increment);
-        monitor_printf(mon, "%s: %u\n",
-            MigrationParameter_str(MIGRATION_PARAMETER_CPU_THROTTLE_INCREMENT),
-            params->cpu_throttle_increment);
-        assert(params->has_cpu_throttle_tailslow);
-        monitor_printf(mon, "%s: %s\n",
-            MigrationParameter_str(MIGRATION_PARAMETER_CPU_THROTTLE_TAILSLOW),
-            params->cpu_throttle_tailslow ? "on" : "off");
-        assert(params->has_max_cpu_throttle);
-        monitor_printf(mon, "%s: %u\n",
-            MigrationParameter_str(MIGRATION_PARAMETER_MAX_CPU_THROTTLE),
-            params->max_cpu_throttle);
-        assert(params->tls_creds);
-        monitor_printf(mon, "%s: %s\n",
-            MigrationParameter_str(MIGRATION_PARAMETER_TLS_CREDS),
-                       params->tls_creds->u.s);
-        assert(params->tls_hostname);
-        monitor_printf(mon, "%s: %s\n",
-            MigrationParameter_str(MIGRATION_PARAMETER_TLS_HOSTNAME),
-                       params->tls_hostname->u.s);
-        assert(params->tls_authz);
-        monitor_printf(mon, "%s: %s\n",
-            MigrationParameter_str(MIGRATION_PARAMETER_TLS_AUTHZ),
-                       params->tls_authz->u.s);
-        assert(params->has_max_bandwidth);
-        monitor_printf(mon, "%s: %" PRIu64 "\n",
-            MigrationParameter_str(MIGRATION_PARAMETER_MAX_BANDWIDTH),
-            params->max_bandwidth);
-        assert(params->has_avail_switchover_bandwidth);
-        monitor_printf(mon, "%s: %" PRIu64 "\n",
-            MigrationParameter_str(MIGRATION_PARAMETER_AVAIL_SWITCHOVER_BANDWIDTH),
-            params->avail_switchover_bandwidth);
-        assert(params->has_max_postcopy_bandwidth);
-        monitor_printf(mon, "%s: %" PRIu64 "\n",
-            MigrationParameter_str(MIGRATION_PARAMETER_MAX_POSTCOPY_BANDWIDTH),
-            params->max_postcopy_bandwidth);
-        assert(params->has_downtime_limit);
-        monitor_printf(mon, "%s: %" PRIu64 "\n",
-            MigrationParameter_str(MIGRATION_PARAMETER_DOWNTIME_LIMIT),
-            params->downtime_limit);
-        assert(params->has_x_checkpoint_delay);
-        monitor_printf(mon, "%s: %u\n",
-            MigrationParameter_str(MIGRATION_PARAMETER_X_CHECKPOINT_DELAY),
-            params->x_checkpoint_delay);
-        monitor_printf(mon, "%s: %u\n",
-            MigrationParameter_str(MIGRATION_PARAMETER_MULTIFD_CHANNELS),
-            params->multifd_channels);
-        monitor_printf(mon, "%s: %s\n",
-            MigrationParameter_str(MIGRATION_PARAMETER_MULTIFD_COMPRESSION),
-            MultiFDCompression_str(params->multifd_compression));
-        assert(params->has_zero_page_detection);
-        monitor_printf(mon, "%s: %s\n",
-            MigrationParameter_str(MIGRATION_PARAMETER_ZERO_PAGE_DETECTION),
-            qapi_enum_lookup(&ZeroPageDetection_lookup,
-                params->zero_page_detection));
-        monitor_printf(mon, "%s: %" PRIu64 "\n",
-            MigrationParameter_str(MIGRATION_PARAMETER_XBZRLE_CACHE_SIZE),
-            params->xbzrle_cache_size);
+    /*
+     * Put a space after labels
+     * foo: bar
+     *     ^
+     */
+    if (label && label[0] && label[strlen(label) - 1] == ':') {
+        sep = " ";
+    } else {
+        sep = "";
+    }
 
-        if (s->has_block_bitmap_mapping) {
-            BitmapMigrationNodeAliasList *nal;
-            BitmapMigrationNodeAlias *na;
-            BitmapMigrationBitmapAliasList *bal;
-            BitmapMigrationBitmapAlias *ba;
-            BitmapMigrationBitmapAliasTransform *bat;
+    switch (qobject_type(obj)) {
+    case QTYPE_NONE:
+        g_assert_not_reached();
+    case QTYPE_QNULL:
+        break;
+    case QTYPE_QNUM: {
+        int64_t i64;
 
-            monitor_printf(mon, "%s:",
-                           MigrationParameter_str(
-                               MIGRATION_PARAMETER_BLOCK_BITMAP_MAPPING));
+        if (qnum_get_try_int(qobject_to(QNum, obj), &i64)) {
+            monitor_printf(mon, "%s%s%" PRId64, label, sep, i64);
+        }
+        break;
+    }
+    case QTYPE_QSTRING: {
+        QString *str = qobject_to(QString, obj);
 
-            for (nal = params->block_bitmap_mapping; nal; nal = nal->next)
-            {
-                na = nal->value;
-                monitor_printf(mon, " bitmaps:");
-                for (bal = na->bitmaps; bal; bal = bal->next) {
-                    ba = bal->value;
-                    bat = ba->transform;
+        if (str) {
+            monitor_printf(mon, "%s%s%s", label, sep, qstring_get_str(str));
+        }
+        break;
+    }
+    case QTYPE_QDICT: {
+        QDict *d = qobject_to(QDict, obj);
+        const QDictEntry *e;
+        int i = 0;
 
-                    monitor_printf(mon, " name: %s", ba->name);
-                    if (bat && bat->has_persistent) {
-                        if (bat->persistent) {
-                            monitor_printf(mon, " persistent: on");
-                        } else {
-                            monitor_printf(mon, " persistent: off");
-                        }
-                    }
-                    monitor_printf(mon, " alias: %s", ba->alias);
+        if (d) {
+            for (e = qdict_first(d); e; e = qdict_next(d, e), i++) {
+                g_autofree char *l = g_strdup_printf("%s:", qdict_entry_key(e));
+                if (i) {
+                    monitor_printf(mon, " ");
                 }
-                monitor_printf(mon, " node-name: %s alias: %s",
-                               na->node_name, na->alias);
+                hmp_migrate_print_qobject(mon, l, qdict_entry_value(e));
             }
-
-            monitor_printf(mon, "\n");
         }
+        break;
+    }
+    case QTYPE_QLIST: {
+        QList *l = qobject_to(QList, obj);
+        const QListEntry *e;
 
-        monitor_printf(mon, "%s: %" PRIu64 "\n",
-        MigrationParameter_str(MIGRATION_PARAMETER_X_VCPU_DIRTY_LIMIT_PERIOD),
-        params->x_vcpu_dirty_limit_period);
+        if (l) {
+            monitor_printf(mon, "%s", label);
 
-        monitor_printf(mon, "%s: %" PRIu64 "\n",
-            MigrationParameter_str(MIGRATION_PARAMETER_VCPU_DIRTY_LIMIT),
-            params->vcpu_dirty_limit);
+            for (e = qlist_first(l); e; e = qlist_next(e)) {
+                /*
+                 * In the first iteration, this is the space after the
+                 * colon, otherwise it's the space between list
+                 * elements.
+                 */
+                monitor_printf(mon, " ");
+                hmp_migrate_print_qobject(mon, "", e->value);
+            }
+        }
+        break;
+    }
+    case QTYPE_QBOOL: {
+        QBool *b = qobject_to(QBool, obj);
+        if (b) {
+            monitor_printf(mon, "%s%s%s", label, sep,
+                           qbool_get_bool(b) ? "on" : "off");
+        }
+        break;
+    }
+    default:
+        g_assert_not_reached();
+        break;
+    }
+}
 
-        assert(params->has_mode);
-        monitor_printf(mon, "%s: %s\n",
-            MigrationParameter_str(MIGRATION_PARAMETER_MODE),
-            qapi_enum_lookup(&MigMode_lookup, params->mode));
+void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict)
+{
+    MigrationParameters *params = qmp_query_migrate_parameters(NULL);
+    g_autoptr(QDict) d;
+    const QDictEntry *e;
 
-        if (params->has_direct_io) {
-            monitor_printf(mon, "%s: %s\n",
-                           MigrationParameter_str(
-                               MIGRATION_PARAMETER_DIRECT_IO),
-                           params->direct_io ? "on" : "off");
-        }
+    assert(params);
 
-        if (params->has_x_rdma_chunk_size) {
-            monitor_printf(mon, "%s: %" PRIu64 "\n",
-                           MigrationParameter_str(
-                               MIGRATION_PARAMETER_X_RDMA_CHUNK_SIZE),
-                           params->x_rdma_chunk_size);
-        }
+    d = migrate_params_to_dict(params, NULL);
+    for (e = qdict_first(d); e; e = qdict_next(d, e)) {
+        g_autofree char *label = g_strdup_printf("%s:", qdict_entry_key(e));
 
-        assert(params->has_cpr_exec_command);
-        monitor_print_cpr_exec_command(mon, params->cpr_exec_command);
+        hmp_migrate_print_qobject(mon, label, qdict_entry_value(e));
+        monitor_printf(mon, "\n");
     }
 
     qapi_free_MigrationParameters(params);
-- 
2.53.0



^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 14/18] migration: Rewrite migrate_set_parameter_completion using QDict
  2026-09-02 22:15 [PATCH 00/18] migration: MigrationParameters changes Fabiano Rosas
                   ` (12 preceding siblings ...)
  2026-09-02 22:15 ` [PATCH 13/18] migration: Use output visitor in info command Fabiano Rosas
@ 2026-09-02 22:15 ` Fabiano Rosas
  2026-09-03 20:23   ` Peter Xu
  2026-09-02 22:15 ` [PATCH 15/18] qapi/migration: Remove MigrationParameter Fabiano Rosas
                   ` (3 subsequent siblings)
  17 siblings, 1 reply; 50+ messages in thread
From: Fabiano Rosas @ 2026-09-02 22:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: Peter Xu

The migrate_set_parameter_completion function is the last user of the
MigrationParameter enum. Write the code using an output visitor and
QDict instead so we can remove the enum in a future patch.

Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
 migration/migration-hmp-cmds.c | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c
index dff69650a0c..d0adda25090 100644
--- a/migration/migration-hmp-cmds.c
+++ b/migration/migration-hmp-cmds.c
@@ -791,14 +791,19 @@ void migrate_set_capability_completion(ReadLineState *rs, int nb_args,
 void migrate_set_parameter_completion(ReadLineState *rs, int nb_args,
                                       const char *str)
 {
+    g_autoptr(MigrationParameters) params = g_new0(MigrationParameters, 1);
+    g_autoptr(QDict) d = migrate_params_to_dict(params, NULL);
+    const QDictEntry *e;
     size_t len;
 
     len = strlen(str);
     readline_set_completion_index(rs, len);
     if (nb_args == 2) {
-        int i;
-        for (i = 0; i < MIGRATION_PARAMETER__MAX; i++) {
-            readline_add_completion_of(rs, str, MigrationParameter_str(i));
+        for (e = qdict_first(d); e; e = qdict_next(d, e)) {
+            const char *key = qdict_entry_key(e);
+            if (!g_str_has_prefix(key, "has-")) {
+                readline_add_completion_of(rs, str, key);
+            }
         }
     }
 }
-- 
2.53.0



^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 15/18] qapi/migration: Remove MigrationParameter
  2026-09-02 22:15 [PATCH 00/18] migration: MigrationParameters changes Fabiano Rosas
                   ` (13 preceding siblings ...)
  2026-09-02 22:15 ` [PATCH 14/18] migration: Rewrite migrate_set_parameter_completion using QDict Fabiano Rosas
@ 2026-09-02 22:15 ` Fabiano Rosas
  2026-09-04  9:07   ` Markus Armbruster
  2026-09-04 12:24   ` Peter Xu
  2026-09-02 22:15 ` [PATCH 16/18] migration: Add capabilities into MigrationParameters Fabiano Rosas
                   ` (2 subsequent siblings)
  17 siblings, 2 replies; 50+ messages in thread
From: Fabiano Rosas @ 2026-09-02 22:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: Peter Xu, Eric Blake, Markus Armbruster

This enum is convenient in two ways (just how enums work):

1- It provides the number of migration parameters as its __MAX member.

2- It allows iterating over an integer range and get a migration
   parameter name string corresponding to that position in the enum.

The migration code doesn't have the need for (2) anymore.

Balancing the benefit of (1) versus the disadvantage of requiring
migration.json to be updated in two different places whenever a
parameter is added, experience shows that the latter churn is enough
to decide to remove the enum.

Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
 migration/options.c |  6 +-----
 qapi/migration.json | 36 ------------------------------------
 2 files changed, 1 insertion(+), 41 deletions(-)

diff --git a/migration/options.c b/migration/options.c
index 5d17acdd881..f988b181f0e 100644
--- a/migration/options.c
+++ b/migration/options.c
@@ -1127,7 +1127,6 @@ static MigrationParameters *migrate_params_from_dict(QDict *d, Error **errp)
  */
 static void migrate_mark_all_params_present(MigrationParameters *p)
 {
-    int len, n_str_args = 3; /* tls-creds, tls-hostname, tls-authz */
     bool *has_fields[] = {
         &p->has_throttle_trigger_threshold, &p->has_cpu_throttle_initial,
         &p->has_cpu_throttle_increment, &p->has_cpu_throttle_tailslow,
@@ -1144,10 +1143,7 @@ static void migrate_mark_all_params_present(MigrationParameters *p)
         &p->has_x_rdma_chunk_size, &p->has_cpr_exec_command,
     };
 
-    len = ARRAY_SIZE(has_fields);
-    assert(len + n_str_args == MIGRATION_PARAMETER__MAX);
-
-    for (int i = 0; i < len; i++) {
+    for (int i = 0; i < ARRAY_SIZE(has_fields); i++) {
         *has_fields[i] = true;
     }
 }
diff --git a/qapi/migration.json b/qapi/migration.json
index b1eaf7b0545..78c6e933cf1 100644
--- a/qapi/migration.json
+++ b/qapi/migration.json
@@ -796,42 +796,6 @@
       'bitmaps': [ 'BitmapMigrationBitmapAlias' ]
   } }
 
-##
-# @MigrationParameter:
-#
-# Migration parameters enumeration.  The enumeration values mirror the
-# members of @MigrationParameters.
-#
-# Features:
-#
-# @unstable: Members @x-checkpoint-delay, @x-rdma-chunk-size, and
-#     @x-vcpu-dirty-limit-period are experimental.
-#
-# Since: 2.4
-##
-{ 'enum': 'MigrationParameter',
-  'data': ['announce-initial', 'announce-max',
-           'announce-rounds', 'announce-step',
-           'throttle-trigger-threshold',
-           'cpu-throttle-initial', 'cpu-throttle-increment',
-           'cpu-throttle-tailslow',
-           'tls-creds', 'tls-hostname', 'tls-authz', 'max-bandwidth',
-           'avail-switchover-bandwidth', 'downtime-limit',
-           { 'name': 'x-checkpoint-delay', 'features': [ 'unstable' ] },
-           'multifd-channels',
-           'xbzrle-cache-size', 'max-postcopy-bandwidth',
-           'max-cpu-throttle', 'multifd-compression',
-           'multifd-zlib-level', 'multifd-zstd-level',
-           'multifd-qatzip-level',
-           'block-bitmap-mapping',
-           { 'name': 'x-vcpu-dirty-limit-period', 'features': ['unstable'] },
-           'vcpu-dirty-limit',
-           'mode',
-           'zero-page-detection',
-           'direct-io',
-           { 'name': 'x-rdma-chunk-size', 'features': [ 'unstable' ] },
-           'cpr-exec-command'] }
-
 ##
 # @migrate-set-parameters:
 #
-- 
2.53.0



^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 16/18] migration: Add capabilities into MigrationParameters
  2026-09-02 22:15 [PATCH 00/18] migration: MigrationParameters changes Fabiano Rosas
                   ` (14 preceding siblings ...)
  2026-09-02 22:15 ` [PATCH 15/18] qapi/migration: Remove MigrationParameter Fabiano Rosas
@ 2026-09-02 22:15 ` Fabiano Rosas
  2026-09-04  9:53   ` Markus Armbruster
  2026-09-02 22:15 ` [PATCH 17/18] migration: Remove s->capabilities Fabiano Rosas
  2026-09-02 22:15 ` [PATCH 18/18] qapi/migration: Deprecate capabilities commands Fabiano Rosas
  17 siblings, 1 reply; 50+ messages in thread
From: Fabiano Rosas @ 2026-09-02 22:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: Peter Xu, Eric Blake, Markus Armbruster

Add capabilities to MigrationParameters. This structure will hold all
migration options. Capabilities will go away in the next patch.

From this point on, both QMP and HMP versions of
migrate-set-parameters and query-migrate-parameters gain the ability
to work with capabilities.

With MigrationParameters now having members for each capability, the
migration capabilities commands (query-migrate-capabilities,
migrate-set-capabilities) will soon be deprecated. Add a set of
helpers to convert between the old MigrationCapability representation
and the new representation as members of MigrationParameters.

Acked-by: Peter Xu <peterx@redhat.com>
Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
 migration/migration.c |   8 +++
 migration/options.c   | 127 ++++++++++++++++++++++++++++++++++++++++++
 migration/options.h   |   5 ++
 qapi/migration.json   | 118 ++++++++++++++++++++++++++++++++++++++-
 4 files changed, 255 insertions(+), 3 deletions(-)

diff --git a/migration/migration.c b/migration/migration.c
index dab282dd2f3..c8e7e86ea05 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -4086,6 +4086,14 @@ static bool migration_object_check(MigrationState *ms, Error **errp)
         return false;
     }
 
+    /*
+     * FIXME: Temporarily while -global capabilties are still using
+     * s->capabilities. Will be gone by the end of the series.
+     */
+    for (int i = 0; i < MIGRATION_CAPABILITY__MAX; i++) {
+        migrate_capability_set_compat(&ms->parameters, i, ms->capabilities[i]);
+    }
+
     return migrate_caps_check(old_caps, ms->capabilities, errp);
 }
 
diff --git a/migration/options.c b/migration/options.c
index f988b181f0e..7c638e204a1 100644
--- a/migration/options.c
+++ b/migration/options.c
@@ -778,6 +778,108 @@ bool migrate_caps_check(bool *old_caps, bool *new_caps, Error **errp)
     return true;
 }
 
+static bool *migrate_capability_get_addr(MigrationParameters *params, int i)
+{
+    bool *cap_addr = NULL;
+
+    switch (i) {
+    case MIGRATION_CAPABILITY_XBZRLE:
+        cap_addr = &params->xbzrle;
+        break;
+    case MIGRATION_CAPABILITY_RDMA_PIN_ALL:
+        cap_addr = &params->rdma_pin_all;
+        break;
+    case MIGRATION_CAPABILITY_AUTO_CONVERGE:
+        cap_addr = &params->auto_converge;
+        break;
+    case MIGRATION_CAPABILITY_EVENTS:
+        cap_addr = &params->events;
+        break;
+    case MIGRATION_CAPABILITY_POSTCOPY_RAM:
+        cap_addr = &params->postcopy_ram;
+        break;
+    case MIGRATION_CAPABILITY_X_COLO:
+        cap_addr = &params->x_colo;
+        break;
+    case MIGRATION_CAPABILITY_RELEASE_RAM:
+        cap_addr = &params->release_ram;
+        break;
+    case MIGRATION_CAPABILITY_RETURN_PATH:
+        cap_addr = &params->return_path;
+        break;
+    case MIGRATION_CAPABILITY_PAUSE_BEFORE_SWITCHOVER:
+        cap_addr = &params->pause_before_switchover;
+        break;
+    case MIGRATION_CAPABILITY_MULTIFD:
+        cap_addr = &params->multifd;
+        break;
+    case MIGRATION_CAPABILITY_DIRTY_BITMAPS:
+        cap_addr = &params->dirty_bitmaps;
+        break;
+    case MIGRATION_CAPABILITY_POSTCOPY_BLOCKTIME:
+        cap_addr = &params->postcopy_blocktime;
+        break;
+    case MIGRATION_CAPABILITY_LATE_BLOCK_ACTIVATE:
+        cap_addr = &params->late_block_activate;
+        break;
+    case MIGRATION_CAPABILITY_X_IGNORE_SHARED:
+        cap_addr = &params->x_ignore_shared;
+        break;
+    case MIGRATION_CAPABILITY_VALIDATE_UUID:
+        cap_addr = &params->validate_uuid;
+        break;
+    case MIGRATION_CAPABILITY_BACKGROUND_SNAPSHOT:
+        cap_addr = &params->background_snapshot;
+        break;
+    case MIGRATION_CAPABILITY_ZERO_COPY_SEND:
+        cap_addr = &params->zero_copy_send;
+        break;
+    case MIGRATION_CAPABILITY_POSTCOPY_PREEMPT:
+        cap_addr = &params->postcopy_preempt;
+        break;
+    case MIGRATION_CAPABILITY_SWITCHOVER_ACK:
+        cap_addr = &params->switchover_ack;
+        break;
+    case MIGRATION_CAPABILITY_DIRTY_LIMIT:
+        cap_addr = &params->dirty_limit;
+        break;
+    case MIGRATION_CAPABILITY_MAPPED_RAM:
+        cap_addr = &params->mapped_ram;
+        break;
+    default:
+        g_assert_not_reached();
+    }
+
+    return cap_addr;
+}
+
+/* Compatibility for code that reads capabilities in a loop */
+bool migrate_capability_get_compat(MigrationParameters *params, int i)
+{
+    return *(migrate_capability_get_addr(params, i));
+}
+
+/* Compatibility for code that writes capabilities in a loop */
+void migrate_capability_set_compat(MigrationParameters *params, int i, bool val)
+{
+    *(migrate_capability_get_addr(params, i)) = val;
+}
+
+/*
+ * Set capabilities for compatibility with the old
+ * migrate-set-capabilities command.
+ */
+void migrate_capabilities_set_compat(MigrationParameters *params,
+                                     MigrationCapabilityStatusList *caps)
+{
+    MigrationCapabilityStatusList *cap;
+
+    for (cap = caps; cap; cap = cap->next) {
+        migrate_capability_set_compat(params, cap->value->capability,
+                                      cap->value->state);
+    }
+}
+
 MigrationCapabilityStatusList *qmp_query_migrate_capabilities(Error **errp)
 {
     MigrationCapabilityStatusList *head = NULL, **tail = &head;
@@ -819,6 +921,8 @@ void qmp_migrate_set_capabilities(MigrationCapabilityStatusList *params,
     for (cap = params; cap; cap = cap->next) {
         s->capabilities[cap->value->capability] = cap->value->state;
     }
+
+    migrate_capabilities_set_compat(&s->parameters, params);
 }
 
 /* parameters */
@@ -1141,6 +1245,15 @@ static void migrate_mark_all_params_present(MigrationParameters *p)
         &p->has_x_vcpu_dirty_limit_period, &p->has_vcpu_dirty_limit,
         &p->has_mode, &p->has_zero_page_detection, &p->has_direct_io,
         &p->has_x_rdma_chunk_size, &p->has_cpr_exec_command,
+        &p->has_xbzrle, &p->has_rdma_pin_all,
+        &p->has_auto_converge, &p->has_events,
+        &p->has_postcopy_ram, &p->has_x_colo, &p->has_release_ram,
+        &p->has_return_path, &p->has_pause_before_switchover, &p->has_multifd,
+        &p->has_dirty_bitmaps, &p->has_postcopy_blocktime,
+        &p->has_late_block_activate, &p->has_x_ignore_shared,
+        &p->has_validate_uuid, &p->has_background_snapshot,
+        &p->has_zero_copy_send, &p->has_postcopy_preempt,
+        &p->has_switchover_ack, &p->has_dirty_limit, &p->has_mapped_ram,
     };
 
     for (int i = 0; i < ARRAY_SIZE(has_fields); i++) {
@@ -1465,6 +1578,20 @@ void qmp_migrate_set_parameters(MigrationParameters *input, Error **errp)
     tls_opt_to_str(input->tls_hostname);
     tls_opt_to_str(input->tls_authz);
 
+    /*
+     * FIXME: Temporarily while migrate_caps_check is not
+     * converted to look at s->parameters. Will be gone the end of
+     * the series.
+     */
+    bool new_caps[MIGRATION_CAPABILITY__MAX] = { 0 };
+    for (int i = 0; i < MIGRATION_CAPABILITY__MAX; i++) {
+        new_caps[i] = migrate_capability_get_compat(cur, i);
+    }
+    if (!migrate_caps_check(migrate_get_current()->capabilities, new_caps,
+                            errp)) {
+        return;
+    }
+
     /* merge input on top of current */
     if (!migrate_params_merge(cur, input, &new, errp)) {
         return;
diff --git a/migration/options.h b/migration/options.h
index c7da2d0b5b0..eedd1aa1f93 100644
--- a/migration/options.h
+++ b/migration/options.h
@@ -94,4 +94,9 @@ uint64_t migrate_rdma_chunk_size(void);
 bool migrate_params_check(MigrationParameters *params, Error **errp);
 void migrate_params_init(MigrationParameters *params);
 bool migrate_params_free(MigrationParameters *params, Error **errp);
+bool migrate_capability_get_compat(MigrationParameters *params, int i);
+void migrate_capability_set_compat(MigrationParameters *params, int i,
+                                   bool val);
+void migrate_capabilities_set_compat(MigrationParameters *params,
+                                     MigrationCapabilityStatusList *caps);
 #endif
diff --git a/qapi/migration.json b/qapi/migration.json
index 78c6e933cf1..7952ef44db9 100644
--- a/qapi/migration.json
+++ b/qapi/migration.json
@@ -976,10 +976,101 @@
 #     Must be set to the same value on both source and destination
 #     before migration starts.  (Since 11.1)
 #
+# @xbzrle: Migration supports xbzrle (Xor Based Zero Run Length
+#     Encoding).  This feature allows us to minimize migration traffic
+#     for certain work loads, by sending compressed difference of the
+#     pages
+#
+# @rdma-pin-all: Controls whether or not the entire VM memory
+#     footprint is mlock()'d on demand or all at once.  Refer to
+#     docs/rdma.txt for usage.  Disabled by default.  (since 2.0)
+#
+# @events: Generate events for each migration state change.
+#     (since 2.4)
+#
+# @auto-converge: If enabled, QEMU will automatically throttle down
+#     the guest to speed up convergence of RAM migration.  (since 1.6)
+#
+# @postcopy-ram: Start executing on the migration target before all of
+#     RAM has been migrated, pulling the remaining pages along as
+#     needed.  The capacity must have the same setting on both source
+#     and target or migration will not even start.  **Note:** If the
+#     migration fails during postcopy the VM will fail.  (since 2.6)
+#
+# @x-colo: If enabled, migration will never end, and the state of the
+#     VM on the primary side will be migrated continuously to the VM
+#     on secondary side, this process is called COarse-Grain LOck
+#     Stepping (COLO) for Non-stop Service.  (since 2.8)
+#
+# @release-ram: If enabled, QEMU will free the migrated ram pages on
+#     the source during postcopy-ram migration.  (since 2.9)
+#
+# @return-path: If enabled, migration will use the return path even
+#     for precopy.  (since 2.10)
+#
+# @pause-before-switchover: Pause outgoing migration before
+#     serialising device state and before disabling block IO.
+#     (since 2.11)
+#
+# @multifd: Use more than one fd for migration.  (since 4.0)
+#
+# @dirty-bitmaps: If enabled, QEMU will migrate named dirty bitmaps.
+#     (since 2.12)
+#
+# @postcopy-blocktime: Calculate downtime for postcopy live migration.
+#     (since 3.0)
+#
+# @late-block-activate: If enabled, the destination will not activate
+#     block devices (and thus take locks) immediately at the end of
+#     migration.  (since 3.0)
+#
+# @x-ignore-shared: If enabled, QEMU will not migrate shared memory
+#     that is accessible on the destination machine.  (since 4.0)
+#
+# @validate-uuid: Send the UUID of the source to allow the destination
+#     to ensure it is the same.  (since 4.2)
+#
+# @background-snapshot: If enabled, the migration stream will be a
+#     snapshot of the VM exactly at the point when the migration
+#     procedure starts.  The VM RAM is saved with running VM.
+#     (since 6.0)
+#
+# @zero-copy-send: Controls behavior on sending memory pages on
+#     migration.  When true, enables a zero-copy mechanism for sending
+#     memory pages, if host supports it.  Requires that QEMU be
+#     permitted to use locked memory for guest RAM pages.  (since 7.1)
+#
+# @postcopy-preempt: If enabled, the migration process will allow
+#     postcopy requests to preempt precopy stream, so postcopy
+#     requests will be handled faster.  This is a performance feature
+#     and should not affect the correctness of postcopy migration.
+#     (since 7.1)
+#
+# @switchover-ack: If enabled, migration will not stop the source VM
+#     and complete the migration until an ACK is received from the
+#     destination that it's OK to do so.  Exactly when this ACK is
+#     sent depends on the migrated devices that use this feature.  For
+#     example, a device can use it to make sure some of its data is
+#     sent and loaded in the destination before doing switchover.
+#     This can reduce downtime if devices that support this capability
+#     are present.  'return-path' capability must be enabled to use
+#     it.  (since 8.1)
+#
+# @dirty-limit: If enabled, migration will throttle vCPUs as needed to
+#     keep their dirty page rate within @vcpu-dirty-limit.  This can
+#     improve responsiveness of large guests during live migration,
+#     and can result in more stable read performance.  Requires KVM
+#     with accelerator property "dirty-ring-size" set.  (Since 8.1)
+#
+# @mapped-ram: Migrate using fixed offsets in the migration file for
+#     each RAM page.  Requires a migration URI that supports seeking,
+#     such as a file.  (since 9.0)
+#
 # Features:
 #
-# @unstable: Members @x-checkpoint-delay, @x-rdma-chunk-size, and
-#     @x-vcpu-dirty-limit-period are experimental.
+# @unstable: Members @x-checkpoint-delay, @x-vcpu-dirty-limit-period,
+#     @x-colo, @x-ignore-shared and @x-rdma-chunk-size are
+#     experimental.
 #
 # Since: 2.4
 ##
@@ -1017,7 +1108,28 @@
             '*direct-io': 'bool',
             '*x-rdma-chunk-size': { 'type': 'uint64',
                                     'features': [ 'unstable' ] },
-            '*cpr-exec-command': [ 'str' ]} }
+            '*cpr-exec-command': [ 'str' ],
+            '*xbzrle': 'bool',
+            '*rdma-pin-all': 'bool',
+            '*auto-converge': 'bool',
+            '*events': 'bool',
+            '*postcopy-ram': 'bool',
+            '*x-colo': { 'type': 'bool', 'features': [ 'unstable' ] },
+            '*release-ram': 'bool',
+            '*return-path': 'bool',
+            '*pause-before-switchover': 'bool',
+            '*multifd': 'bool',
+            '*dirty-bitmaps': 'bool',
+            '*postcopy-blocktime': 'bool',
+            '*late-block-activate': 'bool',
+            '*x-ignore-shared': { 'type': 'bool', 'features': [ 'unstable' ] },
+            '*validate-uuid': 'bool',
+            '*background-snapshot': 'bool',
+            '*zero-copy-send': 'bool',
+            '*postcopy-preempt': 'bool',
+            '*switchover-ack': 'bool',
+            '*dirty-limit': 'bool',
+            '*mapped-ram': 'bool' } }
 
 ##
 # @query-migrate-parameters:
-- 
2.53.0



^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 17/18] migration: Remove s->capabilities
  2026-09-02 22:15 [PATCH 00/18] migration: MigrationParameters changes Fabiano Rosas
                   ` (15 preceding siblings ...)
  2026-09-02 22:15 ` [PATCH 16/18] migration: Add capabilities into MigrationParameters Fabiano Rosas
@ 2026-09-02 22:15 ` Fabiano Rosas
  2026-09-04 12:15   ` Peter Xu
  2026-09-02 22:15 ` [PATCH 18/18] qapi/migration: Deprecate capabilities commands Fabiano Rosas
  17 siblings, 1 reply; 50+ messages in thread
From: Fabiano Rosas @ 2026-09-02 22:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: Peter Xu, Laurent Vivier, Paolo Bonzini

Last patch added capabilities to s->parameters. Now we can replace all
instances of s->capabilities with s->parameters:

- The -global properties now get set directly in s->parameters.

- Accessors from options.c now read from s->parameters.

- migrate_caps_check() now takes a MigrationParameters object. The
  function is still kept around because migrate-set-capabilities will
  still use it.

- The machinery for background-snapshot compatibility check goes
  away. We can check each capability by name (if s->parameters.cap ...)

- savevm uses the helper functions introduced in the last patch to do
  validation of capabilities found on the migration stream.

Reviewed-by: Peter Xu <peterx@redhat.com>
Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
 migration/migration.c              |  22 +-
 migration/migration.h              |   2 +-
 migration/options.c                | 317 ++++++++++++-----------------
 migration/options.h                |  19 +-
 migration/savevm.c                 |   8 +-
 tests/qtest/migration/misc-tests.c | 141 +++++++++++++
 6 files changed, 284 insertions(+), 225 deletions(-)

diff --git a/migration/migration.c b/migration/migration.c
index c8e7e86ea05..69dd4f3d050 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -255,9 +255,10 @@ static bool
 migration_capabilities_and_transport_compatible(MigrationAddress *addr,
                                                 Error **errp)
 {
+    MigrationState *s = migrate_get_current();
+
     if (addr->transport == MIGRATION_ADDRESS_TYPE_RDMA) {
-        return migrate_rdma_caps_check(migrate_get_current()->capabilities,
-                                       errp);
+        return migrate_rdma_caps_check(&s->parameters, errp);
     }
 
     return true;
@@ -4079,22 +4080,7 @@ static void migration_instance_init(Object *obj)
  */
 static bool migration_object_check(MigrationState *ms, Error **errp)
 {
-    /* Assuming all off */
-    bool old_caps[MIGRATION_CAPABILITY__MAX] = { 0 };
-
-    if (!migrate_params_check(&ms->parameters, errp)) {
-        return false;
-    }
-
-    /*
-     * FIXME: Temporarily while -global capabilties are still using
-     * s->capabilities. Will be gone by the end of the series.
-     */
-    for (int i = 0; i < MIGRATION_CAPABILITY__MAX; i++) {
-        migrate_capability_set_compat(&ms->parameters, i, ms->capabilities[i]);
-    }
-
-    return migrate_caps_check(old_caps, ms->capabilities, errp);
+    return migrate_params_check(&ms->parameters, errp);
 }
 
 static const TypeInfo migration_type = {
diff --git a/migration/migration.h b/migration/migration.h
index e47ff4e3d11..e1204b7f0d5 100644
--- a/migration/migration.h
+++ b/migration/migration.h
@@ -357,7 +357,7 @@ struct MigrationState {
     /* Timestamp when VM is down (ms) to migrate the last stuff */
     int64_t downtime_start;
     int64_t downtime;
-    bool capabilities[MIGRATION_CAPABILITY__MAX];
+    int64_t expected_downtime;
     int64_t setup_time;
 
     /*
diff --git a/migration/options.c b/migration/options.c
index 7c638e204a1..780693ceed9 100644
--- a/migration/options.c
+++ b/migration/options.c
@@ -88,9 +88,6 @@
 #define DEFAULT_MIGRATE_ANNOUNCE_ROUNDS    5
 #define DEFAULT_MIGRATE_ANNOUNCE_STEP    100
 
-#define DEFINE_PROP_MIG_CAP(name, x)             \
-    DEFINE_PROP_BOOL(name, MigrationState, capabilities[x], false)
-
 const PropertyInfo qdev_prop_StrOrNull;
 #define DEFINE_PROP_STR_OR_NULL(_name, _state, _field)                  \
     DEFINE_PROP(_name, _state, _field, qdev_prop_StrOrNull, StrOrNull *, \
@@ -198,32 +195,42 @@ const Property migration_properties[] = {
                       parameters.x_rdma_chunk_size,
                       DEFAULT_MIGRATE_X_RDMA_CHUNK_SIZE),
 
-    /* Migration capabilities */
-    DEFINE_PROP_MIG_CAP("x-xbzrle", MIGRATION_CAPABILITY_XBZRLE),
-    DEFINE_PROP_MIG_CAP("x-rdma-pin-all", MIGRATION_CAPABILITY_RDMA_PIN_ALL),
-    DEFINE_PROP_MIG_CAP("x-auto-converge", MIGRATION_CAPABILITY_AUTO_CONVERGE),
-    DEFINE_PROP_MIG_CAP("x-events", MIGRATION_CAPABILITY_EVENTS),
-    DEFINE_PROP_MIG_CAP("x-postcopy-ram", MIGRATION_CAPABILITY_POSTCOPY_RAM),
-    DEFINE_PROP_MIG_CAP("x-postcopy-preempt",
-                        MIGRATION_CAPABILITY_POSTCOPY_PREEMPT),
-    DEFINE_PROP_MIG_CAP("postcopy-blocktime",
-                        MIGRATION_CAPABILITY_POSTCOPY_BLOCKTIME),
-    DEFINE_PROP_MIG_CAP("x-colo", MIGRATION_CAPABILITY_X_COLO),
-    DEFINE_PROP_MIG_CAP("x-release-ram", MIGRATION_CAPABILITY_RELEASE_RAM),
-    DEFINE_PROP_MIG_CAP("x-return-path", MIGRATION_CAPABILITY_RETURN_PATH),
-    DEFINE_PROP_MIG_CAP("x-multifd", MIGRATION_CAPABILITY_MULTIFD),
-    DEFINE_PROP_MIG_CAP("x-background-snapshot",
-            MIGRATION_CAPABILITY_BACKGROUND_SNAPSHOT),
+    DEFINE_PROP_BOOL("x-xbzrle",
+                     MigrationState, parameters.xbzrle, false),
+    DEFINE_PROP_BOOL("x-rdma-pin-all",
+                     MigrationState, parameters.rdma_pin_all, false),
+    DEFINE_PROP_BOOL("x-auto-converge",
+                     MigrationState, parameters.auto_converge, false),
+    DEFINE_PROP_BOOL("x-events",
+                     MigrationState, parameters.events, false),
+    DEFINE_PROP_BOOL("x-postcopy-ram",
+                     MigrationState, parameters.postcopy_ram, false),
+    DEFINE_PROP_BOOL("x-postcopy-preempt",
+                     MigrationState, parameters.postcopy_preempt, false),
+    DEFINE_PROP_BOOL("postcopy-blocktime",
+                     MigrationState, parameters.postcopy_blocktime, false),
+    DEFINE_PROP_BOOL("x-colo",
+                     MigrationState, parameters.x_colo, false),
+    DEFINE_PROP_BOOL("x-release-ram",
+                     MigrationState, parameters.release_ram, false),
+    DEFINE_PROP_BOOL("x-return-path",
+                     MigrationState, parameters.return_path, false),
+    DEFINE_PROP_BOOL("x-multifd",
+                     MigrationState, parameters.multifd, false),
+    DEFINE_PROP_BOOL("x-background-snapshot",
+                     MigrationState, parameters.background_snapshot, false),
 #ifdef CONFIG_LINUX
-    DEFINE_PROP_MIG_CAP("x-zero-copy-send",
-            MIGRATION_CAPABILITY_ZERO_COPY_SEND),
+    DEFINE_PROP_BOOL("x-zero-copy-send",
+                     MigrationState, parameters.zero_copy_send, false),
 #endif
-    DEFINE_PROP_MIG_CAP("x-switchover-ack",
-                        MIGRATION_CAPABILITY_SWITCHOVER_ACK),
-    DEFINE_PROP_MIG_CAP("x-dirty-limit", MIGRATION_CAPABILITY_DIRTY_LIMIT),
-    DEFINE_PROP_MIG_CAP("mapped-ram", MIGRATION_CAPABILITY_MAPPED_RAM),
-    DEFINE_PROP_MIG_CAP("x-ignore-shared",
-                        MIGRATION_CAPABILITY_X_IGNORE_SHARED),
+    DEFINE_PROP_BOOL("x-switchover-ack",
+                     MigrationState, parameters.switchover_ack, false),
+    DEFINE_PROP_BOOL("x-dirty-limit",
+                     MigrationState, parameters.dirty_limit, false),
+    DEFINE_PROP_BOOL("mapped-ram",
+                     MigrationState, parameters.mapped_ram, false),
+    DEFINE_PROP_BOOL("x-ignore-shared",
+                     MigrationState, parameters.x_ignore_shared, false),
 };
 const size_t migration_properties_count = ARRAY_SIZE(migration_properties);
 
@@ -306,7 +313,7 @@ bool migrate_auto_converge(void)
 {
     MigrationState *s = migrate_get_current();
 
-    return s->capabilities[MIGRATION_CAPABILITY_AUTO_CONVERGE];
+    return s->parameters.auto_converge;
 }
 
 bool migrate_send_switchover_start(void)
@@ -320,144 +327,142 @@ bool migrate_background_snapshot(void)
 {
     MigrationState *s = migrate_get_current();
 
-    return s->capabilities[MIGRATION_CAPABILITY_BACKGROUND_SNAPSHOT];
+    return s->parameters.background_snapshot;
 }
 
 bool migrate_colo(void)
 {
     MigrationState *s = migrate_get_current();
 
-    return s->capabilities[MIGRATION_CAPABILITY_X_COLO];
+    return s->parameters.x_colo;
 }
 
 bool migrate_dirty_bitmaps(void)
 {
     MigrationState *s = migrate_get_current();
 
-    return s->capabilities[MIGRATION_CAPABILITY_DIRTY_BITMAPS];
+    return s->parameters.dirty_bitmaps;
 }
 
 bool migrate_dirty_limit(void)
 {
     MigrationState *s = migrate_get_current();
 
-    return s->capabilities[MIGRATION_CAPABILITY_DIRTY_LIMIT];
+    return s->parameters.dirty_limit;
 }
 
 bool migrate_events(void)
 {
     MigrationState *s = migrate_get_current();
 
-    return s->capabilities[MIGRATION_CAPABILITY_EVENTS];
+    return s->parameters.events;
 }
 
 bool migrate_mapped_ram(void)
 {
     MigrationState *s = migrate_get_current();
 
-    return s->capabilities[MIGRATION_CAPABILITY_MAPPED_RAM];
+    return s->parameters.mapped_ram;
 }
 
 bool migrate_ignore_shared(void)
 {
     MigrationState *s = migrate_get_current();
 
-    return s->capabilities[MIGRATION_CAPABILITY_X_IGNORE_SHARED];
+    return s->parameters.x_ignore_shared;
 }
 
 bool migrate_late_block_activate(void)
 {
     MigrationState *s = migrate_get_current();
 
-    return s->capabilities[MIGRATION_CAPABILITY_LATE_BLOCK_ACTIVATE];
+    return s->parameters.late_block_activate;
 }
 
 bool migrate_multifd(void)
 {
     MigrationState *s = migrate_get_current();
 
-    return s->capabilities[MIGRATION_CAPABILITY_MULTIFD];
+    return s->parameters.multifd;
 }
 
 bool migrate_pause_before_switchover(void)
 {
     MigrationState *s = migrate_get_current();
 
-    return s->capabilities[MIGRATION_CAPABILITY_PAUSE_BEFORE_SWITCHOVER];
+    return s->parameters.pause_before_switchover;
 }
 
 bool migrate_postcopy_blocktime(void)
 {
     MigrationState *s = migrate_get_current();
 
-    return s->capabilities[MIGRATION_CAPABILITY_POSTCOPY_BLOCKTIME];
+    return s->parameters.postcopy_blocktime;
 }
 
 bool migrate_postcopy_preempt(void)
 {
     MigrationState *s = migrate_get_current();
 
-    return s->capabilities[MIGRATION_CAPABILITY_POSTCOPY_PREEMPT];
+    return s->parameters.postcopy_preempt;
 }
 
 bool migrate_postcopy_ram(void)
 {
     MigrationState *s = migrate_get_current();
 
-    return s->capabilities[MIGRATION_CAPABILITY_POSTCOPY_RAM];
+    return s->parameters.postcopy_ram;
 }
 
 bool migrate_rdma_pin_all(void)
 {
     MigrationState *s = migrate_get_current();
 
-    return s->capabilities[MIGRATION_CAPABILITY_RDMA_PIN_ALL];
+    return s->parameters.rdma_pin_all;
 }
 
 bool migrate_release_ram(void)
 {
     MigrationState *s = migrate_get_current();
 
-    return s->capabilities[MIGRATION_CAPABILITY_RELEASE_RAM];
+    return s->parameters.release_ram;
 }
 
 bool migrate_return_path(void)
 {
     MigrationState *s = migrate_get_current();
 
-    return s->capabilities[MIGRATION_CAPABILITY_RETURN_PATH];
+    return s->parameters.return_path;
 }
 
 bool migrate_switchover_ack(void)
 {
     MigrationState *s = migrate_get_current();
 
-    return s->capabilities[MIGRATION_CAPABILITY_SWITCHOVER_ACK];
+    return s->parameters.switchover_ack;
 }
 
 bool migrate_validate_uuid(void)
 {
     MigrationState *s = migrate_get_current();
 
-    return s->capabilities[MIGRATION_CAPABILITY_VALIDATE_UUID];
+    return s->parameters.validate_uuid;
 }
 
 bool migrate_xbzrle(void)
 {
     MigrationState *s = migrate_get_current();
 
-    return s->capabilities[MIGRATION_CAPABILITY_XBZRLE];
+    return s->parameters.xbzrle;
 }
 
 bool migrate_zero_copy_send(void)
 {
     MigrationState *s = migrate_get_current();
 
-    return s->capabilities[MIGRATION_CAPABILITY_ZERO_COPY_SEND];
+    return s->parameters.zero_copy_send;
 }
 
-/* pseudo capabilities */
-
 bool migrate_multifd_flush_after_each_section(void)
 {
     MigrationState *s = migrate_get_current();
@@ -509,44 +514,6 @@ WriteTrackingSupport migrate_query_write_tracking(void)
     return WT_SUPPORT_COMPATIBLE;
 }
 
-/* Migration capabilities set */
-struct MigrateCapsSet {
-    int size;                       /* Capability set size */
-    MigrationCapability caps[];     /* Variadic array of capabilities */
-};
-typedef struct MigrateCapsSet MigrateCapsSet;
-
-/* Define and initialize MigrateCapsSet */
-#define INITIALIZE_MIGRATE_CAPS_SET(_name, ...)   \
-    MigrateCapsSet _name = {    \
-        .size = sizeof((int []) { __VA_ARGS__ }) / sizeof(int), \
-        .caps = { __VA_ARGS__ } \
-    }
-
-/* Background-snapshot compatibility check list */
-static const
-INITIALIZE_MIGRATE_CAPS_SET(check_caps_background_snapshot,
-    MIGRATION_CAPABILITY_POSTCOPY_RAM,
-    MIGRATION_CAPABILITY_DIRTY_BITMAPS,
-    MIGRATION_CAPABILITY_POSTCOPY_BLOCKTIME,
-    MIGRATION_CAPABILITY_LATE_BLOCK_ACTIVATE,
-    MIGRATION_CAPABILITY_RETURN_PATH,
-    MIGRATION_CAPABILITY_MULTIFD,
-    MIGRATION_CAPABILITY_PAUSE_BEFORE_SWITCHOVER,
-    MIGRATION_CAPABILITY_AUTO_CONVERGE,
-    MIGRATION_CAPABILITY_RELEASE_RAM,
-    MIGRATION_CAPABILITY_RDMA_PIN_ALL,
-    MIGRATION_CAPABILITY_XBZRLE,
-    MIGRATION_CAPABILITY_X_COLO,
-    MIGRATION_CAPABILITY_VALIDATE_UUID,
-    MIGRATION_CAPABILITY_ZERO_COPY_SEND);
-
-/* Snapshot compatibility check list */
-static const
-INITIALIZE_MIGRATE_CAPS_SET(check_caps_savevm,
-                            MIGRATION_CAPABILITY_MULTIFD,
-);
-
 static bool migrate_incoming_started(void)
 {
     return !!migration_incoming_get_current()->transport_data;
@@ -555,34 +522,28 @@ static bool migrate_incoming_started(void)
 bool migrate_can_snapshot(Error **errp)
 {
     MigrationState *s = migrate_get_current();
-    int i;
 
-    for (i = 0; i < check_caps_savevm.size; i++) {
-        int incomp_cap = check_caps_savevm.caps[i];
-
-        if (s->capabilities[incomp_cap]) {
-            error_setg(errp,
-                       "Snapshots are not compatible with %s",
-                       MigrationCapability_str(incomp_cap));
-            return false;
-        }
+    if (migrate_capability_get_compat(
+            &s->parameters, MIGRATION_CAPABILITY_MULTIFD)) {
+        error_setg(errp,
+                   "Snapshots are not compatible with multifd");
+        return false;
     }
 
     return true;
 }
 
-
-bool migrate_rdma_caps_check(bool *caps, Error **errp)
+bool migrate_rdma_caps_check(MigrationParameters *params, Error **errp)
 {
-    if (caps[MIGRATION_CAPABILITY_XBZRLE]) {
+    if (params->xbzrle) {
         error_setg(errp, "RDMA and XBZRLE can't be used together");
         return false;
     }
-    if (caps[MIGRATION_CAPABILITY_MULTIFD]) {
+    if (params->multifd) {
         error_setg(errp, "RDMA and multifd can't be used together");
         return false;
     }
-    if (caps[MIGRATION_CAPABILITY_POSTCOPY_RAM]) {
+    if (params->postcopy_ram) {
         error_setg(errp, "RDMA and postcopy-ram can't be used together");
         return false;
     }
@@ -590,30 +551,23 @@ bool migrate_rdma_caps_check(bool *caps, Error **errp)
     return true;
 }
 
-/**
- * @migration_caps_check - check capability compatibility
- *
- * @old_caps: old capability list
- * @new_caps: new capability list
- * @errp: set *errp if the check failed, with reason
- *
- * Returns true if check passed, otherwise false.
- */
-bool migrate_caps_check(bool *old_caps, bool *new_caps, Error **errp)
+bool migrate_caps_check(MigrationParameters *new, Error **errp)
 {
-    ERRP_GUARD();
+    MigrationState *s = migrate_get_current();
     MigrationIncomingState *mis = migration_incoming_get_current();
+    bool postcopy_already_on = s->parameters.postcopy_ram;
+    ERRP_GUARD();
 
 #ifdef CONFIG_REPLICATION
-    if (new_caps[MIGRATION_CAPABILITY_X_COLO]) {
-        if (!new_caps[MIGRATION_CAPABILITY_RETURN_PATH]) {
+    if (new->x_colo) {
+        if (!new->return_path) {
             error_setg(errp, "Capability 'x-colo' requires capability "
                              "'return-path'");
             return false;
         }
     }
 #else
-    if (new_caps[MIGRATION_CAPABILITY_X_COLO]) {
+    if (new->x_colo) {
         error_setg(errp, "QEMU compiled without replication module"
                    " can't enable COLO");
         error_append_hint(errp, "Please enable replication before COLO.\n");
@@ -621,27 +575,27 @@ bool migrate_caps_check(bool *old_caps, bool *new_caps, Error **errp)
     }
 #endif
 
-    if (new_caps[MIGRATION_CAPABILITY_POSTCOPY_RAM]) {
+    if (new->postcopy_ram) {
         /* This check is reasonably expensive, so only when it's being
          * set the first time, also it's only the destination that needs
          * special support.
          */
-        if (!old_caps[MIGRATION_CAPABILITY_POSTCOPY_RAM] &&
+        if (!postcopy_already_on &&
             runstate_check(RUN_STATE_INMIGRATE) &&
             !postcopy_ram_supported_by_host(mis, errp)) {
             error_prepend(errp, "Postcopy is not supported: ");
             return false;
         }
 
-        if (new_caps[MIGRATION_CAPABILITY_X_IGNORE_SHARED]) {
+        if (new->x_ignore_shared) {
             error_setg(errp, "Postcopy is not compatible with ignore-shared");
             return false;
         }
     }
 
-    if (new_caps[MIGRATION_CAPABILITY_BACKGROUND_SNAPSHOT]) {
+    if (new->background_snapshot) {
         WriteTrackingSupport wt_support;
-        int idx;
+
         /*
          * Check if 'background-snapshot' capability is supported by
          * host kernel and compatible with guest memory configuration.
@@ -657,41 +611,45 @@ bool migrate_caps_check(bool *old_caps, bool *new_caps, Error **errp)
             return false;
         }
 
-        /*
-         * Check if there are any migration capabilities
-         * incompatible with 'background-snapshot'.
-         */
-        for (idx = 0; idx < check_caps_background_snapshot.size; idx++) {
-            int incomp_cap = check_caps_background_snapshot.caps[idx];
-            if (new_caps[incomp_cap]) {
-                error_setg(errp,
-                        "Background-snapshot is not compatible with %s",
-                        MigrationCapability_str(incomp_cap));
-                return false;
-            }
+        if (new->postcopy_ram ||
+            new->dirty_bitmaps ||
+            new->postcopy_blocktime ||
+            new->late_block_activate ||
+            new->return_path ||
+            new->multifd ||
+            new->pause_before_switchover ||
+            new->auto_converge ||
+            new->release_ram ||
+            new->rdma_pin_all ||
+            new->xbzrle ||
+            new->x_colo ||
+            new->validate_uuid ||
+            new->zero_copy_send) {
+            error_setg(errp,
+                       "Background-snapshot is not compatible with "
+                       "currently set capabilities");
+            return false;
         }
     }
 
 #ifdef CONFIG_LINUX
-    if (new_caps[MIGRATION_CAPABILITY_ZERO_COPY_SEND] &&
-        (!new_caps[MIGRATION_CAPABILITY_MULTIFD] ||
-         new_caps[MIGRATION_CAPABILITY_XBZRLE] ||
-         migrate_multifd_compression() ||
-         migrate_tls())) {
+    if (new->zero_copy_send &&
+        (!new->multifd || new->xbzrle ||
+         migrate_multifd_compression() || migrate_tls())) {
         error_setg(errp,
                    "Zero copy only available for non-compressed non-TLS multifd migration");
         return false;
     }
 #else
-    if (new_caps[MIGRATION_CAPABILITY_ZERO_COPY_SEND]) {
+    if (new->zero_copy_send) {
         error_setg(errp,
                    "Zero copy currently only available on Linux");
         return false;
     }
 #endif
 
-    if (new_caps[MIGRATION_CAPABILITY_POSTCOPY_PREEMPT]) {
-        if (!new_caps[MIGRATION_CAPABILITY_POSTCOPY_RAM]) {
+    if (new->postcopy_preempt) {
+        if (!new->postcopy_ram) {
             error_setg(errp, "Postcopy preempt requires postcopy-ram");
             return false;
         }
@@ -703,22 +661,22 @@ bool migrate_caps_check(bool *old_caps, bool *new_caps, Error **errp)
         }
     }
 
-    if (new_caps[MIGRATION_CAPABILITY_MULTIFD]) {
+    if (new->multifd) {
         if (!migrate_multifd() && migrate_incoming_started()) {
             error_setg(errp, "Multifd must be set before incoming starts");
             return false;
         }
     }
 
-    if (new_caps[MIGRATION_CAPABILITY_SWITCHOVER_ACK]) {
-        if (!new_caps[MIGRATION_CAPABILITY_RETURN_PATH]) {
+    if (new->switchover_ack) {
+        if (!new->return_path) {
             error_setg(errp, "Capability 'switchover-ack' requires capability "
                              "'return-path'");
             return false;
         }
     }
-    if (new_caps[MIGRATION_CAPABILITY_DIRTY_LIMIT]) {
-        if (new_caps[MIGRATION_CAPABILITY_AUTO_CONVERGE]) {
+    if (new->dirty_limit) {
+        if (new->auto_converge) {
             error_setg(errp, "dirty-limit conflicts with auto-converge"
                        " either of then available currently");
             return false;
@@ -731,30 +689,29 @@ bool migrate_caps_check(bool *old_caps, bool *new_caps, Error **errp)
         }
     }
 
-    if (new_caps[MIGRATION_CAPABILITY_MULTIFD]) {
-        if (new_caps[MIGRATION_CAPABILITY_XBZRLE]) {
+    if (new->multifd) {
+        if (new->xbzrle) {
             error_setg(errp, "Multifd is not compatible with xbzrle");
             return false;
         }
     }
 
-    if (new_caps[MIGRATION_CAPABILITY_MAPPED_RAM]) {
-        if (new_caps[MIGRATION_CAPABILITY_XBZRLE]) {
+    if (new->mapped_ram) {
+        if (new->xbzrle) {
             error_setg(errp,
                        "Mapped-ram migration is incompatible with xbzrle");
             return false;
         }
     }
 
-    if (new_caps[MIGRATION_CAPABILITY_MAPPED_RAM] &&
-        new_caps[MIGRATION_CAPABILITY_POSTCOPY_RAM]) {
-        if (new_caps[MIGRATION_CAPABILITY_MULTIFD]) {
+    if (new->mapped_ram && new->postcopy_ram) {
+        if (new->multifd) {
             error_setg(errp,
                        "Multifd is not supported with fast snapshot load");
             return false;
         }
 
-        if (new_caps[MIGRATION_CAPABILITY_POSTCOPY_PREEMPT]) {
+        if (new->postcopy_preempt) {
             error_setg(
                 errp,
                 "Postcopy Preempt is incompatible with fast snapshot load");
@@ -772,7 +729,7 @@ bool migrate_caps_check(bool *old_caps, bool *new_caps, Error **errp)
      * On destination side, check the cases that capability is being set
      * after incoming thread has started.
      */
-    if (migrate_rdma() && !migrate_rdma_caps_check(new_caps, errp)) {
+    if (migrate_rdma() && !migrate_rdma_caps_check(new, errp)) {
         return false;
     }
     return true;
@@ -890,39 +847,37 @@ MigrationCapabilityStatusList *qmp_query_migrate_capabilities(Error **errp)
     for (i = 0; i < MIGRATION_CAPABILITY__MAX; i++) {
         caps = g_malloc0(sizeof(*caps));
         caps->capability = i;
-        caps->state = s->capabilities[i];
+        caps->state = migrate_capability_get_compat(&s->parameters, i);
         QAPI_LIST_APPEND(tail, caps);
     }
 
     return head;
 }
 
-void qmp_migrate_set_capabilities(MigrationCapabilityStatusList *params,
+void qmp_migrate_set_capabilities(MigrationCapabilityStatusList *capabilities,
                                   Error **errp)
 {
     MigrationState *s = migrate_get_current();
-    MigrationCapabilityStatusList *cap;
-    bool new_caps[MIGRATION_CAPABILITY__MAX];
+    g_autoptr(MigrationParameters) params = NULL;
 
     if (migration_is_running() || migration_in_colo_state()) {
         error_setg(errp, "There's a migration process in progress");
         return;
     }
 
-    memcpy(new_caps, s->capabilities, sizeof(new_caps));
-    for (cap = params; cap; cap = cap->next) {
-        new_caps[cap->value->capability] = cap->value->state;
-    }
+    /*
+     * Capabilities validation needs to first copy from s->parameters
+     * in case the incoming capabilities have a capability that
+     * conflicts with another that's already set.
+     */
+    params = QAPI_CLONE(MigrationParameters, &s->parameters);
+    migrate_capabilities_set_compat(params, capabilities);
 
-    if (!migrate_caps_check(s->capabilities, new_caps, errp)) {
+    if (!migrate_caps_check(params, errp)) {
         return;
     }
 
-    for (cap = params; cap; cap = cap->next) {
-        s->capabilities[cap->value->capability] = cap->value->state;
-    }
-
-    migrate_capabilities_set_compat(&s->parameters, params);
+    migrate_capabilities_set_compat(&s->parameters, capabilities);
 }
 
 /* parameters */
@@ -983,9 +938,8 @@ bool migrate_direct_io(void)
      * isolated to the main migration thread while multifd channels
      * process the aligned data with O_DIRECT enabled.
      */
-    return s->parameters.direct_io &&
-        s->capabilities[MIGRATION_CAPABILITY_MAPPED_RAM] &&
-        s->capabilities[MIGRATION_CAPABILITY_MULTIFD];
+    return s->parameters.direct_io && s->parameters.mapped_ram &&
+        s->parameters.multifd;
 }
 
 uint64_t migrate_downtime_limit(void)
@@ -1555,6 +1509,9 @@ bool migrate_params_check(MigrationParameters *params, Error **errp)
          !is_power_of_2(params->x_rdma_chunk_size))) {
         error_setg(errp, "Option x_rdma_chunk_size expects "
                    "a power of 2 in the range 1MiB to 1024MiB");
+    }
+
+    if (!migrate_caps_check(params, errp)) {
         return false;
     }
 
@@ -1578,20 +1535,6 @@ void qmp_migrate_set_parameters(MigrationParameters *input, Error **errp)
     tls_opt_to_str(input->tls_hostname);
     tls_opt_to_str(input->tls_authz);
 
-    /*
-     * FIXME: Temporarily while migrate_caps_check is not
-     * converted to look at s->parameters. Will be gone the end of
-     * the series.
-     */
-    bool new_caps[MIGRATION_CAPABILITY__MAX] = { 0 };
-    for (int i = 0; i < MIGRATION_CAPABILITY__MAX; i++) {
-        new_caps[i] = migrate_capability_get_compat(cur, i);
-    }
-    if (!migrate_caps_check(migrate_get_current()->capabilities, new_caps,
-                            errp)) {
-        return;
-    }
-
     /* merge input on top of current */
     if (!migrate_params_merge(cur, input, &new, errp)) {
         return;
diff --git a/migration/options.h b/migration/options.h
index eedd1aa1f93..27a55c36a5f 100644
--- a/migration/options.h
+++ b/migration/options.h
@@ -1,5 +1,5 @@
 /*
- * QEMU migration capabilities
+ * QEMU migration options
  *
  * Copyright (c) 2012-2023 Red Hat Inc
  *
@@ -23,8 +23,6 @@
 extern const Property migration_properties[];
 extern const size_t migration_properties_count;
 
-/* capabilities */
-
 bool migrate_auto_converge(void);
 bool migrate_colo(void);
 bool migrate_dirty_bitmaps(void);
@@ -43,22 +41,12 @@ bool migrate_validate_uuid(void);
 bool migrate_xbzrle(void);
 bool migrate_zero_copy_send(void);
 
-/*
- * pseudo capabilities
- *
- * These are functions that are used in a similar way to capabilities
- * check, but they are not a capability.
- */
-
 bool migrate_multifd_flush_after_each_section(void);
 bool migrate_postcopy(void);
 bool migrate_rdma(void);
 bool migrate_tls(void);
 
-/* capabilities helpers */
-
-bool migrate_rdma_caps_check(bool *caps, Error **errp);
-bool migrate_caps_check(bool *old_caps, bool *new_caps, Error **errp);
+bool migrate_rdma_caps_check(MigrationParameters *config, Error **errp);
 bool migrate_can_snapshot(Error **errp);
 
 /* parameters */
@@ -89,8 +77,6 @@ uint64_t migrate_xbzrle_cache_size(void);
 ZeroPageDetection migrate_zero_page_detection(void);
 uint64_t migrate_rdma_chunk_size(void);
 
-/* parameters helpers */
-
 bool migrate_params_check(MigrationParameters *params, Error **errp);
 void migrate_params_init(MigrationParameters *params);
 bool migrate_params_free(MigrationParameters *params, Error **errp);
@@ -99,4 +85,5 @@ void migrate_capability_set_compat(MigrationParameters *params, int i,
                                    bool val);
 void migrate_capabilities_set_compat(MigrationParameters *params,
                                      MigrationCapabilityStatusList *caps);
+bool migrate_caps_check(MigrationParameters *new, Error **errp);
 #endif
diff --git a/migration/savevm.c b/migration/savevm.c
index 5b0e89ca7c8..c284b76f609 100644
--- a/migration/savevm.c
+++ b/migration/savevm.c
@@ -291,7 +291,8 @@ static uint32_t get_validatable_capabilities_count(void)
     uint32_t result = 0;
     int i;
     for (i = 0; i < MIGRATION_CAPABILITY__MAX; i++) {
-        if (should_validate_capability(i) && s->capabilities[i]) {
+        if (should_validate_capability(i) &&
+            migrate_capability_get_compat(&s->parameters, i)) {
             result++;
         }
     }
@@ -313,7 +314,8 @@ static bool configuration_pre_save(void *opaque, Error **errp)
     state->capabilities = g_renew(MigrationCapability, state->capabilities,
                                   state->caps_count);
     for (i = j = 0; i < MIGRATION_CAPABILITY__MAX; i++) {
-        if (should_validate_capability(i) && s->capabilities[i]) {
+        if (should_validate_capability(i) &&
+            migrate_capability_get_compat(&s->parameters, i)) {
             state->capabilities[j++] = i;
         }
     }
@@ -362,7 +364,7 @@ static bool configuration_validate_capabilities(SaveState *state)
             continue;
         }
         source_state = test_bit(i, source_caps_bm);
-        target_state = s->capabilities[i];
+        target_state = migrate_capability_get_compat(&s->parameters, i);
         if (source_state != target_state) {
             error_report("Capability %s is %s, but received capability is %s",
                          MigrationCapability_str(i),
diff --git a/tests/qtest/migration/misc-tests.c b/tests/qtest/migration/misc-tests.c
index 34b376562ff..ff11d80f79a 100644
--- a/tests/qtest/migration/misc-tests.c
+++ b/tests/qtest/migration/misc-tests.c
@@ -53,6 +53,147 @@ HMPTestData test_cases[] = {
     /* bool */
     TEST("cpu-throttle-tailslow", "on", "on"),
     TEST("direct-io", "on", "on"),
+    TEST("events", "on", "on"),
+
+    /* bool, with dependencies */
+
+    /*
+     * background-snapshot:
+     *  rejects dirty-bitmaps
+     *  rejects postcopy-blocktime
+     *  rejects late-block-activate
+     *  rejects multifd
+     *  rejects pause-before-switchover
+     *  rejects auto-converge
+     *  rejects release-ram
+     *  rejects rdma-pin-all
+     *  rejects validate-uuid
+     *  rejects zero-copy-send
+     *  rejects postcopy-ram
+     */
+    TEST("background-snapshot", "on", "on"),
+    TEST("dirty-bitmaps", "on", BG_SNAP_MSG),
+    TEST("postcopy-blocktime", "on", BG_SNAP_MSG),
+    TEST("late-block-activate", "on", BG_SNAP_MSG),
+    TEST("multifd", "on", BG_SNAP_MSG),
+    TEST("pause-before-switchover", "on", BG_SNAP_MSG),
+    TEST("auto-converge", "on", BG_SNAP_MSG),
+    TEST("release-ram", "on", BG_SNAP_MSG),
+    TEST("rdma-pin-all", "on", BG_SNAP_MSG),
+    TEST("validate-uuid", "on", BG_SNAP_MSG),
+    TEST("zero-copy-send", "on", BG_SNAP_MSG),
+    TEST("postcopy-ram", "on", BG_SNAP_MSG),
+    TEST("background-snapshot", "off", "off"),
+
+    TEST("dirty-bitmaps", "on", "on"),
+    TEST("postcopy-blocktime", "on", "on"),
+    TEST("late-block-activate", "on", "on"),
+    TEST("pause-before-switchover", "on", "on"),
+    TEST("auto-converge", "on", "on"),
+    TEST("release-ram", "on", "on"),
+    TEST("rdma-pin-all", "on", "on"),
+    TEST("validate-uuid", "on", "on"),
+
+    /*
+     * postcopy-preempt
+     *  requires postcopy-ram:
+     */
+    TEST("postcopy-preempt", "on",
+         "Error: Postcopy preempt requires postcopy-ram"),
+
+    /*
+     * postcopy-ram:
+     *  required by postcopy-preempt
+     *  rejected by x-ignore-shared
+     *  rejected by background-snapshot
+     *  rejected by mapped-ram
+     */
+    TEST("postcopy-ram", "on", "on"),
+    TEST("postcopy-preempt", "on", "on"),
+    TEST("x-ignore-shared", "on",
+         "Error: Postcopy is not compatible with ignore-shared"),
+    TEST("background-snapshot", "on", BG_SNAP_MSG),
+    TEST("mapped-ram", "on",
+         "Error: Postcopy Preempt is incompatible with fast snapshot load"),
+    TEST("postcopy-ram", "off",
+         "Error: Postcopy preempt requires postcopy-ram"),
+    TEST("postcopy-preempt", "off", "off"),
+    TEST("postcopy-ram", "off", "off"),
+
+    /*
+     * x-ignore-shared:
+     *  rejected by postcopy-ram
+     */
+    TEST("x-ignore-shared", "on", "on"),
+    TEST("postcopy-ram", "on",
+         "Error: Postcopy is not compatible with ignore-shared"),
+    TEST("x-ignore-shared", "off", "off"),
+
+    /*
+     * return-path:
+     *  required by x-colo
+     *  required by switchover-ack
+     *  rejected by background-snapshot
+     */
+    TEST("return-path", "on", "on"),
+    TEST("x-colo", "on", "on"),
+    TEST("switchover-ack", "on", "on"),
+    TEST("background-snapshot", "on", BG_SNAP_MSG),
+
+    TEST("return-path", "off",
+         "Error: Capability 'x-colo' requires capability 'return-path'"),
+    TEST("x-colo", "off", "off"),
+
+    TEST("return-path", "off",
+         "Error: Capability 'switchover-ack' requires capability "
+         "'return-path'"),
+    TEST("switchover-ack", "off", "off"),
+    TEST("return-path", "off", "off"),
+
+    TEST("x-colo", "on",
+         "Error: Capability 'x-colo' requires capability 'return-path'"),
+    TEST("switchover-ack", "on", "Error: Capability 'switchover-ack' requires "
+         "capability 'return-path'"),
+
+    /*
+     * xbzrle:
+     *  rejected by multifd
+     */
+    TEST("xbzrle", "on", "on"),
+    TEST("multifd", "on", "Error: Multifd is not compatible with xbzrle"),
+    TEST("xbzrle", "off", "off"),
+
+    /*
+     * multifd:
+     *  rejected by xbzrle
+     *  required by zero-copy-send
+     */
+    TEST("multifd", "on", "on"),
+    TEST("xbzrle", "on", "Error: Multifd is not compatible with xbzrle"),
+    TEST("zero-copy-send", "on", "on"),
+    TEST("multifd", "off", "Error: Zero copy only available for "
+         "non-compressed non-TLS multifd migration"),
+    TEST("zero-copy-send", "off", "off"),
+    TEST("multifd", "off", "off"),
+
+    /*
+     * auto-converge:
+     *  rejected by dirty-limit
+     */
+    TEST("auto-converge", "on", "on"),
+    TEST("dirty-limit", "on",
+         "Error: dirty-limit conflicts with auto-converge "
+         "either of then available currently"),
+    TEST("auto-converge", "off", "off"),
+
+    /*
+     * dirty-limit:
+     *  rejected by auto-converge
+     *  requires KVM acceleration
+     */
+    TEST("dirty-limit", "on",
+         "Error: dirty-limit requires KVM with accelerator "
+         "property 'dirty-ring-size' set"),
 
     /* uint64_t */
     TEST("announce-initial", "60", "60"),
-- 
2.53.0



^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 18/18] qapi/migration: Deprecate capabilities commands
  2026-09-02 22:15 [PATCH 00/18] migration: MigrationParameters changes Fabiano Rosas
                   ` (16 preceding siblings ...)
  2026-09-02 22:15 ` [PATCH 17/18] migration: Remove s->capabilities Fabiano Rosas
@ 2026-09-02 22:15 ` Fabiano Rosas
  17 siblings, 0 replies; 50+ messages in thread
From: Fabiano Rosas @ 2026-09-02 22:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: Peter Xu, Markus Armbruster, Pierrick Bouvier, Eric Blake

The concept of capabilities is being merged into the concept of
parameters. From now on, the commands that handle capabilities are
deprecated in favor of the commands that handle parameters.

Affected commands:

- migrate-set-capabilities
- query-migrate-capabilities

Reviewed-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Peter Xu <peterx@redhat.com>
Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
 docs/about/deprecated.rst      | 13 +++++++++++++
 migration/migration-hmp-cmds.c |  6 ++++++
 qapi/migration.json            | 16 ++++++++++++++--
 3 files changed, 33 insertions(+), 2 deletions(-)

diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst
index 05e4ce8cf16..a28211a98b7 100644
--- a/docs/about/deprecated.rst
+++ b/docs/about/deprecated.rst
@@ -477,3 +477,16 @@ If the user requests a modern x86 CPU model (i.e. not one of ``486``,
 ``athlon``, ``kvm32``, ``pentium``, ``pentium2``, ``pentium3``or ``qemu32``)
 a warning will be displayed until a future QEMU version when such CPUs will
 be rejected.
+
+Migration
+---------
+
+``migrate-set-capabilities`` command (since 11.1)
+'''''''''''''''''''''''''''''''''''''''''''''''''
+
+Use ``migrate-set-parameters`` instead.
+
+``query-migrate-capabilities`` command (since 11.1)
+'''''''''''''''''''''''''''''''''''''''''''''''''''
+
+Use ``query-migrate-parameters`` instead.
diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c
index d0adda25090..d7a8902da01 100644
--- a/migration/migration-hmp-cmds.c
+++ b/migration/migration-hmp-cmds.c
@@ -308,6 +308,9 @@ void hmp_info_migrate_capabilities(Monitor *mon, const QDict *qdict)
 {
     MigrationCapabilityStatusList *caps, *cap;
 
+    warn_report("info migrate_capabilities is deprecated;"
+                " use info migrate_parameters instead");
+
     caps = qmp_query_migrate_capabilities(NULL);
 
     if (caps) {
@@ -542,6 +545,9 @@ void hmp_migrate_set_capability(Monitor *mon, const QDict *qdict)
     MigrationCapabilityStatus *value;
     int val;
 
+    warn_report("migrate_set_capability is deprecated;"
+                " use migrate_set_parameter instead");
+
     val = qapi_enum_parse(&MigrationCapability_lookup, cap, -1, &err);
     if (val < 0) {
         goto end;
diff --git a/qapi/migration.json b/qapi/migration.json
index 7952ef44db9..19fcc7e6072 100644
--- a/qapi/migration.json
+++ b/qapi/migration.json
@@ -564,6 +564,11 @@
 #
 # @capabilities: json array of capability modifications to make
 #
+# Features:
+#
+# @deprecated: This command is deprecated.  Use migrate-set-parameters
+# instead.
+#
 # Since: 1.2
 #
 # .. qmp-example::
@@ -573,13 +578,19 @@
 #     <- { "return": {} }
 ##
 { 'command': 'migrate-set-capabilities',
-  'data': { 'capabilities': ['MigrationCapabilityStatus'] } }
+  'data': { 'capabilities': ['MigrationCapabilityStatus'] },
+  'features': ['deprecated'] }
 
 ##
 # @query-migrate-capabilities:
 #
 # Return information about the current migration capabilities status
 #
+# Features:
+#
+# @deprecated: This command is deprecated.  Use
+# query-migrate-parameters instead.
+#
 # Since: 1.2
 #
 # .. qmp-example::
@@ -594,7 +605,8 @@
 #           {"state": false, "capability": "x-colo"}
 #        ]}
 ##
-{ 'command': 'query-migrate-capabilities', 'returns':   ['MigrationCapabilityStatus']}
+{ 'command': 'query-migrate-capabilities', 'returns':   ['MigrationCapabilityStatus'],
+  'features': ['deprecated'] }
 
 ##
 # @MultiFDCompression:
-- 
2.53.0



^ permalink raw reply related	[flat|nested] 50+ messages in thread

* Re: [PATCH 01/18] checkpatch: Fix checking of newlines in error messages
  2026-09-02 22:15 ` [PATCH 01/18] checkpatch: Fix checking of newlines in error messages Fabiano Rosas
@ 2026-09-03 17:37   ` Peter Xu
  2026-09-03 17:46     ` Fabiano Rosas
  2026-09-04 14:00   ` Markus Armbruster
  1 sibling, 1 reply; 50+ messages in thread
From: Peter Xu @ 2026-09-03 17:37 UTC (permalink / raw)
  To: Fabiano Rosas; +Cc: qemu-devel, Chao Liu

On Wed, Sep 02, 2026 at 07:15:29PM -0300, Fabiano Rosas wrote:
> Using newlines in the g_test_message is fine. It automatically adds
> the '#' required by the TAP protocol to the start of each line.

IIUC we have such check not because TAP, but because all these functions
will append one newline at the end, hence it's not needed.  IOW, if it
applies to g_test_message(), I don't see why it doesn't apply to the rest.
But maybe there're other reasons?

To make it simpler, maybe we just call a few times g_test_message()?

> 
> Relax the regex for this function, but still forbid a trailing newline
> because it's added automatically and usually not what the user wants.
> 
> Signed-off-by: Fabiano Rosas <farosas@suse.de>
> ---
>  scripts/checkpatch.pl | 11 +++++++++--
>  1 file changed, 9 insertions(+), 2 deletions(-)
> 
> diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl
> index 03f35e75012..fd4534b3a1e 100755
> --- a/scripts/checkpatch.pl
> +++ b/scripts/checkpatch.pl
> @@ -3303,13 +3303,20 @@ sub process {
>  					 info_vreport|
>  					 error_report|
>  					 warn_report|
> -					 info_report|
> -					 g_test_message}x;
> +					 info_report}x;
>  
>  		if ($rawline =~ /\b(?:$qemu_error_funcs)\s*\(.*\".*\\n/) {
>  			ERROR("Error messages should not contain newlines\n" . $herecurr);
>  		}
>  
> +		# No newlines at the end
> +		my $trail_newline_error_funcs = qr{g_test_message}x;
> +
> +		if ($rawline =~ /\b(?:$trail_newline_error_funcs)\(.*\".*\\n\"/) {
> +		    ERROR("Error messages should not contain trailing " .
> +			  "newlines\n" . $herecurr);
> +		}
> +
>  		# Continue checking for error messages that contains newlines.
>  		# This check handles cases where string literals are spread
>  		# over multiple lines.
> -- 
> 2.53.0
> 

-- 
Peter Xu



^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 03/18] migration: Rename variables in qmp_migrate_set_parameters
  2026-09-02 22:15 ` [PATCH 03/18] migration: Rename variables in qmp_migrate_set_parameters Fabiano Rosas
@ 2026-09-03 17:44   ` Peter Xu
  0 siblings, 0 replies; 50+ messages in thread
From: Peter Xu @ 2026-09-03 17:44 UTC (permalink / raw)
  To: Fabiano Rosas; +Cc: qemu-devel

On Wed, Sep 02, 2026 at 07:15:31PM -0300, Fabiano Rosas wrote:
> Give the variables in qmp_migrate_set_parameters more semantic
> names.
> 
> s/params/input/
> this is the user input from qapi
> 
> s/tmp/new/
> this is the combination of the current parameters and the input
> 
> Signed-off-by: Fabiano Rosas <farosas@suse.de>

Reviewed-by: Peter Xu <peterx@redhat.com>

-- 
Peter Xu



^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 01/18] checkpatch: Fix checking of newlines in error messages
  2026-09-03 17:37   ` Peter Xu
@ 2026-09-03 17:46     ` Fabiano Rosas
  2026-09-03 18:00       ` Peter Xu
  0 siblings, 1 reply; 50+ messages in thread
From: Fabiano Rosas @ 2026-09-03 17:46 UTC (permalink / raw)
  To: Peter Xu; +Cc: qemu-devel, Chao Liu

Peter Xu <peterx@redhat.com> writes:

> On Wed, Sep 02, 2026 at 07:15:29PM -0300, Fabiano Rosas wrote:
>> Using newlines in the g_test_message is fine. It automatically adds
>> the '#' required by the TAP protocol to the start of each line.
>
> IIUC we have such check not because TAP, but because all these functions
> will append one newline at the end, hence it's not needed.  IOW, if it
> applies to g_test_message(), I don't see why it doesn't apply to the rest.
> But maybe there're other reasons?
>
> To make it simpler, maybe we just call a few times g_test_message()?
>

Not sure I understand your point, Peter. I want to be able to print nice
messages in patch 9:

 g_test_message("expected vs. found:\n\n%s\n---\n%s:%s", str, t2[match], t2[match + 1]);

 # HMP output mismatch for entry at line 55:
 # expected vs. found:
 #
 # max-bandwidth: 10356305952768 bytes/hour
 # ---
 # max-bandwidth: 10356305952768 bytes/second

What would be the issue of having newlines here?

>> 
>> Relax the regex for this function, but still forbid a trailing newline
>> because it's added automatically and usually not what the user wants.
>> 
>> Signed-off-by: Fabiano Rosas <farosas@suse.de>
>> ---
>>  scripts/checkpatch.pl | 11 +++++++++--
>>  1 file changed, 9 insertions(+), 2 deletions(-)
>> 
>> diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl
>> index 03f35e75012..fd4534b3a1e 100755
>> --- a/scripts/checkpatch.pl
>> +++ b/scripts/checkpatch.pl
>> @@ -3303,13 +3303,20 @@ sub process {
>>  					 info_vreport|
>>  					 error_report|
>>  					 warn_report|
>> -					 info_report|
>> -					 g_test_message}x;
>> +					 info_report}x;
>>  
>>  		if ($rawline =~ /\b(?:$qemu_error_funcs)\s*\(.*\".*\\n/) {
>>  			ERROR("Error messages should not contain newlines\n" . $herecurr);
>>  		}
>>  
>> +		# No newlines at the end
>> +		my $trail_newline_error_funcs = qr{g_test_message}x;
>> +
>> +		if ($rawline =~ /\b(?:$trail_newline_error_funcs)\(.*\".*\\n\"/) {
>> +		    ERROR("Error messages should not contain trailing " .
>> +			  "newlines\n" . $herecurr);
>> +		}
>> +
>>  		# Continue checking for error messages that contains newlines.
>>  		# This check handles cases where string literals are spread
>>  		# over multiple lines.
>> -- 
>> 2.53.0
>> 


^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 01/18] checkpatch: Fix checking of newlines in error messages
  2026-09-03 17:46     ` Fabiano Rosas
@ 2026-09-03 18:00       ` Peter Xu
  2026-09-03 18:35         ` Fabiano Rosas
  2026-09-04  8:56         ` Markus Armbruster
  0 siblings, 2 replies; 50+ messages in thread
From: Peter Xu @ 2026-09-03 18:00 UTC (permalink / raw)
  To: Fabiano Rosas; +Cc: qemu-devel, Chao Liu

On Thu, Sep 03, 2026 at 02:46:37PM -0300, Fabiano Rosas wrote:
> Peter Xu <peterx@redhat.com> writes:
> 
> > On Wed, Sep 02, 2026 at 07:15:29PM -0300, Fabiano Rosas wrote:
> >> Using newlines in the g_test_message is fine. It automatically adds
> >> the '#' required by the TAP protocol to the start of each line.
> >
> > IIUC we have such check not because TAP, but because all these functions
> > will append one newline at the end, hence it's not needed.  IOW, if it
> > applies to g_test_message(), I don't see why it doesn't apply to the rest.
> > But maybe there're other reasons?
> >
> > To make it simpler, maybe we just call a few times g_test_message()?
> >
> 
> Not sure I understand your point, Peter. I want to be able to print nice
> messages in patch 9:
> 
>  g_test_message("expected vs. found:\n\n%s\n---\n%s:%s", str, t2[match], t2[match + 1]);
> 
>  # HMP output mismatch for entry at line 55:
>  # expected vs. found:
>  #
>  # max-bandwidth: 10356305952768 bytes/hour
>  # ---
>  # max-bandwidth: 10356305952768 bytes/second
> 
> What would be the issue of having newlines here?

No issue here that I can see.  My question was, why you moved
g_test_message() out only, but not all?

My gut feeling is we check this because people forget that all these
functions includes a newline.

So if your point stands here that "newlines can be in the middle", they
should apply to all, not one.

But still, I also don't see why we can't invoke g_test_message() a few
times here too, if we want to avoid any global touch like this to land the
whole thing faster..

-- 
Peter Xu



^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 05/18] migration: Merge parameter structs instead of assigning one by one
  2026-09-02 22:15 ` [PATCH 05/18] migration: Merge parameter structs instead of assigning one by one Fabiano Rosas
@ 2026-09-03 18:20   ` Peter Xu
  2026-09-03 19:03     ` Fabiano Rosas
  0 siblings, 1 reply; 50+ messages in thread
From: Peter Xu @ 2026-09-03 18:20 UTC (permalink / raw)
  To: Fabiano Rosas; +Cc: qemu-devel

On Wed, Sep 02, 2026 at 07:15:33PM -0300, Fabiano Rosas wrote:
> Convert the code in migrate_params_test_apply() from an open-coded
> copy of every migration parameter to a merge operation using QAPI
> visitors and QDict.
> 
> The purpose of that routine is to update a temporary structure
> (pre-populated with the current migration parameters), with the values
> received from the user via QAPI. As a result, the temporary structure
> will then contain the "to be applied" parameters and it's validated
> before being used to overwrite the parameters currently in use.
> 
> The update is currently done as follows:
> 
> where 'params' is the user input from QAPI,
> for each parameter:
> 
>   a) check if the option is present
>      params->has_<name> == true
>      params-><name> != NULL // for strings
> 
>   b) if the parameter is a pointer, free the to-be-assigned member and
>      allocate memory for the copy from params
> 
>   c) assign the user provided value to the temporary structure.
> 
> Step (a) is the same in principle as what the QAPI visitors do at
> visit_type_MigrationParameters_members().
> 
> Steps (b) and (c) are roughly the same as what the QDict
> implementation does when qdict_del() and qdict_put_obj() are combined.
> 
> Therefore, replace the open-coded function with
> migrate_params_merge(), which achieves the same goal, but uses
> visitors and QDict. This hides the details of QAPI (has_*) from the
> migration code and avoids the need to update
> migrate_params_test_apply() every time a new migration parameter is
> added.
> 
> Signed-off-by: Fabiano Rosas <farosas@suse.de>

Nice,

Reviewed-by: Peter Xu <peterx@redhat.com>

Nitpicks only, inline,

> ---
>  migration/options.c | 201 +++++++++++++++-----------------------------
>  1 file changed, 66 insertions(+), 135 deletions(-)
> 
> diff --git a/migration/options.c b/migration/options.c
> index 6b787950808..de091d2eee3 100644
> --- a/migration/options.c
> +++ b/migration/options.c
> @@ -20,6 +20,9 @@
>  #include "qapi/qapi-commands-migration.h"
>  #include "qapi/qapi-visit-migration.h"
>  #include "qapi/qmp/qerror.h"
> +#include "qapi/qobject-input-visitor.h"
> +#include "qapi/qobject-output-visitor.h"
> +#include "qobject/qdict.h"
>  #include "qobject/qnull.h"
>  #include "system/runstate.h"
>  #include "migration/colo.h"
> @@ -1074,6 +1077,28 @@ static void tls_opt_to_str(StrOrNull *opt)
>      opt->u.s = g_strdup("");
>  }
>  
> +static QDict *migrate_params_to_dict(MigrationParameters *p, Error **errp)
> +{
> +    QObject *obj = NULL;
> +    Visitor *v = qobject_output_visitor_new(&obj);
> +
> +    if (visit_type_MigrationParameters(v, NULL, &p, errp)) {
> +        visit_complete(v, &obj);
> +    }
> +    visit_free(v);
> +    return qobject_to(QDict, obj);
> +}
> +
> +static MigrationParameters *migrate_params_from_dict(QDict *d, Error **errp)
> +{
> +    Visitor *v = qobject_input_visitor_new(QOBJECT(d));
> +    MigrationParameters *tmp = NULL;
> +
> +    visit_type_MigrationParameters(v, NULL, &tmp, errp);
> +    visit_free(v);
> +    return tmp;
> +}
> +
>  /*
>   * query-migrate-parameters expects all members of MigrationParameters
>   * to be present, but we cannot mark them non-optional in QAPI because
> @@ -1166,6 +1191,39 @@ static void migrate_post_update_params(MigrationParameters *new, Error **errp)
>      }
>  }
>  
> +static bool migrate_params_merge(MigrationParameters *in1,
> +                                 MigrationParameters *in2,

Perhaps rename in1/in2/d1/d2 similarly with "cur" / "new" / ..?  As in1 and
in2 are not equal: when merge it only overwrites in1 with in2, not vice
versa.

> +                                 MigrationParameters **out,
> +                                 Error **errp)
> +{
> +    g_autoptr(QDict) d1 = NULL;
> +    g_autoptr(QDict) d2 = NULL;
> +    const QDictEntry *e;
> +
> +    d1 = migrate_params_to_dict(in1, errp);
> +    if (!d1) {
> +        return false;
> +    }
> +
> +    d2 = migrate_params_to_dict(in2, errp);
> +    if (!d2) {
> +        return false;
> +    }
> +
> +    for (e = qdict_first(d2); e; e = qdict_next(d2, e)) {
> +        const char *key = qdict_entry_key(e);
> +        QObject *value = qdict_entry_value(e);
> +
> +        qdict_del(d1, key);

IIUC this line can be dropped due to a smart enough qdict_put_obj().

> +        qobject_ref(value);
> +        qdict_put_obj(d1, key, value);
> +    }
> +
> +    *out = migrate_params_from_dict(d1, errp);
> +
> +    return !!*out;
> +}
> +
>  /*
>   * Check whether the parameters are valid. Error will be put into errp
>   * (if provided). Return true if valid, otherwise false.
> @@ -1328,133 +1386,6 @@ bool migrate_params_check(MigrationParameters *params, Error **errp)
>      return true;
>  }
>  
> -static void migrate_params_test_apply(MigrationParameters *params,
> -                                      MigrationParameters *dest)
> -{
> -    MigrationState *s = migrate_get_current();
> -
> -    QAPI_CLONE_MEMBERS(MigrationParameters, dest, &s->parameters);
> -
> -    if (params->has_throttle_trigger_threshold) {
> -        dest->throttle_trigger_threshold = params->throttle_trigger_threshold;
> -    }
> -
> -    if (params->has_cpu_throttle_initial) {
> -        dest->cpu_throttle_initial = params->cpu_throttle_initial;
> -    }
> -
> -    if (params->has_cpu_throttle_increment) {
> -        dest->cpu_throttle_increment = params->cpu_throttle_increment;
> -    }
> -
> -    if (params->has_cpu_throttle_tailslow) {
> -        dest->cpu_throttle_tailslow = params->cpu_throttle_tailslow;
> -    }
> -
> -    if (params->tls_creds) {
> -        qapi_free_StrOrNull(dest->tls_creds);
> -        dest->tls_creds = QAPI_CLONE(StrOrNull, params->tls_creds);
> -    }
> -
> -    if (params->tls_hostname) {
> -        qapi_free_StrOrNull(dest->tls_hostname);
> -        dest->tls_hostname = QAPI_CLONE(StrOrNull, params->tls_hostname);
> -    }
> -
> -    if (params->tls_authz) {
> -        qapi_free_StrOrNull(dest->tls_authz);
> -        dest->tls_authz = QAPI_CLONE(StrOrNull, params->tls_authz);
> -    }
> -
> -    if (params->has_max_bandwidth) {
> -        dest->max_bandwidth = params->max_bandwidth;
> -    }
> -
> -    if (params->has_avail_switchover_bandwidth) {
> -        dest->avail_switchover_bandwidth = params->avail_switchover_bandwidth;
> -    }
> -
> -    if (params->has_downtime_limit) {
> -        dest->downtime_limit = params->downtime_limit;
> -    }
> -
> -    if (params->has_x_checkpoint_delay) {
> -        dest->x_checkpoint_delay = params->x_checkpoint_delay;
> -    }
> -
> -    if (params->has_multifd_channels) {
> -        dest->multifd_channels = params->multifd_channels;
> -    }
> -    if (params->has_multifd_compression) {
> -        dest->multifd_compression = params->multifd_compression;
> -    }
> -    if (params->has_multifd_qatzip_level) {
> -        dest->multifd_qatzip_level = params->multifd_qatzip_level;
> -    }
> -    if (params->has_multifd_zlib_level) {
> -        dest->multifd_zlib_level = params->multifd_zlib_level;
> -    }
> -    if (params->has_multifd_zstd_level) {
> -        dest->multifd_zstd_level = params->multifd_zstd_level;
> -    }
> -    if (params->has_xbzrle_cache_size) {
> -        dest->xbzrle_cache_size = params->xbzrle_cache_size;
> -    }
> -    if (params->has_max_postcopy_bandwidth) {
> -        dest->max_postcopy_bandwidth = params->max_postcopy_bandwidth;
> -    }
> -    if (params->has_max_cpu_throttle) {
> -        dest->max_cpu_throttle = params->max_cpu_throttle;
> -    }
> -    if (params->has_announce_initial) {
> -        dest->announce_initial = params->announce_initial;
> -    }
> -    if (params->has_announce_max) {
> -        dest->announce_max = params->announce_max;
> -    }
> -    if (params->has_announce_rounds) {
> -        dest->announce_rounds = params->announce_rounds;
> -    }
> -    if (params->has_announce_step) {
> -        dest->announce_step = params->announce_step;
> -    }
> -
> -    if (params->has_block_bitmap_mapping) {
> -        qapi_free_BitmapMigrationNodeAliasList(dest->block_bitmap_mapping);
> -        dest->block_bitmap_mapping = QAPI_CLONE(BitmapMigrationNodeAliasList,
> -                                                params->block_bitmap_mapping);
> -    }
> -
> -    if (params->has_x_vcpu_dirty_limit_period) {
> -        dest->x_vcpu_dirty_limit_period =
> -            params->x_vcpu_dirty_limit_period;
> -    }
> -    if (params->has_vcpu_dirty_limit) {
> -        dest->vcpu_dirty_limit = params->vcpu_dirty_limit;
> -    }
> -
> -    if (params->has_mode) {
> -        dest->mode = params->mode;
> -    }
> -
> -    if (params->has_zero_page_detection) {
> -        dest->zero_page_detection = params->zero_page_detection;
> -    }
> -
> -    if (params->has_direct_io) {
> -        dest->direct_io = params->direct_io;
> -    }
> -
> -    if (params->has_x_rdma_chunk_size) {
> -        dest->x_rdma_chunk_size = params->x_rdma_chunk_size;
> -    }
> -
> -    if (params->has_cpr_exec_command) {
> -        qapi_free_strList(dest->cpr_exec_command);
> -        dest->cpr_exec_command = QAPI_CLONE(strList, params->cpr_exec_command);
> -    }
> -}
> -
>  /*
>   * Caller must ensure the has_* fields of @params are true so they all
>   * get copied and the pointer members don't dangle.
> @@ -1472,7 +1403,8 @@ static void migrate_params_apply(MigrationParameters *params)
>  
>  void qmp_migrate_set_parameters(MigrationParameters *input, Error **errp)
>  {
> -    MigrationParameters new;
> +    MigrationParameters *cur = &migrate_get_current()->parameters;
> +    g_autoptr(MigrationParameters) new = NULL;
>  
>      /*
>       * Convert QTYPE_QNULL and NULL to the empty string (""). Even
> @@ -1486,14 +1418,13 @@ void qmp_migrate_set_parameters(MigrationParameters *input, Error **errp)
>      tls_opt_to_str(input->tls_hostname);
>      tls_opt_to_str(input->tls_authz);
>  
> -    migrate_params_test_apply(input, &new);
> +    /* merge input on top of current */
> +    if (!migrate_params_merge(cur, input, &new, errp)) {
> +        return;
> +    }
>  
> -    if (migrate_params_check(&new, errp)) {
> -        migrate_params_apply(&new);
> +    if (migrate_params_check(new, errp)) {
> +        migrate_params_apply(new);
>          migrate_post_update_params(input, errp);
>      }
> -
> -    migrate_tls_opts_free(&new);
> -    qapi_free_BitmapMigrationNodeAliasList(new.block_bitmap_mapping);
> -    qapi_free_strList(new.cpr_exec_command);
>  }
> -- 
> 2.53.0
> 

-- 
Peter Xu



^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 01/18] checkpatch: Fix checking of newlines in error messages
  2026-09-03 18:00       ` Peter Xu
@ 2026-09-03 18:35         ` Fabiano Rosas
  2026-09-04  8:56         ` Markus Armbruster
  1 sibling, 0 replies; 50+ messages in thread
From: Fabiano Rosas @ 2026-09-03 18:35 UTC (permalink / raw)
  To: Peter Xu; +Cc: qemu-devel, Chao Liu

Peter Xu <peterx@redhat.com> writes:

> On Thu, Sep 03, 2026 at 02:46:37PM -0300, Fabiano Rosas wrote:
>> Peter Xu <peterx@redhat.com> writes:
>> 
>> > On Wed, Sep 02, 2026 at 07:15:29PM -0300, Fabiano Rosas wrote:
>> >> Using newlines in the g_test_message is fine. It automatically adds
>> >> the '#' required by the TAP protocol to the start of each line.
>> >
>> > IIUC we have such check not because TAP, but because all these functions
>> > will append one newline at the end, hence it's not needed.  IOW, if it
>> > applies to g_test_message(), I don't see why it doesn't apply to the rest.
>> > But maybe there're other reasons?
>> >
>> > To make it simpler, maybe we just call a few times g_test_message()?
>> >
>> 
>> Not sure I understand your point, Peter. I want to be able to print nice
>> messages in patch 9:
>> 
>>  g_test_message("expected vs. found:\n\n%s\n---\n%s:%s", str, t2[match], t2[match + 1]);
>> 
>>  # HMP output mismatch for entry at line 55:
>>  # expected vs. found:
>>  #
>>  # max-bandwidth: 10356305952768 bytes/hour
>>  # ---
>>  # max-bandwidth: 10356305952768 bytes/second
>> 
>> What would be the issue of having newlines here?
>
> No issue here that I can see.  My question was, why you moved
> g_test_message() out only, but not all?
>

Ah, I see. I didn't want to touch the others because they are
considerably more important than the tests' messages.

> My gut feeling is we check this because people forget that all these
> functions includes a newline.
>
> So if your point stands here that "newlines can be in the middle", they
> should apply to all, not one.
>
> But still, I also don't see why we can't invoke g_test_message() a few
> times here too, if we want to avoid any global touch like this to land the
> whole thing faster..

Hopefully this patch is uncontroversial. Otherwise I can do what you
suggest.


^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 10/18] migration: Validate that all params are set for query
  2026-09-02 22:15 ` [PATCH 10/18] migration: Validate that all params are set for query Fabiano Rosas
@ 2026-09-03 18:59   ` Peter Xu
  2026-09-04 15:11     ` Fabiano Rosas
  0 siblings, 1 reply; 50+ messages in thread
From: Peter Xu @ 2026-09-03 18:59 UTC (permalink / raw)
  To: Fabiano Rosas; +Cc: qemu-devel

On Wed, Sep 02, 2026 at 07:15:38PM -0300, Fabiano Rosas wrote:
> There are a couple of situations where all fields of a
> MigrationParameters object need to be marked as present: when cloning
> an entire object and when creating the transient object in
> qmp_query_migrate(). The query-migrate-parameters QMP command contract
> requires that all parameters, except block-bitmap-mapping, are present
> in the output.
> 
> Validate that a given object has all has_* fields set to true.
> 
> Signed-off-by: Fabiano Rosas <farosas@suse.de>
> ---
>  migration/options.c | 54 +++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 54 insertions(+)
> 
> diff --git a/migration/options.c b/migration/options.c
> index bd7be8f9832..5d17acdd881 100644
> --- a/migration/options.c
> +++ b/migration/options.c
> @@ -12,6 +12,7 @@
>   */
>  
>  #include "qemu/osdep.h"
> +#include "qemu/cutils.h"
>  #include "qemu/error-report.h"
>  #include "qemu/units.h"
>  #include "exec/target_page.h"
> @@ -23,8 +24,10 @@
>  #include "qapi/qmp/qerror.h"
>  #include "qapi/qobject-input-visitor.h"
>  #include "qapi/qobject-output-visitor.h"
> +#include "qobject/qbool.h"
>  #include "qobject/qdict.h"
>  #include "qobject/qnull.h"
> +#include "qobject/qstring.h"
>  #include "system/runstate.h"
>  #include "migration/colo.h"
>  #include "migration/cpr.h"
> @@ -1149,12 +1152,63 @@ static void migrate_mark_all_params_present(MigrationParameters *p)
>      }
>  }
>  
> +static bool assert_all_params_present(MigrationParameters *params, Error **errp)
> +{
> +    g_autoptr(QDict) d = migrate_params_to_dict(params, errp);
> +    const QDictEntry *e = NULL;
> +    int i = 0;
> +
> +    if (!d) {
> +        return false;
> +    }
> +
> +    for (e = qdict_first(d); e; e = qdict_next(d, e), i++) {
> +        const char *key = qdict_entry_key(e);
> +        const char *p;
> +
> +        if (strstart(key, "tls-", &p)) {
> +            QString *s = qobject_to(QString, qdict_entry_value(e));
> +
> +            if (!s) {
> +                break;
> +            }
> +        } else if (strstart(key, "has-", &p)) {

Does the qdict contain any has- field?  

visit_type_MigrationParameters_members:

    if (visit_optional(v, "announce-initial", &obj->has_announce_initial)) {
        if (!visit_type_size(v, "announce-initial", &obj->announce_initial, errp)) {
            return false;
        }
    }
    ...

It seems the has_* fields are only used to identify existance of objects,
not converted.

> +            if (qdict_haskey(d, p)) {
> +                QBool *b = qobject_to(QBool, qdict_entry_value(e));
> +
> +                if (!b || !qbool_get_bool(b)) {
> +                    break;
> +                }
> +            }
> +        }
> +    }
> +
> +    if (i && !e) {
> +        return true;
> +    }
> +
> +    /*
> +     * Should never happen, but avoid asserting becase this is
> +     * reachable from QMP.

IIUC as long as this fact shouldn't be changed by any possible user input,
we could still assert.  But I understand you want to be careful, maybe
either (1) directly assert, or (2) change the function name,
s/assert/check/?  I vote (1).

Said that, if the qdict trick didn't work it beats the whole patch.. so
IMHO we can also leave this sanity check for later too.  Your call.

> +     */
> +    error_setg(errp, "Missing parameter. Query output will be incomplete.");
> +    return false;
> +}
> +
>  MigrationParameters *qmp_query_migrate_parameters(Error **errp)
>  {
>      MigrationState *s = migrate_get_current();
>      MigrationParameters *params = QAPI_CLONE(MigrationParameters,
>                                               &s->parameters);
>  
> +    /*
> +     * Validate all parameters have their has_* field set to true as
> +     * consequence of the initial migrate_mark_all_params_present().
> +     */
> +    if (!assert_all_params_present(params, errp)) {
> +        return NULL;
> +    }
> +
>      /*
>       * The block-bitmap-mapping breaks the expected API of
>       * query-migrate-parameters of having all members present. To keep
> -- 
> 2.53.0
> 

-- 
Peter Xu



^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 05/18] migration: Merge parameter structs instead of assigning one by one
  2026-09-03 18:20   ` Peter Xu
@ 2026-09-03 19:03     ` Fabiano Rosas
  0 siblings, 0 replies; 50+ messages in thread
From: Fabiano Rosas @ 2026-09-03 19:03 UTC (permalink / raw)
  To: Peter Xu; +Cc: qemu-devel

Peter Xu <peterx@redhat.com> writes:

> On Wed, Sep 02, 2026 at 07:15:33PM -0300, Fabiano Rosas wrote:
>> Convert the code in migrate_params_test_apply() from an open-coded
>> copy of every migration parameter to a merge operation using QAPI
>> visitors and QDict.
>> 
>> The purpose of that routine is to update a temporary structure
>> (pre-populated with the current migration parameters), with the values
>> received from the user via QAPI. As a result, the temporary structure
>> will then contain the "to be applied" parameters and it's validated
>> before being used to overwrite the parameters currently in use.
>> 
>> The update is currently done as follows:
>> 
>> where 'params' is the user input from QAPI,
>> for each parameter:
>> 
>>   a) check if the option is present
>>      params->has_<name> == true
>>      params-><name> != NULL // for strings
>> 
>>   b) if the parameter is a pointer, free the to-be-assigned member and
>>      allocate memory for the copy from params
>> 
>>   c) assign the user provided value to the temporary structure.
>> 
>> Step (a) is the same in principle as what the QAPI visitors do at
>> visit_type_MigrationParameters_members().
>> 
>> Steps (b) and (c) are roughly the same as what the QDict
>> implementation does when qdict_del() and qdict_put_obj() are combined.
>> 
>> Therefore, replace the open-coded function with
>> migrate_params_merge(), which achieves the same goal, but uses
>> visitors and QDict. This hides the details of QAPI (has_*) from the
>> migration code and avoids the need to update
>> migrate_params_test_apply() every time a new migration parameter is
>> added.
>> 
>> Signed-off-by: Fabiano Rosas <farosas@suse.de>
>
> Nice,
>
> Reviewed-by: Peter Xu <peterx@redhat.com>
>
> Nitpicks only, inline,
>
>> ---
>>  migration/options.c | 201 +++++++++++++++-----------------------------
>>  1 file changed, 66 insertions(+), 135 deletions(-)
>> 
>> diff --git a/migration/options.c b/migration/options.c
>> index 6b787950808..de091d2eee3 100644
>> --- a/migration/options.c
>> +++ b/migration/options.c
>> @@ -20,6 +20,9 @@
>>  #include "qapi/qapi-commands-migration.h"
>>  #include "qapi/qapi-visit-migration.h"
>>  #include "qapi/qmp/qerror.h"
>> +#include "qapi/qobject-input-visitor.h"
>> +#include "qapi/qobject-output-visitor.h"
>> +#include "qobject/qdict.h"
>>  #include "qobject/qnull.h"
>>  #include "system/runstate.h"
>>  #include "migration/colo.h"
>> @@ -1074,6 +1077,28 @@ static void tls_opt_to_str(StrOrNull *opt)
>>      opt->u.s = g_strdup("");
>>  }
>>  
>> +static QDict *migrate_params_to_dict(MigrationParameters *p, Error **errp)
>> +{
>> +    QObject *obj = NULL;
>> +    Visitor *v = qobject_output_visitor_new(&obj);
>> +
>> +    if (visit_type_MigrationParameters(v, NULL, &p, errp)) {
>> +        visit_complete(v, &obj);
>> +    }
>> +    visit_free(v);
>> +    return qobject_to(QDict, obj);
>> +}
>> +
>> +static MigrationParameters *migrate_params_from_dict(QDict *d, Error **errp)
>> +{
>> +    Visitor *v = qobject_input_visitor_new(QOBJECT(d));
>> +    MigrationParameters *tmp = NULL;
>> +
>> +    visit_type_MigrationParameters(v, NULL, &tmp, errp);
>> +    visit_free(v);
>> +    return tmp;
>> +}
>> +
>>  /*
>>   * query-migrate-parameters expects all members of MigrationParameters
>>   * to be present, but we cannot mark them non-optional in QAPI because
>> @@ -1166,6 +1191,39 @@ static void migrate_post_update_params(MigrationParameters *new, Error **errp)
>>      }
>>  }
>>  
>> +static bool migrate_params_merge(MigrationParameters *in1,
>> +                                 MigrationParameters *in2,
>
> Perhaps rename in1/in2/d1/d2 similarly with "cur" / "new" / ..?  As in1 and
> in2 are not equal: when merge it only overwrites in1 with in2, not vice
> versa.
>

Hm, let me google around, there might be some terms that fit here. Like
'base' and ... something else.

>> +                                 MigrationParameters **out,
>> +                                 Error **errp)
>> +{
>> +    g_autoptr(QDict) d1 = NULL;
>> +    g_autoptr(QDict) d2 = NULL;
>> +    const QDictEntry *e;
>> +
>> +    d1 = migrate_params_to_dict(in1, errp);
>> +    if (!d1) {
>> +        return false;
>> +    }
>> +
>> +    d2 = migrate_params_to_dict(in2, errp);
>> +    if (!d2) {
>> +        return false;
>> +    }
>> +
>> +    for (e = qdict_first(d2); e; e = qdict_next(d2, e)) {
>> +        const char *key = qdict_entry_key(e);
>> +        QObject *value = qdict_entry_value(e);
>> +
>> +        qdict_del(d1, key);
>
> IIUC this line can be dropped due to a smart enough qdict_put_obj().
>

There is a very obvious reason to keep it. I just don't remember what it
is. I'll check.

>> +        qobject_ref(value);
>> +        qdict_put_obj(d1, key, value);
>> +    }
>> +
>> +    *out = migrate_params_from_dict(d1, errp);
>> +
>> +    return !!*out;
>> +}
>> +
>>  /*
>>   * Check whether the parameters are valid. Error will be put into errp
>>   * (if provided). Return true if valid, otherwise false.
>> @@ -1328,133 +1386,6 @@ bool migrate_params_check(MigrationParameters *params, Error **errp)
>>      return true;
>>  }
>>  
>> -static void migrate_params_test_apply(MigrationParameters *params,
>> -                                      MigrationParameters *dest)
>> -{
>> -    MigrationState *s = migrate_get_current();
>> -
>> -    QAPI_CLONE_MEMBERS(MigrationParameters, dest, &s->parameters);
>> -
>> -    if (params->has_throttle_trigger_threshold) {
>> -        dest->throttle_trigger_threshold = params->throttle_trigger_threshold;
>> -    }
>> -
>> -    if (params->has_cpu_throttle_initial) {
>> -        dest->cpu_throttle_initial = params->cpu_throttle_initial;
>> -    }
>> -
>> -    if (params->has_cpu_throttle_increment) {
>> -        dest->cpu_throttle_increment = params->cpu_throttle_increment;
>> -    }
>> -
>> -    if (params->has_cpu_throttle_tailslow) {
>> -        dest->cpu_throttle_tailslow = params->cpu_throttle_tailslow;
>> -    }
>> -
>> -    if (params->tls_creds) {
>> -        qapi_free_StrOrNull(dest->tls_creds);
>> -        dest->tls_creds = QAPI_CLONE(StrOrNull, params->tls_creds);
>> -    }
>> -
>> -    if (params->tls_hostname) {
>> -        qapi_free_StrOrNull(dest->tls_hostname);
>> -        dest->tls_hostname = QAPI_CLONE(StrOrNull, params->tls_hostname);
>> -    }
>> -
>> -    if (params->tls_authz) {
>> -        qapi_free_StrOrNull(dest->tls_authz);
>> -        dest->tls_authz = QAPI_CLONE(StrOrNull, params->tls_authz);
>> -    }
>> -
>> -    if (params->has_max_bandwidth) {
>> -        dest->max_bandwidth = params->max_bandwidth;
>> -    }
>> -
>> -    if (params->has_avail_switchover_bandwidth) {
>> -        dest->avail_switchover_bandwidth = params->avail_switchover_bandwidth;
>> -    }
>> -
>> -    if (params->has_downtime_limit) {
>> -        dest->downtime_limit = params->downtime_limit;
>> -    }
>> -
>> -    if (params->has_x_checkpoint_delay) {
>> -        dest->x_checkpoint_delay = params->x_checkpoint_delay;
>> -    }
>> -
>> -    if (params->has_multifd_channels) {
>> -        dest->multifd_channels = params->multifd_channels;
>> -    }
>> -    if (params->has_multifd_compression) {
>> -        dest->multifd_compression = params->multifd_compression;
>> -    }
>> -    if (params->has_multifd_qatzip_level) {
>> -        dest->multifd_qatzip_level = params->multifd_qatzip_level;
>> -    }
>> -    if (params->has_multifd_zlib_level) {
>> -        dest->multifd_zlib_level = params->multifd_zlib_level;
>> -    }
>> -    if (params->has_multifd_zstd_level) {
>> -        dest->multifd_zstd_level = params->multifd_zstd_level;
>> -    }
>> -    if (params->has_xbzrle_cache_size) {
>> -        dest->xbzrle_cache_size = params->xbzrle_cache_size;
>> -    }
>> -    if (params->has_max_postcopy_bandwidth) {
>> -        dest->max_postcopy_bandwidth = params->max_postcopy_bandwidth;
>> -    }
>> -    if (params->has_max_cpu_throttle) {
>> -        dest->max_cpu_throttle = params->max_cpu_throttle;
>> -    }
>> -    if (params->has_announce_initial) {
>> -        dest->announce_initial = params->announce_initial;
>> -    }
>> -    if (params->has_announce_max) {
>> -        dest->announce_max = params->announce_max;
>> -    }
>> -    if (params->has_announce_rounds) {
>> -        dest->announce_rounds = params->announce_rounds;
>> -    }
>> -    if (params->has_announce_step) {
>> -        dest->announce_step = params->announce_step;
>> -    }
>> -
>> -    if (params->has_block_bitmap_mapping) {
>> -        qapi_free_BitmapMigrationNodeAliasList(dest->block_bitmap_mapping);
>> -        dest->block_bitmap_mapping = QAPI_CLONE(BitmapMigrationNodeAliasList,
>> -                                                params->block_bitmap_mapping);
>> -    }
>> -
>> -    if (params->has_x_vcpu_dirty_limit_period) {
>> -        dest->x_vcpu_dirty_limit_period =
>> -            params->x_vcpu_dirty_limit_period;
>> -    }
>> -    if (params->has_vcpu_dirty_limit) {
>> -        dest->vcpu_dirty_limit = params->vcpu_dirty_limit;
>> -    }
>> -
>> -    if (params->has_mode) {
>> -        dest->mode = params->mode;
>> -    }
>> -
>> -    if (params->has_zero_page_detection) {
>> -        dest->zero_page_detection = params->zero_page_detection;
>> -    }
>> -
>> -    if (params->has_direct_io) {
>> -        dest->direct_io = params->direct_io;
>> -    }
>> -
>> -    if (params->has_x_rdma_chunk_size) {
>> -        dest->x_rdma_chunk_size = params->x_rdma_chunk_size;
>> -    }
>> -
>> -    if (params->has_cpr_exec_command) {
>> -        qapi_free_strList(dest->cpr_exec_command);
>> -        dest->cpr_exec_command = QAPI_CLONE(strList, params->cpr_exec_command);
>> -    }
>> -}
>> -
>>  /*
>>   * Caller must ensure the has_* fields of @params are true so they all
>>   * get copied and the pointer members don't dangle.
>> @@ -1472,7 +1403,8 @@ static void migrate_params_apply(MigrationParameters *params)
>>  
>>  void qmp_migrate_set_parameters(MigrationParameters *input, Error **errp)
>>  {
>> -    MigrationParameters new;
>> +    MigrationParameters *cur = &migrate_get_current()->parameters;
>> +    g_autoptr(MigrationParameters) new = NULL;
>>  
>>      /*
>>       * Convert QTYPE_QNULL and NULL to the empty string (""). Even
>> @@ -1486,14 +1418,13 @@ void qmp_migrate_set_parameters(MigrationParameters *input, Error **errp)
>>      tls_opt_to_str(input->tls_hostname);
>>      tls_opt_to_str(input->tls_authz);
>>  
>> -    migrate_params_test_apply(input, &new);
>> +    /* merge input on top of current */
>> +    if (!migrate_params_merge(cur, input, &new, errp)) {
>> +        return;
>> +    }
>>  
>> -    if (migrate_params_check(&new, errp)) {
>> -        migrate_params_apply(&new);
>> +    if (migrate_params_check(new, errp)) {
>> +        migrate_params_apply(new);
>>          migrate_post_update_params(input, errp);
>>      }
>> -
>> -    migrate_tls_opts_free(&new);
>> -    qapi_free_BitmapMigrationNodeAliasList(new.block_bitmap_mapping);
>> -    qapi_free_strList(new.cpr_exec_command);
>>  }
>> -- 
>> 2.53.0
>> 


^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 11/18] migration: Use keyval input visitor in HMP set command
  2026-09-02 22:15 ` [PATCH 11/18] migration: Use keyval input visitor in HMP set command Fabiano Rosas
@ 2026-09-03 19:44   ` Peter Xu
  2026-09-03 20:24     ` Dr. David Alan Gilbert
  2026-09-04  9:45   ` Markus Armbruster
  1 sibling, 1 reply; 50+ messages in thread
From: Peter Xu @ 2026-09-03 19:44 UTC (permalink / raw)
  To: Fabiano Rosas
  Cc: qemu-devel, Laurent Vivier, Paolo Bonzini, Dr. David Alan Gilbert

On Wed, Sep 02, 2026 at 07:15:39PM -0300, Fabiano Rosas wrote:
> Change the hmp_migrate_set_parameter command to use a keyval input
> visitor.
> 
> Currently a string visitor is used and due to limitations of that
> particular visitor's implementation it's necessary to consult the QAPI
> type enum (MigrationParameter_lookup) and call each visit_type_*
> function individually. Which makes using a visitor pointless.
> 
> Since there are other visitors implemented properly and generated code
> to iterate the QAPI object, prefer using one of those. The keyval
> input visitor is adequate because HMP provides basically one key and
> one value for each migrate_set_parameter command.
> 
> To switch from string_input_visitor to keyval_input_visitor simply put
> the parameter name and value into a dict and invoke
> visit_type_MigrationParameters().
> 
> Note that it's not necessary to go through any of the keyval_* code
> because due to the nature of HMP, there's no parsing to do (no '=', no
> ',', etc).
> 
> With this the migrate_set_parameters HMP commands will be
> automatically updated anytime a new migration parameter is added.
> 
> The bad part:
> 
> Some parameters accept a non-standard data format, I moved them to a
> "legacy" suffixed function in this patch as there's only 3 of them:
> 
> - "max-bandwidth" and "avail-switchover-bandwidth" take MiB instead of B for
>    a size parameter;

Ah, I think it's only because I reused max-bandwidth unit there, but
didn't reuse the same unit for max-postcopy-bandwidth at least..

IIUC, HMP ABI isn't guaranteed anyway, so I wonder if we can still remove
these non-standard settings, maybe starting with some warning messages.  Cc
Dave.

> 
> - "cpr-exec-command" takes the strList type which needs to be built
>   manually.
> 
> Signed-off-by: Fabiano Rosas <farosas@suse.de>
> ---
>  migration/migration-hmp-cmds.c     | 203 +++++++++--------------------
>  tests/qtest/migration/misc-tests.c |   2 +-
>  2 files changed, 59 insertions(+), 146 deletions(-)
> 
> diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c
> index ad68fa23aa4..220ac28b5e3 100644
> --- a/migration/migration-hmp-cmds.c
> +++ b/migration/migration-hmp-cmds.c
> @@ -24,7 +24,9 @@
>  #include "qapi/error.h"
>  #include "qapi/qapi-commands-migration.h"
>  #include "qapi/qapi-visit-migration.h"
> +#include "qapi/qobject-input-visitor.h"
>  #include "qobject/qdict.h"
> +#include "qobject/qstring.h"
>  #include "qapi/string-input-visitor.h"
>  #include "qapi/string-output-visitor.h"
>  #include "qemu/cutils.h"
> @@ -589,59 +591,16 @@ end:
>      hmp_handle_error(mon, err);
>  }
>  
> -void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict)
> +static void hmp_migrate_set_parameter_legacy(Monitor *mon, const QDict *qdict)
>  {
>      const char *param = qdict_get_str(qdict, "parameter");
>      const char *valuestr = qdict_get_str(qdict, "value");
> -    Visitor *v = string_input_visitor_new(valuestr);
>      MigrationParameters *p = g_new0(MigrationParameters, 1);
>      uint64_t valuebw = 0;
> -    uint64_t cache_size;
>      Error *err = NULL;
> -    int val, ret;
> +    int ret;
>  
> -    val = qapi_enum_parse(&MigrationParameter_lookup, param, -1, &err);
> -    if (val < 0) {
> -        goto cleanup;
> -    }
> -
> -    switch (val) {
> -    case MIGRATION_PARAMETER_THROTTLE_TRIGGER_THRESHOLD:
> -        p->has_throttle_trigger_threshold = true;
> -        visit_type_uint8(v, param, &p->throttle_trigger_threshold, &err);
> -        break;
> -    case MIGRATION_PARAMETER_CPU_THROTTLE_INITIAL:
> -        p->has_cpu_throttle_initial = true;
> -        visit_type_uint8(v, param, &p->cpu_throttle_initial, &err);
> -        break;
> -    case MIGRATION_PARAMETER_CPU_THROTTLE_INCREMENT:
> -        p->has_cpu_throttle_increment = true;
> -        visit_type_uint8(v, param, &p->cpu_throttle_increment, &err);
> -        break;
> -    case MIGRATION_PARAMETER_CPU_THROTTLE_TAILSLOW:
> -        p->has_cpu_throttle_tailslow = true;
> -        visit_type_bool(v, param, &p->cpu_throttle_tailslow, &err);
> -        break;
> -    case MIGRATION_PARAMETER_MAX_CPU_THROTTLE:
> -        p->has_max_cpu_throttle = true;
> -        visit_type_uint8(v, param, &p->max_cpu_throttle, &err);
> -        break;
> -    case MIGRATION_PARAMETER_TLS_CREDS:
> -        p->tls_creds = g_new0(StrOrNull, 1);
> -        p->tls_creds->type = QTYPE_QSTRING;
> -        visit_type_str(v, param, &p->tls_creds->u.s, &err);
> -        break;
> -    case MIGRATION_PARAMETER_TLS_HOSTNAME:
> -        p->tls_hostname = g_new0(StrOrNull, 1);
> -        p->tls_hostname->type = QTYPE_QSTRING;
> -        visit_type_str(v, param, &p->tls_hostname->u.s, &err);
> -        break;
> -    case MIGRATION_PARAMETER_TLS_AUTHZ:
> -        p->tls_authz = g_new0(StrOrNull, 1);
> -        p->tls_authz->type = QTYPE_QSTRING;
> -        visit_type_str(v, param, &p->tls_authz->u.s, &err);
> -        break;
> -    case MIGRATION_PARAMETER_MAX_BANDWIDTH:
> +    if (g_str_equal(param, "max-bandwidth")) {
>          p->has_max_bandwidth = true;
>          /*
>           * Can't use visit_type_size() here, because it
> @@ -651,109 +610,21 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict)
>          if (ret < 0 || valuebw > INT64_MAX
>              || (size_t)valuebw != valuebw) {
>              error_setg(&err, "Invalid size %s", valuestr);
> -            break;
> +            return;

This may leak @err and @p?

>          }
>          p->max_bandwidth = valuebw;
> -        break;
> -    case MIGRATION_PARAMETER_AVAIL_SWITCHOVER_BANDWIDTH:
> +
> +    } else if (g_str_equal(param, "avail-switchover-bandwidth")) {
>          p->has_avail_switchover_bandwidth = true;
>          ret = qemu_strtosz_MiB(valuestr, NULL, &valuebw);
>          if (ret < 0 || valuebw > INT64_MAX
>              || (size_t)valuebw != valuebw) {
>              error_setg(&err, "Invalid size %s", valuestr);
> -            break;
> +            return;

Same.

>          }
>          p->avail_switchover_bandwidth = valuebw;
> -        break;
> -    case MIGRATION_PARAMETER_DOWNTIME_LIMIT:
> -        p->has_downtime_limit = true;
> -        visit_type_size(v, param, &p->downtime_limit, &err);
> -        break;
> -    case MIGRATION_PARAMETER_X_CHECKPOINT_DELAY:
> -        p->has_x_checkpoint_delay = true;
> -        visit_type_uint32(v, param, &p->x_checkpoint_delay, &err);
> -        break;
> -    case MIGRATION_PARAMETER_MULTIFD_CHANNELS:
> -        p->has_multifd_channels = true;
> -        visit_type_uint8(v, param, &p->multifd_channels, &err);
> -        break;
> -    case MIGRATION_PARAMETER_MULTIFD_COMPRESSION:
> -        p->has_multifd_compression = true;
> -        visit_type_MultiFDCompression(v, param, &p->multifd_compression,
> -                                      &err);
> -        break;
> -    case MIGRATION_PARAMETER_MULTIFD_ZLIB_LEVEL:
> -        p->has_multifd_zlib_level = true;
> -        visit_type_uint8(v, param, &p->multifd_zlib_level, &err);
> -        break;
> -    case MIGRATION_PARAMETER_MULTIFD_QATZIP_LEVEL:
> -        p->has_multifd_qatzip_level = true;
> -        visit_type_uint8(v, param, &p->multifd_qatzip_level, &err);
> -        break;
> -    case MIGRATION_PARAMETER_MULTIFD_ZSTD_LEVEL:
> -        p->has_multifd_zstd_level = true;
> -        visit_type_uint8(v, param, &p->multifd_zstd_level, &err);
> -        break;
> -    case MIGRATION_PARAMETER_ZERO_PAGE_DETECTION:
> -        p->has_zero_page_detection = true;
> -        visit_type_ZeroPageDetection(v, param, &p->zero_page_detection, &err);
> -        break;
> -    case MIGRATION_PARAMETER_XBZRLE_CACHE_SIZE:
> -        p->has_xbzrle_cache_size = true;
> -        if (!visit_type_size(v, param, &cache_size, &err)) {
> -            break;
> -        }
> -        if (cache_size > INT64_MAX || (size_t)cache_size != cache_size) {
> -            error_setg(&err, "Invalid size %s", valuestr);
> -            break;
> -        }
> -        p->xbzrle_cache_size = cache_size;
> -        break;
> -    case MIGRATION_PARAMETER_MAX_POSTCOPY_BANDWIDTH:
> -        p->has_max_postcopy_bandwidth = true;
> -        visit_type_size(v, param, &p->max_postcopy_bandwidth, &err);
> -        break;
> -    case MIGRATION_PARAMETER_ANNOUNCE_INITIAL:
> -        p->has_announce_initial = true;
> -        visit_type_size(v, param, &p->announce_initial, &err);
> -        break;
> -    case MIGRATION_PARAMETER_ANNOUNCE_MAX:
> -        p->has_announce_max = true;
> -        visit_type_size(v, param, &p->announce_max, &err);
> -        break;
> -    case MIGRATION_PARAMETER_ANNOUNCE_ROUNDS:
> -        p->has_announce_rounds = true;
> -        visit_type_size(v, param, &p->announce_rounds, &err);
> -        break;
> -    case MIGRATION_PARAMETER_ANNOUNCE_STEP:
> -        p->has_announce_step = true;
> -        visit_type_size(v, param, &p->announce_step, &err);
> -        break;
> -    case MIGRATION_PARAMETER_BLOCK_BITMAP_MAPPING:
> -        error_setg(&err, "The block-bitmap-mapping parameter can only be set "
> -                   "through QMP");
> -        break;
> -    case MIGRATION_PARAMETER_X_VCPU_DIRTY_LIMIT_PERIOD:
> -        p->has_x_vcpu_dirty_limit_period = true;
> -        visit_type_size(v, param, &p->x_vcpu_dirty_limit_period, &err);
> -        break;
> -    case MIGRATION_PARAMETER_VCPU_DIRTY_LIMIT:
> -        p->has_vcpu_dirty_limit = true;
> -        visit_type_size(v, param, &p->vcpu_dirty_limit, &err);
> -        break;
> -    case MIGRATION_PARAMETER_MODE:
> -        p->has_mode = true;
> -        visit_type_MigMode(v, param, &p->mode, &err);
> -        break;
> -    case MIGRATION_PARAMETER_DIRECT_IO:
> -        p->has_direct_io = true;
> -        visit_type_bool(v, param, &p->direct_io, &err);
> -        break;
> -    case MIGRATION_PARAMETER_X_RDMA_CHUNK_SIZE:
> -        p->has_x_rdma_chunk_size = true;
> -        visit_type_size(v, param, &p->x_rdma_chunk_size, &err);
> -        break;
> -    case MIGRATION_PARAMETER_CPR_EXEC_COMMAND: {
> +
> +    } else if (g_str_equal(param, "cpr-exec-command")) {
>          /*
>           * NOTE: g_autofree will only auto g_free() the strv array when
>           * needed, it will not free the strings within the array. It's
> @@ -766,15 +637,14 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict)
>  
>          if (!g_shell_parse_argv(valuestr, NULL, &strv, &gerr)) {
>              error_setg(&err, "%s", gerr->message);
> -            break;
> +            return;

Same.

>          }
>          for (int i = 0; strv[i]; i++) {
>              QAPI_LIST_APPEND(tail, strv[i]);
>          }
>          p->has_cpr_exec_command = true;
> -        break;
> -    }
> -    default:
> +
> +    } else {
>          g_assert_not_reached();
>      }
>  
> @@ -784,12 +654,55 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict)
>  
>      qmp_migrate_set_parameters(p, &err);
>  
> - cleanup:
> +cleanup:
>      qapi_free_MigrationParameters(p);
> +    hmp_handle_error(mon, err);
> +}
> +
> +static void hmp_migrate_set_parameter_qapi(Monitor *mon, const QDict *qdict)
> +{
> +    const char *param = qdict_get_str(qdict, "parameter");
> +    const char *valuestr = qdict_get_str(qdict, "value");
> +    g_autoptr(QDict) input = qdict_new();
> +    g_autoptr(MigrationParameters) p = NULL;
> +    Visitor *v;
> +    Error *err = NULL;
> +
> +    /* the same as keyval_parse(), but here there's no need to parse */
> +    qdict_put_obj(input, param, QOBJECT(qstring_from_str(valuestr)));
> +
> +    v = qobject_input_visitor_new_keyval(QOBJECT(input));
> +    if (visit_type_MigrationParameters(v, NULL, &p, &err)) {
> +        qmp_migrate_set_parameters(p, &err);
> +    }
> +
>      visit_free(v);
>      hmp_handle_error(mon, err);
>  }
>  
> +void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict)
> +{
> +    const char *param = qdict_get_str(qdict, "parameter");
> +
> +    if (g_str_equal(param, "block-bitmap-mapping")) {
> +        Error *err = NULL;
> +
> +        error_setg(&err, "The %s parameter can only be set through QMP", param);
> +        hmp_handle_error(mon, err);
> +        return;
> +    }
> +
> +    /* these have non-standard setters */
> +    if (g_str_equal(param, "max-bandwidth") ||
> +        g_str_equal(param, "avail-switchover-bandwidth") ||
> +        g_str_equal(param, "cpr-exec-command")) {
> +
> +        return hmp_migrate_set_parameter_legacy(mon, qdict);
> +    }
> +
> +    hmp_migrate_set_parameter_qapi(mon, qdict);
> +}
> +
>  void hmp_migrate_start_postcopy(Monitor *mon, const QDict *qdict)
>  {
>      Error *err = NULL;
> diff --git a/tests/qtest/migration/misc-tests.c b/tests/qtest/migration/misc-tests.c
> index 3554900b758..ba8183978b3 100644
> --- a/tests/qtest/migration/misc-tests.c
> +++ b/tests/qtest/migration/misc-tests.c
> @@ -48,7 +48,7 @@ typedef struct HMPTestData {
>  HMPTestData test_cases[] = {
>      TEST("", "", "migrate_set_parameter: string expected"),
>      TEST("foo", "", "migrate_set_parameter: string expected"),
> -    TEST("foo", "on", "Error: invalid parameter value: foo"),
> +    TEST("foo", "on", "Error: Parameter 'foo' is unexpected"),
>  
>      /* bool */
>      TEST("cpu-throttle-tailslow", "on", "on"),
> -- 
> 2.53.0
> 

-- 
Peter Xu



^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 14/18] migration: Rewrite migrate_set_parameter_completion using QDict
  2026-09-02 22:15 ` [PATCH 14/18] migration: Rewrite migrate_set_parameter_completion using QDict Fabiano Rosas
@ 2026-09-03 20:23   ` Peter Xu
  0 siblings, 0 replies; 50+ messages in thread
From: Peter Xu @ 2026-09-03 20:23 UTC (permalink / raw)
  To: Fabiano Rosas; +Cc: qemu-devel

On Wed, Sep 02, 2026 at 07:15:42PM -0300, Fabiano Rosas wrote:
> The migrate_set_parameter_completion function is the last user of the
> MigrationParameter enum. Write the code using an output visitor and
> QDict instead so we can remove the enum in a future patch.
> 
> Signed-off-by: Fabiano Rosas <farosas@suse.de>
> ---
>  migration/migration-hmp-cmds.c | 11 ++++++++---
>  1 file changed, 8 insertions(+), 3 deletions(-)
> 
> diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c
> index dff69650a0c..d0adda25090 100644
> --- a/migration/migration-hmp-cmds.c
> +++ b/migration/migration-hmp-cmds.c
> @@ -791,14 +791,19 @@ void migrate_set_capability_completion(ReadLineState *rs, int nb_args,
>  void migrate_set_parameter_completion(ReadLineState *rs, int nb_args,
>                                        const char *str)
>  {
> +    g_autoptr(MigrationParameters) params = g_new0(MigrationParameters, 1);
> +    g_autoptr(QDict) d = migrate_params_to_dict(params, NULL);

When I played with this branch a bit then I found set_parameter completion
broke, then I found indeed the prior test didn't add set_parameter
completion test.. can add one too.

Here IIUC d is empty dict.  One possible fix:

--- a/migration/migration-hmp-cmds.c
+++ b/migration/migration-hmp-cmds.c
@@ -787,19 +787,19 @@ void migrate_set_capability_completion(ReadLineState *rs, int nb_args,
 void migrate_set_parameter_completion(ReadLineState *rs, int nb_args,
                                       const char *str)
 {
-    g_autoptr(MigrationParameters) params = g_new0(MigrationParameters, 1);                                                             
-    g_autoptr(QDict) d = migrate_params_to_dict(params, NULL);                                                                          
+    g_autoptr(QDict) d;                                                                                                                 
     const QDictEntry *e;
     size_t len;

+    /* Temporarily borrow the global parameters */                                                                                      
+    d = migrate_params_to_dict(&migrate_get_current()->parameters,                                                                      
+                               &error_abort);                                                                                           
     len = strlen(str);
     readline_set_completion_index(rs, len);
     if (nb_args == 2) {
         for (e = qdict_first(d); e; e = qdict_next(d, e)) {
             const char *key = qdict_entry_key(e);
-            if (!g_str_has_prefix(key, "has-")) {                                                                                       
-                readline_add_completion_of(rs, str, key);                                                                               
-            }                                                                                                                           
+            readline_add_completion_of(rs, str, key);                                                                                   
         }
     }
 }

> +    const QDictEntry *e;
>      size_t len;
>  
>      len = strlen(str);
>      readline_set_completion_index(rs, len);
>      if (nb_args == 2) {
> -        int i;
> -        for (i = 0; i < MIGRATION_PARAMETER__MAX; i++) {
> -            readline_add_completion_of(rs, str, MigrationParameter_str(i));
> +        for (e = qdict_first(d); e; e = qdict_next(d, e)) {
> +            const char *key = qdict_entry_key(e);
> +            if (!g_str_has_prefix(key, "has-")) {
> +                readline_add_completion_of(rs, str, key);
> +            }
>          }
>      }
>  }
> -- 
> 2.53.0
> 

-- 
Peter Xu



^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 11/18] migration: Use keyval input visitor in HMP set command
  2026-09-03 19:44   ` Peter Xu
@ 2026-09-03 20:24     ` Dr. David Alan Gilbert
  2026-09-04 15:26       ` Peter Xu
  0 siblings, 1 reply; 50+ messages in thread
From: Dr. David Alan Gilbert @ 2026-09-03 20:24 UTC (permalink / raw)
  To: Peter Xu; +Cc: Fabiano Rosas, qemu-devel, Laurent Vivier, Paolo Bonzini

* Peter Xu (peterx@redhat.com) wrote:
> On Wed, Sep 02, 2026 at 07:15:39PM -0300, Fabiano Rosas wrote:
> > Change the hmp_migrate_set_parameter command to use a keyval input
> > visitor.
> > 
> > Currently a string visitor is used and due to limitations of that
> > particular visitor's implementation it's necessary to consult the QAPI
> > type enum (MigrationParameter_lookup) and call each visit_type_*
> > function individually. Which makes using a visitor pointless.
> > 
> > Since there are other visitors implemented properly and generated code
> > to iterate the QAPI object, prefer using one of those. The keyval
> > input visitor is adequate because HMP provides basically one key and
> > one value for each migrate_set_parameter command.
> > 
> > To switch from string_input_visitor to keyval_input_visitor simply put
> > the parameter name and value into a dict and invoke
> > visit_type_MigrationParameters().
> > 
> > Note that it's not necessary to go through any of the keyval_* code
> > because due to the nature of HMP, there's no parsing to do (no '=', no
> > ',', etc).
> > 
> > With this the migrate_set_parameters HMP commands will be
> > automatically updated anytime a new migration parameter is added.
> > 
> > The bad part:
> > 
> > Some parameters accept a non-standard data format, I moved them to a
> > "legacy" suffixed function in this patch as there's only 3 of them:
> > 
> > - "max-bandwidth" and "avail-switchover-bandwidth" take MiB instead of B for
> >    a size parameter;
> 
> Ah, I think it's only because I reused max-bandwidth unit there, but
> didn't reuse the same unit for max-postcopy-bandwidth at least..
> 
> IIUC, HMP ABI isn't guaranteed anyway, so I wonder if we can still remove
> these non-standard settings, maybe starting with some warning messages.  Cc
> Dave.

Right; I'm fine with those changes; put a note at the top
saying that it changes and a note in the changelog.

Someone will have a test script somewhere that might depend on it
(I know I did when I was debugging migration - and I never remembered what
was MB or byte!)

(I also thought there was some subtlety in the parameters for tls that
I can't remember)

> > - "cpr-exec-command" takes the strList type which needs to be built
> >   manually.

If that changes behaviour please put an example before and after in
the commit message.

Dave

> > Signed-off-by: Fabiano Rosas <farosas@suse.de>
> > ---
> >  migration/migration-hmp-cmds.c     | 203 +++++++++--------------------
> >  tests/qtest/migration/misc-tests.c |   2 +-
> >  2 files changed, 59 insertions(+), 146 deletions(-)
> > 
> > diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c
> > index ad68fa23aa4..220ac28b5e3 100644
> > --- a/migration/migration-hmp-cmds.c
> > +++ b/migration/migration-hmp-cmds.c
> > @@ -24,7 +24,9 @@
> >  #include "qapi/error.h"
> >  #include "qapi/qapi-commands-migration.h"
> >  #include "qapi/qapi-visit-migration.h"
> > +#include "qapi/qobject-input-visitor.h"
> >  #include "qobject/qdict.h"
> > +#include "qobject/qstring.h"
> >  #include "qapi/string-input-visitor.h"
> >  #include "qapi/string-output-visitor.h"
> >  #include "qemu/cutils.h"
> > @@ -589,59 +591,16 @@ end:
> >      hmp_handle_error(mon, err);
> >  }
> >  
> > -void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict)
> > +static void hmp_migrate_set_parameter_legacy(Monitor *mon, const QDict *qdict)
> >  {
> >      const char *param = qdict_get_str(qdict, "parameter");
> >      const char *valuestr = qdict_get_str(qdict, "value");
> > -    Visitor *v = string_input_visitor_new(valuestr);
> >      MigrationParameters *p = g_new0(MigrationParameters, 1);
> >      uint64_t valuebw = 0;
> > -    uint64_t cache_size;
> >      Error *err = NULL;
> > -    int val, ret;
> > +    int ret;
> >  
> > -    val = qapi_enum_parse(&MigrationParameter_lookup, param, -1, &err);
> > -    if (val < 0) {
> > -        goto cleanup;
> > -    }
> > -
> > -    switch (val) {
> > -    case MIGRATION_PARAMETER_THROTTLE_TRIGGER_THRESHOLD:
> > -        p->has_throttle_trigger_threshold = true;
> > -        visit_type_uint8(v, param, &p->throttle_trigger_threshold, &err);
> > -        break;
> > -    case MIGRATION_PARAMETER_CPU_THROTTLE_INITIAL:
> > -        p->has_cpu_throttle_initial = true;
> > -        visit_type_uint8(v, param, &p->cpu_throttle_initial, &err);
> > -        break;
> > -    case MIGRATION_PARAMETER_CPU_THROTTLE_INCREMENT:
> > -        p->has_cpu_throttle_increment = true;
> > -        visit_type_uint8(v, param, &p->cpu_throttle_increment, &err);
> > -        break;
> > -    case MIGRATION_PARAMETER_CPU_THROTTLE_TAILSLOW:
> > -        p->has_cpu_throttle_tailslow = true;
> > -        visit_type_bool(v, param, &p->cpu_throttle_tailslow, &err);
> > -        break;
> > -    case MIGRATION_PARAMETER_MAX_CPU_THROTTLE:
> > -        p->has_max_cpu_throttle = true;
> > -        visit_type_uint8(v, param, &p->max_cpu_throttle, &err);
> > -        break;
> > -    case MIGRATION_PARAMETER_TLS_CREDS:
> > -        p->tls_creds = g_new0(StrOrNull, 1);
> > -        p->tls_creds->type = QTYPE_QSTRING;
> > -        visit_type_str(v, param, &p->tls_creds->u.s, &err);
> > -        break;
> > -    case MIGRATION_PARAMETER_TLS_HOSTNAME:
> > -        p->tls_hostname = g_new0(StrOrNull, 1);
> > -        p->tls_hostname->type = QTYPE_QSTRING;
> > -        visit_type_str(v, param, &p->tls_hostname->u.s, &err);
> > -        break;
> > -    case MIGRATION_PARAMETER_TLS_AUTHZ:
> > -        p->tls_authz = g_new0(StrOrNull, 1);
> > -        p->tls_authz->type = QTYPE_QSTRING;
> > -        visit_type_str(v, param, &p->tls_authz->u.s, &err);
> > -        break;
> > -    case MIGRATION_PARAMETER_MAX_BANDWIDTH:
> > +    if (g_str_equal(param, "max-bandwidth")) {
> >          p->has_max_bandwidth = true;
> >          /*
> >           * Can't use visit_type_size() here, because it
> > @@ -651,109 +610,21 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict)
> >          if (ret < 0 || valuebw > INT64_MAX
> >              || (size_t)valuebw != valuebw) {
> >              error_setg(&err, "Invalid size %s", valuestr);
> > -            break;
> > +            return;
> 
> This may leak @err and @p?
> 
> >          }
> >          p->max_bandwidth = valuebw;
> > -        break;
> > -    case MIGRATION_PARAMETER_AVAIL_SWITCHOVER_BANDWIDTH:
> > +
> > +    } else if (g_str_equal(param, "avail-switchover-bandwidth")) {
> >          p->has_avail_switchover_bandwidth = true;
> >          ret = qemu_strtosz_MiB(valuestr, NULL, &valuebw);
> >          if (ret < 0 || valuebw > INT64_MAX
> >              || (size_t)valuebw != valuebw) {
> >              error_setg(&err, "Invalid size %s", valuestr);
> > -            break;
> > +            return;
> 
> Same.
> 
> >          }
> >          p->avail_switchover_bandwidth = valuebw;
> > -        break;
> > -    case MIGRATION_PARAMETER_DOWNTIME_LIMIT:
> > -        p->has_downtime_limit = true;
> > -        visit_type_size(v, param, &p->downtime_limit, &err);
> > -        break;
> > -    case MIGRATION_PARAMETER_X_CHECKPOINT_DELAY:
> > -        p->has_x_checkpoint_delay = true;
> > -        visit_type_uint32(v, param, &p->x_checkpoint_delay, &err);
> > -        break;
> > -    case MIGRATION_PARAMETER_MULTIFD_CHANNELS:
> > -        p->has_multifd_channels = true;
> > -        visit_type_uint8(v, param, &p->multifd_channels, &err);
> > -        break;
> > -    case MIGRATION_PARAMETER_MULTIFD_COMPRESSION:
> > -        p->has_multifd_compression = true;
> > -        visit_type_MultiFDCompression(v, param, &p->multifd_compression,
> > -                                      &err);
> > -        break;
> > -    case MIGRATION_PARAMETER_MULTIFD_ZLIB_LEVEL:
> > -        p->has_multifd_zlib_level = true;
> > -        visit_type_uint8(v, param, &p->multifd_zlib_level, &err);
> > -        break;
> > -    case MIGRATION_PARAMETER_MULTIFD_QATZIP_LEVEL:
> > -        p->has_multifd_qatzip_level = true;
> > -        visit_type_uint8(v, param, &p->multifd_qatzip_level, &err);
> > -        break;
> > -    case MIGRATION_PARAMETER_MULTIFD_ZSTD_LEVEL:
> > -        p->has_multifd_zstd_level = true;
> > -        visit_type_uint8(v, param, &p->multifd_zstd_level, &err);
> > -        break;
> > -    case MIGRATION_PARAMETER_ZERO_PAGE_DETECTION:
> > -        p->has_zero_page_detection = true;
> > -        visit_type_ZeroPageDetection(v, param, &p->zero_page_detection, &err);
> > -        break;
> > -    case MIGRATION_PARAMETER_XBZRLE_CACHE_SIZE:
> > -        p->has_xbzrle_cache_size = true;
> > -        if (!visit_type_size(v, param, &cache_size, &err)) {
> > -            break;
> > -        }
> > -        if (cache_size > INT64_MAX || (size_t)cache_size != cache_size) {
> > -            error_setg(&err, "Invalid size %s", valuestr);
> > -            break;
> > -        }
> > -        p->xbzrle_cache_size = cache_size;
> > -        break;
> > -    case MIGRATION_PARAMETER_MAX_POSTCOPY_BANDWIDTH:
> > -        p->has_max_postcopy_bandwidth = true;
> > -        visit_type_size(v, param, &p->max_postcopy_bandwidth, &err);
> > -        break;
> > -    case MIGRATION_PARAMETER_ANNOUNCE_INITIAL:
> > -        p->has_announce_initial = true;
> > -        visit_type_size(v, param, &p->announce_initial, &err);
> > -        break;
> > -    case MIGRATION_PARAMETER_ANNOUNCE_MAX:
> > -        p->has_announce_max = true;
> > -        visit_type_size(v, param, &p->announce_max, &err);
> > -        break;
> > -    case MIGRATION_PARAMETER_ANNOUNCE_ROUNDS:
> > -        p->has_announce_rounds = true;
> > -        visit_type_size(v, param, &p->announce_rounds, &err);
> > -        break;
> > -    case MIGRATION_PARAMETER_ANNOUNCE_STEP:
> > -        p->has_announce_step = true;
> > -        visit_type_size(v, param, &p->announce_step, &err);
> > -        break;
> > -    case MIGRATION_PARAMETER_BLOCK_BITMAP_MAPPING:
> > -        error_setg(&err, "The block-bitmap-mapping parameter can only be set "
> > -                   "through QMP");
> > -        break;
> > -    case MIGRATION_PARAMETER_X_VCPU_DIRTY_LIMIT_PERIOD:
> > -        p->has_x_vcpu_dirty_limit_period = true;
> > -        visit_type_size(v, param, &p->x_vcpu_dirty_limit_period, &err);
> > -        break;
> > -    case MIGRATION_PARAMETER_VCPU_DIRTY_LIMIT:
> > -        p->has_vcpu_dirty_limit = true;
> > -        visit_type_size(v, param, &p->vcpu_dirty_limit, &err);
> > -        break;
> > -    case MIGRATION_PARAMETER_MODE:
> > -        p->has_mode = true;
> > -        visit_type_MigMode(v, param, &p->mode, &err);
> > -        break;
> > -    case MIGRATION_PARAMETER_DIRECT_IO:
> > -        p->has_direct_io = true;
> > -        visit_type_bool(v, param, &p->direct_io, &err);
> > -        break;
> > -    case MIGRATION_PARAMETER_X_RDMA_CHUNK_SIZE:
> > -        p->has_x_rdma_chunk_size = true;
> > -        visit_type_size(v, param, &p->x_rdma_chunk_size, &err);
> > -        break;
> > -    case MIGRATION_PARAMETER_CPR_EXEC_COMMAND: {
> > +
> > +    } else if (g_str_equal(param, "cpr-exec-command")) {
> >          /*
> >           * NOTE: g_autofree will only auto g_free() the strv array when
> >           * needed, it will not free the strings within the array. It's
> > @@ -766,15 +637,14 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict)
> >  
> >          if (!g_shell_parse_argv(valuestr, NULL, &strv, &gerr)) {
> >              error_setg(&err, "%s", gerr->message);
> > -            break;
> > +            return;
> 
> Same.
> 
> >          }
> >          for (int i = 0; strv[i]; i++) {
> >              QAPI_LIST_APPEND(tail, strv[i]);
> >          }
> >          p->has_cpr_exec_command = true;
> > -        break;
> > -    }
> > -    default:
> > +
> > +    } else {
> >          g_assert_not_reached();
> >      }
> >  
> > @@ -784,12 +654,55 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict)
> >  
> >      qmp_migrate_set_parameters(p, &err);
> >  
> > - cleanup:
> > +cleanup:
> >      qapi_free_MigrationParameters(p);
> > +    hmp_handle_error(mon, err);
> > +}
> > +
> > +static void hmp_migrate_set_parameter_qapi(Monitor *mon, const QDict *qdict)
> > +{
> > +    const char *param = qdict_get_str(qdict, "parameter");
> > +    const char *valuestr = qdict_get_str(qdict, "value");
> > +    g_autoptr(QDict) input = qdict_new();
> > +    g_autoptr(MigrationParameters) p = NULL;
> > +    Visitor *v;
> > +    Error *err = NULL;
> > +
> > +    /* the same as keyval_parse(), but here there's no need to parse */
> > +    qdict_put_obj(input, param, QOBJECT(qstring_from_str(valuestr)));
> > +
> > +    v = qobject_input_visitor_new_keyval(QOBJECT(input));
> > +    if (visit_type_MigrationParameters(v, NULL, &p, &err)) {
> > +        qmp_migrate_set_parameters(p, &err);
> > +    }
> > +
> >      visit_free(v);
> >      hmp_handle_error(mon, err);
> >  }
> >  
> > +void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict)
> > +{
> > +    const char *param = qdict_get_str(qdict, "parameter");
> > +
> > +    if (g_str_equal(param, "block-bitmap-mapping")) {
> > +        Error *err = NULL;
> > +
> > +        error_setg(&err, "The %s parameter can only be set through QMP", param);
> > +        hmp_handle_error(mon, err);
> > +        return;
> > +    }
> > +
> > +    /* these have non-standard setters */
> > +    if (g_str_equal(param, "max-bandwidth") ||
> > +        g_str_equal(param, "avail-switchover-bandwidth") ||
> > +        g_str_equal(param, "cpr-exec-command")) {
> > +
> > +        return hmp_migrate_set_parameter_legacy(mon, qdict);
> > +    }
> > +
> > +    hmp_migrate_set_parameter_qapi(mon, qdict);
> > +}
> > +
> >  void hmp_migrate_start_postcopy(Monitor *mon, const QDict *qdict)
> >  {
> >      Error *err = NULL;
> > diff --git a/tests/qtest/migration/misc-tests.c b/tests/qtest/migration/misc-tests.c
> > index 3554900b758..ba8183978b3 100644
> > --- a/tests/qtest/migration/misc-tests.c
> > +++ b/tests/qtest/migration/misc-tests.c
> > @@ -48,7 +48,7 @@ typedef struct HMPTestData {
> >  HMPTestData test_cases[] = {
> >      TEST("", "", "migrate_set_parameter: string expected"),
> >      TEST("foo", "", "migrate_set_parameter: string expected"),
> > -    TEST("foo", "on", "Error: invalid parameter value: foo"),
> > +    TEST("foo", "on", "Error: Parameter 'foo' is unexpected"),
> >  
> >      /* bool */
> >      TEST("cpu-throttle-tailslow", "on", "on"),
> > -- 
> > 2.53.0
> > 
> 
> -- 
> Peter Xu
> 
-- 
 -----Open up your eyes, open up your mind, open up your code -------   
/ Dr. David Alan Gilbert    |       Running GNU/Linux       | Happy  \ 
\        dave @ treblig.org |                               | In Hex /
 \ _________________________|_____ http://www.treblig.org   |_______/


^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 12/18] migration: Change HMP 'info migrate_parameters' output
  2026-09-02 22:15 ` [PATCH 12/18] migration: Change HMP 'info migrate_parameters' output Fabiano Rosas
@ 2026-09-03 20:25   ` Peter Xu
  0 siblings, 0 replies; 50+ messages in thread
From: Peter Xu @ 2026-09-03 20:25 UTC (permalink / raw)
  To: Fabiano Rosas
  Cc: qemu-devel, Kevin Wolf, Hanna Reitz, Laurent Vivier,
	Paolo Bonzini

On Wed, Sep 02, 2026 at 07:15:40PM -0300, Fabiano Rosas wrote:
> The output of 'info migrate_parameters' includes units of measurement
> for a few parameters. This is convenient for a user. It also requires
> every parameter to be individually listed in the
> hmp_migrate_set_parameter() function, which in turn requires the
> MigrationParameter (singular) enum to exist. While the latter is not
> bothersome at all, the former is.
> 
> From a development and maintenance perspective, having a list of
> parameters explicitly written in several parts of the code brings
> several annoyances: conflicts during rebase, multiple extra hits when
> grepping, requires contributors to search for every location a change
> needs to be mirrored to, etc.
> 
> Remove the units from the output so we can write this code in a more
> convenient way. The HMP output is not part of any ABI.
> 
> Also remove quotes from around the TLS options strings as this is
> inconsistent with all the other strings.
> 
> Change block-bitmap-mapping format to a single line. This requires
> updating one of the iotests to match.
> 
> Signed-off-by: Fabiano Rosas <farosas@suse.de>

HMP info commands are less of a worry.  I think this is ok, but I didn't
check the block layer details.  From migration side:

Acked-by: Peter Xu <peterx@redhat.com>

-- 
Peter Xu



^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 09/18] tests/qtest/migration: Add a test for HMP
  2026-09-02 22:15 ` [PATCH 09/18] tests/qtest/migration: Add a test for HMP Fabiano Rosas
@ 2026-09-03 20:38   ` Peter Xu
  2026-09-03 20:42     ` Peter Xu
  0 siblings, 1 reply; 50+ messages in thread
From: Peter Xu @ 2026-09-03 20:38 UTC (permalink / raw)
  To: Fabiano Rosas; +Cc: qemu-devel, Laurent Vivier, Paolo Bonzini

On Wed, Sep 02, 2026 at 07:15:37PM -0300, Fabiano Rosas wrote:
> The following patches will change how parameters are set and shown in
> HMP, including readline completion so add two minimal tests.
> 
> - set/info migrate_parameters
> 
> The test uses qtest facilities to issue HMP migrate_set_parameters for
> each of the existing migration parameters and queries them back with
> the info command. A list is kept with the expected strings. A
> substring match function inspired by glib's g_str_match_string is
> implemented for this test so the test can produce a decent error
> output instead of just assert failure. E.g:
> 
>  # HMP output mismatch for entry at line 55:
>  # expected vs. found:
>  #
>  # max-bandwidth: 10356305952768 bytes/hour
>  # ---
>  # max-bandwidth: 10356305952768 bytes/second
> 
> (note that line 55 above is the source line where the test case for
> max-bandwith is)
> 
> - readline completion
> 
> The test puts the monitor on a chardev via socket and bypasses qtest
> facilities because it needs to emit raw codes to readline. It
> therefore requires a couple of new helpers to read/write to the
> monitor socket.
> 
> Usage:
> QTEST_QEMU_BINARY=./qemu-system-x86_64 \
> ./tests/qtest/migration-test --full -p /x86_64/migration/hmp
> 
> Signed-off-by: Fabiano Rosas <farosas@suse.de>

Acked-by: Peter Xu <peterx@redhat.com>

-- 
Peter Xu



^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 09/18] tests/qtest/migration: Add a test for HMP
  2026-09-03 20:38   ` Peter Xu
@ 2026-09-03 20:42     ` Peter Xu
  0 siblings, 0 replies; 50+ messages in thread
From: Peter Xu @ 2026-09-03 20:42 UTC (permalink / raw)
  To: Fabiano Rosas; +Cc: qemu-devel, Laurent Vivier, Paolo Bonzini

On Thu, Sep 03, 2026 at 04:38:43PM -0400, Peter Xu wrote:
> On Wed, Sep 02, 2026 at 07:15:37PM -0300, Fabiano Rosas wrote:
> > The following patches will change how parameters are set and shown in
> > HMP, including readline completion so add two minimal tests.
> > 
> > - set/info migrate_parameters
> > 
> > The test uses qtest facilities to issue HMP migrate_set_parameters for
> > each of the existing migration parameters and queries them back with
> > the info command. A list is kept with the expected strings. A
> > substring match function inspired by glib's g_str_match_string is
> > implemented for this test so the test can produce a decent error
> > output instead of just assert failure. E.g:
> > 
> >  # HMP output mismatch for entry at line 55:
> >  # expected vs. found:
> >  #
> >  # max-bandwidth: 10356305952768 bytes/hour
> >  # ---
> >  # max-bandwidth: 10356305952768 bytes/second
> > 
> > (note that line 55 above is the source line where the test case for
> > max-bandwith is)
> > 
> > - readline completion
> > 
> > The test puts the monitor on a chardev via socket and bypasses qtest
> > facilities because it needs to emit raw codes to readline. It
> > therefore requires a couple of new helpers to read/write to the
> > monitor socket.
> > 
> > Usage:
> > QTEST_QEMU_BINARY=./qemu-system-x86_64 \
> > ./tests/qtest/migration-test --full -p /x86_64/migration/hmp
> > 
> > Signed-off-by: Fabiano Rosas <farosas@suse.de>
> 
> Acked-by: Peter Xu <peterx@redhat.com>

Ah I forgot to say: Logically this test should not be in migration-test,
simply because it uses 1 VM not 2.. hence MigrateCommon doesn't make sense
to be passed into the two HMP tests.  But can be done later too.

Also feel free to also keep my A-b if you lightly update it, e.g. add
set-parameter test entries, duplicate g_test_message()s, etc.

-- 
Peter Xu



^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 01/18] checkpatch: Fix checking of newlines in error messages
  2026-09-03 18:00       ` Peter Xu
  2026-09-03 18:35         ` Fabiano Rosas
@ 2026-09-04  8:56         ` Markus Armbruster
  2026-09-04  9:15           ` Peter Maydell
  1 sibling, 1 reply; 50+ messages in thread
From: Markus Armbruster @ 2026-09-04  8:56 UTC (permalink / raw)
  To: Peter Xu; +Cc: Fabiano Rosas, qemu-devel, Chao Liu

Peter Xu <peterx@redhat.com> writes:

> On Thu, Sep 03, 2026 at 02:46:37PM -0300, Fabiano Rosas wrote:
>> Peter Xu <peterx@redhat.com> writes:
>> 
>> > On Wed, Sep 02, 2026 at 07:15:29PM -0300, Fabiano Rosas wrote:
>> >> Using newlines in the g_test_message is fine. It automatically adds
>> >> the '#' required by the TAP protocol to the start of each line.
>> >
>> > IIUC we have such check not because TAP, but because all these functions
>> > will append one newline at the end, hence it's not needed.  IOW, if it
>> > applies to g_test_message(), I don't see why it doesn't apply to the rest.
>> > But maybe there're other reasons?
>> >
>> > To make it simpler, maybe we just call a few times g_test_message()?
>> >
>> 
>> Not sure I understand your point, Peter. I want to be able to print nice
>> messages in patch 9:
>> 
>>  g_test_message("expected vs. found:\n\n%s\n---\n%s:%s", str, t2[match], t2[match + 1]);
>> 
>>  # HMP output mismatch for entry at line 55:
>>  # expected vs. found:
>>  #
>>  # max-bandwidth: 10356305952768 bytes/hour
>>  # ---
>>  # max-bandwidth: 10356305952768 bytes/second
>> 
>> What would be the issue of having newlines here?
>
> No issue here that I can see.  My question was, why you moved
> g_test_message() out only, but not all?
>
> My gut feeling is we check this because people forget that all these
> functions includes a newline.
>
> So if your point stands here that "newlines can be in the middle", they
> should apply to all, not one.

I'm not sure I understand you correctly.  If you suggest to permit
newlines in the middle of error_setg(), error_report() & friends, I
disagree.  qapi/error.h:

 * The resulting message should be a single phrase, with no newline or
 * trailing punctuation.

If you want to provide additional information, use error_append_hint() /
error_printf().

> But still, I also don't see why we can't invoke g_test_message() a few
> times here too, if we want to avoid any global touch like this to land the
> whole thing faster..



^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 15/18] qapi/migration: Remove MigrationParameter
  2026-09-02 22:15 ` [PATCH 15/18] qapi/migration: Remove MigrationParameter Fabiano Rosas
@ 2026-09-04  9:07   ` Markus Armbruster
  2026-09-04 12:24   ` Peter Xu
  1 sibling, 0 replies; 50+ messages in thread
From: Markus Armbruster @ 2026-09-04  9:07 UTC (permalink / raw)
  To: Fabiano Rosas; +Cc: qemu-devel, Peter Xu, Eric Blake

Fabiano Rosas <farosas@suse.de> writes:

> This enum is convenient in two ways (just how enums work):
>
> 1- It provides the number of migration parameters as its __MAX member.
>
> 2- It allows iterating over an integer range and get a migration
>    parameter name string corresponding to that position in the enum.
>
> The migration code doesn't have the need for (2) anymore.
>
> Balancing the benefit of (1) versus the disadvantage of requiring
> migration.json to be updated in two different places whenever a
> parameter is added, experience shows that the latter churn is enough
> to decide to remove the enum.
>
> Signed-off-by: Fabiano Rosas <farosas@suse.de>
> ---
>  migration/options.c |  6 +-----
>  qapi/migration.json | 36 ------------------------------------
>  2 files changed, 1 insertion(+), 41 deletions(-)
>
> diff --git a/migration/options.c b/migration/options.c
> index 5d17acdd881..f988b181f0e 100644
> --- a/migration/options.c
> +++ b/migration/options.c
> @@ -1127,7 +1127,6 @@ static MigrationParameters *migrate_params_from_dict(QDict *d, Error **errp)
>   */
>  static void migrate_mark_all_params_present(MigrationParameters *p)
>  {
> -    int len, n_str_args = 3; /* tls-creds, tls-hostname, tls-authz */
>      bool *has_fields[] = {
>          &p->has_throttle_trigger_threshold, &p->has_cpu_throttle_initial,
>          &p->has_cpu_throttle_increment, &p->has_cpu_throttle_tailslow,
> @@ -1144,10 +1143,7 @@ static void migrate_mark_all_params_present(MigrationParameters *p)
>          &p->has_x_rdma_chunk_size, &p->has_cpr_exec_command,
>      };
>  
> -    len = ARRAY_SIZE(has_fields);
> -    assert(len + n_str_args == MIGRATION_PARAMETER__MAX);
> -
> -    for (int i = 0; i < len; i++) {
> +    for (int i = 0; i < ARRAY_SIZE(has_fields); i++) {
>          *has_fields[i] = true;
>      }
>  }

We lose the guard against forgetting to update has_fields[].  It's the
last user of MigrationParameter.  I agree the guard doesn't justify
keeping MigrationParameter.

The guard is somewhat unclean anyway: it assumes the number of
MigrationParameter values matches the number of MigrationParameters
members.

Add a non-doc comment to MigrationParameters to remind of of updating
has_fields[] when it changes?

We don't have a convenient way to find the number of members.

> diff --git a/qapi/migration.json b/qapi/migration.json
> index b1eaf7b0545..78c6e933cf1 100644
> --- a/qapi/migration.json
> +++ b/qapi/migration.json
> @@ -796,42 +796,6 @@
>        'bitmaps': [ 'BitmapMigrationBitmapAlias' ]
>    } }
>  
> -##
> -# @MigrationParameter:
> -#
> -# Migration parameters enumeration.  The enumeration values mirror the
> -# members of @MigrationParameters.
> -#
> -# Features:
> -#
> -# @unstable: Members @x-checkpoint-delay, @x-rdma-chunk-size, and
> -#     @x-vcpu-dirty-limit-period are experimental.
> -#
> -# Since: 2.4
> -##
> -{ 'enum': 'MigrationParameter',
> -  'data': ['announce-initial', 'announce-max',
> -           'announce-rounds', 'announce-step',
> -           'throttle-trigger-threshold',
> -           'cpu-throttle-initial', 'cpu-throttle-increment',
> -           'cpu-throttle-tailslow',
> -           'tls-creds', 'tls-hostname', 'tls-authz', 'max-bandwidth',
> -           'avail-switchover-bandwidth', 'downtime-limit',
> -           { 'name': 'x-checkpoint-delay', 'features': [ 'unstable' ] },
> -           'multifd-channels',
> -           'xbzrle-cache-size', 'max-postcopy-bandwidth',
> -           'max-cpu-throttle', 'multifd-compression',
> -           'multifd-zlib-level', 'multifd-zstd-level',
> -           'multifd-qatzip-level',
> -           'block-bitmap-mapping',
> -           { 'name': 'x-vcpu-dirty-limit-period', 'features': ['unstable'] },
> -           'vcpu-dirty-limit',
> -           'mode',
> -           'zero-page-detection',
> -           'direct-io',
> -           { 'name': 'x-rdma-chunk-size', 'features': [ 'unstable' ] },
> -           'cpr-exec-command'] }
> -
>  ##
>  # @migrate-set-parameters:
>  #

Reviewed-by: Markus Armbruster <armbru@redhat.com>



^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 01/18] checkpatch: Fix checking of newlines in error messages
  2026-09-04  8:56         ` Markus Armbruster
@ 2026-09-04  9:15           ` Peter Maydell
  2026-09-04 12:09             ` Peter Xu
  0 siblings, 1 reply; 50+ messages in thread
From: Peter Maydell @ 2026-09-04  9:15 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: Peter Xu, Fabiano Rosas, qemu-devel, Chao Liu

On Fri, 4 Sept 2026 at 09:56, Markus Armbruster <armbru@redhat.com> wrote:
>
> Peter Xu <peterx@redhat.com> writes:
>
> > On Thu, Sep 03, 2026 at 02:46:37PM -0300, Fabiano Rosas wrote:
> >> Peter Xu <peterx@redhat.com> writes:
> >>
> >> > On Wed, Sep 02, 2026 at 07:15:29PM -0300, Fabiano Rosas wrote:
> >> >> Using newlines in the g_test_message is fine. It automatically adds
> >> >> the '#' required by the TAP protocol to the start of each line.
> >> >
> >> > IIUC we have such check not because TAP, but because all these functions
> >> > will append one newline at the end, hence it's not needed.  IOW, if it
> >> > applies to g_test_message(), I don't see why it doesn't apply to the rest.
> >> > But maybe there're other reasons?
> >> >
> >> > To make it simpler, maybe we just call a few times g_test_message()?
> >> >
> >>
> >> Not sure I understand your point, Peter. I want to be able to print nice
> >> messages in patch 9:
> >>
> >>  g_test_message("expected vs. found:\n\n%s\n---\n%s:%s", str, t2[match], t2[match + 1]);
> >>
> >>  # HMP output mismatch for entry at line 55:
> >>  # expected vs. found:
> >>  #
> >>  # max-bandwidth: 10356305952768 bytes/hour
> >>  # ---
> >>  # max-bandwidth: 10356305952768 bytes/second
> >>
> >> What would be the issue of having newlines here?
> >
> > No issue here that I can see.  My question was, why you moved
> > g_test_message() out only, but not all?
> >
> > My gut feeling is we check this because people forget that all these
> > functions includes a newline.
> >
> > So if your point stands here that "newlines can be in the middle", they
> > should apply to all, not one.
>
> I'm not sure I understand you correctly.  If you suggest to permit
> newlines in the middle of error_setg(), error_report() & friends, I
> disagree.  qapi/error.h:
>
>  * The resulting message should be a single phrase, with no newline or
>  * trailing punctuation.
>
> If you want to provide additional information, use error_append_hint() /
> error_printf().

Right. These functions have a genuinely different set of semantics
from g_test_message(), which is why Fabiano's patch only changes
how checkpatch handles that g_test_message(), not the various
QEMU error/warning functions.

-- PMM


^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 11/18] migration: Use keyval input visitor in HMP set command
  2026-09-02 22:15 ` [PATCH 11/18] migration: Use keyval input visitor in HMP set command Fabiano Rosas
  2026-09-03 19:44   ` Peter Xu
@ 2026-09-04  9:45   ` Markus Armbruster
  1 sibling, 0 replies; 50+ messages in thread
From: Markus Armbruster @ 2026-09-04  9:45 UTC (permalink / raw)
  To: Fabiano Rosas; +Cc: qemu-devel, Peter Xu, Laurent Vivier, Paolo Bonzini

Needs a rebase due to conflicts with commit f9e8f28f47 (migration:
Validate that all params are set for query).



^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 16/18] migration: Add capabilities into MigrationParameters
  2026-09-02 22:15 ` [PATCH 16/18] migration: Add capabilities into MigrationParameters Fabiano Rosas
@ 2026-09-04  9:53   ` Markus Armbruster
  2026-09-04 13:58     ` Fabiano Rosas
  0 siblings, 1 reply; 50+ messages in thread
From: Markus Armbruster @ 2026-09-04  9:53 UTC (permalink / raw)
  To: Fabiano Rosas; +Cc: qemu-devel, Peter Xu, Eric Blake

Fabiano Rosas <farosas@suse.de> writes:

> Add capabilities to MigrationParameters. This structure will hold all
> migration options. Capabilities will go away in the next patch.
>
> From this point on, both QMP and HMP versions of
> migrate-set-parameters and query-migrate-parameters gain the ability
> to work with capabilities.
>
> With MigrationParameters now having members for each capability, the
> migration capabilities commands (query-migrate-capabilities,
> migrate-set-capabilities) will soon be deprecated. Add a set of
> helpers to convert between the old MigrationCapability representation
> and the new representation as members of MigrationParameters.
>
> Acked-by: Peter Xu <peterx@redhat.com>
> Signed-off-by: Fabiano Rosas <farosas@suse.de>
> ---
>  migration/migration.c |   8 +++
>  migration/options.c   | 127 ++++++++++++++++++++++++++++++++++++++++++
>  migration/options.h   |   5 ++
>  qapi/migration.json   | 118 ++++++++++++++++++++++++++++++++++++++-
>  4 files changed, 255 insertions(+), 3 deletions(-)
>
> diff --git a/migration/migration.c b/migration/migration.c
> index dab282dd2f3..c8e7e86ea05 100644
> --- a/migration/migration.c
> +++ b/migration/migration.c
> @@ -4086,6 +4086,14 @@ static bool migration_object_check(MigrationState *ms, Error **errp)
>          return false;
>      }
>  
> +    /*
> +     * FIXME: Temporarily while -global capabilties are still using
> +     * s->capabilities. Will be gone by the end of the series.
> +     */
> +    for (int i = 0; i < MIGRATION_CAPABILITY__MAX; i++) {
> +        migrate_capability_set_compat(&ms->parameters, i, ms->capabilities[i]);
> +    }
> +
>      return migrate_caps_check(old_caps, ms->capabilities, errp);
>  }
>  
> diff --git a/migration/options.c b/migration/options.c
> index f988b181f0e..7c638e204a1 100644
> --- a/migration/options.c
> +++ b/migration/options.c
> @@ -778,6 +778,108 @@ bool migrate_caps_check(bool *old_caps, bool *new_caps, Error **errp)
>      return true;
>  }
>  
> +static bool *migrate_capability_get_addr(MigrationParameters *params, int i)
> +{
> +    bool *cap_addr = NULL;
> +
> +    switch (i) {
> +    case MIGRATION_CAPABILITY_XBZRLE:
> +        cap_addr = &params->xbzrle;
> +        break;
> +    case MIGRATION_CAPABILITY_RDMA_PIN_ALL:
> +        cap_addr = &params->rdma_pin_all;
> +        break;
> +    case MIGRATION_CAPABILITY_AUTO_CONVERGE:
> +        cap_addr = &params->auto_converge;
> +        break;
> +    case MIGRATION_CAPABILITY_EVENTS:
> +        cap_addr = &params->events;
> +        break;
> +    case MIGRATION_CAPABILITY_POSTCOPY_RAM:
> +        cap_addr = &params->postcopy_ram;
> +        break;
> +    case MIGRATION_CAPABILITY_X_COLO:
> +        cap_addr = &params->x_colo;
> +        break;
> +    case MIGRATION_CAPABILITY_RELEASE_RAM:
> +        cap_addr = &params->release_ram;
> +        break;
> +    case MIGRATION_CAPABILITY_RETURN_PATH:
> +        cap_addr = &params->return_path;
> +        break;
> +    case MIGRATION_CAPABILITY_PAUSE_BEFORE_SWITCHOVER:
> +        cap_addr = &params->pause_before_switchover;
> +        break;
> +    case MIGRATION_CAPABILITY_MULTIFD:
> +        cap_addr = &params->multifd;
> +        break;
> +    case MIGRATION_CAPABILITY_DIRTY_BITMAPS:
> +        cap_addr = &params->dirty_bitmaps;
> +        break;
> +    case MIGRATION_CAPABILITY_POSTCOPY_BLOCKTIME:
> +        cap_addr = &params->postcopy_blocktime;
> +        break;
> +    case MIGRATION_CAPABILITY_LATE_BLOCK_ACTIVATE:
> +        cap_addr = &params->late_block_activate;
> +        break;
> +    case MIGRATION_CAPABILITY_X_IGNORE_SHARED:
> +        cap_addr = &params->x_ignore_shared;
> +        break;
> +    case MIGRATION_CAPABILITY_VALIDATE_UUID:
> +        cap_addr = &params->validate_uuid;
> +        break;
> +    case MIGRATION_CAPABILITY_BACKGROUND_SNAPSHOT:
> +        cap_addr = &params->background_snapshot;
> +        break;
> +    case MIGRATION_CAPABILITY_ZERO_COPY_SEND:
> +        cap_addr = &params->zero_copy_send;
> +        break;
> +    case MIGRATION_CAPABILITY_POSTCOPY_PREEMPT:
> +        cap_addr = &params->postcopy_preempt;
> +        break;
> +    case MIGRATION_CAPABILITY_SWITCHOVER_ACK:
> +        cap_addr = &params->switchover_ack;
> +        break;
> +    case MIGRATION_CAPABILITY_DIRTY_LIMIT:
> +        cap_addr = &params->dirty_limit;
> +        break;
> +    case MIGRATION_CAPABILITY_MAPPED_RAM:
> +        cap_addr = &params->mapped_ram;
> +        break;
> +    default:
> +        g_assert_not_reached();
> +    }

I'd use an array instead of a switch.  Matter of taste.

> +
> +    return cap_addr;
> +}
> +
> +/* Compatibility for code that reads capabilities in a loop */
> +bool migrate_capability_get_compat(MigrationParameters *params, int i)
> +{
> +    return *(migrate_capability_get_addr(params, i));
> +}
> +
> +/* Compatibility for code that writes capabilities in a loop */
> +void migrate_capability_set_compat(MigrationParameters *params, int i, bool val)
> +{
> +    *(migrate_capability_get_addr(params, i)) = val;
> +}
> +
> +/*
> + * Set capabilities for compatibility with the old
> + * migrate-set-capabilities command.
> + */
> +void migrate_capabilities_set_compat(MigrationParameters *params,
> +                                     MigrationCapabilityStatusList *caps)
> +{
> +    MigrationCapabilityStatusList *cap;
> +
> +    for (cap = caps; cap; cap = cap->next) {
> +        migrate_capability_set_compat(params, cap->value->capability,
> +                                      cap->value->state);
> +    }
> +}
> +
>  MigrationCapabilityStatusList *qmp_query_migrate_capabilities(Error **errp)
>  {
>      MigrationCapabilityStatusList *head = NULL, **tail = &head;
> @@ -819,6 +921,8 @@ void qmp_migrate_set_capabilities(MigrationCapabilityStatusList *params,
>      for (cap = params; cap; cap = cap->next) {
>          s->capabilities[cap->value->capability] = cap->value->state;
>      }
> +
> +    migrate_capabilities_set_compat(&s->parameters, params);
>  }
>  
>  /* parameters */
> @@ -1141,6 +1245,15 @@ static void migrate_mark_all_params_present(MigrationParameters *p)
>          &p->has_x_vcpu_dirty_limit_period, &p->has_vcpu_dirty_limit,
>          &p->has_mode, &p->has_zero_page_detection, &p->has_direct_io,
>          &p->has_x_rdma_chunk_size, &p->has_cpr_exec_command,
> +        &p->has_xbzrle, &p->has_rdma_pin_all,
> +        &p->has_auto_converge, &p->has_events,
> +        &p->has_postcopy_ram, &p->has_x_colo, &p->has_release_ram,
> +        &p->has_return_path, &p->has_pause_before_switchover, &p->has_multifd,
> +        &p->has_dirty_bitmaps, &p->has_postcopy_blocktime,
> +        &p->has_late_block_activate, &p->has_x_ignore_shared,
> +        &p->has_validate_uuid, &p->has_background_snapshot,
> +        &p->has_zero_copy_send, &p->has_postcopy_preempt,
> +        &p->has_switchover_ack, &p->has_dirty_limit, &p->has_mapped_ram,
>      };
>  
>      for (int i = 0; i < ARRAY_SIZE(has_fields); i++) {
> @@ -1465,6 +1578,20 @@ void qmp_migrate_set_parameters(MigrationParameters *input, Error **errp)
>      tls_opt_to_str(input->tls_hostname);
>      tls_opt_to_str(input->tls_authz);
>  
> +    /*
> +     * FIXME: Temporarily while migrate_caps_check is not
> +     * converted to look at s->parameters. Will be gone the end of
> +     * the series.
> +     */
> +    bool new_caps[MIGRATION_CAPABILITY__MAX] = { 0 };
> +    for (int i = 0; i < MIGRATION_CAPABILITY__MAX; i++) {
> +        new_caps[i] = migrate_capability_get_compat(cur, i);
> +    }
> +    if (!migrate_caps_check(migrate_get_current()->capabilities, new_caps,
> +                            errp)) {
> +        return;
> +    }
> +
>      /* merge input on top of current */
>      if (!migrate_params_merge(cur, input, &new, errp)) {
>          return;
> diff --git a/migration/options.h b/migration/options.h
> index c7da2d0b5b0..eedd1aa1f93 100644
> --- a/migration/options.h
> +++ b/migration/options.h
> @@ -94,4 +94,9 @@ uint64_t migrate_rdma_chunk_size(void);
>  bool migrate_params_check(MigrationParameters *params, Error **errp);
>  void migrate_params_init(MigrationParameters *params);
>  bool migrate_params_free(MigrationParameters *params, Error **errp);
> +bool migrate_capability_get_compat(MigrationParameters *params, int i);
> +void migrate_capability_set_compat(MigrationParameters *params, int i,
> +                                   bool val);
> +void migrate_capabilities_set_compat(MigrationParameters *params,
> +                                     MigrationCapabilityStatusList *caps);
>  #endif
> diff --git a/qapi/migration.json b/qapi/migration.json
> index 78c6e933cf1..7952ef44db9 100644
> --- a/qapi/migration.json
> +++ b/qapi/migration.json
> @@ -976,10 +976,101 @@
>  #     Must be set to the same value on both source and destination
>  #     before migration starts.  (Since 11.1)
>  #
> +# @xbzrle: Migration supports xbzrle (Xor Based Zero Run Length
> +#     Encoding).  This feature allows us to minimize migration traffic
> +#     for certain work loads, by sending compressed difference of the
> +#     pages
> +#
> +# @rdma-pin-all: Controls whether or not the entire VM memory
> +#     footprint is mlock()'d on demand or all at once.  Refer to
> +#     docs/rdma.txt for usage.  Disabled by default.  (since 2.0)
> +#
> +# @events: Generate events for each migration state change.
> +#     (since 2.4)
> +#
> +# @auto-converge: If enabled, QEMU will automatically throttle down
> +#     the guest to speed up convergence of RAM migration.  (since 1.6)
> +#
> +# @postcopy-ram: Start executing on the migration target before all of
> +#     RAM has been migrated, pulling the remaining pages along as
> +#     needed.  The capacity must have the same setting on both source
> +#     and target or migration will not even start.  **Note:** If the
> +#     migration fails during postcopy the VM will fail.  (since 2.6)
> +#
> +# @x-colo: If enabled, migration will never end, and the state of the
> +#     VM on the primary side will be migrated continuously to the VM
> +#     on secondary side, this process is called COarse-Grain LOck
> +#     Stepping (COLO) for Non-stop Service.  (since 2.8)
> +#
> +# @release-ram: If enabled, QEMU will free the migrated ram pages on
> +#     the source during postcopy-ram migration.  (since 2.9)
> +#
> +# @return-path: If enabled, migration will use the return path even
> +#     for precopy.  (since 2.10)
> +#
> +# @pause-before-switchover: Pause outgoing migration before
> +#     serialising device state and before disabling block IO.
> +#     (since 2.11)
> +#
> +# @multifd: Use more than one fd for migration.  (since 4.0)
> +#
> +# @dirty-bitmaps: If enabled, QEMU will migrate named dirty bitmaps.
> +#     (since 2.12)
> +#
> +# @postcopy-blocktime: Calculate downtime for postcopy live migration.
> +#     (since 3.0)
> +#
> +# @late-block-activate: If enabled, the destination will not activate
> +#     block devices (and thus take locks) immediately at the end of
> +#     migration.  (since 3.0)
> +#
> +# @x-ignore-shared: If enabled, QEMU will not migrate shared memory
> +#     that is accessible on the destination machine.  (since 4.0)
> +#
> +# @validate-uuid: Send the UUID of the source to allow the destination
> +#     to ensure it is the same.  (since 4.2)
> +#
> +# @background-snapshot: If enabled, the migration stream will be a
> +#     snapshot of the VM exactly at the point when the migration
> +#     procedure starts.  The VM RAM is saved with running VM.
> +#     (since 6.0)
> +#
> +# @zero-copy-send: Controls behavior on sending memory pages on
> +#     migration.  When true, enables a zero-copy mechanism for sending
> +#     memory pages, if host supports it.  Requires that QEMU be
> +#     permitted to use locked memory for guest RAM pages.  (since 7.1)
> +#
> +# @postcopy-preempt: If enabled, the migration process will allow
> +#     postcopy requests to preempt precopy stream, so postcopy
> +#     requests will be handled faster.  This is a performance feature
> +#     and should not affect the correctness of postcopy migration.
> +#     (since 7.1)
> +#
> +# @switchover-ack: If enabled, migration will not stop the source VM
> +#     and complete the migration until an ACK is received from the
> +#     destination that it's OK to do so.  Exactly when this ACK is
> +#     sent depends on the migrated devices that use this feature.  For
> +#     example, a device can use it to make sure some of its data is
> +#     sent and loaded in the destination before doing switchover.
> +#     This can reduce downtime if devices that support this capability
> +#     are present.  'return-path' capability must be enabled to use
> +#     it.  (since 8.1)
> +#
> +# @dirty-limit: If enabled, migration will throttle vCPUs as needed to
> +#     keep their dirty page rate within @vcpu-dirty-limit.  This can
> +#     improve responsiveness of large guests during live migration,
> +#     and can result in more stable read performance.  Requires KVM
> +#     with accelerator property "dirty-ring-size" set.  (Since 8.1)
> +#
> +# @mapped-ram: Migrate using fixed offsets in the migration file for
> +#     each RAM page.  Requires a migration URI that supports seeking,
> +#     such as a file.  (since 9.0)
> +#
>  # Features:
>  #
> -# @unstable: Members @x-checkpoint-delay, @x-rdma-chunk-size, and
> -#     @x-vcpu-dirty-limit-period are experimental.
> +# @unstable: Members @x-checkpoint-delay, @x-vcpu-dirty-limit-period,
> +#     @x-colo, @x-ignore-shared and @x-rdma-chunk-size are
> +#     experimental.

This isn't a clean copy from MigrationCapability.  Why?

>  #
>  # Since: 2.4
>  ##
> @@ -1017,7 +1108,28 @@
>              '*direct-io': 'bool',
>              '*x-rdma-chunk-size': { 'type': 'uint64',
>                                      'features': [ 'unstable' ] },
> -            '*cpr-exec-command': [ 'str' ]} }
> +            '*cpr-exec-command': [ 'str' ],
> +            '*xbzrle': 'bool',
> +            '*rdma-pin-all': 'bool',
> +            '*auto-converge': 'bool',
> +            '*events': 'bool',
> +            '*postcopy-ram': 'bool',
> +            '*x-colo': { 'type': 'bool', 'features': [ 'unstable' ] },
> +            '*release-ram': 'bool',
> +            '*return-path': 'bool',
> +            '*pause-before-switchover': 'bool',
> +            '*multifd': 'bool',
> +            '*dirty-bitmaps': 'bool',
> +            '*postcopy-blocktime': 'bool',
> +            '*late-block-activate': 'bool',
> +            '*x-ignore-shared': { 'type': 'bool', 'features': [ 'unstable' ] },
> +            '*validate-uuid': 'bool',
> +            '*background-snapshot': 'bool',
> +            '*zero-copy-send': 'bool',
> +            '*postcopy-preempt': 'bool',
> +            '*switchover-ack': 'bool',
> +            '*dirty-limit': 'bool',
> +            '*mapped-ram': 'bool' } }
>  
>  ##
>  # @query-migrate-parameters:



^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 13/18] migration: Use output visitor in info command
  2026-09-02 22:15 ` [PATCH 13/18] migration: Use output visitor in info command Fabiano Rosas
@ 2026-09-04 12:04   ` Peter Xu
  2026-09-04 13:41     ` Fabiano Rosas
  0 siblings, 1 reply; 50+ messages in thread
From: Peter Xu @ 2026-09-04 12:04 UTC (permalink / raw)
  To: Fabiano Rosas; +Cc: qemu-devel

On Wed, Sep 02, 2026 at 07:15:41PM -0300, Fabiano Rosas wrote:
> The hmp_info_migrate_parameters function currently open-codes the
> mon_printf calls for each migration parameter. As with the set command
> in the last patch, this should not be necessary as the QAPI
> infrastructure already has generated code that takes type and struct
> member names into account, including converting _ from C into the '-'
> character as part of parameter names strings.
> 
> The current code is also quite painful to rebase if a series has been
> carried for a long time while parameters have been added in master.
> 
> Replace all of this with a conversion from MigrationParameters to
> QDict using an output visitor and a loop over the QDict that prints
> per-QAPI-type formatted strings.
> 
> Modelled after block/qapi.c:dump_qobject, but with some changes to
> keep the migration command output formatting.
> 
> Note that this was not a for-free improvement, the HMP command format
> output was changed incompatibly in a previous patch. It doesn't output
> units anymore.

IIUC both qlist and qdict are only used in BitmapMigrationNodeAlias only.
It would be nice to attach an example of HMP dump before/after the change
of these two patches, either in previous patch or here (assuming the layout
changed once).  In case HMP has some suggestion in general, I suggest copy
Dave when repost in case he has something to say.

> 
> Signed-off-by: Fabiano Rosas <farosas@suse.de>
> ---
>  migration/migration-hmp-cmds.c | 243 ++++++++++++++-------------------
>  1 file changed, 100 insertions(+), 143 deletions(-)
> 
> diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c
> index 089c6d4ff46..dff69650a0c 100644
> --- a/migration/migration-hmp-cmds.c
> +++ b/migration/migration-hmp-cmds.c
> @@ -25,7 +25,12 @@
>  #include "qapi/qapi-commands-migration.h"
>  #include "qapi/qapi-visit-migration.h"
>  #include "qapi/qobject-input-visitor.h"
> +#include "qapi/qobject-output-visitor.h"
> +#include "qobject/qbool.h"
>  #include "qobject/qdict.h"
> +#include "qobject/qjson.h"
> +#include "qobject/qlist.h"
> +#include "qobject/qnum.h"
>  #include "qobject/qstring.h"
>  #include "qapi/string-input-visitor.h"
>  #include "qapi/string-output-visitor.h"
> @@ -316,170 +321,122 @@ void hmp_info_migrate_capabilities(Monitor *mon, const QDict *qdict)
>      qapi_free_MigrationCapabilityStatusList(caps);
>  }
>  
> -static void monitor_print_cpr_exec_command(Monitor *mon, strList *args)
> +static QDict *migrate_params_to_dict(MigrationParameters *p, Error **errp)
>  {
> -    monitor_printf(mon, "%s:",
> -        MigrationParameter_str(MIGRATION_PARAMETER_CPR_EXEC_COMMAND));
> +    QObject *obj = NULL;
> +    Visitor *v = qobject_output_visitor_new(&obj);
>  
> -    while (args) {
> -        monitor_printf(mon, " %s", args->value);
> -        args = args->next;
> +    if (visit_type_MigrationParameters(v, NULL, &p, errp)) {
> +        visit_complete(v, &obj);
>      }
> -    monitor_printf(mon, "\n");
> +    visit_free(v);
> +    return qobject_to(QDict, obj);
>  }

This exact function also exists in options.c (with the same name and
impl).  Can reuse.

>  
> -void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict)
> +static void hmp_migrate_print_qobject(Monitor *mon, const char *label,
> +                                      QObject *obj)
>  {
> -    MigrationParameters *params;
> -    MigrationState *s = migrate_get_current();
> +    const char *sep;
>  
> -    params = qmp_query_migrate_parameters(NULL);
> +    if (!obj) {
> +        return;
> +    }
>  
> -    if (params) {
> -        monitor_printf(mon, "%s: %" PRIu64 "\n",
> -            MigrationParameter_str(MIGRATION_PARAMETER_ANNOUNCE_INITIAL),
> -            params->announce_initial);
> -        monitor_printf(mon, "%s: %" PRIu64 "\n",
> -            MigrationParameter_str(MIGRATION_PARAMETER_ANNOUNCE_MAX),
> -            params->announce_max);
> -        monitor_printf(mon, "%s: %" PRIu64 "\n",
> -            MigrationParameter_str(MIGRATION_PARAMETER_ANNOUNCE_ROUNDS),
> -            params->announce_rounds);
> -        monitor_printf(mon, "%s: %" PRIu64 "\n",
> -            MigrationParameter_str(MIGRATION_PARAMETER_ANNOUNCE_STEP),
> -            params->announce_step);
> -        assert(params->has_throttle_trigger_threshold);
> -        monitor_printf(mon, "%s: %u\n",
> -            MigrationParameter_str(MIGRATION_PARAMETER_THROTTLE_TRIGGER_THRESHOLD),
> -            params->throttle_trigger_threshold);
> -        assert(params->has_cpu_throttle_initial);
> -        monitor_printf(mon, "%s: %u\n",
> -            MigrationParameter_str(MIGRATION_PARAMETER_CPU_THROTTLE_INITIAL),
> -            params->cpu_throttle_initial);
> -        assert(params->has_cpu_throttle_increment);
> -        monitor_printf(mon, "%s: %u\n",
> -            MigrationParameter_str(MIGRATION_PARAMETER_CPU_THROTTLE_INCREMENT),
> -            params->cpu_throttle_increment);
> -        assert(params->has_cpu_throttle_tailslow);
> -        monitor_printf(mon, "%s: %s\n",
> -            MigrationParameter_str(MIGRATION_PARAMETER_CPU_THROTTLE_TAILSLOW),
> -            params->cpu_throttle_tailslow ? "on" : "off");
> -        assert(params->has_max_cpu_throttle);
> -        monitor_printf(mon, "%s: %u\n",
> -            MigrationParameter_str(MIGRATION_PARAMETER_MAX_CPU_THROTTLE),
> -            params->max_cpu_throttle);
> -        assert(params->tls_creds);
> -        monitor_printf(mon, "%s: %s\n",
> -            MigrationParameter_str(MIGRATION_PARAMETER_TLS_CREDS),
> -                       params->tls_creds->u.s);
> -        assert(params->tls_hostname);
> -        monitor_printf(mon, "%s: %s\n",
> -            MigrationParameter_str(MIGRATION_PARAMETER_TLS_HOSTNAME),
> -                       params->tls_hostname->u.s);
> -        assert(params->tls_authz);
> -        monitor_printf(mon, "%s: %s\n",
> -            MigrationParameter_str(MIGRATION_PARAMETER_TLS_AUTHZ),
> -                       params->tls_authz->u.s);
> -        assert(params->has_max_bandwidth);
> -        monitor_printf(mon, "%s: %" PRIu64 "\n",
> -            MigrationParameter_str(MIGRATION_PARAMETER_MAX_BANDWIDTH),
> -            params->max_bandwidth);
> -        assert(params->has_avail_switchover_bandwidth);
> -        monitor_printf(mon, "%s: %" PRIu64 "\n",
> -            MigrationParameter_str(MIGRATION_PARAMETER_AVAIL_SWITCHOVER_BANDWIDTH),
> -            params->avail_switchover_bandwidth);
> -        assert(params->has_max_postcopy_bandwidth);
> -        monitor_printf(mon, "%s: %" PRIu64 "\n",
> -            MigrationParameter_str(MIGRATION_PARAMETER_MAX_POSTCOPY_BANDWIDTH),
> -            params->max_postcopy_bandwidth);
> -        assert(params->has_downtime_limit);
> -        monitor_printf(mon, "%s: %" PRIu64 "\n",
> -            MigrationParameter_str(MIGRATION_PARAMETER_DOWNTIME_LIMIT),
> -            params->downtime_limit);
> -        assert(params->has_x_checkpoint_delay);
> -        monitor_printf(mon, "%s: %u\n",
> -            MigrationParameter_str(MIGRATION_PARAMETER_X_CHECKPOINT_DELAY),
> -            params->x_checkpoint_delay);
> -        monitor_printf(mon, "%s: %u\n",
> -            MigrationParameter_str(MIGRATION_PARAMETER_MULTIFD_CHANNELS),
> -            params->multifd_channels);
> -        monitor_printf(mon, "%s: %s\n",
> -            MigrationParameter_str(MIGRATION_PARAMETER_MULTIFD_COMPRESSION),
> -            MultiFDCompression_str(params->multifd_compression));
> -        assert(params->has_zero_page_detection);
> -        monitor_printf(mon, "%s: %s\n",
> -            MigrationParameter_str(MIGRATION_PARAMETER_ZERO_PAGE_DETECTION),
> -            qapi_enum_lookup(&ZeroPageDetection_lookup,
> -                params->zero_page_detection));
> -        monitor_printf(mon, "%s: %" PRIu64 "\n",
> -            MigrationParameter_str(MIGRATION_PARAMETER_XBZRLE_CACHE_SIZE),
> -            params->xbzrle_cache_size);
> +    /*
> +     * Put a space after labels
> +     * foo: bar
> +     *     ^
> +     */
> +    if (label && label[0] && label[strlen(label) - 1] == ':') {
> +        sep = " ";
> +    } else {
> +        sep = "";
> +    }
>  
> -        if (s->has_block_bitmap_mapping) {
> -            BitmapMigrationNodeAliasList *nal;
> -            BitmapMigrationNodeAlias *na;
> -            BitmapMigrationBitmapAliasList *bal;
> -            BitmapMigrationBitmapAlias *ba;
> -            BitmapMigrationBitmapAliasTransform *bat;
> +    switch (qobject_type(obj)) {
> +    case QTYPE_NONE:
> +        g_assert_not_reached();
> +    case QTYPE_QNULL:
> +        break;

Shall we assert too if we don't expect it?

> +    case QTYPE_QNUM: {
> +        int64_t i64;
>  
> -            monitor_printf(mon, "%s:",
> -                           MigrationParameter_str(
> -                               MIGRATION_PARAMETER_BLOCK_BITMAP_MAPPING));
> +        if (qnum_get_try_int(qobject_to(QNum, obj), &i64)) {
> +            monitor_printf(mon, "%s%s%" PRId64, label, sep, i64);

Should we use qnum_to_string() like what dump_qobject() does?

Logically any size/u64 parameter (which may not be a normal use case..) can
be set larger than INT64_MAX, so qnum_get_try_int() may return false? Here
skipping to print anything is benign if it only happens with a malicious
monitor client, but I think we should try to capture all cases.

> +        }
> +        break;
> +    }
> +    case QTYPE_QSTRING: {
> +        QString *str = qobject_to(QString, obj);
>  
> -            for (nal = params->block_bitmap_mapping; nal; nal = nal->next)
> -            {
> -                na = nal->value;
> -                monitor_printf(mon, " bitmaps:");
> -                for (bal = na->bitmaps; bal; bal = bal->next) {
> -                    ba = bal->value;
> -                    bat = ba->transform;
> +        if (str) {

Any chance str==NULL here?

> +            monitor_printf(mon, "%s%s%s", label, sep, qstring_get_str(str));
> +        }
> +        break;
> +    }
> +    case QTYPE_QDICT: {
> +        QDict *d = qobject_to(QDict, obj);
> +        const QDictEntry *e;
> +        int i = 0;
>  
> -                    monitor_printf(mon, " name: %s", ba->name);
> -                    if (bat && bat->has_persistent) {
> -                        if (bat->persistent) {
> -                            monitor_printf(mon, " persistent: on");
> -                        } else {
> -                            monitor_printf(mon, " persistent: off");
> -                        }
> -                    }
> -                    monitor_printf(mon, " alias: %s", ba->alias);
> +        if (d) {
> +            for (e = qdict_first(d); e; e = qdict_next(d, e), i++) {
> +                g_autofree char *l = g_strdup_printf("%s:", qdict_entry_key(e));
> +                if (i) {
> +                    monitor_printf(mon, " ");
>                  }
> -                monitor_printf(mon, " node-name: %s alias: %s",
> -                               na->node_name, na->alias);
> +                hmp_migrate_print_qobject(mon, l, qdict_entry_value(e));
>              }
> -
> -            monitor_printf(mon, "\n");
>          }
> +        break;
> +    }
> +    case QTYPE_QLIST: {
> +        QList *l = qobject_to(QList, obj);
> +        const QListEntry *e;
>  
> -        monitor_printf(mon, "%s: %" PRIu64 "\n",
> -        MigrationParameter_str(MIGRATION_PARAMETER_X_VCPU_DIRTY_LIMIT_PERIOD),
> -        params->x_vcpu_dirty_limit_period);
> +        if (l) {
> +            monitor_printf(mon, "%s", label);
>  
> -        monitor_printf(mon, "%s: %" PRIu64 "\n",
> -            MigrationParameter_str(MIGRATION_PARAMETER_VCPU_DIRTY_LIMIT),
> -            params->vcpu_dirty_limit);
> +            for (e = qlist_first(l); e; e = qlist_next(e)) {
> +                /*
> +                 * In the first iteration, this is the space after the
> +                 * colon, otherwise it's the space between list
> +                 * elements.
> +                 */
> +                monitor_printf(mon, " ");
> +                hmp_migrate_print_qobject(mon, "", e->value);
> +            }
> +        }
> +        break;
> +    }
> +    case QTYPE_QBOOL: {
> +        QBool *b = qobject_to(QBool, obj);
> +        if (b) {

Similarly, I'd drop "if" if it will always happen, making qbool_get_bool()
assert itself by deref.

> +            monitor_printf(mon, "%s%s%s", label, sep,
> +                           qbool_get_bool(b) ? "on" : "off");
> +        }
> +        break;
> +    }
> +    default:
> +        g_assert_not_reached();
> +        break;
> +    }
> +}
>  
> -        assert(params->has_mode);
> -        monitor_printf(mon, "%s: %s\n",
> -            MigrationParameter_str(MIGRATION_PARAMETER_MODE),
> -            qapi_enum_lookup(&MigMode_lookup, params->mode));
> +void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict)
> +{
> +    MigrationParameters *params = qmp_query_migrate_parameters(NULL);
> +    g_autoptr(QDict) d;
> +    const QDictEntry *e;
>  
> -        if (params->has_direct_io) {
> -            monitor_printf(mon, "%s: %s\n",
> -                           MigrationParameter_str(
> -                               MIGRATION_PARAMETER_DIRECT_IO),
> -                           params->direct_io ? "on" : "off");
> -        }
> +    assert(params);
>  
> -        if (params->has_x_rdma_chunk_size) {
> -            monitor_printf(mon, "%s: %" PRIu64 "\n",
> -                           MigrationParameter_str(
> -                               MIGRATION_PARAMETER_X_RDMA_CHUNK_SIZE),
> -                           params->x_rdma_chunk_size);
> -        }
> +    d = migrate_params_to_dict(params, NULL);
> +    for (e = qdict_first(d); e; e = qdict_next(d, e)) {
> +        g_autofree char *label = g_strdup_printf("%s:", qdict_entry_key(e));
>  
> -        assert(params->has_cpr_exec_command);
> -        monitor_print_cpr_exec_command(mon, params->cpr_exec_command);
> +        hmp_migrate_print_qobject(mon, label, qdict_entry_value(e));
> +        monitor_printf(mon, "\n");
>      }
>  
>      qapi_free_MigrationParameters(params);
> -- 
> 2.53.0
> 

-- 
Peter Xu



^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 01/18] checkpatch: Fix checking of newlines in error messages
  2026-09-04  9:15           ` Peter Maydell
@ 2026-09-04 12:09             ` Peter Xu
  0 siblings, 0 replies; 50+ messages in thread
From: Peter Xu @ 2026-09-04 12:09 UTC (permalink / raw)
  To: Peter Maydell; +Cc: Markus Armbruster, Fabiano Rosas, qemu-devel, Chao Liu

On Fri, Sep 04, 2026 at 10:15:21AM +0100, Peter Maydell wrote:
> On Fri, 4 Sept 2026 at 09:56, Markus Armbruster <armbru@redhat.com> wrote:
> >
> > Peter Xu <peterx@redhat.com> writes:
> >
> > > On Thu, Sep 03, 2026 at 02:46:37PM -0300, Fabiano Rosas wrote:
> > >> Peter Xu <peterx@redhat.com> writes:
> > >>
> > >> > On Wed, Sep 02, 2026 at 07:15:29PM -0300, Fabiano Rosas wrote:
> > >> >> Using newlines in the g_test_message is fine. It automatically adds
> > >> >> the '#' required by the TAP protocol to the start of each line.
> > >> >
> > >> > IIUC we have such check not because TAP, but because all these functions
> > >> > will append one newline at the end, hence it's not needed.  IOW, if it
> > >> > applies to g_test_message(), I don't see why it doesn't apply to the rest.
> > >> > But maybe there're other reasons?
> > >> >
> > >> > To make it simpler, maybe we just call a few times g_test_message()?
> > >> >
> > >>
> > >> Not sure I understand your point, Peter. I want to be able to print nice
> > >> messages in patch 9:
> > >>
> > >>  g_test_message("expected vs. found:\n\n%s\n---\n%s:%s", str, t2[match], t2[match + 1]);
> > >>
> > >>  # HMP output mismatch for entry at line 55:
> > >>  # expected vs. found:
> > >>  #
> > >>  # max-bandwidth: 10356305952768 bytes/hour
> > >>  # ---
> > >>  # max-bandwidth: 10356305952768 bytes/second
> > >>
> > >> What would be the issue of having newlines here?
> > >
> > > No issue here that I can see.  My question was, why you moved
> > > g_test_message() out only, but not all?
> > >
> > > My gut feeling is we check this because people forget that all these
> > > functions includes a newline.
> > >
> > > So if your point stands here that "newlines can be in the middle", they
> > > should apply to all, not one.
> >
> > I'm not sure I understand you correctly.  If you suggest to permit
> > newlines in the middle of error_setg(), error_report() & friends, I
> > disagree.  qapi/error.h:
> >
> >  * The resulting message should be a single phrase, with no newline or
> >  * trailing punctuation.
> >
> > If you want to provide additional information, use error_append_hint() /
> > error_printf().
> 
> Right. These functions have a genuinely different set of semantics
> from g_test_message(), which is why Fabiano's patch only changes
> how checkpatch handles that g_test_message(), not the various
> QEMU error/warning functions.

But in this case g_test_message() implies one message to be emitted, shall
we follow the same rule that we should stick with g_test_message() without
newlines, and use it multiple times?  I don't think I have a solid clue of
such, that is why I actually suggested dropping this patch and just invoke
the g_test_message() a few times.

I'm personally fine either way, if we go with this patch, I suggest:

  - When repost/queue, add some real reasoning on why g_test_message() is
    different from the others.  I don't think it's relevant to TAP format..

  - Please if any of you agree with Fabiano's change, provide one ACK..

Thanks for chimming in.

-- 
Peter Xu



^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 17/18] migration: Remove s->capabilities
  2026-09-02 22:15 ` [PATCH 17/18] migration: Remove s->capabilities Fabiano Rosas
@ 2026-09-04 12:15   ` Peter Xu
  0 siblings, 0 replies; 50+ messages in thread
From: Peter Xu @ 2026-09-04 12:15 UTC (permalink / raw)
  To: Fabiano Rosas; +Cc: qemu-devel, Laurent Vivier, Paolo Bonzini

On Wed, Sep 02, 2026 at 07:15:45PM -0300, Fabiano Rosas wrote:
> @@ -1555,6 +1509,9 @@ bool migrate_params_check(MigrationParameters *params, Error **errp)
>           !is_power_of_2(params->x_rdma_chunk_size))) {
>          error_setg(errp, "Option x_rdma_chunk_size expects "
>                     "a power of 2 in the range 1MiB to 1024MiB");

Miss a return here?  Can keep the R-b with this fixed.

> +    }
> +
> +    if (!migrate_caps_check(params, errp)) {
>          return false;
>      }

-- 
Peter Xu



^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 15/18] qapi/migration: Remove MigrationParameter
  2026-09-02 22:15 ` [PATCH 15/18] qapi/migration: Remove MigrationParameter Fabiano Rosas
  2026-09-04  9:07   ` Markus Armbruster
@ 2026-09-04 12:24   ` Peter Xu
  2026-09-04 13:49     ` Fabiano Rosas
  1 sibling, 1 reply; 50+ messages in thread
From: Peter Xu @ 2026-09-04 12:24 UTC (permalink / raw)
  To: Fabiano Rosas; +Cc: qemu-devel, Eric Blake, Markus Armbruster

On Wed, Sep 02, 2026 at 07:15:43PM -0300, Fabiano Rosas wrote:
> This enum is convenient in two ways (just how enums work):
> 
> 1- It provides the number of migration parameters as its __MAX member.
> 
> 2- It allows iterating over an integer range and get a migration
>    parameter name string corresponding to that position in the enum.
> 
> The migration code doesn't have the need for (2) anymore.
> 
> Balancing the benefit of (1) versus the disadvantage of requiring
> migration.json to be updated in two different places whenever a
> parameter is added, experience shows that the latter churn is enough
> to decide to remove the enum.

I may have a slightly different feeling, but that takes a few things into
account, (1) we don't have issue duplicating docs for the two parameter
names anymore, (2) the dup is only about adding the same string once more
in qapi/, (3) we now have this migrate_mark_all_params_present() function
that must set all has_* fields, which the __MAX did help to guard a bit..

I think it's also fine to remove it completely, we just need to be more
careful instead on migrate_mark_all_params_present() later.  Which one is
easier to be forgotten?  I don't know..

Acked-by: Peter Xu <peterx@redhat.com>

> 
> Signed-off-by: Fabiano Rosas <farosas@suse.de>
> ---
>  migration/options.c |  6 +-----
>  qapi/migration.json | 36 ------------------------------------
>  2 files changed, 1 insertion(+), 41 deletions(-)
> 
> diff --git a/migration/options.c b/migration/options.c
> index 5d17acdd881..f988b181f0e 100644
> --- a/migration/options.c
> +++ b/migration/options.c
> @@ -1127,7 +1127,6 @@ static MigrationParameters *migrate_params_from_dict(QDict *d, Error **errp)
>   */
>  static void migrate_mark_all_params_present(MigrationParameters *p)
>  {
> -    int len, n_str_args = 3; /* tls-creds, tls-hostname, tls-authz */
>      bool *has_fields[] = {
>          &p->has_throttle_trigger_threshold, &p->has_cpu_throttle_initial,
>          &p->has_cpu_throttle_increment, &p->has_cpu_throttle_tailslow,
> @@ -1144,10 +1143,7 @@ static void migrate_mark_all_params_present(MigrationParameters *p)
>          &p->has_x_rdma_chunk_size, &p->has_cpr_exec_command,
>      };
>  
> -    len = ARRAY_SIZE(has_fields);
> -    assert(len + n_str_args == MIGRATION_PARAMETER__MAX);
> -
> -    for (int i = 0; i < len; i++) {
> +    for (int i = 0; i < ARRAY_SIZE(has_fields); i++) {
>          *has_fields[i] = true;
>      }
>  }
> diff --git a/qapi/migration.json b/qapi/migration.json
> index b1eaf7b0545..78c6e933cf1 100644
> --- a/qapi/migration.json
> +++ b/qapi/migration.json
> @@ -796,42 +796,6 @@
>        'bitmaps': [ 'BitmapMigrationBitmapAlias' ]
>    } }
>  
> -##
> -# @MigrationParameter:
> -#
> -# Migration parameters enumeration.  The enumeration values mirror the
> -# members of @MigrationParameters.
> -#
> -# Features:
> -#
> -# @unstable: Members @x-checkpoint-delay, @x-rdma-chunk-size, and
> -#     @x-vcpu-dirty-limit-period are experimental.
> -#
> -# Since: 2.4
> -##
> -{ 'enum': 'MigrationParameter',
> -  'data': ['announce-initial', 'announce-max',
> -           'announce-rounds', 'announce-step',
> -           'throttle-trigger-threshold',
> -           'cpu-throttle-initial', 'cpu-throttle-increment',
> -           'cpu-throttle-tailslow',
> -           'tls-creds', 'tls-hostname', 'tls-authz', 'max-bandwidth',
> -           'avail-switchover-bandwidth', 'downtime-limit',
> -           { 'name': 'x-checkpoint-delay', 'features': [ 'unstable' ] },
> -           'multifd-channels',
> -           'xbzrle-cache-size', 'max-postcopy-bandwidth',
> -           'max-cpu-throttle', 'multifd-compression',
> -           'multifd-zlib-level', 'multifd-zstd-level',
> -           'multifd-qatzip-level',
> -           'block-bitmap-mapping',
> -           { 'name': 'x-vcpu-dirty-limit-period', 'features': ['unstable'] },
> -           'vcpu-dirty-limit',
> -           'mode',
> -           'zero-page-detection',
> -           'direct-io',
> -           { 'name': 'x-rdma-chunk-size', 'features': [ 'unstable' ] },
> -           'cpr-exec-command'] }
> -
>  ##
>  # @migrate-set-parameters:
>  #
> -- 
> 2.53.0
> 

-- 
Peter Xu



^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 13/18] migration: Use output visitor in info command
  2026-09-04 12:04   ` Peter Xu
@ 2026-09-04 13:41     ` Fabiano Rosas
  2026-09-04 14:54       ` Peter Xu
  0 siblings, 1 reply; 50+ messages in thread
From: Fabiano Rosas @ 2026-09-04 13:41 UTC (permalink / raw)
  To: Peter Xu; +Cc: qemu-devel

Peter Xu <peterx@redhat.com> writes:

> On Wed, Sep 02, 2026 at 07:15:41PM -0300, Fabiano Rosas wrote:
>> The hmp_info_migrate_parameters function currently open-codes the
>> mon_printf calls for each migration parameter. As with the set command
>> in the last patch, this should not be necessary as the QAPI
>> infrastructure already has generated code that takes type and struct
>> member names into account, including converting _ from C into the '-'
>> character as part of parameter names strings.
>> 
>> The current code is also quite painful to rebase if a series has been
>> carried for a long time while parameters have been added in master.
>> 
>> Replace all of this with a conversion from MigrationParameters to
>> QDict using an output visitor and a loop over the QDict that prints
>> per-QAPI-type formatted strings.
>> 
>> Modelled after block/qapi.c:dump_qobject, but with some changes to
>> keep the migration command output formatting.
>> 
>> Note that this was not a for-free improvement, the HMP command format
>> output was changed incompatibly in a previous patch. It doesn't output
>> units anymore.
>
> IIUC both qlist and qdict are only used in BitmapMigrationNodeAlias only.
> It would be nice to attach an example of HMP dump before/after the change
> of these two patches, either in previous patch or here (assuming the layout
> changed once).  In case HMP has some suggestion in general, I suggest copy
> Dave when repost in case he has something to say.
>

Will do.

>> 
>> Signed-off-by: Fabiano Rosas <farosas@suse.de>
>> ---
>>  migration/migration-hmp-cmds.c | 243 ++++++++++++++-------------------
>>  1 file changed, 100 insertions(+), 143 deletions(-)
>> 
>> diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c
>> index 089c6d4ff46..dff69650a0c 100644
>> --- a/migration/migration-hmp-cmds.c
>> +++ b/migration/migration-hmp-cmds.c
>> @@ -25,7 +25,12 @@
>>  #include "qapi/qapi-commands-migration.h"
>>  #include "qapi/qapi-visit-migration.h"
>>  #include "qapi/qobject-input-visitor.h"
>> +#include "qapi/qobject-output-visitor.h"
>> +#include "qobject/qbool.h"
>>  #include "qobject/qdict.h"
>> +#include "qobject/qjson.h"
>> +#include "qobject/qlist.h"
>> +#include "qobject/qnum.h"
>>  #include "qobject/qstring.h"
>>  #include "qapi/string-input-visitor.h"
>>  #include "qapi/string-output-visitor.h"
>> @@ -316,170 +321,122 @@ void hmp_info_migrate_capabilities(Monitor *mon, const QDict *qdict)
>>      qapi_free_MigrationCapabilityStatusList(caps);
>>  }
>>  
>> -static void monitor_print_cpr_exec_command(Monitor *mon, strList *args)
>> +static QDict *migrate_params_to_dict(MigrationParameters *p, Error **errp)
>>  {
>> -    monitor_printf(mon, "%s:",
>> -        MigrationParameter_str(MIGRATION_PARAMETER_CPR_EXEC_COMMAND));
>> +    QObject *obj = NULL;
>> +    Visitor *v = qobject_output_visitor_new(&obj);
>>  
>> -    while (args) {
>> -        monitor_printf(mon, " %s", args->value);
>> -        args = args->next;
>> +    if (visit_type_MigrationParameters(v, NULL, &p, errp)) {
>> +        visit_complete(v, &obj);
>>      }
>> -    monitor_printf(mon, "\n");
>> +    visit_free(v);
>> +    return qobject_to(QDict, obj);
>>  }
>
> This exact function also exists in options.c (with the same name and
> impl).  Can reuse.
>

Of course! I just forgot to do it. Thanks.

>>  
>> -void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict)
>> +static void hmp_migrate_print_qobject(Monitor *mon, const char *label,
>> +                                      QObject *obj)
>>  {
>> -    MigrationParameters *params;
>> -    MigrationState *s = migrate_get_current();
>> +    const char *sep;
>>  
>> -    params = qmp_query_migrate_parameters(NULL);
>> +    if (!obj) {
>> +        return;
>> +    }
>>  
>> -    if (params) {
>> -        monitor_printf(mon, "%s: %" PRIu64 "\n",
>> -            MigrationParameter_str(MIGRATION_PARAMETER_ANNOUNCE_INITIAL),
>> -            params->announce_initial);
>> -        monitor_printf(mon, "%s: %" PRIu64 "\n",
>> -            MigrationParameter_str(MIGRATION_PARAMETER_ANNOUNCE_MAX),
>> -            params->announce_max);
>> -        monitor_printf(mon, "%s: %" PRIu64 "\n",
>> -            MigrationParameter_str(MIGRATION_PARAMETER_ANNOUNCE_ROUNDS),
>> -            params->announce_rounds);
>> -        monitor_printf(mon, "%s: %" PRIu64 "\n",
>> -            MigrationParameter_str(MIGRATION_PARAMETER_ANNOUNCE_STEP),
>> -            params->announce_step);
>> -        assert(params->has_throttle_trigger_threshold);
>> -        monitor_printf(mon, "%s: %u\n",
>> -            MigrationParameter_str(MIGRATION_PARAMETER_THROTTLE_TRIGGER_THRESHOLD),
>> -            params->throttle_trigger_threshold);
>> -        assert(params->has_cpu_throttle_initial);
>> -        monitor_printf(mon, "%s: %u\n",
>> -            MigrationParameter_str(MIGRATION_PARAMETER_CPU_THROTTLE_INITIAL),
>> -            params->cpu_throttle_initial);
>> -        assert(params->has_cpu_throttle_increment);
>> -        monitor_printf(mon, "%s: %u\n",
>> -            MigrationParameter_str(MIGRATION_PARAMETER_CPU_THROTTLE_INCREMENT),
>> -            params->cpu_throttle_increment);
>> -        assert(params->has_cpu_throttle_tailslow);
>> -        monitor_printf(mon, "%s: %s\n",
>> -            MigrationParameter_str(MIGRATION_PARAMETER_CPU_THROTTLE_TAILSLOW),
>> -            params->cpu_throttle_tailslow ? "on" : "off");
>> -        assert(params->has_max_cpu_throttle);
>> -        monitor_printf(mon, "%s: %u\n",
>> -            MigrationParameter_str(MIGRATION_PARAMETER_MAX_CPU_THROTTLE),
>> -            params->max_cpu_throttle);
>> -        assert(params->tls_creds);
>> -        monitor_printf(mon, "%s: %s\n",
>> -            MigrationParameter_str(MIGRATION_PARAMETER_TLS_CREDS),
>> -                       params->tls_creds->u.s);
>> -        assert(params->tls_hostname);
>> -        monitor_printf(mon, "%s: %s\n",
>> -            MigrationParameter_str(MIGRATION_PARAMETER_TLS_HOSTNAME),
>> -                       params->tls_hostname->u.s);
>> -        assert(params->tls_authz);
>> -        monitor_printf(mon, "%s: %s\n",
>> -            MigrationParameter_str(MIGRATION_PARAMETER_TLS_AUTHZ),
>> -                       params->tls_authz->u.s);
>> -        assert(params->has_max_bandwidth);
>> -        monitor_printf(mon, "%s: %" PRIu64 "\n",
>> -            MigrationParameter_str(MIGRATION_PARAMETER_MAX_BANDWIDTH),
>> -            params->max_bandwidth);
>> -        assert(params->has_avail_switchover_bandwidth);
>> -        monitor_printf(mon, "%s: %" PRIu64 "\n",
>> -            MigrationParameter_str(MIGRATION_PARAMETER_AVAIL_SWITCHOVER_BANDWIDTH),
>> -            params->avail_switchover_bandwidth);
>> -        assert(params->has_max_postcopy_bandwidth);
>> -        monitor_printf(mon, "%s: %" PRIu64 "\n",
>> -            MigrationParameter_str(MIGRATION_PARAMETER_MAX_POSTCOPY_BANDWIDTH),
>> -            params->max_postcopy_bandwidth);
>> -        assert(params->has_downtime_limit);
>> -        monitor_printf(mon, "%s: %" PRIu64 "\n",
>> -            MigrationParameter_str(MIGRATION_PARAMETER_DOWNTIME_LIMIT),
>> -            params->downtime_limit);
>> -        assert(params->has_x_checkpoint_delay);
>> -        monitor_printf(mon, "%s: %u\n",
>> -            MigrationParameter_str(MIGRATION_PARAMETER_X_CHECKPOINT_DELAY),
>> -            params->x_checkpoint_delay);
>> -        monitor_printf(mon, "%s: %u\n",
>> -            MigrationParameter_str(MIGRATION_PARAMETER_MULTIFD_CHANNELS),
>> -            params->multifd_channels);
>> -        monitor_printf(mon, "%s: %s\n",
>> -            MigrationParameter_str(MIGRATION_PARAMETER_MULTIFD_COMPRESSION),
>> -            MultiFDCompression_str(params->multifd_compression));
>> -        assert(params->has_zero_page_detection);
>> -        monitor_printf(mon, "%s: %s\n",
>> -            MigrationParameter_str(MIGRATION_PARAMETER_ZERO_PAGE_DETECTION),
>> -            qapi_enum_lookup(&ZeroPageDetection_lookup,
>> -                params->zero_page_detection));
>> -        monitor_printf(mon, "%s: %" PRIu64 "\n",
>> -            MigrationParameter_str(MIGRATION_PARAMETER_XBZRLE_CACHE_SIZE),
>> -            params->xbzrle_cache_size);
>> +    /*
>> +     * Put a space after labels
>> +     * foo: bar
>> +     *     ^
>> +     */
>> +    if (label && label[0] && label[strlen(label) - 1] == ':') {
>> +        sep = " ";
>> +    } else {
>> +        sep = "";
>> +    }
>>  
>> -        if (s->has_block_bitmap_mapping) {
>> -            BitmapMigrationNodeAliasList *nal;
>> -            BitmapMigrationNodeAlias *na;
>> -            BitmapMigrationBitmapAliasList *bal;
>> -            BitmapMigrationBitmapAlias *ba;
>> -            BitmapMigrationBitmapAliasTransform *bat;
>> +    switch (qobject_type(obj)) {
>> +    case QTYPE_NONE:
>> +        g_assert_not_reached();
>> +    case QTYPE_QNULL:
>> +        break;
>
> Shall we assert too if we don't expect it?
>

Could be, I'll check.

>> +    case QTYPE_QNUM: {
>> +        int64_t i64;
>>  
>> -            monitor_printf(mon, "%s:",
>> -                           MigrationParameter_str(
>> -                               MIGRATION_PARAMETER_BLOCK_BITMAP_MAPPING));
>> +        if (qnum_get_try_int(qobject_to(QNum, obj), &i64)) {
>> +            monitor_printf(mon, "%s%s%" PRId64, label, sep, i64);
>
> Should we use qnum_to_string() like what dump_qobject() does?
>

I missed that helper, let me see.

> Logically any size/u64 parameter (which may not be a normal use case..) can
> be set larger than INT64_MAX, so qnum_get_try_int() may return false? Here
> skipping to print anything is benign if it only happens with a malicious
> monitor client, but I think we should try to capture all cases.
>

Yeah, makes sense.

>> +        }
>> +        break;
>> +    }
>> +    case QTYPE_QSTRING: {
>> +        QString *str = qobject_to(QString, obj);
>>  
>> -            for (nal = params->block_bitmap_mapping; nal; nal = nal->next)
>> -            {
>> -                na = nal->value;
>> -                monitor_printf(mon, " bitmaps:");
>> -                for (bal = na->bitmaps; bal; bal = bal->next) {
>> -                    ba = bal->value;
>> -                    bat = ba->transform;
>> +        if (str) {
>
> Any chance str==NULL here?
>

Yes. But I get your point, see below.

>> +            monitor_printf(mon, "%s%s%s", label, sep, qstring_get_str(str));
>> +        }
>> +        break;
>> +    }
>> +    case QTYPE_QDICT: {
>> +        QDict *d = qobject_to(QDict, obj);
>> +        const QDictEntry *e;
>> +        int i = 0;
>>  
>> -                    monitor_printf(mon, " name: %s", ba->name);
>> -                    if (bat && bat->has_persistent) {
>> -                        if (bat->persistent) {
>> -                            monitor_printf(mon, " persistent: on");
>> -                        } else {
>> -                            monitor_printf(mon, " persistent: off");
>> -                        }
>> -                    }
>> -                    monitor_printf(mon, " alias: %s", ba->alias);
>> +        if (d) {
>> +            for (e = qdict_first(d); e; e = qdict_next(d, e), i++) {
>> +                g_autofree char *l = g_strdup_printf("%s:", qdict_entry_key(e));
>> +                if (i) {
>> +                    monitor_printf(mon, " ");
>>                  }
>> -                monitor_printf(mon, " node-name: %s alias: %s",
>> -                               na->node_name, na->alias);
>> +                hmp_migrate_print_qobject(mon, l, qdict_entry_value(e));
>>              }
>> -
>> -            monitor_printf(mon, "\n");
>>          }
>> +        break;
>> +    }
>> +    case QTYPE_QLIST: {
>> +        QList *l = qobject_to(QList, obj);
>> +        const QListEntry *e;
>>  
>> -        monitor_printf(mon, "%s: %" PRIu64 "\n",
>> -        MigrationParameter_str(MIGRATION_PARAMETER_X_VCPU_DIRTY_LIMIT_PERIOD),
>> -        params->x_vcpu_dirty_limit_period);
>> +        if (l) {
>> +            monitor_printf(mon, "%s", label);
>>  
>> -        monitor_printf(mon, "%s: %" PRIu64 "\n",
>> -            MigrationParameter_str(MIGRATION_PARAMETER_VCPU_DIRTY_LIMIT),
>> -            params->vcpu_dirty_limit);
>> +            for (e = qlist_first(l); e; e = qlist_next(e)) {
>> +                /*
>> +                 * In the first iteration, this is the space after the
>> +                 * colon, otherwise it's the space between list
>> +                 * elements.
>> +                 */
>> +                monitor_printf(mon, " ");
>> +                hmp_migrate_print_qobject(mon, "", e->value);
>> +            }
>> +        }
>> +        break;
>> +    }
>> +    case QTYPE_QBOOL: {
>> +        QBool *b = qobject_to(QBool, obj);
>> +        if (b) {
>
> Similarly, I'd drop "if" if it will always happen, making qbool_get_bool()
> assert itself by deref.
>

I'd rather not have such asserts in user-facing code. Even with testing,
it's hard to ensure this 'obj' will reach here in integrity.

>> +            monitor_printf(mon, "%s%s%s", label, sep,
>> +                           qbool_get_bool(b) ? "on" : "off");
>> +        }
>> +        break;
>> +    }
>> +    default:
>> +        g_assert_not_reached();
>> +        break;
>> +    }
>> +}
>>  
>> -        assert(params->has_mode);
>> -        monitor_printf(mon, "%s: %s\n",
>> -            MigrationParameter_str(MIGRATION_PARAMETER_MODE),
>> -            qapi_enum_lookup(&MigMode_lookup, params->mode));
>> +void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict)
>> +{
>> +    MigrationParameters *params = qmp_query_migrate_parameters(NULL);
>> +    g_autoptr(QDict) d;
>> +    const QDictEntry *e;
>>  
>> -        if (params->has_direct_io) {
>> -            monitor_printf(mon, "%s: %s\n",
>> -                           MigrationParameter_str(
>> -                               MIGRATION_PARAMETER_DIRECT_IO),
>> -                           params->direct_io ? "on" : "off");
>> -        }
>> +    assert(params);
>>  
>> -        if (params->has_x_rdma_chunk_size) {
>> -            monitor_printf(mon, "%s: %" PRIu64 "\n",
>> -                           MigrationParameter_str(
>> -                               MIGRATION_PARAMETER_X_RDMA_CHUNK_SIZE),
>> -                           params->x_rdma_chunk_size);
>> -        }
>> +    d = migrate_params_to_dict(params, NULL);
>> +    for (e = qdict_first(d); e; e = qdict_next(d, e)) {
>> +        g_autofree char *label = g_strdup_printf("%s:", qdict_entry_key(e));
>>  
>> -        assert(params->has_cpr_exec_command);
>> -        monitor_print_cpr_exec_command(mon, params->cpr_exec_command);
>> +        hmp_migrate_print_qobject(mon, label, qdict_entry_value(e));
>> +        monitor_printf(mon, "\n");
>>      }
>>  
>>      qapi_free_MigrationParameters(params);
>> -- 
>> 2.53.0
>> 


^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 15/18] qapi/migration: Remove MigrationParameter
  2026-09-04 12:24   ` Peter Xu
@ 2026-09-04 13:49     ` Fabiano Rosas
  0 siblings, 0 replies; 50+ messages in thread
From: Fabiano Rosas @ 2026-09-04 13:49 UTC (permalink / raw)
  To: Peter Xu; +Cc: qemu-devel, Eric Blake, Markus Armbruster

Peter Xu <peterx@redhat.com> writes:

> On Wed, Sep 02, 2026 at 07:15:43PM -0300, Fabiano Rosas wrote:
>> This enum is convenient in two ways (just how enums work):
>> 
>> 1- It provides the number of migration parameters as its __MAX member.
>> 
>> 2- It allows iterating over an integer range and get a migration
>>    parameter name string corresponding to that position in the enum.
>> 
>> The migration code doesn't have the need for (2) anymore.
>> 
>> Balancing the benefit of (1) versus the disadvantage of requiring
>> migration.json to be updated in two different places whenever a
>> parameter is added, experience shows that the latter churn is enough
>> to decide to remove the enum.
>
> I may have a slightly different feeling, but that takes a few things into
> account, (1) we don't have issue duplicating docs for the two parameter
> names anymore, (2) the dup is only about adding the same string once more
> in qapi/, (3) we now have this migrate_mark_all_params_present() function
> that must set all has_* fields, which the __MAX did help to guard a bit..
>

I understand your points, I'm also unsure. I slightly prefer to have the
"problem" inside migration/ than inside qapi/. Let me ponder a bit more
during the respin, let's see.

> I think it's also fine to remove it completely, we just need to be more
> careful instead on migrate_mark_all_params_present() later.  Which one is
> easier to be forgotten?  I don't know..
>
> Acked-by: Peter Xu <peterx@redhat.com>
>
>> 
>> Signed-off-by: Fabiano Rosas <farosas@suse.de>
>> ---
>>  migration/options.c |  6 +-----
>>  qapi/migration.json | 36 ------------------------------------
>>  2 files changed, 1 insertion(+), 41 deletions(-)
>> 
>> diff --git a/migration/options.c b/migration/options.c
>> index 5d17acdd881..f988b181f0e 100644
>> --- a/migration/options.c
>> +++ b/migration/options.c
>> @@ -1127,7 +1127,6 @@ static MigrationParameters *migrate_params_from_dict(QDict *d, Error **errp)
>>   */
>>  static void migrate_mark_all_params_present(MigrationParameters *p)
>>  {
>> -    int len, n_str_args = 3; /* tls-creds, tls-hostname, tls-authz */
>>      bool *has_fields[] = {
>>          &p->has_throttle_trigger_threshold, &p->has_cpu_throttle_initial,
>>          &p->has_cpu_throttle_increment, &p->has_cpu_throttle_tailslow,
>> @@ -1144,10 +1143,7 @@ static void migrate_mark_all_params_present(MigrationParameters *p)
>>          &p->has_x_rdma_chunk_size, &p->has_cpr_exec_command,
>>      };
>>  
>> -    len = ARRAY_SIZE(has_fields);
>> -    assert(len + n_str_args == MIGRATION_PARAMETER__MAX);
>> -
>> -    for (int i = 0; i < len; i++) {
>> +    for (int i = 0; i < ARRAY_SIZE(has_fields); i++) {
>>          *has_fields[i] = true;
>>      }
>>  }
>> diff --git a/qapi/migration.json b/qapi/migration.json
>> index b1eaf7b0545..78c6e933cf1 100644
>> --- a/qapi/migration.json
>> +++ b/qapi/migration.json
>> @@ -796,42 +796,6 @@
>>        'bitmaps': [ 'BitmapMigrationBitmapAlias' ]
>>    } }
>>  
>> -##
>> -# @MigrationParameter:
>> -#
>> -# Migration parameters enumeration.  The enumeration values mirror the
>> -# members of @MigrationParameters.
>> -#
>> -# Features:
>> -#
>> -# @unstable: Members @x-checkpoint-delay, @x-rdma-chunk-size, and
>> -#     @x-vcpu-dirty-limit-period are experimental.
>> -#
>> -# Since: 2.4
>> -##
>> -{ 'enum': 'MigrationParameter',
>> -  'data': ['announce-initial', 'announce-max',
>> -           'announce-rounds', 'announce-step',
>> -           'throttle-trigger-threshold',
>> -           'cpu-throttle-initial', 'cpu-throttle-increment',
>> -           'cpu-throttle-tailslow',
>> -           'tls-creds', 'tls-hostname', 'tls-authz', 'max-bandwidth',
>> -           'avail-switchover-bandwidth', 'downtime-limit',
>> -           { 'name': 'x-checkpoint-delay', 'features': [ 'unstable' ] },
>> -           'multifd-channels',
>> -           'xbzrle-cache-size', 'max-postcopy-bandwidth',
>> -           'max-cpu-throttle', 'multifd-compression',
>> -           'multifd-zlib-level', 'multifd-zstd-level',
>> -           'multifd-qatzip-level',
>> -           'block-bitmap-mapping',
>> -           { 'name': 'x-vcpu-dirty-limit-period', 'features': ['unstable'] },
>> -           'vcpu-dirty-limit',
>> -           'mode',
>> -           'zero-page-detection',
>> -           'direct-io',
>> -           { 'name': 'x-rdma-chunk-size', 'features': [ 'unstable' ] },
>> -           'cpr-exec-command'] }
>> -
>>  ##
>>  # @migrate-set-parameters:
>>  #
>> -- 
>> 2.53.0
>> 


^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 16/18] migration: Add capabilities into MigrationParameters
  2026-09-04  9:53   ` Markus Armbruster
@ 2026-09-04 13:58     ` Fabiano Rosas
  0 siblings, 0 replies; 50+ messages in thread
From: Fabiano Rosas @ 2026-09-04 13:58 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel, Peter Xu, Eric Blake

Markus Armbruster <armbru@redhat.com> writes:

> Fabiano Rosas <farosas@suse.de> writes:
>
>> Add capabilities to MigrationParameters. This structure will hold all
>> migration options. Capabilities will go away in the next patch.
>>
>> From this point on, both QMP and HMP versions of
>> migrate-set-parameters and query-migrate-parameters gain the ability
>> to work with capabilities.
>>
>> With MigrationParameters now having members for each capability, the
>> migration capabilities commands (query-migrate-capabilities,
>> migrate-set-capabilities) will soon be deprecated. Add a set of
>> helpers to convert between the old MigrationCapability representation
>> and the new representation as members of MigrationParameters.
>>
>> Acked-by: Peter Xu <peterx@redhat.com>
>> Signed-off-by: Fabiano Rosas <farosas@suse.de>
>> ---
>>  migration/migration.c |   8 +++
>>  migration/options.c   | 127 ++++++++++++++++++++++++++++++++++++++++++
>>  migration/options.h   |   5 ++
>>  qapi/migration.json   | 118 ++++++++++++++++++++++++++++++++++++++-
>>  4 files changed, 255 insertions(+), 3 deletions(-)
>>
>> diff --git a/migration/migration.c b/migration/migration.c
>> index dab282dd2f3..c8e7e86ea05 100644
>> --- a/migration/migration.c
>> +++ b/migration/migration.c
>> @@ -4086,6 +4086,14 @@ static bool migration_object_check(MigrationState *ms, Error **errp)
>>          return false;
>>      }
>>  
>> +    /*
>> +     * FIXME: Temporarily while -global capabilties are still using
>> +     * s->capabilities. Will be gone by the end of the series.
>> +     */
>> +    for (int i = 0; i < MIGRATION_CAPABILITY__MAX; i++) {
>> +        migrate_capability_set_compat(&ms->parameters, i, ms->capabilities[i]);
>> +    }
>> +
>>      return migrate_caps_check(old_caps, ms->capabilities, errp);
>>  }
>>  
>> diff --git a/migration/options.c b/migration/options.c
>> index f988b181f0e..7c638e204a1 100644
>> --- a/migration/options.c
>> +++ b/migration/options.c
>> @@ -778,6 +778,108 @@ bool migrate_caps_check(bool *old_caps, bool *new_caps, Error **errp)
>>      return true;
>>  }
>>  
>> +static bool *migrate_capability_get_addr(MigrationParameters *params, int i)
>> +{
>> +    bool *cap_addr = NULL;
>> +
>> +    switch (i) {
>> +    case MIGRATION_CAPABILITY_XBZRLE:
>> +        cap_addr = &params->xbzrle;
>> +        break;
>> +    case MIGRATION_CAPABILITY_RDMA_PIN_ALL:
>> +        cap_addr = &params->rdma_pin_all;
>> +        break;
>> +    case MIGRATION_CAPABILITY_AUTO_CONVERGE:
>> +        cap_addr = &params->auto_converge;
>> +        break;
>> +    case MIGRATION_CAPABILITY_EVENTS:
>> +        cap_addr = &params->events;
>> +        break;
>> +    case MIGRATION_CAPABILITY_POSTCOPY_RAM:
>> +        cap_addr = &params->postcopy_ram;
>> +        break;
>> +    case MIGRATION_CAPABILITY_X_COLO:
>> +        cap_addr = &params->x_colo;
>> +        break;
>> +    case MIGRATION_CAPABILITY_RELEASE_RAM:
>> +        cap_addr = &params->release_ram;
>> +        break;
>> +    case MIGRATION_CAPABILITY_RETURN_PATH:
>> +        cap_addr = &params->return_path;
>> +        break;
>> +    case MIGRATION_CAPABILITY_PAUSE_BEFORE_SWITCHOVER:
>> +        cap_addr = &params->pause_before_switchover;
>> +        break;
>> +    case MIGRATION_CAPABILITY_MULTIFD:
>> +        cap_addr = &params->multifd;
>> +        break;
>> +    case MIGRATION_CAPABILITY_DIRTY_BITMAPS:
>> +        cap_addr = &params->dirty_bitmaps;
>> +        break;
>> +    case MIGRATION_CAPABILITY_POSTCOPY_BLOCKTIME:
>> +        cap_addr = &params->postcopy_blocktime;
>> +        break;
>> +    case MIGRATION_CAPABILITY_LATE_BLOCK_ACTIVATE:
>> +        cap_addr = &params->late_block_activate;
>> +        break;
>> +    case MIGRATION_CAPABILITY_X_IGNORE_SHARED:
>> +        cap_addr = &params->x_ignore_shared;
>> +        break;
>> +    case MIGRATION_CAPABILITY_VALIDATE_UUID:
>> +        cap_addr = &params->validate_uuid;
>> +        break;
>> +    case MIGRATION_CAPABILITY_BACKGROUND_SNAPSHOT:
>> +        cap_addr = &params->background_snapshot;
>> +        break;
>> +    case MIGRATION_CAPABILITY_ZERO_COPY_SEND:
>> +        cap_addr = &params->zero_copy_send;
>> +        break;
>> +    case MIGRATION_CAPABILITY_POSTCOPY_PREEMPT:
>> +        cap_addr = &params->postcopy_preempt;
>> +        break;
>> +    case MIGRATION_CAPABILITY_SWITCHOVER_ACK:
>> +        cap_addr = &params->switchover_ack;
>> +        break;
>> +    case MIGRATION_CAPABILITY_DIRTY_LIMIT:
>> +        cap_addr = &params->dirty_limit;
>> +        break;
>> +    case MIGRATION_CAPABILITY_MAPPED_RAM:
>> +        cap_addr = &params->mapped_ram;
>> +        break;
>> +    default:
>> +        g_assert_not_reached();
>> +    }
>
> I'd use an array instead of a switch.  Matter of taste.
>
>> +
>> +    return cap_addr;
>> +}
>> +
>> +/* Compatibility for code that reads capabilities in a loop */
>> +bool migrate_capability_get_compat(MigrationParameters *params, int i)
>> +{
>> +    return *(migrate_capability_get_addr(params, i));
>> +}
>> +
>> +/* Compatibility for code that writes capabilities in a loop */
>> +void migrate_capability_set_compat(MigrationParameters *params, int i, bool val)
>> +{
>> +    *(migrate_capability_get_addr(params, i)) = val;
>> +}
>> +
>> +/*
>> + * Set capabilities for compatibility with the old
>> + * migrate-set-capabilities command.
>> + */
>> +void migrate_capabilities_set_compat(MigrationParameters *params,
>> +                                     MigrationCapabilityStatusList *caps)
>> +{
>> +    MigrationCapabilityStatusList *cap;
>> +
>> +    for (cap = caps; cap; cap = cap->next) {
>> +        migrate_capability_set_compat(params, cap->value->capability,
>> +                                      cap->value->state);
>> +    }
>> +}
>> +
>>  MigrationCapabilityStatusList *qmp_query_migrate_capabilities(Error **errp)
>>  {
>>      MigrationCapabilityStatusList *head = NULL, **tail = &head;
>> @@ -819,6 +921,8 @@ void qmp_migrate_set_capabilities(MigrationCapabilityStatusList *params,
>>      for (cap = params; cap; cap = cap->next) {
>>          s->capabilities[cap->value->capability] = cap->value->state;
>>      }
>> +
>> +    migrate_capabilities_set_compat(&s->parameters, params);
>>  }
>>  
>>  /* parameters */
>> @@ -1141,6 +1245,15 @@ static void migrate_mark_all_params_present(MigrationParameters *p)
>>          &p->has_x_vcpu_dirty_limit_period, &p->has_vcpu_dirty_limit,
>>          &p->has_mode, &p->has_zero_page_detection, &p->has_direct_io,
>>          &p->has_x_rdma_chunk_size, &p->has_cpr_exec_command,
>> +        &p->has_xbzrle, &p->has_rdma_pin_all,
>> +        &p->has_auto_converge, &p->has_events,
>> +        &p->has_postcopy_ram, &p->has_x_colo, &p->has_release_ram,
>> +        &p->has_return_path, &p->has_pause_before_switchover, &p->has_multifd,
>> +        &p->has_dirty_bitmaps, &p->has_postcopy_blocktime,
>> +        &p->has_late_block_activate, &p->has_x_ignore_shared,
>> +        &p->has_validate_uuid, &p->has_background_snapshot,
>> +        &p->has_zero_copy_send, &p->has_postcopy_preempt,
>> +        &p->has_switchover_ack, &p->has_dirty_limit, &p->has_mapped_ram,
>>      };
>>  
>>      for (int i = 0; i < ARRAY_SIZE(has_fields); i++) {
>> @@ -1465,6 +1578,20 @@ void qmp_migrate_set_parameters(MigrationParameters *input, Error **errp)
>>      tls_opt_to_str(input->tls_hostname);
>>      tls_opt_to_str(input->tls_authz);
>>  
>> +    /*
>> +     * FIXME: Temporarily while migrate_caps_check is not
>> +     * converted to look at s->parameters. Will be gone the end of
>> +     * the series.
>> +     */
>> +    bool new_caps[MIGRATION_CAPABILITY__MAX] = { 0 };
>> +    for (int i = 0; i < MIGRATION_CAPABILITY__MAX; i++) {
>> +        new_caps[i] = migrate_capability_get_compat(cur, i);
>> +    }
>> +    if (!migrate_caps_check(migrate_get_current()->capabilities, new_caps,
>> +                            errp)) {
>> +        return;
>> +    }
>> +
>>      /* merge input on top of current */
>>      if (!migrate_params_merge(cur, input, &new, errp)) {
>>          return;
>> diff --git a/migration/options.h b/migration/options.h
>> index c7da2d0b5b0..eedd1aa1f93 100644
>> --- a/migration/options.h
>> +++ b/migration/options.h
>> @@ -94,4 +94,9 @@ uint64_t migrate_rdma_chunk_size(void);
>>  bool migrate_params_check(MigrationParameters *params, Error **errp);
>>  void migrate_params_init(MigrationParameters *params);
>>  bool migrate_params_free(MigrationParameters *params, Error **errp);
>> +bool migrate_capability_get_compat(MigrationParameters *params, int i);
>> +void migrate_capability_set_compat(MigrationParameters *params, int i,
>> +                                   bool val);
>> +void migrate_capabilities_set_compat(MigrationParameters *params,
>> +                                     MigrationCapabilityStatusList *caps);
>>  #endif
>> diff --git a/qapi/migration.json b/qapi/migration.json
>> index 78c6e933cf1..7952ef44db9 100644
>> --- a/qapi/migration.json
>> +++ b/qapi/migration.json
>> @@ -976,10 +976,101 @@
>>  #     Must be set to the same value on both source and destination
>>  #     before migration starts.  (Since 11.1)
>>  #
>> +# @xbzrle: Migration supports xbzrle (Xor Based Zero Run Length
>> +#     Encoding).  This feature allows us to minimize migration traffic
>> +#     for certain work loads, by sending compressed difference of the
>> +#     pages
>> +#
>> +# @rdma-pin-all: Controls whether or not the entire VM memory
>> +#     footprint is mlock()'d on demand or all at once.  Refer to
>> +#     docs/rdma.txt for usage.  Disabled by default.  (since 2.0)
>> +#
>> +# @events: Generate events for each migration state change.
>> +#     (since 2.4)
>> +#
>> +# @auto-converge: If enabled, QEMU will automatically throttle down
>> +#     the guest to speed up convergence of RAM migration.  (since 1.6)
>> +#
>> +# @postcopy-ram: Start executing on the migration target before all of
>> +#     RAM has been migrated, pulling the remaining pages along as
>> +#     needed.  The capacity must have the same setting on both source
>> +#     and target or migration will not even start.  **Note:** If the
>> +#     migration fails during postcopy the VM will fail.  (since 2.6)
>> +#
>> +# @x-colo: If enabled, migration will never end, and the state of the
>> +#     VM on the primary side will be migrated continuously to the VM
>> +#     on secondary side, this process is called COarse-Grain LOck
>> +#     Stepping (COLO) for Non-stop Service.  (since 2.8)
>> +#
>> +# @release-ram: If enabled, QEMU will free the migrated ram pages on
>> +#     the source during postcopy-ram migration.  (since 2.9)
>> +#
>> +# @return-path: If enabled, migration will use the return path even
>> +#     for precopy.  (since 2.10)
>> +#
>> +# @pause-before-switchover: Pause outgoing migration before
>> +#     serialising device state and before disabling block IO.
>> +#     (since 2.11)
>> +#
>> +# @multifd: Use more than one fd for migration.  (since 4.0)
>> +#
>> +# @dirty-bitmaps: If enabled, QEMU will migrate named dirty bitmaps.
>> +#     (since 2.12)
>> +#
>> +# @postcopy-blocktime: Calculate downtime for postcopy live migration.
>> +#     (since 3.0)
>> +#
>> +# @late-block-activate: If enabled, the destination will not activate
>> +#     block devices (and thus take locks) immediately at the end of
>> +#     migration.  (since 3.0)
>> +#
>> +# @x-ignore-shared: If enabled, QEMU will not migrate shared memory
>> +#     that is accessible on the destination machine.  (since 4.0)
>> +#
>> +# @validate-uuid: Send the UUID of the source to allow the destination
>> +#     to ensure it is the same.  (since 4.2)
>> +#
>> +# @background-snapshot: If enabled, the migration stream will be a
>> +#     snapshot of the VM exactly at the point when the migration
>> +#     procedure starts.  The VM RAM is saved with running VM.
>> +#     (since 6.0)
>> +#
>> +# @zero-copy-send: Controls behavior on sending memory pages on
>> +#     migration.  When true, enables a zero-copy mechanism for sending
>> +#     memory pages, if host supports it.  Requires that QEMU be
>> +#     permitted to use locked memory for guest RAM pages.  (since 7.1)
>> +#
>> +# @postcopy-preempt: If enabled, the migration process will allow
>> +#     postcopy requests to preempt precopy stream, so postcopy
>> +#     requests will be handled faster.  This is a performance feature
>> +#     and should not affect the correctness of postcopy migration.
>> +#     (since 7.1)
>> +#
>> +# @switchover-ack: If enabled, migration will not stop the source VM
>> +#     and complete the migration until an ACK is received from the
>> +#     destination that it's OK to do so.  Exactly when this ACK is
>> +#     sent depends on the migrated devices that use this feature.  For
>> +#     example, a device can use it to make sure some of its data is
>> +#     sent and loaded in the destination before doing switchover.
>> +#     This can reduce downtime if devices that support this capability
>> +#     are present.  'return-path' capability must be enabled to use
>> +#     it.  (since 8.1)
>> +#
>> +# @dirty-limit: If enabled, migration will throttle vCPUs as needed to
>> +#     keep their dirty page rate within @vcpu-dirty-limit.  This can
>> +#     improve responsiveness of large guests during live migration,
>> +#     and can result in more stable read performance.  Requires KVM
>> +#     with accelerator property "dirty-ring-size" set.  (Since 8.1)
>> +#
>> +# @mapped-ram: Migrate using fixed offsets in the migration file for
>> +#     each RAM page.  Requires a migration URI that supports seeking,
>> +#     such as a file.  (since 9.0)
>> +#
>>  # Features:
>>  #
>> -# @unstable: Members @x-checkpoint-delay, @x-rdma-chunk-size, and
>> -#     @x-vcpu-dirty-limit-period are experimental.
>> +# @unstable: Members @x-checkpoint-delay, @x-vcpu-dirty-limit-period,
>> +#     @x-colo, @x-ignore-shared and @x-rdma-chunk-size are
>> +#     experimental.
>
> This isn't a clean copy from MigrationCapability.  Why?
>

I blundered.

>>  #
>>  # Since: 2.4
>>  ##
>> @@ -1017,7 +1108,28 @@
>>              '*direct-io': 'bool',
>>              '*x-rdma-chunk-size': { 'type': 'uint64',
>>                                      'features': [ 'unstable' ] },
>> -            '*cpr-exec-command': [ 'str' ]} }
>> +            '*cpr-exec-command': [ 'str' ],
>> +            '*xbzrle': 'bool',
>> +            '*rdma-pin-all': 'bool',
>> +            '*auto-converge': 'bool',
>> +            '*events': 'bool',
>> +            '*postcopy-ram': 'bool',
>> +            '*x-colo': { 'type': 'bool', 'features': [ 'unstable' ] },
>> +            '*release-ram': 'bool',
>> +            '*return-path': 'bool',
>> +            '*pause-before-switchover': 'bool',
>> +            '*multifd': 'bool',
>> +            '*dirty-bitmaps': 'bool',
>> +            '*postcopy-blocktime': 'bool',
>> +            '*late-block-activate': 'bool',
>> +            '*x-ignore-shared': { 'type': 'bool', 'features': [ 'unstable' ] },
>> +            '*validate-uuid': 'bool',
>> +            '*background-snapshot': 'bool',
>> +            '*zero-copy-send': 'bool',
>> +            '*postcopy-preempt': 'bool',
>> +            '*switchover-ack': 'bool',
>> +            '*dirty-limit': 'bool',
>> +            '*mapped-ram': 'bool' } }
>>  
>>  ##
>>  # @query-migrate-parameters:


^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 01/18] checkpatch: Fix checking of newlines in error messages
  2026-09-02 22:15 ` [PATCH 01/18] checkpatch: Fix checking of newlines in error messages Fabiano Rosas
  2026-09-03 17:37   ` Peter Xu
@ 2026-09-04 14:00   ` Markus Armbruster
  1 sibling, 0 replies; 50+ messages in thread
From: Markus Armbruster @ 2026-09-04 14:00 UTC (permalink / raw)
  To: Fabiano Rosas; +Cc: qemu-devel, Peter Xu, Chao Liu

Fabiano Rosas <farosas@suse.de> writes:

> Using newlines in the g_test_message is fine. It automatically adds
> the '#' required by the TAP protocol to the start of each line.
>
> Relax the regex for this function, but still forbid a trailing newline
> because it's added automatically and usually not what the user wants.
>
> Signed-off-by: Fabiano Rosas <farosas@suse.de>
> ---
>  scripts/checkpatch.pl | 11 +++++++++--
>  1 file changed, 9 insertions(+), 2 deletions(-)
>
> diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl
> index 03f35e75012..fd4534b3a1e 100755
> --- a/scripts/checkpatch.pl
> +++ b/scripts/checkpatch.pl
> @@ -3303,13 +3303,20 @@ sub process {
>  					 info_vreport|
>  					 error_report|
>  					 warn_report|
> -					 info_report|
> -					 g_test_message}x;
> +					 info_report}x;
>  
>  		if ($rawline =~ /\b(?:$qemu_error_funcs)\s*\(.*\".*\\n/) {
>  			ERROR("Error messages should not contain newlines\n" . $herecurr);
>  		}
>  
> +		# No newlines at the end
> +		my $trail_newline_error_funcs = qr{g_test_message}x;
> +
> +		if ($rawline =~ /\b(?:$trail_newline_error_funcs)\(.*\".*\\n\"/) {

Unlike the regexp above, this one doesn't accept spaces between the
function name and the parenthesis.  We generally don't have spaces
there, but why rely on that?

> +		    ERROR("Error messages should not contain trailing " .
> +			  "newlines\n" . $herecurr);
> +		}
> +
>  		# Continue checking for error messages that contains newlines.
>  		# This check handles cases where string literals are spread
>  		# over multiple lines.

With the spaces accepted
Reviewed-by: Markus Armbruster <armbru@redhat.com>



^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 13/18] migration: Use output visitor in info command
  2026-09-04 13:41     ` Fabiano Rosas
@ 2026-09-04 14:54       ` Peter Xu
  2026-09-04 15:08         ` Fabiano Rosas
  0 siblings, 1 reply; 50+ messages in thread
From: Peter Xu @ 2026-09-04 14:54 UTC (permalink / raw)
  To: Fabiano Rosas; +Cc: qemu-devel

On Fri, Sep 04, 2026 at 10:41:21AM -0300, Fabiano Rosas wrote:
> >> +    case QTYPE_QBOOL: {
> >> +        QBool *b = qobject_to(QBool, obj);
> >> +        if (b) {
> >
> > Similarly, I'd drop "if" if it will always happen, making qbool_get_bool()
> > assert itself by deref.
> >
> 
> I'd rather not have such asserts in user-facing code. Even with testing,
> it's hard to ensure this 'obj' will reach here in integrity.

Not a big deal here, but just for sake of pure discussion..

IMHO it's not the "user triggerable path" that we are avoiding assert()s,
but user input that may affect the result of the assert().

Here if we just checked obj type is QTYPE_QBOOL, I can't see anything that
can make this if not true.

I still think assert() good guarding programming errors.  Say, if something
we wanted to print here but skipped, I want it to crash hard, rather than
silently ignored.

-- 
Peter Xu



^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 13/18] migration: Use output visitor in info command
  2026-09-04 14:54       ` Peter Xu
@ 2026-09-04 15:08         ` Fabiano Rosas
  0 siblings, 0 replies; 50+ messages in thread
From: Fabiano Rosas @ 2026-09-04 15:08 UTC (permalink / raw)
  To: Peter Xu; +Cc: qemu-devel

Peter Xu <peterx@redhat.com> writes:

> On Fri, Sep 04, 2026 at 10:41:21AM -0300, Fabiano Rosas wrote:
>> >> +    case QTYPE_QBOOL: {
>> >> +        QBool *b = qobject_to(QBool, obj);
>> >> +        if (b) {
>> >
>> > Similarly, I'd drop "if" if it will always happen, making qbool_get_bool()
>> > assert itself by deref.
>> >
>> 
>> I'd rather not have such asserts in user-facing code. Even with testing,
>> it's hard to ensure this 'obj' will reach here in integrity.
>
> Not a big deal here, but just for sake of pure discussion..
>
> IMHO it's not the "user triggerable path" that we are avoiding assert()s,
> but user input that may affect the result of the assert().
>

In this case, there should be none, agreed.

> Here if we just checked obj type is QTYPE_QBOOL, I can't see anything that
> can make this if not true.
>

Thanks for saying it explicitly, I wasn't seeing the obvious redundancy
there. You're right, I'll remove the checks.

> I still think assert() good guarding programming errors.  Say, if something
> we wanted to print here but skipped, I want it to crash hard, rather than
> silently ignored.


^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 10/18] migration: Validate that all params are set for query
  2026-09-03 18:59   ` Peter Xu
@ 2026-09-04 15:11     ` Fabiano Rosas
  0 siblings, 0 replies; 50+ messages in thread
From: Fabiano Rosas @ 2026-09-04 15:11 UTC (permalink / raw)
  To: Peter Xu; +Cc: qemu-devel

Peter Xu <peterx@redhat.com> writes:

> On Wed, Sep 02, 2026 at 07:15:38PM -0300, Fabiano Rosas wrote:
>> There are a couple of situations where all fields of a
>> MigrationParameters object need to be marked as present: when cloning
>> an entire object and when creating the transient object in
>> qmp_query_migrate(). The query-migrate-parameters QMP command contract
>> requires that all parameters, except block-bitmap-mapping, are present
>> in the output.
>> 
>> Validate that a given object has all has_* fields set to true.
>> 
>> Signed-off-by: Fabiano Rosas <farosas@suse.de>
>> ---
>>  migration/options.c | 54 +++++++++++++++++++++++++++++++++++++++++++++
>>  1 file changed, 54 insertions(+)
>> 
>> diff --git a/migration/options.c b/migration/options.c
>> index bd7be8f9832..5d17acdd881 100644
>> --- a/migration/options.c
>> +++ b/migration/options.c
>> @@ -12,6 +12,7 @@
>>   */
>>  
>>  #include "qemu/osdep.h"
>> +#include "qemu/cutils.h"
>>  #include "qemu/error-report.h"
>>  #include "qemu/units.h"
>>  #include "exec/target_page.h"
>> @@ -23,8 +24,10 @@
>>  #include "qapi/qmp/qerror.h"
>>  #include "qapi/qobject-input-visitor.h"
>>  #include "qapi/qobject-output-visitor.h"
>> +#include "qobject/qbool.h"
>>  #include "qobject/qdict.h"
>>  #include "qobject/qnull.h"
>> +#include "qobject/qstring.h"
>>  #include "system/runstate.h"
>>  #include "migration/colo.h"
>>  #include "migration/cpr.h"
>> @@ -1149,12 +1152,63 @@ static void migrate_mark_all_params_present(MigrationParameters *p)
>>      }
>>  }
>>  
>> +static bool assert_all_params_present(MigrationParameters *params, Error **errp)
>> +{
>> +    g_autoptr(QDict) d = migrate_params_to_dict(params, errp);
>> +    const QDictEntry *e = NULL;
>> +    int i = 0;
>> +
>> +    if (!d) {
>> +        return false;
>> +    }
>> +
>> +    for (e = qdict_first(d); e; e = qdict_next(d, e), i++) {
>> +        const char *key = qdict_entry_key(e);
>> +        const char *p;
>> +
>> +        if (strstart(key, "tls-", &p)) {
>> +            QString *s = qobject_to(QString, qdict_entry_value(e));
>> +
>> +            if (!s) {
>> +                break;
>> +            }
>> +        } else if (strstart(key, "has-", &p)) {
>
> Does the qdict contain any has- field?  
>

¬¬

I guess that decides the fate of the MigrationParameter enum.

> visit_type_MigrationParameters_members:
>
>     if (visit_optional(v, "announce-initial", &obj->has_announce_initial)) {
>         if (!visit_type_size(v, "announce-initial", &obj->announce_initial, errp)) {
>             return false;
>         }
>     }
>     ...
>
> It seems the has_* fields are only used to identify existance of objects,
> not converted.
>
>> +            if (qdict_haskey(d, p)) {
>> +                QBool *b = qobject_to(QBool, qdict_entry_value(e));
>> +
>> +                If (!b || !qbool_get_bool(b)) {
>> +                    break;
>> +                }
>> +            }
>> +        }
>> +    }
>> +
>> +    if (i && !e) {
>> +        return true;
>> +    }
>> +
>> +    /*
>> +     * Should never happen, but avoid asserting becase this is
>> +     * reachable from QMP.
>
> IIUC as long as this fact shouldn't be changed by any possible user input,
> we could still assert.  But I understand you want to be careful, maybe
> either (1) directly assert, or (2) change the function name,
> s/assert/check/?  I vote (1).
>
> Said that, if the qdict trick didn't work it beats the whole patch.. so
> IMHO we can also leave this sanity check for later too.  Your call.
>
>> +     */
>> +    error_setg(errp, "Missing parameter. Query output will be incomplete.");
>> +    return false;
>> +}
>> +
>>  MigrationParameters *qmp_query_migrate_parameters(Error **errp)
>>  {
>>      MigrationState *s = migrate_get_current();
>>      MigrationParameters *params = QAPI_CLONE(MigrationParameters,
>>                                               &s->parameters);
>>  
>> +    /*
>> +     * Validate all parameters have their has_* field set to true as
>> +     * consequence of the initial migrate_mark_all_params_present().
>> +     */
>> +    if (!assert_all_params_present(params, errp)) {
>> +        return NULL;
>> +    }
>> +
>>      /*
>>       * The block-bitmap-mapping breaks the expected API of
>>       * query-migrate-parameters of having all members present. To keep
>> -- 
>> 2.53.0
>> 


^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 11/18] migration: Use keyval input visitor in HMP set command
  2026-09-03 20:24     ` Dr. David Alan Gilbert
@ 2026-09-04 15:26       ` Peter Xu
  0 siblings, 0 replies; 50+ messages in thread
From: Peter Xu @ 2026-09-04 15:26 UTC (permalink / raw)
  To: Dr. David Alan Gilbert
  Cc: Fabiano Rosas, qemu-devel, Laurent Vivier, Paolo Bonzini

On Thu, Sep 03, 2026 at 08:24:23PM +0000, Dr. David Alan Gilbert wrote:
> Someone will have a test script somewhere that might depend on it
> (I know I did when I was debugging migration - and I never remembered what
> was MB or byte!)

Me too! :)

-- 
Peter Xu



^ permalink raw reply	[flat|nested] 50+ messages in thread

end of thread, other threads:[~2026-09-04 15:27 UTC | newest]

Thread overview: 50+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-02 22:15 [PATCH 00/18] migration: MigrationParameters changes Fabiano Rosas
2026-09-02 22:15 ` [PATCH 01/18] checkpatch: Fix checking of newlines in error messages Fabiano Rosas
2026-09-03 17:37   ` Peter Xu
2026-09-03 17:46     ` Fabiano Rosas
2026-09-03 18:00       ` Peter Xu
2026-09-03 18:35         ` Fabiano Rosas
2026-09-04  8:56         ` Markus Armbruster
2026-09-04  9:15           ` Peter Maydell
2026-09-04 12:09             ` Peter Xu
2026-09-04 14:00   ` Markus Armbruster
2026-09-02 22:15 ` [PATCH 02/18] migration/options.c: Don't export migrate_tls_opts_free Fabiano Rosas
2026-09-02 22:15 ` [PATCH 03/18] migration: Rename variables in qmp_migrate_set_parameters Fabiano Rosas
2026-09-03 17:44   ` Peter Xu
2026-09-02 22:15 ` [PATCH 04/18] migration: Use QAPI_CLONE_MEMBERS in migrate_params_apply Fabiano Rosas
2026-09-02 22:15 ` [PATCH 05/18] migration: Merge parameter structs instead of assigning one by one Fabiano Rosas
2026-09-03 18:20   ` Peter Xu
2026-09-03 19:03     ` Fabiano Rosas
2026-09-02 22:15 ` [PATCH 06/18] migration: Open code migrate_params_apply Fabiano Rosas
2026-09-02 22:15 ` [PATCH 07/18] migration: Stop freeing s->parameters members individually Fabiano Rosas
2026-09-02 22:15 ` [PATCH 08/18] migration: Use migrate_params_free during finalize Fabiano Rosas
2026-09-02 22:15 ` [PATCH 09/18] tests/qtest/migration: Add a test for HMP Fabiano Rosas
2026-09-03 20:38   ` Peter Xu
2026-09-03 20:42     ` Peter Xu
2026-09-02 22:15 ` [PATCH 10/18] migration: Validate that all params are set for query Fabiano Rosas
2026-09-03 18:59   ` Peter Xu
2026-09-04 15:11     ` Fabiano Rosas
2026-09-02 22:15 ` [PATCH 11/18] migration: Use keyval input visitor in HMP set command Fabiano Rosas
2026-09-03 19:44   ` Peter Xu
2026-09-03 20:24     ` Dr. David Alan Gilbert
2026-09-04 15:26       ` Peter Xu
2026-09-04  9:45   ` Markus Armbruster
2026-09-02 22:15 ` [PATCH 12/18] migration: Change HMP 'info migrate_parameters' output Fabiano Rosas
2026-09-03 20:25   ` Peter Xu
2026-09-02 22:15 ` [PATCH 13/18] migration: Use output visitor in info command Fabiano Rosas
2026-09-04 12:04   ` Peter Xu
2026-09-04 13:41     ` Fabiano Rosas
2026-09-04 14:54       ` Peter Xu
2026-09-04 15:08         ` Fabiano Rosas
2026-09-02 22:15 ` [PATCH 14/18] migration: Rewrite migrate_set_parameter_completion using QDict Fabiano Rosas
2026-09-03 20:23   ` Peter Xu
2026-09-02 22:15 ` [PATCH 15/18] qapi/migration: Remove MigrationParameter Fabiano Rosas
2026-09-04  9:07   ` Markus Armbruster
2026-09-04 12:24   ` Peter Xu
2026-09-04 13:49     ` Fabiano Rosas
2026-09-02 22:15 ` [PATCH 16/18] migration: Add capabilities into MigrationParameters Fabiano Rosas
2026-09-04  9:53   ` Markus Armbruster
2026-09-04 13:58     ` Fabiano Rosas
2026-09-02 22:15 ` [PATCH 17/18] migration: Remove s->capabilities Fabiano Rosas
2026-09-04 12:15   ` Peter Xu
2026-09-02 22:15 ` [PATCH 18/18] qapi/migration: Deprecate capabilities commands Fabiano Rosas

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.