qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 00/21] migration: Unify capabilities and parameters
@ 2025-06-03  1:37 Fabiano Rosas
  2025-06-03  1:37 ` [PATCH 01/21] migration: Normalize tls arguments Fabiano Rosas
                   ` (21 more replies)
  0 siblings, 22 replies; 83+ messages in thread
From: Fabiano Rosas @ 2025-06-03  1:37 UTC (permalink / raw)
  To: qemu-devel; +Cc: Peter Xu, Markus Armbruster, Daniel P . Berrangé

Hi,

Thanks for the reviews in the last round. Your feedback has allowed me
to reduce the complexity of the series and the code considerably.

Changes in this v2:

- Followed the suggestion to unify the TLS strings as StrOrNull. This
  allows the complete removal of the MigrateSetParameters
  type. Therefore, no need for playing tricks with base types.

- I figured out that block_bitmap_mapping was set as optional in
  qmp_query_migrate_parameters in error. Fixing that allows to
  simplify that function somewhat.

- The suggestion of not checking the has_* fields when setting the
  parameters also led to great simplification. Now there's no need to
  open-code the setting of every single parameter.

- Deprecated the capabilities commands.

- Removed some redundant documentation from migration.json. Now
  there's only 1 (one) place where migration parameters need to be
  documented.

The series:
- passes CI: https://gitlab.com/farosas/qemu/-/pipelines/1849885920
- passes the migration tests in the ASAN build.
- passes the migration compat tests against each of the 3 last QEMU versions.
- passes the iotest 300 (related to block_bitmap_mapping).

v1:
https://lore.kernel.org/r/20250411191443.22565-1-farosas@suse.de

Fabiano Rosas (21):
  migration: Normalize tls arguments
  migration: Remove MigrateSetParameters
  qapi/migration: Don't document MigrationParameter
  migration: Run a post update routine after setting parameters
  migration: Add a flag to track block-bitmap-mapping input
  migration: Remove checks for s->parameters has_* fields
  migration: Set block_bitmap_mapping unconditionally in
    query-migrate-parameters
  migration: Do away with usage of QERR_INVALID_PARAMETER_VALUE
  migration: Extract code to mark all parameters as present
  migration: Use QAPI_CLONE_MEMBERS in query_migrate_parameters
  migration: Use QAPI_CLONE_MEMBERS in migrate_params_test_apply
  migration: Use QAPI_CLONE_MEMBERS in migrate_params_apply
  migration: Use visitors in migrate_params_test_apply
  migration: Cleanup hmp_info_migrate_parameters
  migration: Add capabilities into MigrationParameters
  qapi/migration: Mark that query/set-migrate-parameters support
    capabilities
  migration: Remove s->capabilities
  qapi/migration: Deprecate capabilities commands
  migration: Allow migrate commands to provide the migration config
  libqtest: Add a function to check whether a QMP command supports a
    feature
  tests/qtest/migration: Add a test for config passing

 docs/about/deprecated.rst          |   12 +
 migration/migration-hmp-cmds.c     |  484 ++++++++----
 migration/migration.c              |   50 +-
 migration/migration.h              |    9 +-
 migration/options.c                | 1090 +++++++++++++---------------
 migration/options.h                |   29 +-
 migration/page_cache.c             |    6 +-
 migration/ram.c                    |    5 +-
 migration/savevm.c                 |    8 +-
 migration/tls.c                    |    2 +-
 qapi/migration.json                |  573 ++++++---------
 qapi/pragma.json                   |    3 +-
 system/vl.c                        |    3 +-
 tests/qtest/libqtest.c             |   42 ++
 tests/qtest/libqtest.h             |   12 +
 tests/qtest/migration/framework.h  |    2 +
 tests/qtest/migration/misc-tests.c |   39 +
 17 files changed, 1243 insertions(+), 1126 deletions(-)

-- 
2.35.3



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

* [PATCH 01/21] migration: Normalize tls arguments
  2025-06-03  1:37 [PATCH 00/21] migration: Unify capabilities and parameters Fabiano Rosas
@ 2025-06-03  1:37 ` Fabiano Rosas
  2025-06-05 20:51   ` Peter Xu
  2025-06-25  9:41   ` Markus Armbruster
  2025-06-03  1:37 ` [PATCH 02/21] migration: Remove MigrateSetParameters Fabiano Rosas
                   ` (20 subsequent siblings)
  21 siblings, 2 replies; 83+ messages in thread
From: Fabiano Rosas @ 2025-06-03  1:37 UTC (permalink / raw)
  To: qemu-devel; +Cc: Peter Xu, Markus Armbruster, Daniel P . Berrangé

The migration parameters tls_creds, tls_authz and tls_hostname
currently have a non-uniform handling. When used as arguments to
migrate-set-parameters, their type is StrOrNull and when used as
return value from query-migrate-parameters, their type is a plain
string.

Not only having to convert between the types is cumbersome, but it
also creates the issue of requiring two different QAPI types to be
used, one for each command. MigrateSetParameters is used for
migrate-set-parameters with the TLS arguments as StrOrNull while
MigrationParameters is used for query-migrate-parameters with the TLS
arguments as str.

Since StrOrNull could be considered a superset of str, change the type
of the TLS arguments in MigrationParameters to StrOrNull and add a
helper to ensure they're never actually used as QTYPE_QNULL.

This will allow the type duplication to be removed in the next
patches.

Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
 migration/migration-hmp-cmds.c |   8 +-
 migration/migration.c          |   2 +
 migration/options.c            | 149 ++++++++++++++++++++-------------
 migration/options.h            |   1 +
 migration/tls.c                |   2 +-
 qapi/migration.json            |   6 +-
 6 files changed, 99 insertions(+), 69 deletions(-)

diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c
index e8a563c7d8..bc8179c582 100644
--- a/migration/migration-hmp-cmds.c
+++ b/migration/migration-hmp-cmds.c
@@ -276,14 +276,12 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict)
         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);
-        assert(params->tls_hostname);
+                       params->tls_creds ? params->tls_creds->u.s : "");
         monitor_printf(mon, "%s: '%s'\n",
             MigrationParameter_str(MIGRATION_PARAMETER_TLS_HOSTNAME),
-            params->tls_hostname);
+                       params->tls_hostname ? params->tls_hostname->u.s : "");
         assert(params->has_max_bandwidth);
         monitor_printf(mon, "%s: %" PRIu64 " bytes/second\n",
             MigrationParameter_str(MIGRATION_PARAMETER_MAX_BANDWIDTH),
@@ -319,7 +317,7 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict)
             params->max_postcopy_bandwidth);
         monitor_printf(mon, "%s: '%s'\n",
             MigrationParameter_str(MIGRATION_PARAMETER_TLS_AUTHZ),
-            params->tls_authz);
+                       params->tls_authz ? params->tls_authz->u.s : "");
 
         if (params->has_block_bitmap_mapping) {
             const BitmapMigrationNodeAliasList *bmnal;
diff --git a/migration/migration.c b/migration/migration.c
index 4697732bef..f65cb81b6d 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -4053,6 +4053,8 @@ static void migration_instance_finalize(Object *obj)
 {
     MigrationState *ms = MIGRATION_OBJ(obj);
 
+    migrate_tls_opts_free(&ms->parameters);
+
     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 162c72cda4..45a95dc6da 100644
--- a/migration/options.c
+++ b/migration/options.c
@@ -162,9 +162,11 @@ const Property migration_properties[] = {
     DEFINE_PROP_SIZE("announce-step", MigrationState,
                       parameters.announce_step,
                       DEFAULT_MIGRATE_ANNOUNCE_STEP),
-    DEFINE_PROP_STRING("tls-creds", MigrationState, parameters.tls_creds),
-    DEFINE_PROP_STRING("tls-hostname", MigrationState, parameters.tls_hostname),
-    DEFINE_PROP_STRING("tls-authz", MigrationState, parameters.tls_authz),
+    /*
+     * tls-creds, tls-hostname and tls-authz are of type StrOrNull,
+     * which can't be easily handled (if at all) by qdev. So these
+     * will not be exposed as global migration options (-global).
+     */
     DEFINE_PROP_UINT64("x-vcpu-dirty-limit-period", MigrationState,
                        parameters.x_vcpu_dirty_limit_period,
                        DEFAULT_MIGRATE_VCPU_DIRTY_LIMIT_PERIOD),
@@ -379,13 +381,6 @@ bool migrate_rdma(void)
     return s->rdma_migration;
 }
 
-bool migrate_tls(void)
-{
-    MigrationState *s = migrate_get_current();
-
-    return s->parameters.tls_creds && *s->parameters.tls_creds;
-}
-
 typedef enum WriteTrackingSupport {
     WT_SUPPORT_UNKNOWN = 0,
     WT_SUPPORT_ABSENT,
@@ -834,21 +829,44 @@ const char *migrate_tls_authz(void)
 {
     MigrationState *s = migrate_get_current();
 
-    return s->parameters.tls_authz;
+    if (s->parameters.tls_authz &&
+        s->parameters.tls_authz->type == QTYPE_QSTRING &&
+        *s->parameters.tls_authz->u.s) {
+        return s->parameters.tls_authz->u.s;
+    }
+
+    return NULL;
 }
 
 const char *migrate_tls_creds(void)
 {
     MigrationState *s = migrate_get_current();
 
-    return s->parameters.tls_creds;
+    if (s->parameters.tls_creds &&
+        s->parameters.tls_creds->type == QTYPE_QSTRING &&
+        *s->parameters.tls_creds->u.s) {
+        return s->parameters.tls_creds->u.s;
+    }
+
+    return NULL;
 }
 
 const char *migrate_tls_hostname(void)
 {
     MigrationState *s = migrate_get_current();
 
-    return s->parameters.tls_hostname;
+    if (s->parameters.tls_hostname &&
+        s->parameters.tls_hostname->type == QTYPE_QSTRING &&
+        *s->parameters.tls_hostname->u.s) {
+        return s->parameters.tls_hostname->u.s;
+    }
+
+    return NULL;
+}
+
+bool migrate_tls(void)
+{
+    return !!migrate_tls_creds();
 }
 
 uint64_t migrate_vcpu_dirty_limit_period(void)
@@ -888,6 +906,36 @@ AnnounceParameters *migrate_announce_params(void)
     return &ap;
 }
 
+void migrate_tls_opts_free(MigrationParameters *params)
+{
+    qapi_free_StrOrNull(params->tls_creds);
+    qapi_free_StrOrNull(params->tls_hostname);
+    qapi_free_StrOrNull(params->tls_authz);
+}
+
+/* needs BQL if dst is part of s->parameters */
+static void tls_option_set_str(StrOrNull **dstp, StrOrNull *src)
+{
+    StrOrNull *dst = *dstp;
+
+    assert(!dst);
+
+    dst = *dstp = g_new0(StrOrNull, 1);
+    dst->type = QTYPE_QSTRING;
+
+    if (!src) {
+        dst->u.s = g_strdup("");
+        return;
+    }
+
+    if (src->type == QTYPE_QSTRING) {
+        dst->u.s = g_strdup(src->u.s);
+    } else {
+        assert(src->type == QTYPE_QNULL);
+        dst->u.s = g_strdup("");
+    }
+}
+
 MigrationParameters *qmp_query_migrate_parameters(Error **errp)
 {
     MigrationParameters *params;
@@ -903,10 +951,11 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp)
     params->cpu_throttle_increment = s->parameters.cpu_throttle_increment;
     params->has_cpu_throttle_tailslow = true;
     params->cpu_throttle_tailslow = s->parameters.cpu_throttle_tailslow;
-    params->tls_creds = g_strdup(s->parameters.tls_creds);
-    params->tls_hostname = g_strdup(s->parameters.tls_hostname);
-    params->tls_authz = g_strdup(s->parameters.tls_authz ?
-                                 s->parameters.tls_authz : "");
+
+    tls_option_set_str(&params->tls_creds, s->parameters.tls_creds);
+    tls_option_set_str(&params->tls_hostname, s->parameters.tls_hostname);
+    tls_option_set_str(&params->tls_authz, s->parameters.tls_authz);
+
     params->has_max_bandwidth = true;
     params->max_bandwidth = s->parameters.max_bandwidth;
     params->has_avail_switchover_bandwidth = true;
@@ -963,9 +1012,6 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp)
 
 void migrate_params_init(MigrationParameters *params)
 {
-    params->tls_hostname = g_strdup("");
-    params->tls_creds = g_strdup("");
-
     /* Set has_* up only for parameter checks */
     params->has_throttle_trigger_threshold = true;
     params->has_cpu_throttle_initial = true;
@@ -1142,7 +1188,8 @@ bool migrate_params_check(MigrationParameters *params, Error **errp)
 #ifdef CONFIG_LINUX
     if (migrate_zero_copy_send() &&
         ((params->has_multifd_compression && params->multifd_compression) ||
-         (params->tls_creds && *params->tls_creds))) {
+         (params->tls_creds && params->tls_creds->type == QTYPE_QSTRING &&
+          *params->tls_creds->u.s))) {
         error_setg(errp,
                    "Zero copy only available for non-compressed non-TLS multifd migration");
         return false;
@@ -1204,18 +1251,24 @@ static void migrate_params_test_apply(MigrateSetParameters *params,
     }
 
     if (params->tls_creds) {
-        assert(params->tls_creds->type == QTYPE_QSTRING);
-        dest->tls_creds = params->tls_creds->u.s;
+        tls_option_set_str(&dest->tls_creds, params->tls_creds);
+    } else {
+        /* drop the reference, it's owned by s->parameters */
+        dest->tls_creds = NULL;
     }
 
     if (params->tls_hostname) {
-        assert(params->tls_hostname->type == QTYPE_QSTRING);
-        dest->tls_hostname = params->tls_hostname->u.s;
+        tls_option_set_str(&dest->tls_hostname, params->tls_hostname);
+    } else {
+        /* drop the reference, it's owned by s->parameters */
+        dest->tls_hostname = NULL;
     }
 
     if (params->tls_authz) {
-        assert(params->tls_authz->type == QTYPE_QSTRING);
-        dest->tls_authz = params->tls_authz->u.s;
+        tls_option_set_str(&dest->tls_authz, params->tls_authz);
+    } else {
+        /* drop the reference, it's owned by s->parameters */
+        dest->tls_authz = NULL;
     }
 
     if (params->has_max_bandwidth) {
@@ -1320,21 +1373,18 @@ static void migrate_params_apply(MigrateSetParameters *params, Error **errp)
     }
 
     if (params->tls_creds) {
-        g_free(s->parameters.tls_creds);
-        assert(params->tls_creds->type == QTYPE_QSTRING);
-        s->parameters.tls_creds = g_strdup(params->tls_creds->u.s);
+        qapi_free_StrOrNull(s->parameters.tls_creds);
+        tls_option_set_str(&s->parameters.tls_creds, params->tls_creds);
     }
 
     if (params->tls_hostname) {
-        g_free(s->parameters.tls_hostname);
-        assert(params->tls_hostname->type == QTYPE_QSTRING);
-        s->parameters.tls_hostname = g_strdup(params->tls_hostname->u.s);
+        qapi_free_StrOrNull(s->parameters.tls_hostname);
+        tls_option_set_str(&s->parameters.tls_hostname, params->tls_hostname);
     }
 
     if (params->tls_authz) {
-        g_free(s->parameters.tls_authz);
-        assert(params->tls_authz->type == QTYPE_QSTRING);
-        s->parameters.tls_authz = g_strdup(params->tls_authz->u.s);
+        qapi_free_StrOrNull(s->parameters.tls_authz);
+        tls_option_set_str(&s->parameters.tls_authz, params->tls_authz);
     }
 
     if (params->has_max_bandwidth) {
@@ -1433,32 +1483,11 @@ void qmp_migrate_set_parameters(MigrateSetParameters *params, Error **errp)
 {
     MigrationParameters tmp;
 
-    /* TODO Rewrite "" to null instead for all three tls_* parameters */
-    if (params->tls_creds
-        && params->tls_creds->type == QTYPE_QNULL) {
-        qobject_unref(params->tls_creds->u.n);
-        params->tls_creds->type = QTYPE_QSTRING;
-        params->tls_creds->u.s = strdup("");
-    }
-    if (params->tls_hostname
-        && params->tls_hostname->type == QTYPE_QNULL) {
-        qobject_unref(params->tls_hostname->u.n);
-        params->tls_hostname->type = QTYPE_QSTRING;
-        params->tls_hostname->u.s = strdup("");
-    }
-    if (params->tls_authz
-        && params->tls_authz->type == QTYPE_QNULL) {
-        qobject_unref(params->tls_authz->u.n);
-        params->tls_authz->type = QTYPE_QSTRING;
-        params->tls_authz->u.s = strdup("");
-    }
-
     migrate_params_test_apply(params, &tmp);
 
-    if (!migrate_params_check(&tmp, errp)) {
-        /* Invalid parameter */
-        return;
+    if (migrate_params_check(&tmp, errp)) {
+        migrate_params_apply(params, errp);
     }
 
-    migrate_params_apply(params, errp);
+    migrate_tls_opts_free(&tmp);
 }
diff --git a/migration/options.h b/migration/options.h
index 82d839709e..999eee6f3b 100644
--- a/migration/options.h
+++ b/migration/options.h
@@ -91,4 +91,5 @@ ZeroPageDetection migrate_zero_page_detection(void);
 
 bool migrate_params_check(MigrationParameters *params, Error **errp);
 void migrate_params_init(MigrationParameters *params);
+void migrate_tls_opts_free(MigrationParameters *params);
 #endif
diff --git a/migration/tls.c b/migration/tls.c
index 5cbf952383..8a89d3f767 100644
--- a/migration/tls.c
+++ b/migration/tls.c
@@ -126,7 +126,7 @@ QIOChannelTLS *migration_tls_client_create(QIOChannel *ioc,
     }
 
     const char *tls_hostname = migrate_tls_hostname();
-    if (tls_hostname && *tls_hostname) {
+    if (tls_hostname) {
         hostname = tls_hostname;
     }
 
diff --git a/qapi/migration.json b/qapi/migration.json
index 41826bde45..fa42d94810 100644
--- a/qapi/migration.json
+++ b/qapi/migration.json
@@ -1293,9 +1293,9 @@
             '*cpu-throttle-initial': 'uint8',
             '*cpu-throttle-increment': 'uint8',
             '*cpu-throttle-tailslow': 'bool',
-            '*tls-creds': 'str',
-            '*tls-hostname': 'str',
-            '*tls-authz': 'str',
+            '*tls-creds': 'StrOrNull',
+            '*tls-hostname': 'StrOrNull',
+            '*tls-authz': 'StrOrNull',
             '*max-bandwidth': 'size',
             '*avail-switchover-bandwidth': 'size',
             '*downtime-limit': 'uint64',
-- 
2.35.3



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

* [PATCH 02/21] migration: Remove MigrateSetParameters
  2025-06-03  1:37 [PATCH 00/21] migration: Unify capabilities and parameters Fabiano Rosas
  2025-06-03  1:37 ` [PATCH 01/21] migration: Normalize tls arguments Fabiano Rosas
@ 2025-06-03  1:37 ` Fabiano Rosas
  2025-06-05 20:58   ` Peter Xu
  2025-06-25 11:31   ` Markus Armbruster
  2025-06-03  1:37 ` [PATCH 03/21] qapi/migration: Don't document MigrationParameter Fabiano Rosas
                   ` (19 subsequent siblings)
  21 siblings, 2 replies; 83+ messages in thread
From: Fabiano Rosas @ 2025-06-03  1:37 UTC (permalink / raw)
  To: qemu-devel; +Cc: Peter Xu, Markus Armbruster, Daniel P . Berrangé

Now that the TLS options have been made the same between
migrate-set-parameters and query-migrate-parameters, a single type can
be used. Remove MigrateSetParameters.

The TLS options documentation from MigrationParameters were replaced
with the ones from MigrateSetParameters which was more complete.

I'm choosing to somewhat ignore any ambiguity between "query" and
"set" because other options' docs are already ambiguous in that
regard.

Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
 migration/migration-hmp-cmds.c |   4 +-
 migration/options.c            |   6 +-
 qapi/migration.json            | 221 +++------------------------------
 3 files changed, 20 insertions(+), 211 deletions(-)

diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c
index bc8179c582..aacffdc532 100644
--- a/migration/migration-hmp-cmds.c
+++ b/migration/migration-hmp-cmds.c
@@ -490,7 +490,7 @@ void hmp_migrate_set_parameter(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);
-    MigrateSetParameters *p = g_new0(MigrateSetParameters, 1);
+    MigrationParameters *p = g_new0(MigrationParameters, 1);
     uint64_t valuebw = 0;
     uint64_t cache_size;
     Error *err = NULL;
@@ -656,7 +656,7 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict)
     qmp_migrate_set_parameters(p, &err);
 
  cleanup:
-    qapi_free_MigrateSetParameters(p);
+    qapi_free_MigrationParameters(p);
     visit_free(v);
     hmp_handle_error(mon, err);
 }
diff --git a/migration/options.c b/migration/options.c
index 45a95dc6da..e49d584a99 100644
--- a/migration/options.c
+++ b/migration/options.c
@@ -1227,7 +1227,7 @@ bool migrate_params_check(MigrationParameters *params, Error **errp)
     return true;
 }
 
-static void migrate_params_test_apply(MigrateSetParameters *params,
+static void migrate_params_test_apply(MigrationParameters *params,
                                       MigrationParameters *dest)
 {
     *dest = migrate_get_current()->parameters;
@@ -1350,7 +1350,7 @@ static void migrate_params_test_apply(MigrateSetParameters *params,
     }
 }
 
-static void migrate_params_apply(MigrateSetParameters *params, Error **errp)
+static void migrate_params_apply(MigrationParameters *params, Error **errp)
 {
     MigrationState *s = migrate_get_current();
 
@@ -1479,7 +1479,7 @@ static void migrate_params_apply(MigrateSetParameters *params, Error **errp)
     }
 }
 
-void qmp_migrate_set_parameters(MigrateSetParameters *params, Error **errp)
+void qmp_migrate_set_parameters(MigrationParameters *params, Error **errp)
 {
     MigrationParameters tmp;
 
diff --git a/qapi/migration.json b/qapi/migration.json
index fa42d94810..080968993a 100644
--- a/qapi/migration.json
+++ b/qapi/migration.json
@@ -914,202 +914,6 @@
            'zero-page-detection',
            'direct-io'] }
 
-##
-# @MigrateSetParameters:
-#
-# @announce-initial: Initial delay (in milliseconds) before sending
-#     the first announce (Since 4.0)
-#
-# @announce-max: Maximum delay (in milliseconds) between packets in
-#     the announcement (Since 4.0)
-#
-# @announce-rounds: Number of self-announce packets sent after
-#     migration (Since 4.0)
-#
-# @announce-step: Increase in delay (in milliseconds) between
-#     subsequent packets in the announcement (Since 4.0)
-#
-# @throttle-trigger-threshold: The ratio of bytes_dirty_period and
-#     bytes_xfer_period to trigger throttling.  It is expressed as
-#     percentage.  The default value is 50.  (Since 5.0)
-#
-# @cpu-throttle-initial: Initial percentage of time guest cpus are
-#     throttled when migration auto-converge is activated.  The
-#     default value is 20.  (Since 2.7)
-#
-# @cpu-throttle-increment: throttle percentage increase each time
-#     auto-converge detects that migration is not making progress.
-#     The default value is 10.  (Since 2.7)
-#
-# @cpu-throttle-tailslow: Make CPU throttling slower at tail stage At
-#     the tail stage of throttling, the Guest is very sensitive to CPU
-#     percentage while the @cpu-throttle -increment is excessive
-#     usually at tail stage.  If this parameter is true, we will
-#     compute the ideal CPU percentage used by the Guest, which may
-#     exactly make the dirty rate match the dirty rate threshold.
-#     Then we will choose a smaller throttle increment between the one
-#     specified by @cpu-throttle-increment and the one generated by
-#     ideal CPU percentage.  Therefore, it is compatible to
-#     traditional throttling, meanwhile the throttle increment won't
-#     be excessive at tail stage.  The default value is false.  (Since
-#     5.1)
-#
-# @tls-creds: ID of the 'tls-creds' object that provides credentials
-#     for establishing a TLS connection over the migration data
-#     channel.  On the outgoing side of the migration, the credentials
-#     must be for a 'client' endpoint, while for the incoming side the
-#     credentials must be for a 'server' endpoint.  Setting this to a
-#     non-empty string enables TLS for all migrations.  An empty
-#     string means that QEMU will use plain text mode for migration,
-#     rather than TLS.  This is the default.  (Since 2.7)
-#
-# @tls-hostname: migration target's hostname for validating the
-#     server's x509 certificate identity.  If empty, QEMU will use the
-#     hostname from the migration URI, if any.  A non-empty value is
-#     required when using x509 based TLS credentials and the migration
-#     URI does not include a hostname, such as fd: or exec: based
-#     migration.  (Since 2.7)
-#
-#     Note: empty value works only since 2.9.
-#
-# @tls-authz: ID of the 'authz' object subclass that provides access
-#     control checking of the TLS x509 certificate distinguished name.
-#     This object is only resolved at time of use, so can be deleted
-#     and recreated on the fly while the migration server is active.
-#     If missing, it will default to denying access (Since 4.0)
-#
-# @max-bandwidth: maximum speed for migration, in bytes per second.
-#     (Since 2.8)
-#
-# @avail-switchover-bandwidth: to set the available bandwidth that
-#     migration can use during switchover phase.  NOTE!  This does not
-#     limit the bandwidth during switchover, but only for calculations
-#     when making decisions to switchover.  By default, this value is
-#     zero, which means QEMU will estimate the bandwidth
-#     automatically.  This can be set when the estimated value is not
-#     accurate, while the user is able to guarantee such bandwidth is
-#     available when switching over.  When specified correctly, this
-#     can make the switchover decision much more accurate.
-#     (Since 8.2)
-#
-# @downtime-limit: set maximum tolerated downtime for migration.
-#     maximum downtime in milliseconds (Since 2.8)
-#
-# @x-checkpoint-delay: The delay time (in ms) between two COLO
-#     checkpoints in periodic mode.  (Since 2.8)
-#
-# @multifd-channels: Number of channels used to migrate data in
-#     parallel.  This is the same number that the number of sockets
-#     used for migration.  The default value is 2 (since 4.0)
-#
-# @xbzrle-cache-size: cache size to be used by XBZRLE migration.  It
-#     needs to be a multiple of the target page size and a power of 2
-#     (Since 2.11)
-#
-# @max-postcopy-bandwidth: Background transfer bandwidth during
-#     postcopy.  Defaults to 0 (unlimited).  In bytes per second.
-#     (Since 3.0)
-#
-# @max-cpu-throttle: maximum cpu throttle percentage.  Defaults to 99.
-#     (Since 3.1)
-#
-# @multifd-compression: Which compression method to use.  Defaults to
-#     none.  (Since 5.0)
-#
-# @multifd-zlib-level: Set the compression level to be used in live
-#     migration, the compression level is an integer between 0 and 9,
-#     where 0 means no compression, 1 means the best compression
-#     speed, and 9 means best compression ratio which will consume
-#     more CPU.  Defaults to 1.  (Since 5.0)
-#
-# @multifd-qatzip-level: Set the compression level to be used in live
-#     migration. The level is an integer between 1 and 9, where 1 means
-#     the best compression speed, and 9 means the best compression
-#     ratio which will consume more CPU. Defaults to 1.  (Since 9.2)
-#
-# @multifd-zstd-level: Set the compression level to be used in live
-#     migration, the compression level is an integer between 0 and 20,
-#     where 0 means no compression, 1 means the best compression
-#     speed, and 20 means best compression ratio which will consume
-#     more CPU.  Defaults to 1.  (Since 5.0)
-#
-# @block-bitmap-mapping: Maps block nodes and bitmaps on them to
-#     aliases for the purpose of dirty bitmap migration.  Such aliases
-#     may for example be the corresponding names on the opposite site.
-#     The mapping must be one-to-one, but not necessarily complete: On
-#     the source, unmapped bitmaps and all bitmaps on unmapped nodes
-#     will be ignored.  On the destination, encountering an unmapped
-#     alias in the incoming migration stream will result in a report,
-#     and all further bitmap migration data will then be discarded.
-#     Note that the destination does not know about bitmaps it does
-#     not receive, so there is no limitation or requirement regarding
-#     the number of bitmaps received, or how they are named, or on
-#     which nodes they are placed.  By default (when this parameter
-#     has never been set), bitmap names are mapped to themselves.
-#     Nodes are mapped to their block device name if there is one, and
-#     to their node name otherwise.  (Since 5.2)
-#
-# @x-vcpu-dirty-limit-period: Periodic time (in milliseconds) of dirty
-#     limit during live migration.  Should be in the range 1 to
-#     1000ms.  Defaults to 1000ms.  (Since 8.1)
-#
-# @vcpu-dirty-limit: Dirtyrate limit (MB/s) during live migration.
-#     Defaults to 1.  (Since 8.1)
-#
-# @mode: Migration mode.  See description in @MigMode.  Default is
-#     'normal'.  (Since 8.2)
-#
-# @zero-page-detection: Whether and how to detect zero pages.
-#     See description in @ZeroPageDetection.  Default is 'multifd'.
-#     (since 9.0)
-#
-# @direct-io: Open migration files with O_DIRECT when possible.  This
-#     only has effect if the @mapped-ram capability is enabled.
-#     (Since 9.1)
-#
-# Features:
-#
-# @unstable: Members @x-checkpoint-delay and
-#     @x-vcpu-dirty-limit-period are experimental.
-#
-# TODO: either fuse back into MigrationParameters, or make
-#     MigrationParameters members mandatory
-#
-# Since: 2.4
-##
-{ 'struct': 'MigrateSetParameters',
-  'data': { '*announce-initial': 'size',
-            '*announce-max': 'size',
-            '*announce-rounds': 'size',
-            '*announce-step': 'size',
-            '*throttle-trigger-threshold': 'uint8',
-            '*cpu-throttle-initial': 'uint8',
-            '*cpu-throttle-increment': 'uint8',
-            '*cpu-throttle-tailslow': 'bool',
-            '*tls-creds': 'StrOrNull',
-            '*tls-hostname': 'StrOrNull',
-            '*tls-authz': 'StrOrNull',
-            '*max-bandwidth': 'size',
-            '*avail-switchover-bandwidth': 'size',
-            '*downtime-limit': 'uint64',
-            '*x-checkpoint-delay': { 'type': 'uint32',
-                                     'features': [ 'unstable' ] },
-            '*multifd-channels': 'uint8',
-            '*xbzrle-cache-size': 'size',
-            '*max-postcopy-bandwidth': 'size',
-            '*max-cpu-throttle': 'uint8',
-            '*multifd-compression': 'MultiFDCompression',
-            '*multifd-zlib-level': 'uint8',
-            '*multifd-qatzip-level': 'uint8',
-            '*multifd-zstd-level': 'uint8',
-            '*block-bitmap-mapping': [ 'BitmapMigrationNodeAlias' ],
-            '*x-vcpu-dirty-limit-period': { 'type': 'uint64',
-                                            'features': [ 'unstable' ] },
-            '*vcpu-dirty-limit': 'uint64',
-            '*mode': 'MigMode',
-            '*zero-page-detection': 'ZeroPageDetection',
-            '*direct-io': 'bool' } }
-
 ##
 # @migrate-set-parameters:
 #
@@ -1124,12 +928,13 @@
 #     <- { "return": {} }
 ##
 { 'command': 'migrate-set-parameters', 'boxed': true,
-  'data': 'MigrateSetParameters' }
+  'data': 'MigrationParameters' }
 
 ##
 # @MigrationParameters:
 #
-# The optional members aren't actually optional.
+# Migration parameters. Optional members are optional when used with
+# an input command, otherwise mandatory.
 #
 # @announce-initial: Initial delay (in milliseconds) before sending
 #     the first announce (Since 4.0)
@@ -1172,21 +977,25 @@
 #     for establishing a TLS connection over the migration data
 #     channel.  On the outgoing side of the migration, the credentials
 #     must be for a 'client' endpoint, while for the incoming side the
-#     credentials must be for a 'server' endpoint.  An empty string
-#     means that QEMU will use plain text mode for migration, rather
-#     than TLS.  (Since 2.7)
-#
-#     Note: 2.8 omits empty @tls-creds instead.
+#     credentials must be for a 'server' endpoint.  Setting this to a
+#     non-empty string enables TLS for all migrations.  An empty
+#     string means that QEMU will use plain text mode for migration,
+#     rather than TLS.  This is the default.  (Since 2.7)
 #
 # @tls-hostname: migration target's hostname for validating the
 #     server's x509 certificate identity.  If empty, QEMU will use the
-#     hostname from the migration URI, if any.  (Since 2.7)
+#     hostname from the migration URI, if any.  A non-empty value is
+#     required when using x509 based TLS credentials and the migration
+#     URI does not include a hostname, such as fd: or exec: based
+#     migration.  (Since 2.7)
 #
-#     Note: 2.8 omits empty @tls-hostname instead.
+#     Note: empty value works only since 2.9.
 #
 # @tls-authz: ID of the 'authz' object subclass that provides access
 #     control checking of the TLS x509 certificate distinguished name.
-#     (Since 4.0)
+#     This object is only resolved at time of use, so can be deleted
+#     and recreated on the fly while the migration server is active.
+#     If missing, it will default to denying access (Since 4.0)
 #
 # @max-bandwidth: maximum speed for migration, in bytes per second.
 #     (Since 2.8)
-- 
2.35.3



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

* [PATCH 03/21] qapi/migration: Don't document MigrationParameter
  2025-06-03  1:37 [PATCH 00/21] migration: Unify capabilities and parameters Fabiano Rosas
  2025-06-03  1:37 ` [PATCH 01/21] migration: Normalize tls arguments Fabiano Rosas
  2025-06-03  1:37 ` [PATCH 02/21] migration: Remove MigrateSetParameters Fabiano Rosas
@ 2025-06-03  1:37 ` Fabiano Rosas
  2025-06-05 21:00   ` Peter Xu
  2025-06-25 12:04   ` Markus Armbruster
  2025-06-03  1:37 ` [PATCH 04/21] migration: Run a post update routine after setting parameters Fabiano Rosas
                   ` (18 subsequent siblings)
  21 siblings, 2 replies; 83+ messages in thread
From: Fabiano Rosas @ 2025-06-03  1:37 UTC (permalink / raw)
  To: qemu-devel; +Cc: Peter Xu, Markus Armbruster, Daniel P . Berrangé

The MigrationParameter (singular) enumeration is not part of the
migration QMP API, it's only used for nicely converting HMP strings
into MigrationParameters (plural) members and for providing readline
completion.

Documenting this enum only serves to duplicate documentation between
MigrationParameter and MigrationParameters.

Add an exception to QAPIs pragma.json and stop documenting it.

Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
 qapi/migration.json | 152 +-------------------------------------------
 qapi/pragma.json    |   3 +-
 2 files changed, 3 insertions(+), 152 deletions(-)

diff --git a/qapi/migration.json b/qapi/migration.json
index 080968993a..452e6dedaa 100644
--- a/qapi/migration.json
+++ b/qapi/migration.json
@@ -734,157 +734,7 @@
 ##
 # @MigrationParameter:
 #
-# Migration parameters enumeration
-#
-# @announce-initial: Initial delay (in milliseconds) before sending
-#     the first announce (Since 4.0)
-#
-# @announce-max: Maximum delay (in milliseconds) between packets in
-#     the announcement (Since 4.0)
-#
-# @announce-rounds: Number of self-announce packets sent after
-#     migration (Since 4.0)
-#
-# @announce-step: Increase in delay (in milliseconds) between
-#     subsequent packets in the announcement (Since 4.0)
-#
-# @throttle-trigger-threshold: The ratio of bytes_dirty_period and
-#     bytes_xfer_period to trigger throttling.  It is expressed as
-#     percentage.  The default value is 50.  (Since 5.0)
-#
-# @cpu-throttle-initial: Initial percentage of time guest cpus are
-#     throttled when migration auto-converge is activated.  The
-#     default value is 20.  (Since 2.7)
-#
-# @cpu-throttle-increment: throttle percentage increase each time
-#     auto-converge detects that migration is not making progress.
-#     The default value is 10.  (Since 2.7)
-#
-# @cpu-throttle-tailslow: Make CPU throttling slower at tail stage At
-#     the tail stage of throttling, the Guest is very sensitive to CPU
-#     percentage while the @cpu-throttle -increment is excessive
-#     usually at tail stage.  If this parameter is true, we will
-#     compute the ideal CPU percentage used by the Guest, which may
-#     exactly make the dirty rate match the dirty rate threshold.
-#     Then we will choose a smaller throttle increment between the one
-#     specified by @cpu-throttle-increment and the one generated by
-#     ideal CPU percentage.  Therefore, it is compatible to
-#     traditional throttling, meanwhile the throttle increment won't
-#     be excessive at tail stage.  The default value is false.  (Since
-#     5.1)
-#
-# @tls-creds: ID of the 'tls-creds' object that provides credentials
-#     for establishing a TLS connection over the migration data
-#     channel.  On the outgoing side of the migration, the credentials
-#     must be for a 'client' endpoint, while for the incoming side the
-#     credentials must be for a 'server' endpoint.  Setting this to a
-#     non-empty string enables TLS for all migrations.  An empty
-#     string means that QEMU will use plain text mode for migration,
-#     rather than TLS.  (Since 2.7)
-#
-# @tls-hostname: migration target's hostname for validating the
-#     server's x509 certificate identity.  If empty, QEMU will use the
-#     hostname from the migration URI, if any.  A non-empty value is
-#     required when using x509 based TLS credentials and the migration
-#     URI does not include a hostname, such as fd: or exec: based
-#     migration.  (Since 2.7)
-#
-#     Note: empty value works only since 2.9.
-#
-# @tls-authz: ID of the 'authz' object subclass that provides access
-#     control checking of the TLS x509 certificate distinguished name.
-#     This object is only resolved at time of use, so can be deleted
-#     and recreated on the fly while the migration server is active.
-#     If missing, it will default to denying access (Since 4.0)
-#
-# @max-bandwidth: maximum speed for migration, in bytes per second.
-#     (Since 2.8)
-#
-# @avail-switchover-bandwidth: to set the available bandwidth that
-#     migration can use during switchover phase.  NOTE!  This does not
-#     limit the bandwidth during switchover, but only for calculations
-#     when making decisions to switchover.  By default, this value is
-#     zero, which means QEMU will estimate the bandwidth
-#     automatically.  This can be set when the estimated value is not
-#     accurate, while the user is able to guarantee such bandwidth is
-#     available when switching over.  When specified correctly, this
-#     can make the switchover decision much more accurate.
-#     (Since 8.2)
-#
-# @downtime-limit: set maximum tolerated downtime for migration.
-#     maximum downtime in milliseconds (Since 2.8)
-#
-# @x-checkpoint-delay: The delay time (in ms) between two COLO
-#     checkpoints in periodic mode.  (Since 2.8)
-#
-# @multifd-channels: Number of channels used to migrate data in
-#     parallel.  This is the same number that the number of sockets
-#     used for migration.  The default value is 2 (since 4.0)
-#
-# @xbzrle-cache-size: cache size to be used by XBZRLE migration.  It
-#     needs to be a multiple of the target page size and a power of 2
-#     (Since 2.11)
-#
-# @max-postcopy-bandwidth: Background transfer bandwidth during
-#     postcopy.  Defaults to 0 (unlimited).  In bytes per second.
-#     (Since 3.0)
-#
-# @max-cpu-throttle: maximum cpu throttle percentage.  Defaults to 99.
-#     (Since 3.1)
-#
-# @multifd-compression: Which compression method to use.  Defaults to
-#     none.  (Since 5.0)
-#
-# @multifd-zlib-level: Set the compression level to be used in live
-#     migration, the compression level is an integer between 0 and 9,
-#     where 0 means no compression, 1 means the best compression
-#     speed, and 9 means best compression ratio which will consume
-#     more CPU.  Defaults to 1.  (Since 5.0)
-#
-# @multifd-qatzip-level: Set the compression level to be used in live
-#     migration. The level is an integer between 1 and 9, where 1 means
-#     the best compression speed, and 9 means the best compression
-#     ratio which will consume more CPU. Defaults to 1.  (Since 9.2)
-#
-# @multifd-zstd-level: Set the compression level to be used in live
-#     migration, the compression level is an integer between 0 and 20,
-#     where 0 means no compression, 1 means the best compression
-#     speed, and 20 means best compression ratio which will consume
-#     more CPU.  Defaults to 1.  (Since 5.0)
-#
-# @block-bitmap-mapping: Maps block nodes and bitmaps on them to
-#     aliases for the purpose of dirty bitmap migration.  Such aliases
-#     may for example be the corresponding names on the opposite site.
-#     The mapping must be one-to-one, but not necessarily complete: On
-#     the source, unmapped bitmaps and all bitmaps on unmapped nodes
-#     will be ignored.  On the destination, encountering an unmapped
-#     alias in the incoming migration stream will result in a report,
-#     and all further bitmap migration data will then be discarded.
-#     Note that the destination does not know about bitmaps it does
-#     not receive, so there is no limitation or requirement regarding
-#     the number of bitmaps received, or how they are named, or on
-#     which nodes they are placed.  By default (when this parameter
-#     has never been set), bitmap names are mapped to themselves.
-#     Nodes are mapped to their block device name if there is one, and
-#     to their node name otherwise.  (Since 5.2)
-#
-# @x-vcpu-dirty-limit-period: Periodic time (in milliseconds) of dirty
-#     limit during live migration.  Should be in the range 1 to
-#     1000ms.  Defaults to 1000ms.  (Since 8.1)
-#
-# @vcpu-dirty-limit: Dirtyrate limit (MB/s) during live migration.
-#     Defaults to 1.  (Since 8.1)
-#
-# @mode: Migration mode.  See description in @MigMode.  Default is
-#     'normal'.  (Since 8.2)
-#
-# @zero-page-detection: Whether and how to detect zero pages.
-#     See description in @ZeroPageDetection.  Default is 'multifd'.
-#     (since 9.0)
-#
-# @direct-io: Open migration files with O_DIRECT when possible.  This
-#     only has effect if the @mapped-ram capability is enabled.
-#     (Since 9.1)
+# Migration parameters enumeration. See @MigrationParameters for more info.
 #
 # Features:
 #
diff --git a/qapi/pragma.json b/qapi/pragma.json
index 023a2ef7bc..58133907b6 100644
--- a/qapi/pragma.json
+++ b/qapi/pragma.json
@@ -76,7 +76,8 @@
         'X86CPURegister32',
         'XDbgBlockGraph',
         'YankInstanceType',
-        'blockdev-reopen' ],
+        'blockdev-reopen',
+        'MigrationParameter'],
     # Externally visible types whose member names may use uppercase
     'member-name-exceptions': [     # visible in:
         'ACPISlotType',             # query-acpi-ospm-status
-- 
2.35.3



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

* [PATCH 04/21] migration: Run a post update routine after setting parameters
  2025-06-03  1:37 [PATCH 00/21] migration: Unify capabilities and parameters Fabiano Rosas
                   ` (2 preceding siblings ...)
  2025-06-03  1:37 ` [PATCH 03/21] qapi/migration: Don't document MigrationParameter Fabiano Rosas
@ 2025-06-03  1:37 ` Fabiano Rosas
  2025-06-03  1:37 ` [PATCH 05/21] migration: Add a flag to track block-bitmap-mapping input Fabiano Rosas
                   ` (17 subsequent siblings)
  21 siblings, 0 replies; 83+ messages in thread
From: Fabiano Rosas @ 2025-06-03  1:37 UTC (permalink / raw)
  To: qemu-devel; +Cc: Peter Xu, Markus Armbruster, Daniel P . Berrangé

Some migration parameters are updated immediately once they are set
via migrate-set-parameters. Move that work outside of
migrate_params_apply() and leave that function with the single
responsibility of setting s->parameters and not doing any
side-effects.

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

diff --git a/migration/options.c b/migration/options.c
index e49d584a99..f64e141394 100644
--- a/migration/options.c
+++ b/migration/options.c
@@ -1039,6 +1039,31 @@ void migrate_params_init(MigrationParameters *params)
     params->has_direct_io = true;
 }
 
+static void migrate_post_update_params(MigrationParameters *new, Error **errp)
+{
+    MigrationState *s = migrate_get_current();
+
+    if (new->has_max_bandwidth) {
+        if (s->to_dst_file && !migration_in_postcopy()) {
+            migration_rate_set(new->max_bandwidth);
+        }
+    }
+
+    if (new->has_x_checkpoint_delay) {
+        colo_checkpoint_delay_set();
+    }
+
+    if (new->has_xbzrle_cache_size) {
+        xbzrle_cache_resize(new->xbzrle_cache_size, errp);
+    }
+
+    if (new->has_max_postcopy_bandwidth) {
+        if (s->to_dst_file && migration_in_postcopy()) {
+            migration_rate_set(new->max_postcopy_bandwidth);
+        }
+    }
+}
+
 /*
  * Check whether the parameters are valid. Error will be put into errp
  * (if provided). Return true if valid, otherwise false.
@@ -1350,7 +1375,7 @@ static void migrate_params_test_apply(MigrationParameters *params,
     }
 }
 
-static void migrate_params_apply(MigrationParameters *params, Error **errp)
+static void migrate_params_apply(MigrationParameters *params)
 {
     MigrationState *s = migrate_get_current();
 
@@ -1389,9 +1414,6 @@ static void migrate_params_apply(MigrationParameters *params, Error **errp)
 
     if (params->has_max_bandwidth) {
         s->parameters.max_bandwidth = params->max_bandwidth;
-        if (s->to_dst_file && !migration_in_postcopy()) {
-            migration_rate_set(s->parameters.max_bandwidth);
-        }
     }
 
     if (params->has_avail_switchover_bandwidth) {
@@ -1404,7 +1426,6 @@ static void migrate_params_apply(MigrationParameters *params, Error **errp)
 
     if (params->has_x_checkpoint_delay) {
         s->parameters.x_checkpoint_delay = params->x_checkpoint_delay;
-        colo_checkpoint_delay_set();
     }
 
     if (params->has_multifd_channels) {
@@ -1424,13 +1445,9 @@ static void migrate_params_apply(MigrationParameters *params, Error **errp)
     }
     if (params->has_xbzrle_cache_size) {
         s->parameters.xbzrle_cache_size = params->xbzrle_cache_size;
-        xbzrle_cache_resize(params->xbzrle_cache_size, errp);
     }
     if (params->has_max_postcopy_bandwidth) {
         s->parameters.max_postcopy_bandwidth = params->max_postcopy_bandwidth;
-        if (s->to_dst_file && migration_in_postcopy()) {
-            migration_rate_set(s->parameters.max_postcopy_bandwidth);
-        }
     }
     if (params->has_max_cpu_throttle) {
         s->parameters.max_cpu_throttle = params->max_cpu_throttle;
@@ -1486,7 +1503,8 @@ void qmp_migrate_set_parameters(MigrationParameters *params, Error **errp)
     migrate_params_test_apply(params, &tmp);
 
     if (migrate_params_check(&tmp, errp)) {
-        migrate_params_apply(params, errp);
+        migrate_params_apply(params);
+        migrate_post_update_params(params, errp);
     }
 
     migrate_tls_opts_free(&tmp);
diff --git a/migration/ram.c b/migration/ram.c
index d26dbd37c4..22e462bf72 100644
--- a/migration/ram.c
+++ b/migration/ram.c
@@ -174,7 +174,7 @@ static void XBZRLE_cache_unlock(void)
 /**
  * xbzrle_cache_resize: resize the xbzrle cache
  *
- * This function is called from migrate_params_apply in main
+ * This function is called from migrate_post_update_config in main
  * thread, possibly while a migration is in progress.  A running
  * migration may be using the cache and might finish during this call,
  * hence changes to the cache are protected by XBZRLE.lock().
-- 
2.35.3



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

* [PATCH 05/21] migration: Add a flag to track block-bitmap-mapping input
  2025-06-03  1:37 [PATCH 00/21] migration: Unify capabilities and parameters Fabiano Rosas
                   ` (3 preceding siblings ...)
  2025-06-03  1:37 ` [PATCH 04/21] migration: Run a post update routine after setting parameters Fabiano Rosas
@ 2025-06-03  1:37 ` Fabiano Rosas
  2025-06-06 15:03   ` Peter Xu
  2025-06-03  1:37 ` [PATCH 06/21] migration: Remove checks for s->parameters has_* fields Fabiano Rosas
                   ` (16 subsequent siblings)
  21 siblings, 1 reply; 83+ messages in thread
From: Fabiano Rosas @ 2025-06-03  1:37 UTC (permalink / raw)
  To: qemu-devel; +Cc: Peter Xu, Markus Armbruster, Daniel P . Berrangé

The QAPI converts an empty list on the block-bitmap-mapping input into
a NULL BitmapMigrationNodeAliasList. The empty list is a valid input
for the block-bitmap-mapping option, so commit 3cba22c9ad ("migration:
Fix block_bitmap_mapping migration") started using the
s->parameters.has_block_bitmap_mapping field to tell when the user has
passed in an empty list vs. when no list has been passed at all.

However, using the has_block_bitmap_mapping field of s->parameters is
only possible because MigrationParameters has had its members made
optional due to historical reasons.

In order to make improvements to the way configuration options are set
for a migration, we'd like to reduce the usage of the has_* fields of
the global configuration object (s->parameters).

Add a separate boolean to track the status of the block_bitmap_mapping
option.

(this was verified to not regress iotest 300, which is the test that
3cba22c9ad refers to)

Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
 migration/migration.h | 7 +++++++
 migration/options.c   | 6 +++---
 2 files changed, 10 insertions(+), 3 deletions(-)

diff --git a/migration/migration.h b/migration/migration.h
index d53f7cad84..ab797540b0 100644
--- a/migration/migration.h
+++ b/migration/migration.h
@@ -510,6 +510,13 @@ struct MigrationState {
     bool rdma_migration;
 
     GSource *hup_source;
+
+    /*
+     * The block-bitmap-mapping option is allowed to be an emtpy list,
+     * therefore we need a way to know wheter the user has given
+     * anything as input.
+     */
+    bool has_block_bitmap_mapping;
 };
 
 void migrate_set_state(MigrationStatus *state, MigrationStatus old_state,
diff --git a/migration/options.c b/migration/options.c
index f64e141394..cf77826204 100644
--- a/migration/options.c
+++ b/migration/options.c
@@ -685,7 +685,7 @@ bool migrate_has_block_bitmap_mapping(void)
 {
     MigrationState *s = migrate_get_current();
 
-    return s->parameters.has_block_bitmap_mapping;
+    return s->has_block_bitmap_mapping;
 }
 
 uint32_t migrate_checkpoint_delay(void)
@@ -989,7 +989,7 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp)
     params->has_announce_step = true;
     params->announce_step = s->parameters.announce_step;
 
-    if (s->parameters.has_block_bitmap_mapping) {
+    if (s->has_block_bitmap_mapping) {
         params->has_block_bitmap_mapping = true;
         params->block_bitmap_mapping =
             QAPI_CLONE(BitmapMigrationNodeAliasList,
@@ -1469,7 +1469,7 @@ static void migrate_params_apply(MigrationParameters *params)
         qapi_free_BitmapMigrationNodeAliasList(
             s->parameters.block_bitmap_mapping);
 
-        s->parameters.has_block_bitmap_mapping = true;
+        s->has_block_bitmap_mapping = true;
         s->parameters.block_bitmap_mapping =
             QAPI_CLONE(BitmapMigrationNodeAliasList,
                        params->block_bitmap_mapping);
-- 
2.35.3



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

* [PATCH 06/21] migration: Remove checks for s->parameters has_* fields
  2025-06-03  1:37 [PATCH 00/21] migration: Unify capabilities and parameters Fabiano Rosas
                   ` (4 preceding siblings ...)
  2025-06-03  1:37 ` [PATCH 05/21] migration: Add a flag to track block-bitmap-mapping input Fabiano Rosas
@ 2025-06-03  1:37 ` Fabiano Rosas
  2025-06-06 15:13   ` Peter Xu
  2025-06-03  1:37 ` [PATCH 07/21] migration: Set block_bitmap_mapping unconditionally in query-migrate-parameters Fabiano Rosas
                   ` (15 subsequent siblings)
  21 siblings, 1 reply; 83+ messages in thread
From: Fabiano Rosas @ 2025-06-03  1:37 UTC (permalink / raw)
  To: qemu-devel; +Cc: Peter Xu, Markus Armbruster, Daniel P . Berrangé

The migration parameters validation produces a temporary structure
which is the merge of the current parameter values (s->parameters,
MigrationParameters) with the new parameters set by the user
(MigrateSetParameters).

When copying the values from s->parameters into the temporary
structure, the has_* fields are copied along, but when merging the
user-input values (MigrateSetParameters) they are not.

During migrate_params_check(), only the parameters that have the
corresponding has_* field will be checked, so only the parameters that
were initialized in migrate_params_init() will be validated.

This causes (almost) all of the migration parameters to be validated
every time a parameter is set, regardless of which fields the user
touched, but it also skips validation of any values that are not set
in migrate_params_init().

It's not clear what was the intention of the original code, whether to
validate all fields always, or only validate what the user input
changed. Since the current situation is closer to the former option,
make the choice of validating all parameters by removing the checks
for the has_* fields when validating.

Note that bringing the user input into the temporary structure for
validation still needs to look at the has_* fields, otherwise any
parameters not set by the user (i.e. 0) would override the
corresponding value in s->parameters.

Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
 migration/migration.c |   2 -
 migration/options.c   | 104 ++++++++++++------------------------------
 2 files changed, 29 insertions(+), 77 deletions(-)

diff --git a/migration/migration.c b/migration/migration.c
index f65cb81b6d..45e15ffeb6 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -4077,8 +4077,6 @@ static void migration_instance_init(Object *obj)
     qemu_sem_init(&ms->pause_sem, 0);
     qemu_mutex_init(&ms->error_mutex);
 
-    migrate_params_init(&ms->parameters);
-
     qemu_sem_init(&ms->postcopy_pause_sem, 0);
     qemu_sem_init(&ms->rp_state.rp_sem, 0);
     qemu_sem_init(&ms->rp_state.rp_pong_acks, 0);
diff --git a/migration/options.c b/migration/options.c
index cf77826204..d4021bc520 100644
--- a/migration/options.c
+++ b/migration/options.c
@@ -1010,35 +1010,6 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp)
     return params;
 }
 
-void migrate_params_init(MigrationParameters *params)
-{
-    /* Set has_* up only for parameter checks */
-    params->has_throttle_trigger_threshold = true;
-    params->has_cpu_throttle_initial = true;
-    params->has_cpu_throttle_increment = true;
-    params->has_cpu_throttle_tailslow = true;
-    params->has_max_bandwidth = true;
-    params->has_downtime_limit = true;
-    params->has_x_checkpoint_delay = true;
-    params->has_multifd_channels = true;
-    params->has_multifd_compression = true;
-    params->has_multifd_zlib_level = true;
-    params->has_multifd_qatzip_level = true;
-    params->has_multifd_zstd_level = true;
-    params->has_xbzrle_cache_size = true;
-    params->has_max_postcopy_bandwidth = true;
-    params->has_max_cpu_throttle = true;
-    params->has_announce_initial = true;
-    params->has_announce_max = true;
-    params->has_announce_rounds = true;
-    params->has_announce_step = true;
-    params->has_x_vcpu_dirty_limit_period = true;
-    params->has_vcpu_dirty_limit = true;
-    params->has_mode = true;
-    params->has_zero_page_detection = true;
-    params->has_direct_io = true;
-}
-
 static void migrate_post_update_params(MigrationParameters *new, Error **errp)
 {
     MigrationState *s = migrate_get_current();
@@ -1072,34 +1043,31 @@ bool migrate_params_check(MigrationParameters *params, Error **errp)
 {
     ERRP_GUARD();
 
-    if (params->has_throttle_trigger_threshold &&
-        (params->throttle_trigger_threshold < 1 ||
-         params->throttle_trigger_threshold > 100)) {
+    if (params->throttle_trigger_threshold < 1 ||
+        params->throttle_trigger_threshold > 100) {
         error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
                    "throttle_trigger_threshold",
                    "an integer in the range of 1 to 100");
         return false;
     }
 
-    if (params->has_cpu_throttle_initial &&
-        (params->cpu_throttle_initial < 1 ||
-         params->cpu_throttle_initial > 99)) {
+    if (params->cpu_throttle_initial < 1 ||
+        params->cpu_throttle_initial > 99) {
         error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
                    "cpu_throttle_initial",
                    "an integer in the range of 1 to 99");
         return false;
     }
 
-    if (params->has_cpu_throttle_increment &&
-        (params->cpu_throttle_increment < 1 ||
-         params->cpu_throttle_increment > 99)) {
+    if (params->cpu_throttle_increment < 1 ||
+        params->cpu_throttle_increment > 99) {
         error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
                    "cpu_throttle_increment",
                    "an integer in the range of 1 to 99");
         return false;
     }
 
-    if (params->has_max_bandwidth && (params->max_bandwidth > SIZE_MAX)) {
+    if (params->max_bandwidth > SIZE_MAX) {
         error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
                    "max_bandwidth",
                    "an integer in the range of 0 to "stringify(SIZE_MAX)
@@ -1107,8 +1075,7 @@ bool migrate_params_check(MigrationParameters *params, Error **errp)
         return false;
     }
 
-    if (params->has_avail_switchover_bandwidth &&
-        (params->avail_switchover_bandwidth > SIZE_MAX)) {
+    if (params->avail_switchover_bandwidth > SIZE_MAX) {
         error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
                    "avail_switchover_bandwidth",
                    "an integer in the range of 0 to "stringify(SIZE_MAX)
@@ -1116,8 +1083,7 @@ bool migrate_params_check(MigrationParameters *params, Error **errp)
         return false;
     }
 
-    if (params->has_downtime_limit &&
-        (params->downtime_limit > MAX_MIGRATE_DOWNTIME)) {
+    if (params->downtime_limit > MAX_MIGRATE_DOWNTIME) {
         error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
                    "downtime_limit",
                    "an integer in the range of 0 to "
@@ -1127,92 +1093,82 @@ bool migrate_params_check(MigrationParameters *params, Error **errp)
 
     /* x_checkpoint_delay is now always positive */
 
-    if (params->has_multifd_channels && (params->multifd_channels < 1)) {
+    if (params->multifd_channels < 1) {
         error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
                    "multifd_channels",
                    "a value between 1 and 255");
         return false;
     }
 
-    if (params->has_multifd_zlib_level &&
-        (params->multifd_zlib_level > 9)) {
+    if (params->multifd_zlib_level > 9) {
         error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "multifd_zlib_level",
                    "a value between 0 and 9");
         return false;
     }
 
-    if (params->has_multifd_qatzip_level &&
-        ((params->multifd_qatzip_level > 9) ||
-        (params->multifd_qatzip_level < 1))) {
+    if (params->multifd_qatzip_level > 9 ||
+        params->multifd_qatzip_level < 1) {
         error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "multifd_qatzip_level",
                    "a value between 1 and 9");
         return false;
     }
 
-    if (params->has_multifd_zstd_level &&
-        (params->multifd_zstd_level > 20)) {
+    if (params->multifd_zstd_level > 20) {
         error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "multifd_zstd_level",
                    "a value between 0 and 20");
         return false;
     }
 
-    if (params->has_xbzrle_cache_size &&
-        (params->xbzrle_cache_size < qemu_target_page_size() ||
-         !is_power_of_2(params->xbzrle_cache_size))) {
+    if (params->xbzrle_cache_size < qemu_target_page_size() ||
+        !is_power_of_2(params->xbzrle_cache_size)) {
         error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
                    "xbzrle_cache_size",
                    "a power of two no less than the target page size");
         return false;
     }
 
-    if (params->has_max_cpu_throttle &&
-        (params->max_cpu_throttle < params->cpu_throttle_initial ||
-         params->max_cpu_throttle > 99)) {
+    if (params->max_cpu_throttle < params->cpu_throttle_initial ||
+        params->max_cpu_throttle > 99) {
         error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
                    "max_cpu_throttle",
                    "an integer in the range of cpu_throttle_initial to 99");
         return false;
     }
 
-    if (params->has_announce_initial &&
-        params->announce_initial > 100000) {
+    if (params->announce_initial > 100000) {
         error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
                    "announce_initial",
                    "a value between 0 and 100000");
         return false;
     }
-    if (params->has_announce_max &&
-        params->announce_max > 100000) {
+    if (params->announce_max > 100000) {
         error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
                    "announce_max",
                    "a value between 0 and 100000");
        return false;
     }
-    if (params->has_announce_rounds &&
-        params->announce_rounds > 1000) {
+    if (params->announce_rounds > 1000) {
         error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
                    "announce_rounds",
                    "a value between 0 and 1000");
        return false;
     }
-    if (params->has_announce_step &&
-        (params->announce_step < 1 ||
-        params->announce_step > 10000)) {
+    if (params->announce_step < 1 ||
+        params->announce_step > 10000) {
         error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
                    "announce_step",
                    "a value between 0 and 10000");
        return false;
     }
 
-    if (params->has_block_bitmap_mapping &&
-        !check_dirty_bitmap_mig_alias_map(params->block_bitmap_mapping, errp)) {
+    if (!check_dirty_bitmap_mig_alias_map(params->block_bitmap_mapping, errp)) {
         error_prepend(errp, "Invalid mapping given for block-bitmap-mapping: ");
         return false;
     }
 
 #ifdef CONFIG_LINUX
     if (migrate_zero_copy_send() &&
-        ((params->has_multifd_compression && params->multifd_compression) ||
+        (params->multifd_compression ||
          (params->tls_creds && params->tls_creds->type == QTYPE_QSTRING &&
           *params->tls_creds->u.s))) {
         error_setg(errp,
@@ -1228,23 +1184,21 @@ bool migrate_params_check(MigrationParameters *params, Error **errp)
         return false;
     }
 
-    if (params->has_x_vcpu_dirty_limit_period &&
-        (params->x_vcpu_dirty_limit_period < 1 ||
-         params->x_vcpu_dirty_limit_period > 1000)) {
+    if (params->x_vcpu_dirty_limit_period < 1 ||
+        params->x_vcpu_dirty_limit_period > 1000) {
         error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
                    "x-vcpu-dirty-limit-period",
                    "a value between 1 and 1000");
         return false;
     }
 
-    if (params->has_vcpu_dirty_limit &&
-        (params->vcpu_dirty_limit < 1)) {
+    if (params->vcpu_dirty_limit < 1) {
         error_setg(errp,
                    "Parameter 'vcpu_dirty_limit' must be greater than 1 MB/s");
         return false;
     }
 
-    if (params->has_direct_io && params->direct_io && !qemu_has_direct_io()) {
+    if (params->direct_io && !qemu_has_direct_io()) {
         error_setg(errp, "No build-time support for direct-io");
         return false;
     }
-- 
2.35.3



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

* [PATCH 07/21] migration: Set block_bitmap_mapping unconditionally in query-migrate-parameters
  2025-06-03  1:37 [PATCH 00/21] migration: Unify capabilities and parameters Fabiano Rosas
                   ` (5 preceding siblings ...)
  2025-06-03  1:37 ` [PATCH 06/21] migration: Remove checks for s->parameters has_* fields Fabiano Rosas
@ 2025-06-03  1:37 ` Fabiano Rosas
  2025-06-03  1:37 ` [PATCH 08/21] migration: Do away with usage of QERR_INVALID_PARAMETER_VALUE Fabiano Rosas
                   ` (14 subsequent siblings)
  21 siblings, 0 replies; 83+ messages in thread
From: Fabiano Rosas @ 2025-06-03  1:37 UTC (permalink / raw)
  To: qemu-devel; +Cc: Peter Xu, Markus Armbruster, Daniel P . Berrangé

The response type of query-migrate-parameters is MigrationParameters,
the members of which are marked as optional in
migration.json. However, the design of query-migrate-parameters is
that all members are to be non-optional.

The optionality is an attempt at compatibility with
MigrateSetParameters, which needs it to allow migrate-set-parameters
to be given only a subset of parameters.

To satisfy the design of query-migrate-parameters, the
qmp_query_migrate_parameters function sets all (but one) has_* fields
of MigrationParameters to true before returning. The
block-bitmap-mapping parameter is instead set conditionally. This is
incorrect.

Since block-bitmap-mapping is allowed to be passed as input by the
user as an empty list, technically the empty list value output in
query-migrate-parameters has always been valid for that
parameter. Make the parameter non-optional like the rest of
MigrationParameters.

Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
 migration/migration-hmp-cmds.c | 36 ++++++++++++++++------------------
 migration/options.c            | 10 ++++------
 2 files changed, 21 insertions(+), 25 deletions(-)

diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c
index aacffdc532..685c8ebd53 100644
--- a/migration/migration-hmp-cmds.c
+++ b/migration/migration-hmp-cmds.c
@@ -240,6 +240,7 @@ void hmp_info_migrate_capabilities(Monitor *mon, const QDict *qdict)
 void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict)
 {
     MigrationParameters *params;
+    const BitmapMigrationNodeAliasList *bmnal;
 
     params = qmp_query_migrate_parameters(NULL);
 
@@ -319,29 +320,26 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict)
             MigrationParameter_str(MIGRATION_PARAMETER_TLS_AUTHZ),
                        params->tls_authz ? params->tls_authz->u.s : "");
 
-        if (params->has_block_bitmap_mapping) {
-            const BitmapMigrationNodeAliasList *bmnal;
+        assert(params->has_block_bitmap_mapping);
+        monitor_printf(mon, "%s:\n",
+                       MigrationParameter_str(
+                           MIGRATION_PARAMETER_BLOCK_BITMAP_MAPPING));
 
-            monitor_printf(mon, "%s:\n",
-                           MigrationParameter_str(
-                               MIGRATION_PARAMETER_BLOCK_BITMAP_MAPPING));
+        for (bmnal = params->block_bitmap_mapping;
+             bmnal;
+             bmnal = bmnal->next)
+        {
+            const BitmapMigrationNodeAlias *bmna = bmnal->value;
+            const BitmapMigrationBitmapAliasList *bmbal;
 
-            for (bmnal = params->block_bitmap_mapping;
-                 bmnal;
-                 bmnal = bmnal->next)
-            {
-                const BitmapMigrationNodeAlias *bmna = bmnal->value;
-                const BitmapMigrationBitmapAliasList *bmbal;
+            monitor_printf(mon, "  '%s' -> '%s'\n",
+                           bmna->node_name, bmna->alias);
 
-                monitor_printf(mon, "  '%s' -> '%s'\n",
-                               bmna->node_name, bmna->alias);
+            for (bmbal = bmna->bitmaps; bmbal; bmbal = bmbal->next) {
+                const BitmapMigrationBitmapAlias *bmba = bmbal->value;
 
-                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, "    '%s' -> '%s'\n",
+                               bmba->name, bmba->alias);
             }
         }
 
diff --git a/migration/options.c b/migration/options.c
index d4021bc520..190001f8ac 100644
--- a/migration/options.c
+++ b/migration/options.c
@@ -989,12 +989,10 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp)
     params->has_announce_step = true;
     params->announce_step = s->parameters.announce_step;
 
-    if (s->has_block_bitmap_mapping) {
-        params->has_block_bitmap_mapping = true;
-        params->block_bitmap_mapping =
-            QAPI_CLONE(BitmapMigrationNodeAliasList,
-                       s->parameters.block_bitmap_mapping);
-    }
+    params->has_block_bitmap_mapping = true;
+    params->block_bitmap_mapping =
+        QAPI_CLONE(BitmapMigrationNodeAliasList,
+                   s->parameters.block_bitmap_mapping);
 
     params->has_x_vcpu_dirty_limit_period = true;
     params->x_vcpu_dirty_limit_period = s->parameters.x_vcpu_dirty_limit_period;
-- 
2.35.3



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

* [PATCH 08/21] migration: Do away with usage of QERR_INVALID_PARAMETER_VALUE
  2025-06-03  1:37 [PATCH 00/21] migration: Unify capabilities and parameters Fabiano Rosas
                   ` (6 preceding siblings ...)
  2025-06-03  1:37 ` [PATCH 07/21] migration: Set block_bitmap_mapping unconditionally in query-migrate-parameters Fabiano Rosas
@ 2025-06-03  1:37 ` Fabiano Rosas
  2025-06-06 15:17   ` Peter Xu
  2025-06-03  1:37 ` [PATCH 09/21] migration: Extract code to mark all parameters as present Fabiano Rosas
                   ` (13 subsequent siblings)
  21 siblings, 1 reply; 83+ messages in thread
From: Fabiano Rosas @ 2025-06-03  1:37 UTC (permalink / raw)
  To: qemu-devel; +Cc: Peter Xu, Markus Armbruster, Daniel P . Berrangé

The QERR_INVALID_PARAMETER_VALUE macro is documented as not to be used
in new code. Remove the usage from migration/options.c.

Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
 migration/migration.c  |  3 +--
 migration/options.c    | 56 +++++++++++++++---------------------------
 migration/page_cache.c |  6 ++---
 migration/ram.c        |  3 +--
 4 files changed, 24 insertions(+), 44 deletions(-)

diff --git a/migration/migration.c b/migration/migration.c
index 45e15ffeb6..031a5f8ede 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -2302,8 +2302,7 @@ static void qmp_migrate_finish(MigrationAddress *addr, bool resume_requested,
     } else if (addr->transport == MIGRATION_ADDRESS_TYPE_FILE) {
         file_start_outgoing_migration(s, &addr->u.file, &local_err);
     } else {
-        error_setg(&local_err, QERR_INVALID_PARAMETER_VALUE, "uri",
-                   "a valid migration protocol");
+        error_setg(&local_err, "uri is not a valid migration protocol");
         migrate_set_state(&s->state, MIGRATION_STATUS_SETUP,
                           MIGRATION_STATUS_FAILED);
     }
diff --git a/migration/options.c b/migration/options.c
index 190001f8ac..e2e3ab717f 100644
--- a/migration/options.c
+++ b/migration/options.c
@@ -1043,120 +1043,105 @@ bool migrate_params_check(MigrationParameters *params, Error **errp)
 
     if (params->throttle_trigger_threshold < 1 ||
         params->throttle_trigger_threshold > 100) {
-        error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
-                   "throttle_trigger_threshold",
+        error_setg(errp, "Option throttle_trigger_threshold expects "
                    "an integer in the range of 1 to 100");
         return false;
     }
 
     if (params->cpu_throttle_initial < 1 ||
         params->cpu_throttle_initial > 99) {
-        error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
-                   "cpu_throttle_initial",
+        error_setg(errp, "Option cpu_throttle_initial expects "
                    "an integer in the range of 1 to 99");
         return false;
     }
 
     if (params->cpu_throttle_increment < 1 ||
         params->cpu_throttle_increment > 99) {
-        error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
-                   "cpu_throttle_increment",
+        error_setg(errp, "Option cpu_throttle_increment expects "
                    "an integer in the range of 1 to 99");
         return false;
     }
 
     if (params->max_bandwidth > SIZE_MAX) {
-        error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
-                   "max_bandwidth",
+        error_setg(errp, "Option max_bandwidth expects "
                    "an integer in the range of 0 to "stringify(SIZE_MAX)
                    " bytes/second");
         return false;
     }
 
     if (params->avail_switchover_bandwidth > SIZE_MAX) {
-        error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
-                   "avail_switchover_bandwidth",
+        error_setg(errp, "Option avail_switchover_bandwidth expects "
                    "an integer in the range of 0 to "stringify(SIZE_MAX)
                    " bytes/second");
         return false;
     }
 
     if (params->downtime_limit > MAX_MIGRATE_DOWNTIME) {
-        error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
-                   "downtime_limit",
+        error_setg(errp, "Option downtime_limit expects "
                    "an integer in the range of 0 to "
                     stringify(MAX_MIGRATE_DOWNTIME)" ms");
         return false;
     }
 
-    /* x_checkpoint_delay is now always positive */
-
     if (params->multifd_channels < 1) {
-        error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
-                   "multifd_channels",
+        error_setg(errp, "Option multifd_channels expects "
                    "a value between 1 and 255");
         return false;
     }
 
     if (params->multifd_zlib_level > 9) {
-        error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "multifd_zlib_level",
+        error_setg(errp, "Option multifd_zlib_level expects "
                    "a value between 0 and 9");
         return false;
     }
 
     if (params->multifd_qatzip_level > 9 ||
         params->multifd_qatzip_level < 1) {
-        error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "multifd_qatzip_level",
+        error_setg(errp, "Option multifd_qatzip_level expects "
                    "a value between 1 and 9");
         return false;
     }
 
     if (params->multifd_zstd_level > 20) {
-        error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "multifd_zstd_level",
+        error_setg(errp, "Option multifd_zstd_level expects "
                    "a value between 0 and 20");
         return false;
     }
 
     if (params->xbzrle_cache_size < qemu_target_page_size() ||
         !is_power_of_2(params->xbzrle_cache_size)) {
-        error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
-                   "xbzrle_cache_size",
+        error_setg(errp, "Option xbzrle_cache_size expects "
                    "a power of two no less than the target page size");
         return false;
     }
 
     if (params->max_cpu_throttle < params->cpu_throttle_initial ||
         params->max_cpu_throttle > 99) {
-        error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
-                   "max_cpu_throttle",
+        error_setg(errp, "max_Option cpu_throttle expects "
                    "an integer in the range of cpu_throttle_initial to 99");
         return false;
     }
 
     if (params->announce_initial > 100000) {
-        error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
-                   "announce_initial",
+        error_setg(errp, "Option announce_initial expects "
                    "a value between 0 and 100000");
         return false;
     }
     if (params->announce_max > 100000) {
-        error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
-                   "announce_max",
+        error_setg(errp, "Option announce_max expects "
                    "a value between 0 and 100000");
-       return false;
+        return false;
     }
     if (params->announce_rounds > 1000) {
-        error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
-                   "announce_rounds",
+        error_setg(errp, "Option announce_rounds expects "
                    "a value between 0 and 1000");
-       return false;
+        return false;
     }
     if (params->announce_step < 1 ||
         params->announce_step > 10000) {
-        error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
-                   "announce_step",
+        error_setg(errp, "Option announce_step expects "
                    "a value between 0 and 10000");
-       return false;
+        return false;
     }
 
     if (!check_dirty_bitmap_mig_alias_map(params->block_bitmap_mapping, errp)) {
@@ -1184,8 +1169,7 @@ bool migrate_params_check(MigrationParameters *params, Error **errp)
 
     if (params->x_vcpu_dirty_limit_period < 1 ||
         params->x_vcpu_dirty_limit_period > 1000) {
-        error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
-                   "x-vcpu-dirty-limit-period",
+        error_setg(errp, "Option x-vcpu-dirty-limit-period expects "
                    "a value between 1 and 1000");
         return false;
     }
diff --git a/migration/page_cache.c b/migration/page_cache.c
index 6d4f7a9bbc..650b15e48c 100644
--- a/migration/page_cache.c
+++ b/migration/page_cache.c
@@ -45,15 +45,13 @@ PageCache *cache_init(uint64_t new_size, size_t page_size, Error **errp)
     PageCache *cache;
 
     if (new_size < page_size) {
-        error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "cache size",
-                   "is smaller than one target page size");
+        error_setg(errp, "cache size is smaller than target page size");
         return NULL;
     }
 
     /* round down to the nearest power of 2 */
     if (!is_power_of_2(num_pages)) {
-        error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "cache size",
-                   "is not a power of two number of pages");
+        error_setg(errp, "number of pages is not a power of two");
         return NULL;
     }
 
diff --git a/migration/ram.c b/migration/ram.c
index 22e462bf72..028358ef0d 100644
--- a/migration/ram.c
+++ b/migration/ram.c
@@ -191,8 +191,7 @@ int xbzrle_cache_resize(uint64_t new_size, Error **errp)
 
     /* Check for truncation */
     if (new_size != (size_t)new_size) {
-        error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "cache size",
-                   "exceeding address space");
+        error_setg(errp, "xbzrle cache size integer overflow");
         return -1;
     }
 
-- 
2.35.3



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

* [PATCH 09/21] migration: Extract code to mark all parameters as present
  2025-06-03  1:37 [PATCH 00/21] migration: Unify capabilities and parameters Fabiano Rosas
                   ` (7 preceding siblings ...)
  2025-06-03  1:37 ` [PATCH 08/21] migration: Do away with usage of QERR_INVALID_PARAMETER_VALUE Fabiano Rosas
@ 2025-06-03  1:37 ` Fabiano Rosas
  2025-06-06 15:26   ` Peter Xu
  2025-06-03  1:37 ` [PATCH 10/21] migration: Use QAPI_CLONE_MEMBERS in query_migrate_parameters Fabiano Rosas
                   ` (12 subsequent siblings)
  21 siblings, 1 reply; 83+ messages in thread
From: Fabiano Rosas @ 2025-06-03  1:37 UTC (permalink / raw)
  To: qemu-devel; +Cc: Peter Xu, Markus Armbruster, Daniel P . Berrangé

MigrationParameters needs to have all of its has_* fields marked as
true when used as the return of query_migrate_parameters because the
corresponding QMP command has all of its members non-optional by
design, despite them being marked as optional in migration.json.

Extract this code into a function and make it assert if any field is
missing. With this we ensure future changes will not inadvertently
leave any parameters missing.

Also assert that s->parameters _does not_ have any of its has_* fields
set. This structure is internal to the migration code and it should
not rely on the QAPI-generate has_* fields. We might want to store
migration parameters differently in the future.

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

diff --git a/migration/options.c b/migration/options.c
index e2e3ab717f..dd62e726cb 100644
--- a/migration/options.c
+++ b/migration/options.c
@@ -936,6 +936,40 @@ static void tls_option_set_str(StrOrNull **dstp, StrOrNull *src)
     }
 }
 
+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,
+        &p->has_max_bandwidth, &p->has_avail_switchover_bandwidth,
+        &p->has_downtime_limit, &p->has_x_checkpoint_delay,
+        &p->has_multifd_channels, &p->has_multifd_compression,
+        &p->has_multifd_zlib_level, &p->has_multifd_qatzip_level,
+        &p->has_multifd_zstd_level, &p->has_xbzrle_cache_size,
+        &p->has_max_postcopy_bandwidth, &p->has_max_cpu_throttle,
+        &p->has_announce_initial, &p->has_announce_max, &p->has_announce_rounds,
+        &p->has_announce_step, &p->has_block_bitmap_mapping,
+        &p->has_x_vcpu_dirty_limit_period, &p->has_vcpu_dirty_limit,
+        &p->has_mode, &p->has_zero_page_detection, &p->has_direct_io,
+    };
+
+    /*
+     * The has_* fields of MigrationParameters are used by QAPI to
+     * inform whether an optional struct member is present. Keep this
+     * decoupled from the internal usage (not QAPI) by leaving the
+     * has_* fields of s->parameters unused.
+     */
+    assert(p != &(migrate_get_current())->parameters);
+
+    len = ARRAY_SIZE(has_fields);
+    assert(len + n_str_args == MIGRATION_PARAMETER__MAX);
+
+    for (int i = 0; i < len; i++) {
+        *has_fields[i] = true;
+    }
+}
+
 MigrationParameters *qmp_query_migrate_parameters(Error **errp)
 {
     MigrationParameters *params;
@@ -943,68 +977,52 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp)
 
     /* TODO use QAPI_CLONE() instead of duplicating it inline */
     params = g_malloc0(sizeof(*params));
-    params->has_throttle_trigger_threshold = true;
+
     params->throttle_trigger_threshold = s->parameters.throttle_trigger_threshold;
-    params->has_cpu_throttle_initial = true;
     params->cpu_throttle_initial = s->parameters.cpu_throttle_initial;
-    params->has_cpu_throttle_increment = true;
     params->cpu_throttle_increment = s->parameters.cpu_throttle_increment;
-    params->has_cpu_throttle_tailslow = true;
     params->cpu_throttle_tailslow = s->parameters.cpu_throttle_tailslow;
 
     tls_option_set_str(&params->tls_creds, s->parameters.tls_creds);
     tls_option_set_str(&params->tls_hostname, s->parameters.tls_hostname);
     tls_option_set_str(&params->tls_authz, s->parameters.tls_authz);
 
-    params->has_max_bandwidth = true;
     params->max_bandwidth = s->parameters.max_bandwidth;
-    params->has_avail_switchover_bandwidth = true;
     params->avail_switchover_bandwidth = s->parameters.avail_switchover_bandwidth;
-    params->has_downtime_limit = true;
     params->downtime_limit = s->parameters.downtime_limit;
-    params->has_x_checkpoint_delay = true;
     params->x_checkpoint_delay = s->parameters.x_checkpoint_delay;
-    params->has_multifd_channels = true;
     params->multifd_channels = s->parameters.multifd_channels;
-    params->has_multifd_compression = true;
     params->multifd_compression = s->parameters.multifd_compression;
-    params->has_multifd_zlib_level = true;
     params->multifd_zlib_level = s->parameters.multifd_zlib_level;
-    params->has_multifd_qatzip_level = true;
     params->multifd_qatzip_level = s->parameters.multifd_qatzip_level;
-    params->has_multifd_zstd_level = true;
     params->multifd_zstd_level = s->parameters.multifd_zstd_level;
-    params->has_xbzrle_cache_size = true;
     params->xbzrle_cache_size = s->parameters.xbzrle_cache_size;
-    params->has_max_postcopy_bandwidth = true;
     params->max_postcopy_bandwidth = s->parameters.max_postcopy_bandwidth;
-    params->has_max_cpu_throttle = true;
     params->max_cpu_throttle = s->parameters.max_cpu_throttle;
-    params->has_announce_initial = true;
     params->announce_initial = s->parameters.announce_initial;
-    params->has_announce_max = true;
     params->announce_max = s->parameters.announce_max;
-    params->has_announce_rounds = true;
     params->announce_rounds = s->parameters.announce_rounds;
-    params->has_announce_step = true;
     params->announce_step = s->parameters.announce_step;
-
-    params->has_block_bitmap_mapping = true;
     params->block_bitmap_mapping =
         QAPI_CLONE(BitmapMigrationNodeAliasList,
                    s->parameters.block_bitmap_mapping);
-
-    params->has_x_vcpu_dirty_limit_period = true;
     params->x_vcpu_dirty_limit_period = s->parameters.x_vcpu_dirty_limit_period;
-    params->has_vcpu_dirty_limit = true;
     params->vcpu_dirty_limit = s->parameters.vcpu_dirty_limit;
-    params->has_mode = true;
     params->mode = s->parameters.mode;
-    params->has_zero_page_detection = true;
     params->zero_page_detection = s->parameters.zero_page_detection;
-    params->has_direct_io = true;
     params->direct_io = s->parameters.direct_io;
 
+    /*
+     * query-migrate-parameters expects all members of
+     * MigrationParameters to be present, but we cannot mark them
+     * non-optional in QAPI because the structure is also used for
+     * migrate-set-parameters, which needs the optionality. Force all
+     * parameters to be seen as present now. Note that this depends on
+     * some form of default being set for every member of
+     * MigrationParameters, currently done during qdev init using
+     * migration_properties defined in this file.
+     */
+    migrate_mark_all_params_present(params);
     return params;
 }
 
-- 
2.35.3



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

* [PATCH 10/21] migration: Use QAPI_CLONE_MEMBERS in query_migrate_parameters
  2025-06-03  1:37 [PATCH 00/21] migration: Unify capabilities and parameters Fabiano Rosas
                   ` (8 preceding siblings ...)
  2025-06-03  1:37 ` [PATCH 09/21] migration: Extract code to mark all parameters as present Fabiano Rosas
@ 2025-06-03  1:37 ` Fabiano Rosas
  2025-06-06 15:29   ` Peter Xu
  2025-06-03  1:38 ` [PATCH 11/21] migration: Use QAPI_CLONE_MEMBERS in migrate_params_test_apply Fabiano Rosas
                   ` (11 subsequent siblings)
  21 siblings, 1 reply; 83+ messages in thread
From: Fabiano Rosas @ 2025-06-03  1:37 UTC (permalink / raw)
  To: qemu-devel; +Cc: Peter Xu, Markus Armbruster, Daniel P . Berrangé

QAPI_CLONE_MEMBERS is a better option than copying parameters one by
one because it operates on the entire struct and follows pointers. It
also avoids the need to alter this function every time a new parameter
is added.

Note, since this is a deep clone, now we must free the TLS strings
before assignment.

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

diff --git a/migration/options.c b/migration/options.c
index dd62e726cb..0a2a3050ec 100644
--- a/migration/options.c
+++ b/migration/options.c
@@ -918,7 +918,9 @@ static void tls_option_set_str(StrOrNull **dstp, StrOrNull *src)
 {
     StrOrNull *dst = *dstp;
 
-    assert(!dst);
+    if (dst) {
+        qapi_free_StrOrNull(dst);
+    }
 
     dst = *dstp = g_new0(StrOrNull, 1);
     dst->type = QTYPE_QSTRING;
@@ -975,42 +977,17 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp)
     MigrationParameters *params;
     MigrationState *s = migrate_get_current();
 
-    /* TODO use QAPI_CLONE() instead of duplicating it inline */
     params = g_malloc0(sizeof(*params));
 
-    params->throttle_trigger_threshold = s->parameters.throttle_trigger_threshold;
-    params->cpu_throttle_initial = s->parameters.cpu_throttle_initial;
-    params->cpu_throttle_increment = s->parameters.cpu_throttle_increment;
-    params->cpu_throttle_tailslow = s->parameters.cpu_throttle_tailslow;
+    QAPI_CLONE_MEMBERS(MigrationParameters, params, &s->parameters);
 
     tls_option_set_str(&params->tls_creds, s->parameters.tls_creds);
     tls_option_set_str(&params->tls_hostname, s->parameters.tls_hostname);
     tls_option_set_str(&params->tls_authz, s->parameters.tls_authz);
 
-    params->max_bandwidth = s->parameters.max_bandwidth;
-    params->avail_switchover_bandwidth = s->parameters.avail_switchover_bandwidth;
-    params->downtime_limit = s->parameters.downtime_limit;
-    params->x_checkpoint_delay = s->parameters.x_checkpoint_delay;
-    params->multifd_channels = s->parameters.multifd_channels;
-    params->multifd_compression = s->parameters.multifd_compression;
-    params->multifd_zlib_level = s->parameters.multifd_zlib_level;
-    params->multifd_qatzip_level = s->parameters.multifd_qatzip_level;
-    params->multifd_zstd_level = s->parameters.multifd_zstd_level;
-    params->xbzrle_cache_size = s->parameters.xbzrle_cache_size;
-    params->max_postcopy_bandwidth = s->parameters.max_postcopy_bandwidth;
-    params->max_cpu_throttle = s->parameters.max_cpu_throttle;
-    params->announce_initial = s->parameters.announce_initial;
-    params->announce_max = s->parameters.announce_max;
-    params->announce_rounds = s->parameters.announce_rounds;
-    params->announce_step = s->parameters.announce_step;
     params->block_bitmap_mapping =
         QAPI_CLONE(BitmapMigrationNodeAliasList,
                    s->parameters.block_bitmap_mapping);
-    params->x_vcpu_dirty_limit_period = s->parameters.x_vcpu_dirty_limit_period;
-    params->vcpu_dirty_limit = s->parameters.vcpu_dirty_limit;
-    params->mode = s->parameters.mode;
-    params->zero_page_detection = s->parameters.zero_page_detection;
-    params->direct_io = s->parameters.direct_io;
 
     /*
      * query-migrate-parameters expects all members of
-- 
2.35.3



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

* [PATCH 11/21] migration: Use QAPI_CLONE_MEMBERS in migrate_params_test_apply
  2025-06-03  1:37 [PATCH 00/21] migration: Unify capabilities and parameters Fabiano Rosas
                   ` (9 preceding siblings ...)
  2025-06-03  1:37 ` [PATCH 10/21] migration: Use QAPI_CLONE_MEMBERS in query_migrate_parameters Fabiano Rosas
@ 2025-06-03  1:38 ` Fabiano Rosas
  2025-06-03  1:38 ` [PATCH 12/21] migration: Use QAPI_CLONE_MEMBERS in migrate_params_apply Fabiano Rosas
                   ` (10 subsequent siblings)
  21 siblings, 0 replies; 83+ messages in thread
From: Fabiano Rosas @ 2025-06-03  1:38 UTC (permalink / raw)
  To: qemu-devel; +Cc: Peter Xu, Markus Armbruster, Daniel P . Berrangé

Use QAPI_CLONE_MEMBERS instead of making an assignment. The QAPI
method makes the handling of the TLS strings more intuitive because it
clones them as well.

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

diff --git a/migration/options.c b/migration/options.c
index 0a2a3050ec..4453933c4c 100644
--- a/migration/options.c
+++ b/migration/options.c
@@ -1186,9 +1186,9 @@ bool migrate_params_check(MigrationParameters *params, Error **errp)
 static void migrate_params_test_apply(MigrationParameters *params,
                                       MigrationParameters *dest)
 {
-    *dest = migrate_get_current()->parameters;
+    MigrationState *s = migrate_get_current();
 
-    /* TODO use QAPI_CLONE() instead of duplicating it inline */
+    QAPI_CLONE_MEMBERS(MigrationParameters, dest, &s->parameters);
 
     if (params->has_throttle_trigger_threshold) {
         dest->throttle_trigger_threshold = params->throttle_trigger_threshold;
@@ -1208,23 +1208,14 @@ static void migrate_params_test_apply(MigrationParameters *params,
 
     if (params->tls_creds) {
         tls_option_set_str(&dest->tls_creds, params->tls_creds);
-    } else {
-        /* drop the reference, it's owned by s->parameters */
-        dest->tls_creds = NULL;
     }
 
     if (params->tls_hostname) {
         tls_option_set_str(&dest->tls_hostname, params->tls_hostname);
-    } else {
-        /* drop the reference, it's owned by s->parameters */
-        dest->tls_hostname = NULL;
     }
 
     if (params->tls_authz) {
         tls_option_set_str(&dest->tls_authz, params->tls_authz);
-    } else {
-        /* drop the reference, it's owned by s->parameters */
-        dest->tls_authz = NULL;
     }
 
     if (params->has_max_bandwidth) {
@@ -1281,7 +1272,6 @@ static void migrate_params_test_apply(MigrationParameters *params,
     }
 
     if (params->has_block_bitmap_mapping) {
-        dest->has_block_bitmap_mapping = true;
         dest->block_bitmap_mapping = params->block_bitmap_mapping;
     }
 
@@ -1433,6 +1423,14 @@ void qmp_migrate_set_parameters(MigrationParameters *params, Error **errp)
 
     migrate_params_test_apply(params, &tmp);
 
+    /*
+     * Mark block_bitmap_mapping as present now while we have the
+     * params structure with the user input around.
+     */
+    if (params->has_block_bitmap_mapping) {
+        migrate_get_current()->has_block_bitmap_mapping = true;
+    }
+
     if (migrate_params_check(&tmp, errp)) {
         migrate_params_apply(params);
         migrate_post_update_params(params, errp);
-- 
2.35.3



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

* [PATCH 12/21] migration: Use QAPI_CLONE_MEMBERS in migrate_params_apply
  2025-06-03  1:37 [PATCH 00/21] migration: Unify capabilities and parameters Fabiano Rosas
                   ` (10 preceding siblings ...)
  2025-06-03  1:38 ` [PATCH 11/21] migration: Use QAPI_CLONE_MEMBERS in migrate_params_test_apply Fabiano Rosas
@ 2025-06-03  1:38 ` Fabiano Rosas
  2025-06-03  1:38 ` [PATCH 13/21] migration: Use visitors in migrate_params_test_apply Fabiano Rosas
                   ` (9 subsequent siblings)
  21 siblings, 0 replies; 83+ messages in thread
From: Fabiano Rosas @ 2025-06-03  1:38 UTC (permalink / raw)
  To: qemu-devel; +Cc: Peter Xu, Markus Armbruster, Daniel P . Berrangé

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.

Note that s->parameters is part of MigrationState, so it doesn't
require freeing, but the TLS strings do.

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

diff --git a/migration/options.c b/migration/options.c
index 4453933c4c..27b5f549f9 100644
--- a/migration/options.c
+++ b/migration/options.c
@@ -13,6 +13,7 @@
 
 #include "qemu/osdep.h"
 #include "qemu/error-report.h"
+#include "qemu/main-loop.h"
 #include "exec/target_page.h"
 #include "qapi/clone-visitor.h"
 #include "qapi/error.h"
@@ -1299,122 +1300,17 @@ static void migrate_params_test_apply(MigrationParameters *params,
 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 */
+    assert(bql_locked());
 
-    if (params->has_throttle_trigger_threshold) {
-        s->parameters.throttle_trigger_threshold = params->throttle_trigger_threshold;
-    }
+    migrate_tls_opts_free(cur);
+    qapi_free_BitmapMigrationNodeAliasList(cur->block_bitmap_mapping);
 
-    if (params->has_cpu_throttle_initial) {
-        s->parameters.cpu_throttle_initial = params->cpu_throttle_initial;
-    }
+    QAPI_CLONE_MEMBERS(MigrationParameters, cur, params);
 
-    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);
-        tls_option_set_str(&s->parameters.tls_creds, params->tls_creds);
-    }
-
-    if (params->tls_hostname) {
-        qapi_free_StrOrNull(s->parameters.tls_hostname);
-        tls_option_set_str(&s->parameters.tls_hostname, params->tls_hostname);
-    }
-
-    if (params->tls_authz) {
-        qapi_free_StrOrNull(s->parameters.tls_authz);
-        tls_option_set_str(&s->parameters.tls_authz, 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;
-    }
+    cur->block_bitmap_mapping = QAPI_CLONE(BitmapMigrationNodeAliasList,
+                                           params->block_bitmap_mapping);
 }
 
 void qmp_migrate_set_parameters(MigrationParameters *params, Error **errp)
@@ -1432,7 +1328,7 @@ void qmp_migrate_set_parameters(MigrationParameters *params, Error **errp)
     }
 
     if (migrate_params_check(&tmp, errp)) {
-        migrate_params_apply(params);
+        migrate_params_apply(&tmp);
         migrate_post_update_params(params, errp);
     }
 
-- 
2.35.3



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

* [PATCH 13/21] migration: Use visitors in migrate_params_test_apply
  2025-06-03  1:37 [PATCH 00/21] migration: Unify capabilities and parameters Fabiano Rosas
                   ` (11 preceding siblings ...)
  2025-06-03  1:38 ` [PATCH 12/21] migration: Use QAPI_CLONE_MEMBERS in migrate_params_apply Fabiano Rosas
@ 2025-06-03  1:38 ` Fabiano Rosas
  2025-06-03  1:38 ` [PATCH 14/21] migration: Cleanup hmp_info_migrate_parameters Fabiano Rosas
                   ` (8 subsequent siblings)
  21 siblings, 0 replies; 83+ messages in thread
From: Fabiano Rosas @ 2025-06-03  1:38 UTC (permalink / raw)
  To: qemu-devel; +Cc: Peter Xu, Markus Armbruster, Daniel P . Berrangé

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

This hides the details of QAPI from the migration code and avoids the
need to update migrate_params_test_apply() every time a new migration
parameter is added. Both were very confusing and while the visitor
code can become a bit involved, there is no need for new contributors
to ever touch it.

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

diff --git a/migration/options.c b/migration/options.c
index 27b5f549f9..ac4b06da7e 100644
--- a/migration/options.c
+++ b/migration/options.c
@@ -20,6 +20,10 @@
 #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 "qapi/visitor.h"
+#include "qobject/qdict.h"
 #include "qobject/qnull.h"
 #include "system/runstate.h"
 #include "migration/colo.h"
@@ -1184,117 +1188,46 @@ bool migrate_params_check(MigrationParameters *params, Error **errp)
     return true;
 }
 
-static void migrate_params_test_apply(MigrationParameters *params,
-                                      MigrationParameters *dest)
+static bool migrate_params_test_apply(MigrationParameters *params,
+                                      MigrationParameters *dest, Error **errp)
 {
     MigrationState *s = migrate_get_current();
+    QObject *ret_out = NULL;
+    Visitor *v;
+    bool ok;
 
     QAPI_CLONE_MEMBERS(MigrationParameters, dest, &s->parameters);
 
-    if (params->has_throttle_trigger_threshold) {
-        dest->throttle_trigger_threshold = params->throttle_trigger_threshold;
+    /* read in from params */
+    v = qobject_output_visitor_new(&ret_out);
+    ok = visit_type_MigrationParameters(v, NULL, &params, errp);
+    if (!ok) {
+        goto out;
     }
+    visit_complete(v, &ret_out);
+    visit_free(v);
 
-    if (params->has_cpu_throttle_initial) {
-        dest->cpu_throttle_initial = params->cpu_throttle_initial;
+    /* write out to dest */
+    v = qobject_input_visitor_new(ret_out);
+    ok = visit_start_struct(v, NULL, NULL, 0, errp);
+    if (!ok) {
+        goto out;
     }
-
-    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) {
-        tls_option_set_str(&dest->tls_creds, params->tls_creds);
-    }
-
-    if (params->tls_hostname) {
-        tls_option_set_str(&dest->tls_hostname, params->tls_hostname);
-    }
-
-    if (params->tls_authz) {
-        tls_option_set_str(&dest->tls_authz, 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;
+    ok = visit_type_MigrationParameters_members(v, dest, errp);
+    if (!ok) {
+        goto out;
     }
-    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;
+    ok = visit_check_struct(v, errp);
+    visit_end_struct(v, NULL);
+    if (!ok) {
+        goto out;
     }
 
-    if (params->has_block_bitmap_mapping) {
-        dest->block_bitmap_mapping = params->block_bitmap_mapping;
-    }
+out:
+    visit_free(v);
+    qobject_unref(ret_out);
 
-    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;
-    }
+    return ok;
 }
 
 static void migrate_params_apply(MigrationParameters *params)
@@ -1315,9 +1248,11 @@ static void migrate_params_apply(MigrationParameters *params)
 
 void qmp_migrate_set_parameters(MigrationParameters *params, Error **errp)
 {
-    MigrationParameters tmp;
+    MigrationParameters *tmp = g_new0(MigrationParameters, 1);
 
-    migrate_params_test_apply(params, &tmp);
+    if (!migrate_params_test_apply(params, tmp, errp)) {
+        return;
+    }
 
     /*
      * Mark block_bitmap_mapping as present now while we have the
@@ -1327,10 +1262,10 @@ void qmp_migrate_set_parameters(MigrationParameters *params, Error **errp)
         migrate_get_current()->has_block_bitmap_mapping = true;
     }
 
-    if (migrate_params_check(&tmp, errp)) {
-        migrate_params_apply(&tmp);
+    if (migrate_params_check(tmp, errp)) {
+        migrate_params_apply(tmp);
         migrate_post_update_params(params, errp);
     }
 
-    migrate_tls_opts_free(&tmp);
+    qapi_free_MigrationParameters(tmp);
 }
-- 
2.35.3



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

* [PATCH 14/21] migration: Cleanup hmp_info_migrate_parameters
  2025-06-03  1:37 [PATCH 00/21] migration: Unify capabilities and parameters Fabiano Rosas
                   ` (12 preceding siblings ...)
  2025-06-03  1:38 ` [PATCH 13/21] migration: Use visitors in migrate_params_test_apply Fabiano Rosas
@ 2025-06-03  1:38 ` Fabiano Rosas
  2025-06-06 18:52   ` Peter Xu
  2025-06-03  1:38 ` [PATCH 15/21] migration: Add capabilities into MigrationParameters Fabiano Rosas
                   ` (7 subsequent siblings)
  21 siblings, 1 reply; 83+ messages in thread
From: Fabiano Rosas @ 2025-06-03  1:38 UTC (permalink / raw)
  To: qemu-devel; +Cc: Peter Xu, Markus Armbruster, Daniel P . Berrangé

Do a cleanup of hmp_info_migrate_parameters() before adding more lines
into it:

- Make sure every parameter asserts that the has_* field is
  set. qmp_query_migrate_parameters should have set them all.

- Remove the if (params), qmp_query_migrate_parameters never returns
  NULL.

- Add a macro to encapsulate boilerplate.

- Line breaks for legibility.

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

diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c
index 685c8ebd53..4f68719eda 100644
--- a/migration/migration-hmp-cmds.c
+++ b/migration/migration-hmp-cmds.c
@@ -33,6 +33,11 @@
 #include "options.h"
 #include "migration.h"
 
+#define PARAM_INFO(_a, _f, _e, _v) do {                                \
+        assert(_a);                                                     \
+        monitor_printf(mon, _f, MigrationParameter_str(_e), _v);        \
+    } while (0)
+
 static void migration_global_dump(Monitor *mon)
 {
     MigrationState *ms = migrate_get_current();
@@ -243,127 +248,153 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict)
     const BitmapMigrationNodeAliasList *bmnal;
 
     params = qmp_query_migrate_parameters(NULL);
+    assert(params);
 
-    if (params) {
-        monitor_printf(mon, "%s: %" PRIu64 " ms\n",
-            MigrationParameter_str(MIGRATION_PARAMETER_ANNOUNCE_INITIAL),
-            params->announce_initial);
-        monitor_printf(mon, "%s: %" PRIu64 " ms\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",
-            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);
-        monitor_printf(mon, "%s: '%s'\n",
-            MigrationParameter_str(MIGRATION_PARAMETER_TLS_CREDS),
-                       params->tls_creds ? params->tls_creds->u.s : "");
-        monitor_printf(mon, "%s: '%s'\n",
-            MigrationParameter_str(MIGRATION_PARAMETER_TLS_HOSTNAME),
-                       params->tls_hostname ? params->tls_hostname->u.s : "");
-        assert(params->has_max_bandwidth);
-        monitor_printf(mon, "%s: %" PRIu64 " bytes/second\n",
-            MigrationParameter_str(MIGRATION_PARAMETER_MAX_BANDWIDTH),
-            params->max_bandwidth);
-        assert(params->has_avail_switchover_bandwidth);
-        monitor_printf(mon, "%s: %" PRIu64 " bytes/second\n",
-            MigrationParameter_str(MIGRATION_PARAMETER_AVAIL_SWITCHOVER_BANDWIDTH),
-            params->avail_switchover_bandwidth);
-        assert(params->has_downtime_limit);
-        monitor_printf(mon, "%s: %" PRIu64 " ms\n",
-            MigrationParameter_str(MIGRATION_PARAMETER_DOWNTIME_LIMIT),
-            params->downtime_limit);
-        assert(params->has_x_checkpoint_delay);
-        monitor_printf(mon, "%s: %u ms\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 " bytes\n",
-            MigrationParameter_str(MIGRATION_PARAMETER_XBZRLE_CACHE_SIZE),
-            params->xbzrle_cache_size);
-        monitor_printf(mon, "%s: %" PRIu64 "\n",
-            MigrationParameter_str(MIGRATION_PARAMETER_MAX_POSTCOPY_BANDWIDTH),
-            params->max_postcopy_bandwidth);
-        monitor_printf(mon, "%s: '%s'\n",
-            MigrationParameter_str(MIGRATION_PARAMETER_TLS_AUTHZ),
-                       params->tls_authz ? params->tls_authz->u.s : "");
-
-        assert(params->has_block_bitmap_mapping);
-        monitor_printf(mon, "%s:\n",
-                       MigrationParameter_str(
-                           MIGRATION_PARAMETER_BLOCK_BITMAP_MAPPING));
-
-        for (bmnal = params->block_bitmap_mapping;
-             bmnal;
-             bmnal = bmnal->next)
-        {
-            const BitmapMigrationNodeAlias *bmna = bmnal->value;
-            const BitmapMigrationBitmapAliasList *bmbal;
-
-            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, "%s: %" PRIu64 " ms\n",
-        MigrationParameter_str(MIGRATION_PARAMETER_X_VCPU_DIRTY_LIMIT_PERIOD),
-        params->x_vcpu_dirty_limit_period);
-
-        monitor_printf(mon, "%s: %" PRIu64 " MB/s\n",
-            MigrationParameter_str(MIGRATION_PARAMETER_VCPU_DIRTY_LIMIT),
-            params->vcpu_dirty_limit);
-
-        assert(params->has_mode);
-        monitor_printf(mon, "%s: %s\n",
-            MigrationParameter_str(MIGRATION_PARAMETER_MODE),
-            qapi_enum_lookup(&MigMode_lookup, params->mode));
-
-        if (params->has_direct_io) {
-            monitor_printf(mon, "%s: %s\n",
-                           MigrationParameter_str(
-                               MIGRATION_PARAMETER_DIRECT_IO),
-                           params->direct_io ? "on" : "off");
+    PARAM_INFO(params->has_announce_initial,
+               "%s: %" PRIu64 " ms\n",
+               MIGRATION_PARAMETER_ANNOUNCE_INITIAL,
+               params->announce_initial);
+
+    PARAM_INFO(params->has_announce_max,
+               "%s: %" PRIu64 " ms\n",
+               MIGRATION_PARAMETER_ANNOUNCE_MAX,
+               params->announce_max);
+
+    PARAM_INFO(params->has_announce_rounds,
+               "%s: %" PRIu64 "\n",
+               MIGRATION_PARAMETER_ANNOUNCE_ROUNDS,
+               params->announce_rounds);
+
+    PARAM_INFO(params->has_announce_step,
+               "%s: %" PRIu64 " ms\n",
+               MIGRATION_PARAMETER_ANNOUNCE_STEP,
+               params->announce_step);
+
+    PARAM_INFO(params->has_throttle_trigger_threshold,
+               "%s: %u\n",
+               MIGRATION_PARAMETER_THROTTLE_TRIGGER_THRESHOLD,
+               params->throttle_trigger_threshold);
+
+    PARAM_INFO(params->has_cpu_throttle_initial,
+               "%s: %u\n",
+               MIGRATION_PARAMETER_CPU_THROTTLE_INITIAL,
+               params->cpu_throttle_initial);
+
+    PARAM_INFO(params->has_cpu_throttle_increment,
+               "%s: %u\n",
+               MIGRATION_PARAMETER_CPU_THROTTLE_INCREMENT,
+               params->cpu_throttle_increment);
+
+    PARAM_INFO(params->has_cpu_throttle_tailslow,
+               "%s: %s\n",
+               MIGRATION_PARAMETER_CPU_THROTTLE_TAILSLOW,
+               params->cpu_throttle_tailslow ? "on" : "off");
+
+    PARAM_INFO(params->has_max_cpu_throttle,
+               "%s: %u\n",
+               MIGRATION_PARAMETER_MAX_CPU_THROTTLE,
+               params->max_cpu_throttle);
+
+    PARAM_INFO(params->tls_creds,
+               "%s: '%s'\n",
+               MIGRATION_PARAMETER_TLS_CREDS,
+               params->tls_creds->u.s);
+
+    PARAM_INFO(params->tls_hostname,
+               "%s: '%s'\n",
+               MIGRATION_PARAMETER_TLS_HOSTNAME,
+               params->tls_hostname->u.s);
+
+    PARAM_INFO(params->has_max_bandwidth,
+               "%s: %" PRIu64 " bytes/second\n",
+               MIGRATION_PARAMETER_MAX_BANDWIDTH,
+               params->max_bandwidth);
+
+    PARAM_INFO(params->has_avail_switchover_bandwidth,
+               "%s: %" PRIu64 " bytes/second\n",
+               MIGRATION_PARAMETER_AVAIL_SWITCHOVER_BANDWIDTH,
+               params->avail_switchover_bandwidth);
+
+    PARAM_INFO(params->has_downtime_limit,
+               "%s: %" PRIu64 " ms\n",
+               MIGRATION_PARAMETER_DOWNTIME_LIMIT,
+               params->downtime_limit);
+
+    PARAM_INFO(params->has_x_checkpoint_delay,
+               "%s: %u ms\n",
+               MIGRATION_PARAMETER_X_CHECKPOINT_DELAY,
+               params->x_checkpoint_delay);
+
+    PARAM_INFO(params->has_multifd_channels,
+               "%s: %u\n",
+               MIGRATION_PARAMETER_MULTIFD_CHANNELS,
+               params->multifd_channels);
+
+    PARAM_INFO(params->has_multifd_compression,
+               "%s: %s\n",
+               MIGRATION_PARAMETER_MULTIFD_COMPRESSION,
+               qapi_enum_lookup(&MultiFDCompression_lookup,
+                                params->multifd_compression));
+
+    PARAM_INFO(params->has_zero_page_detection,
+               "%s: %s\n",
+               MIGRATION_PARAMETER_ZERO_PAGE_DETECTION,
+               qapi_enum_lookup(&ZeroPageDetection_lookup,
+                                params->zero_page_detection));
+
+    PARAM_INFO(params->has_xbzrle_cache_size,
+               "%s: %" PRIu64 " bytes\n",
+               MIGRATION_PARAMETER_XBZRLE_CACHE_SIZE,
+               params->xbzrle_cache_size);
+
+    PARAM_INFO(params->has_max_postcopy_bandwidth,
+               "%s: %" PRIu64 "\n",
+               MIGRATION_PARAMETER_MAX_POSTCOPY_BANDWIDTH,
+               params->max_postcopy_bandwidth);
+
+    PARAM_INFO(params->tls_authz,
+               "%s: '%s'\n",
+               MIGRATION_PARAMETER_TLS_AUTHZ,
+               params->tls_authz->u.s);
+
+    PARAM_INFO(params->has_block_bitmap_mapping,
+               "%s:%s\n",
+               MIGRATION_PARAMETER_BLOCK_BITMAP_MAPPING,
+               "");
+
+    for (bmnal = params->block_bitmap_mapping; bmnal; bmnal = bmnal->next) {
+        const BitmapMigrationNodeAlias *bmna = bmnal->value;
+        const BitmapMigrationBitmapAliasList *bmbal;
+
+        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);
         }
     }
 
+    PARAM_INFO(params->has_x_vcpu_dirty_limit_period,
+               "%s: %" PRIu64 " ms\n",
+               MIGRATION_PARAMETER_X_VCPU_DIRTY_LIMIT_PERIOD,
+               params->x_vcpu_dirty_limit_period);
+
+    PARAM_INFO(params->has_vcpu_dirty_limit,
+               "%s: %" PRIu64 " MB/s\n",
+               MIGRATION_PARAMETER_VCPU_DIRTY_LIMIT,
+               params->vcpu_dirty_limit);
+
+    PARAM_INFO(params->has_mode,
+               "%s: %s\n",
+               MIGRATION_PARAMETER_MODE,
+               qapi_enum_lookup(&MigMode_lookup, params->mode));
+
+    PARAM_INFO(params->has_direct_io,
+               "%s: %s\n",
+               MIGRATION_PARAMETER_DIRECT_IO,
+               params->direct_io ? "on" : "off");
+
     qapi_free_MigrationParameters(params);
 }
 
-- 
2.35.3



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

* [PATCH 15/21] migration: Add capabilities into MigrationParameters
  2025-06-03  1:37 [PATCH 00/21] migration: Unify capabilities and parameters Fabiano Rosas
                   ` (13 preceding siblings ...)
  2025-06-03  1:38 ` [PATCH 14/21] migration: Cleanup hmp_info_migrate_parameters Fabiano Rosas
@ 2025-06-03  1:38 ` Fabiano Rosas
  2025-06-06 19:01   ` Peter Xu
  2025-06-03  1:38 ` [PATCH 16/21] qapi/migration: Mark that query/set-migrate-parameters support capabilities Fabiano Rosas
                   ` (6 subsequent siblings)
  21 siblings, 1 reply; 83+ messages in thread
From: Fabiano Rosas @ 2025-06-03  1:38 UTC (permalink / raw)
  To: qemu-devel; +Cc: Peter Xu, Markus Armbruster, Daniel P . Berrangé

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

Also add capabilities to MigrationParameter as the enum needs to be
kept in sync with MigrationParameters. This affects the parsing of
migration HMP commands so make the necessary additions there too.

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.

Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
 migration/migration-hmp-cmds.c | 198 +++++++++++++++++++++++++++++++++
 migration/migration.c          |   8 ++
 migration/options.c            | 130 ++++++++++++++++++++++
 migration/options.h            |   5 +
 qapi/migration.json            | 135 +++++++++++++++++++++-
 5 files changed, 472 insertions(+), 4 deletions(-)

diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c
index 4f68719eda..6d8d189b81 100644
--- a/migration/migration-hmp-cmds.c
+++ b/migration/migration-hmp-cmds.c
@@ -395,6 +395,116 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict)
                MIGRATION_PARAMETER_DIRECT_IO,
                params->direct_io ? "on" : "off");
 
+    PARAM_INFO(params->has_xbzrle,
+               "%s: %s\n",
+               MIGRATION_PARAMETER_XBZRLE,
+               params->xbzrle ? "on" : "off");
+
+    PARAM_INFO(params->has_rdma_pin_all,
+               "%s: %s\n",
+               MIGRATION_PARAMETER_RDMA_PIN_ALL,
+               params->rdma_pin_all ? "on" : "off");
+
+    PARAM_INFO(params->has_auto_converge,
+               "%s: %s\n",
+               MIGRATION_PARAMETER_AUTO_CONVERGE,
+               params->auto_converge ? "on" : "off");
+
+    PARAM_INFO(params->has_zero_blocks,
+               "%s: %s\n",
+               MIGRATION_PARAMETER_ZERO_BLOCKS,
+               params->zero_blocks ? "on" : "off");
+
+    PARAM_INFO(params->has_events,
+               "%s: %s\n",
+               MIGRATION_PARAMETER_EVENTS,
+               params->events ? "on" : "off");
+
+    PARAM_INFO(params->has_postcopy_ram,
+               "%s: %s\n",
+               MIGRATION_PARAMETER_POSTCOPY_RAM,
+               params->postcopy_ram ? "on" : "off");
+
+    PARAM_INFO(params->has_x_colo,
+               "%s: %s\n",
+               MIGRATION_PARAMETER_X_COLO,
+               params->x_colo ? "on" : "off");
+
+    PARAM_INFO(params->has_release_ram,
+               "%s: %s\n",
+               MIGRATION_PARAMETER_RELEASE_RAM,
+               params->release_ram ? "on" : "off");
+
+    PARAM_INFO(params->has_return_path,
+               "%s: %s\n",
+               MIGRATION_PARAMETER_RETURN_PATH,
+               params->return_path ? "on" : "off");
+
+    PARAM_INFO(params->has_pause_before_switchover,
+               "%s: %s\n",
+               MIGRATION_PARAMETER_PAUSE_BEFORE_SWITCHOVER,
+               params->pause_before_switchover ? "on" : "off");
+
+    PARAM_INFO(params->has_multifd,
+               "%s: %s\n",
+               MIGRATION_PARAMETER_MULTIFD,
+               params->multifd ? "on" : "off");
+
+    PARAM_INFO(params->has_dirty_bitmaps,
+               "%s: %s\n",
+               MIGRATION_PARAMETER_DIRTY_BITMAPS,
+               params->dirty_bitmaps ? "on" : "off");
+
+    PARAM_INFO(params->has_postcopy_blocktime,
+               "%s: %s\n",
+               MIGRATION_PARAMETER_POSTCOPY_BLOCKTIME,
+               params->postcopy_blocktime ? "on" : "off");
+
+    PARAM_INFO(params->has_late_block_activate,
+               "%s: %s\n",
+               MIGRATION_PARAMETER_LATE_BLOCK_ACTIVATE,
+               params->late_block_activate ? "on" : "off");
+
+    PARAM_INFO(params->has_x_ignore_shared,
+               "%s: %s\n",
+               MIGRATION_PARAMETER_X_IGNORE_SHARED,
+               params->x_ignore_shared ? "on" : "off");
+
+    PARAM_INFO(params->has_validate_uuid,
+               "%s: %s\n",
+               MIGRATION_PARAMETER_VALIDATE_UUID,
+               params->validate_uuid ? "on" : "off");
+
+    PARAM_INFO(params->has_background_snapshot,
+               "%s: %s\n",
+               MIGRATION_PARAMETER_BACKGROUND_SNAPSHOT,
+               params->background_snapshot ? "on" : "off");
+
+    PARAM_INFO(params->has_zero_copy_send,
+               "%s: %s\n",
+               MIGRATION_PARAMETER_ZERO_COPY_SEND,
+               params->zero_copy_send ? "on" : "off");
+
+    PARAM_INFO(params->has_postcopy_preempt,
+               "%s: %s\n",
+               MIGRATION_PARAMETER_POSTCOPY_PREEMPT,
+               params->postcopy_preempt ? "on" : "off");
+
+    PARAM_INFO(params->has_switchover_ack,
+               "%s: %s\n",
+               MIGRATION_PARAMETER_SWITCHOVER_ACK,
+               params->switchover_ack ? "on" : "off");
+
+    PARAM_INFO(params->has_dirty_limit,
+               "%s: %s\n",
+               MIGRATION_PARAMETER_DIRTY_LIMIT,
+               params->dirty_limit ? "on" : "off");
+
+    PARAM_INFO(params->has_mapped_ram,
+               "%s: %s\n",
+               MIGRATION_PARAMETER_MAPPED_RAM,
+               params->mapped_ram ? "on" : "off");
+
     qapi_free_MigrationParameters(params);
 }
 
@@ -674,6 +784,94 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict)
         p->has_direct_io = true;
         visit_type_bool(v, param, &p->direct_io, &err);
         break;
+    case MIGRATION_PARAMETER_XBZRLE:
+        p->has_xbzrle = true;
+        visit_type_bool(v, param, &p->xbzrle, &err);
+        break;
+    case MIGRATION_PARAMETER_RDMA_PIN_ALL:
+        p->has_rdma_pin_all = true;
+        visit_type_bool(v, param, &p->rdma_pin_all, &err);
+        break;
+    case MIGRATION_PARAMETER_AUTO_CONVERGE:
+        p->has_auto_converge = true;
+        visit_type_bool(v, param, &p->auto_converge, &err);
+        break;
+    case MIGRATION_PARAMETER_ZERO_BLOCKS:
+        p->has_zero_blocks = true;
+        visit_type_bool(v, param, &p->zero_blocks, &err);
+        break;
+    case MIGRATION_PARAMETER_EVENTS:
+        p->has_events = true;
+        visit_type_bool(v, param, &p->events, &err);
+        break;
+    case MIGRATION_PARAMETER_POSTCOPY_RAM:
+        p->has_postcopy_ram = true;
+        visit_type_bool(v, param, &p->postcopy_ram, &err);
+        break;
+    case MIGRATION_PARAMETER_X_COLO:
+        p->has_x_colo = true;
+        visit_type_bool(v, param, &p->x_colo, &err);
+        break;
+    case MIGRATION_PARAMETER_RELEASE_RAM:
+        p->has_release_ram = true;
+        visit_type_bool(v, param, &p->release_ram, &err);
+        break;
+    case MIGRATION_PARAMETER_RETURN_PATH:
+        p->has_return_path = true;
+        visit_type_bool(v, param, &p->return_path, &err);
+        break;
+    case MIGRATION_PARAMETER_PAUSE_BEFORE_SWITCHOVER:
+        p->has_pause_before_switchover = true;
+        visit_type_bool(v, param, &p->pause_before_switchover, &err);
+        break;
+    case MIGRATION_PARAMETER_MULTIFD:
+        p->has_multifd = true;
+        visit_type_bool(v, param, &p->multifd, &err);
+        break;
+    case MIGRATION_PARAMETER_DIRTY_BITMAPS:
+        p->has_dirty_bitmaps = true;
+        visit_type_bool(v, param, &p->dirty_bitmaps, &err);
+        break;
+    case MIGRATION_PARAMETER_POSTCOPY_BLOCKTIME:
+        p->has_postcopy_blocktime = true;
+        visit_type_bool(v, param, &p->postcopy_blocktime, &err);
+        break;
+    case MIGRATION_PARAMETER_LATE_BLOCK_ACTIVATE:
+        p->has_late_block_activate = true;
+        visit_type_bool(v, param, &p->late_block_activate, &err);
+        break;
+    case MIGRATION_PARAMETER_X_IGNORE_SHARED:
+        p->has_x_ignore_shared = true;
+        visit_type_bool(v, param, &p->x_ignore_shared, &err);
+        break;
+    case MIGRATION_PARAMETER_VALIDATE_UUID:
+        p->has_validate_uuid = true;
+        visit_type_bool(v, param, &p->validate_uuid, &err);
+        break;
+    case MIGRATION_PARAMETER_BACKGROUND_SNAPSHOT:
+        p->has_background_snapshot = true;
+        visit_type_bool(v, param, &p->background_snapshot, &err);
+        break;
+    case MIGRATION_PARAMETER_ZERO_COPY_SEND:
+        p->has_zero_copy_send = true;
+        visit_type_bool(v, param, &p->zero_copy_send, &err);
+        break;
+    case MIGRATION_PARAMETER_POSTCOPY_PREEMPT:
+        p->has_postcopy_preempt = true;
+        visit_type_bool(v, param, &p->postcopy_preempt, &err);
+        break;
+    case MIGRATION_PARAMETER_SWITCHOVER_ACK:
+        p->has_switchover_ack = true;
+        visit_type_bool(v, param, &p->switchover_ack, &err);
+        break;
+    case MIGRATION_PARAMETER_DIRTY_LIMIT:
+        p->has_dirty_limit = true;
+        visit_type_bool(v, param, &p->dirty_limit, &err);
+        break;
+    case MIGRATION_PARAMETER_MAPPED_RAM:
+        p->has_mapped_ram = true;
+        visit_type_bool(v, param, &p->mapped_ram, &err);
+        break;
     default:
         g_assert_not_reached();
     }
diff --git a/migration/migration.c b/migration/migration.c
index 031a5f8ede..efb1dbb0c4 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -4098,6 +4098,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 ac4b06da7e..5808bea53f 100644
--- a/migration/options.c
+++ b/migration/options.c
@@ -634,6 +634,111 @@ 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_ZERO_BLOCKS:
+        cap_addr = &params->zero_blocks;
+        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;
@@ -675,6 +780,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 */
@@ -959,6 +1066,15 @@ static void migrate_mark_all_params_present(MigrationParameters *p)
         &p->has_announce_step, &p->has_block_bitmap_mapping,
         &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_xbzrle, &p->has_rdma_pin_all, &p->has_auto_converge,
+        &p->has_zero_blocks, &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,
     };
 
     /*
@@ -1250,6 +1366,20 @@ void qmp_migrate_set_parameters(MigrationParameters *params, Error **errp)
 {
     MigrationParameters *tmp = g_new0(MigrationParameters, 1);
 
+    /*
+     * FIXME: Temporarily while migrate_caps_check is not
+     * converted to look at s->parameters. Will be gone the end of
+     * the series.
+     */
+    MigrationState *s = migrate_get_current();
+    bool new_caps[MIGRATION_CAPABILITY__MAX] = { 0 };
+    for (int i = 0; i < MIGRATION_CAPABILITY__MAX; i++) {
+        new_caps[i] = migrate_capability_get_compat(&s->parameters, i);
+    }
+    if (!migrate_caps_check(s->capabilities, new_caps, errp)) {
+        return;
+    }
+
     if (!migrate_params_test_apply(params, tmp, errp)) {
         return;
     }
diff --git a/migration/options.h b/migration/options.h
index 999eee6f3b..cac9201a5e 100644
--- a/migration/options.h
+++ b/migration/options.h
@@ -92,4 +92,9 @@ ZeroPageDetection migrate_zero_page_detection(void);
 bool migrate_params_check(MigrationParameters *params, Error **errp);
 void migrate_params_init(MigrationParameters *params);
 void migrate_tls_opts_free(MigrationParameters *params);
+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 452e6dedaa..5942622ba7 100644
--- a/qapi/migration.json
+++ b/qapi/migration.json
@@ -762,7 +762,14 @@
            'vcpu-dirty-limit',
            'mode',
            'zero-page-detection',
-           'direct-io'] }
+           'direct-io', 'xbzrle', 'rdma-pin-all', 'auto-converge',
+           'zero-blocks', 'events', 'postcopy-ram', 'x-colo',
+           'release-ram', 'return-path', 'pause-before-switchover',
+           'multifd', 'dirty-bitmaps', 'postcopy-blocktime',
+           'late-block-activate', 'x-ignore-shared',
+           'validate-uuid', 'background-snapshot',
+           'zero-copy-send', 'postcopy-preempt',
+           'switchover-ack', 'dirty-limit', 'mapped-ram' ] }
 
 ##
 # @migrate-set-parameters:
@@ -936,10 +943,108 @@
 #     only has effect if the @mapped-ram capability is enabled.
 #     (Since 9.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)
+#
+# @zero-blocks: During storage migration encode blocks of zeroes
+#     efficiently.  This essentially saves 1MB of zeroes per block on
+#     the wire.  Enabling requires source and target VM to support
+#     this feature.  To enable it is sufficient to enable the
+#     capability on the source VM.  The feature is disabled by
+#     default.  (since 1.6)
+#
+# @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 and
-#     @x-vcpu-dirty-limit-period are experimental.
+# @unstable: Members @x-checkpoint-delay, @x-vcpu-dirty-limit-period,
+#     @x-colo and @x-ignore-shared are experimental.
+# @deprecated: Member @zero-blocks is deprecated as being part of
+#     block migration which was already removed.
 #
 # Since: 2.4
 ##
@@ -974,7 +1079,29 @@
             '*vcpu-dirty-limit': 'uint64',
             '*mode': 'MigMode',
             '*zero-page-detection': 'ZeroPageDetection',
-            '*direct-io': 'bool' } }
+            '*direct-io': 'bool',
+            '*xbzrle': 'bool',
+            '*rdma-pin-all': 'bool',
+            '*auto-converge': 'bool',
+            '*zero-blocks': { 'type': 'bool', 'features': [ 'deprecated' ] },
+            '*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.35.3



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

* [PATCH 16/21] qapi/migration: Mark that query/set-migrate-parameters support capabilities
  2025-06-03  1:37 [PATCH 00/21] migration: Unify capabilities and parameters Fabiano Rosas
                   ` (14 preceding siblings ...)
  2025-06-03  1:38 ` [PATCH 15/21] migration: Add capabilities into MigrationParameters Fabiano Rosas
@ 2025-06-03  1:38 ` Fabiano Rosas
  2025-06-03  9:01   ` Daniel P. Berrangé
  2025-06-03  1:38 ` [PATCH 17/21] migration: Remove s->capabilities Fabiano Rosas
                   ` (5 subsequent siblings)
  21 siblings, 1 reply; 83+ messages in thread
From: Fabiano Rosas @ 2025-06-03  1:38 UTC (permalink / raw)
  To: qemu-devel; +Cc: Peter Xu, Markus Armbruster, Daniel P . Berrangé

Add a QAPI command feature "capabilities" that can be queried by the
client to check that the parameters commands now also support
capabilities.

Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
 qapi/migration.json | 20 ++++++++++++++++++--
 1 file changed, 18 insertions(+), 2 deletions(-)

diff --git a/qapi/migration.json b/qapi/migration.json
index 5942622ba7..557a9c523e 100644
--- a/qapi/migration.json
+++ b/qapi/migration.json
@@ -776,6 +776,13 @@
 #
 # Set various migration parameters.
 #
+# Features:
+#
+# @capabilities: Indicates this command supports setting the set of
+# parameters previously known as capabilities.  This means this
+# command can (and should) be used instead of the depreacated
+# @migrate-set-capabilities.
+#
 # Since: 2.4
 #
 # .. qmp-example::
@@ -785,7 +792,8 @@
 #     <- { "return": {} }
 ##
 { 'command': 'migrate-set-parameters', 'boxed': true,
-  'data': 'MigrationParameters' }
+  'data': 'MigrationParameters',
+  'features': [ 'capabilities' ] }
 
 ##
 # @MigrationParameters:
@@ -1110,6 +1118,13 @@
 #
 # Returns: @MigrationParameters
 #
+# Features:
+#
+# @capabilities: Indicates this command supports setting the set of
+# parameters previously known as capabilities.  This means this
+# command can (and should) be used instead of the depreacated
+# @migrate-set-capabilities.
+#
 # Since: 2.4
 #
 # .. qmp-example::
@@ -1125,7 +1140,8 @@
 #        }
 ##
 { 'command': 'query-migrate-parameters',
-  'returns': 'MigrationParameters' }
+  'returns': 'MigrationParameters',
+  'features': [ 'capabilities' ] }
 
 ##
 # @migrate-start-postcopy:
-- 
2.35.3



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

* [PATCH 17/21] migration: Remove s->capabilities
  2025-06-03  1:37 [PATCH 00/21] migration: Unify capabilities and parameters Fabiano Rosas
                   ` (15 preceding siblings ...)
  2025-06-03  1:38 ` [PATCH 16/21] qapi/migration: Mark that query/set-migrate-parameters support capabilities Fabiano Rosas
@ 2025-06-03  1:38 ` Fabiano Rosas
  2025-06-06 19:16   ` Peter Xu
  2025-06-03  1:38 ` [PATCH 18/21] qapi/migration: Deprecate capabilities commands Fabiano Rosas
                   ` (4 subsequent siblings)
  21 siblings, 1 reply; 83+ messages in thread
From: Fabiano Rosas @ 2025-06-03  1:38 UTC (permalink / raw)
  To: qemu-devel; +Cc: Peter Xu, Markus Armbruster, Daniel P . Berrangé

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.

Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
 migration/migration.c |  22 +---
 migration/migration.h |   1 -
 migration/options.c   | 285 ++++++++++++++++++------------------------
 migration/options.h   |  20 +--
 migration/savevm.c    |   8 +-
 5 files changed, 133 insertions(+), 203 deletions(-)

diff --git a/migration/migration.c b/migration/migration.c
index efb1dbb0c4..75c4ec9a95 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -266,9 +266,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;
@@ -4091,22 +4092,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 ab797540b0..993d51aedd 100644
--- a/migration/migration.h
+++ b/migration/migration.h
@@ -358,7 +358,6 @@ struct MigrationState {
     int64_t downtime_start;
     int64_t downtime;
     int64_t expected_downtime;
-    bool capabilities[MIGRATION_CAPABILITY__MAX];
     int64_t setup_time;
 
     /*
diff --git a/migration/options.c b/migration/options.c
index 5808bea53f..fa3f7035c8 100644
--- a/migration/options.c
+++ b/migration/options.c
@@ -85,9 +85,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)
-
 #define DEFAULT_MIGRATE_VCPU_DIRTY_LIMIT_PERIOD     1000    /* milliseconds */
 #define DEFAULT_MIGRATE_VCPU_DIRTY_LIMIT            1       /* MB/s */
 
@@ -184,30 +181,40 @@ const Property migration_properties[] = {
     DEFINE_PROP_ZERO_PAGE_DETECTION("zero-page-detection", MigrationState,
                        parameters.zero_page_detection,
                        ZERO_PAGE_DETECTION_MULTIFD),
-
-    /* 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-zero-blocks", MIGRATION_CAPABILITY_ZERO_BLOCKS),
-    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("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-zero-blocks",
+                     MigrationState, parameters.zero_blocks, 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("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_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),
 };
 const size_t migration_properties_count = ARRAY_SIZE(migration_properties);
 
@@ -215,7 +222,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)
@@ -229,144 +236,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();
@@ -411,54 +416,22 @@ 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);
-
 static bool migrate_incoming_started(void)
 {
     return !!migration_incoming_get_current()->transport_data;
 }
 
-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;
     }
@@ -466,26 +439,19 @@ 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();
 
-    if (new_caps[MIGRATION_CAPABILITY_ZERO_BLOCKS]) {
+    if (new->zero_blocks) {
         warn_report("zero-blocks capability is deprecated");
     }
 
 #ifndef CONFIG_REPLICATION
-    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");
@@ -493,27 +459,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.
@@ -529,41 +495,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;
         }
@@ -575,22 +545,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;
@@ -603,21 +573,21 @@ 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_POSTCOPY_RAM]) {
+        if (new->postcopy_ram) {
             error_setg(errp,
                        "Mapped-ram migration is incompatible with postcopy");
             return false;
@@ -628,7 +598,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;
@@ -749,39 +719,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 */
@@ -842,9 +810,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)
@@ -1301,6 +1268,10 @@ bool migrate_params_check(MigrationParameters *params, Error **errp)
         return false;
     }
 
+    if (!migrate_caps_check(params, errp)) {
+        return false;
+    }
+
     return true;
 }
 
@@ -1366,20 +1337,6 @@ void qmp_migrate_set_parameters(MigrationParameters *params, Error **errp)
 {
     MigrationParameters *tmp = g_new0(MigrationParameters, 1);
 
-    /*
-     * FIXME: Temporarily while migrate_caps_check is not
-     * converted to look at s->parameters. Will be gone the end of
-     * the series.
-     */
-    MigrationState *s = migrate_get_current();
-    bool new_caps[MIGRATION_CAPABILITY__MAX] = { 0 };
-    for (int i = 0; i < MIGRATION_CAPABILITY__MAX; i++) {
-        new_caps[i] = migrate_capability_get_compat(&s->parameters, i);
-    }
-    if (!migrate_caps_check(s->capabilities, new_caps, errp)) {
-        return;
-    }
-
     if (!migrate_params_test_apply(params, tmp, errp)) {
         return;
     }
diff --git a/migration/options.h b/migration/options.h
index cac9201a5e..fcfd120cd7 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);
 
 /* parameters */
 
@@ -87,14 +75,12 @@ const char *migrate_tls_hostname(void);
 uint64_t migrate_xbzrle_cache_size(void);
 ZeroPageDetection migrate_zero_page_detection(void);
 
-/* parameters helpers */
-
 bool migrate_params_check(MigrationParameters *params, Error **errp);
-void migrate_params_init(MigrationParameters *params);
 void migrate_tls_opts_free(MigrationParameters *params);
 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);
+bool migrate_caps_check(MigrationParameters *new, Error **errp);
 #endif
diff --git a/migration/savevm.c b/migration/savevm.c
index 006514c3e3..e052a4ff97 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 int configuration_pre_save(void *opaque)
     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;
         }
     }
@@ -363,7 +365,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),
-- 
2.35.3



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

* [PATCH 18/21] qapi/migration: Deprecate capabilities commands
  2025-06-03  1:37 [PATCH 00/21] migration: Unify capabilities and parameters Fabiano Rosas
                   ` (16 preceding siblings ...)
  2025-06-03  1:38 ` [PATCH 17/21] migration: Remove s->capabilities Fabiano Rosas
@ 2025-06-03  1:38 ` Fabiano Rosas
  2025-06-06 19:16   ` Peter Xu
  2025-06-03  1:38 ` [PATCH 19/21] migration: Allow migrate commands to provide the migration config Fabiano Rosas
                   ` (3 subsequent siblings)
  21 siblings, 1 reply; 83+ messages in thread
From: Fabiano Rosas @ 2025-06-03  1:38 UTC (permalink / raw)
  To: qemu-devel; +Cc: Peter Xu, Markus Armbruster, Daniel P . Berrangé

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

Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
 docs/about/deprecated.rst      | 12 ++++++++++++
 migration/migration-hmp-cmds.c |  6 ++++++
 qapi/migration.json            | 16 ++++++++++++++--
 3 files changed, 32 insertions(+), 2 deletions(-)

diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst
index 42037131de..15474833ea 100644
--- a/docs/about/deprecated.rst
+++ b/docs/about/deprecated.rst
@@ -605,3 +605,15 @@ command documentation for details on the ``fdset`` usage.
 
 The ``zero-blocks`` capability was part of the block migration which
 doesn't exist anymore since it was removed in QEMU v9.1.
+
+``migrate-set-capabilities`` command (since 10.1)
+'''''''''''''''''''''''''''''''''''''''''''''''''
+
+This command was deprecated. Use ``migrate-set-parameters`` instead
+which now supports setting capabilities.
+
+``query-migrate-capabilities`` command (since 10.1)
+'''''''''''''''''''''''''''''''''''''''''''''''''''
+
+This command was deprecated. Use ``query-migrate-parameters`` instead
+which now supports querying capabilities.
diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c
index 6d8d189b81..a8c3515e9d 100644
--- a/migration/migration-hmp-cmds.c
+++ b/migration/migration-hmp-cmds.c
@@ -229,6 +229,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) {
@@ -608,6 +611,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 557a9c523e..7282e4b9eb 100644
--- a/qapi/migration.json
+++ b/qapi/migration.json
@@ -521,6 +521,11 @@
 #
 # @capabilities: json array of capability modifications to make
 #
+# Features:
+#
+# @deprecated: This command is deprecated in favor of
+# migrate-set-parameters.
+#
 # Since: 1.2
 #
 # .. qmp-example::
@@ -530,7 +535,8 @@
 #     <- { "return": {} }
 ##
 { 'command': 'migrate-set-capabilities',
-  'data': { 'capabilities': ['MigrationCapabilityStatus'] } }
+  'data': { 'capabilities': ['MigrationCapabilityStatus'] },
+  'features': ['deprecated'] }
 
 ##
 # @query-migrate-capabilities:
@@ -539,6 +545,11 @@
 #
 # Returns: @MigrationCapabilityStatus
 #
+# Features:
+#
+# @deprecated: This command is deprecated in favor of
+# query-migrate-parameters.
+#
 # Since: 1.2
 #
 # .. qmp-example::
@@ -554,7 +565,8 @@
 #           {"state": false, "capability": "x-colo"}
 #        ]}
 ##
-{ 'command': 'query-migrate-capabilities', 'returns':   ['MigrationCapabilityStatus']}
+{ 'command': 'query-migrate-capabilities', 'returns':   ['MigrationCapabilityStatus'],
+  'features': ['deprecated'] }
 
 ##
 # @MultiFDCompression:
-- 
2.35.3



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

* [PATCH 19/21] migration: Allow migrate commands to provide the migration config
  2025-06-03  1:37 [PATCH 00/21] migration: Unify capabilities and parameters Fabiano Rosas
                   ` (17 preceding siblings ...)
  2025-06-03  1:38 ` [PATCH 18/21] qapi/migration: Deprecate capabilities commands Fabiano Rosas
@ 2025-06-03  1:38 ` Fabiano Rosas
  2025-06-03  9:03   ` Daniel P. Berrangé
  2025-06-06 19:28   ` Peter Xu
  2025-06-03  1:38 ` [PATCH 20/21] libqtest: Add a function to check whether a QMP command supports a feature Fabiano Rosas
                   ` (2 subsequent siblings)
  21 siblings, 2 replies; 83+ messages in thread
From: Fabiano Rosas @ 2025-06-03  1:38 UTC (permalink / raw)
  To: qemu-devel; +Cc: Peter Xu, Markus Armbruster, Daniel P . Berrangé

Allow the migrate and migrate_incoming commands to pass the migration
configuration options all at once, dispensing the use of
migrate-set-parameters and migrate-set-capabilities.

The motivation of this is to simplify the interface with the
management layer and avoid the usage of several command invocations to
configure a migration. It also avoids stale parameters from a previous
migration to influence the current migration.

The options that are changed during the migration can still be set
with the existing commands.

The order of precedence is:

'config' argument > -global cmdline > defaults (migration_properties)

I.e. the config takes precedence over all, values not present in the
config assume the default values. The (debug) -global command line
option allows the defaults to be overridden.

Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
 migration/migration-hmp-cmds.c |  5 +++--
 migration/migration.c          | 29 ++++++++++++++++++++++++++---
 migration/migration.h          |  1 +
 migration/options.c            | 30 ++++++++++++++++++++++++++++++
 migration/options.h            |  3 +++
 qapi/migration.json            | 25 +++++++++++++++++++++++--
 system/vl.c                    |  3 ++-
 7 files changed, 88 insertions(+), 8 deletions(-)

diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c
index a8c3515e9d..38b289e8d8 100644
--- a/migration/migration-hmp-cmds.c
+++ b/migration/migration-hmp-cmds.c
@@ -575,7 +575,7 @@ void hmp_migrate_incoming(Monitor *mon, const QDict *qdict)
     }
     QAPI_LIST_PREPEND(caps, g_steal_pointer(&channel));
 
-    qmp_migrate_incoming(NULL, true, caps, true, false, &err);
+    qmp_migrate_incoming(NULL, true, caps, NULL, true, false, &err);
     qapi_free_MigrationChannelList(caps);
 
 end:
@@ -952,7 +952,8 @@ void hmp_migrate(Monitor *mon, const QDict *qdict)
     }
     QAPI_LIST_PREPEND(caps, g_steal_pointer(&channel));
 
-    qmp_migrate(NULL, true, caps, false, false, true, resume, &err);
+    qmp_migrate(NULL, true, caps, NULL, false, false, true, resume,
+                &err);
     if (hmp_handle_error(mon, err)) {
         return;
     }
diff --git a/migration/migration.c b/migration/migration.c
index 75c4ec9a95..7b450b8836 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -335,6 +335,7 @@ void migration_object_init(void)
     current_incoming->exit_on_error = INMIGRATE_DEFAULT_EXIT_ON_ERROR;
 
     migration_object_check(current_migration, &error_fatal);
+    migrate_params_store_defaults(current_migration);
 
     ram_mig_init();
     dirty_bitmap_mig_init();
@@ -1916,13 +1917,24 @@ void migrate_del_blocker(Error **reasonp)
 
 void qmp_migrate_incoming(const char *uri, bool has_channels,
                           MigrationChannelList *channels,
-                          bool has_exit_on_error, bool exit_on_error,
-                          Error **errp)
+                          MigrationParameters *config, bool has_exit_on_error,
+                          bool exit_on_error, Error **errp)
 {
     Error *local_err = NULL;
     static bool once = true;
+    MigrationState *s = migrate_get_current();
     MigrationIncomingState *mis = migration_incoming_get_current();
 
+    if (config) {
+        /*
+         * If a config was provided, all options set previously get
+         * ignored.
+         */
+        if (!migrate_params_override(s, config, errp)) {
+            return;
+        }
+    }
+
     if (!once) {
         error_setg(errp, "The incoming migration has already been started");
         return;
@@ -2182,7 +2194,8 @@ static gboolean qmp_migrate_finish_cb(QIOChannel *channel,
 }
 
 void qmp_migrate(const char *uri, bool has_channels,
-                 MigrationChannelList *channels, bool has_detach, bool detach,
+                 MigrationChannelList *channels,
+                 bool has_detach, bool detach, MigrationParameters *config,
                  bool has_resume, bool resume, Error **errp)
 {
     bool resume_requested;
@@ -2193,6 +2206,16 @@ void qmp_migrate(const char *uri, bool has_channels,
     MigrationChannel *channelv[MIGRATION_CHANNEL_TYPE__MAX] = { NULL };
     MigrationChannel *cpr_channel = NULL;
 
+    if (config) {
+        /*
+         * If a config was provided, all options set previously get
+         * ignored.
+         */
+        if (!migrate_params_override(s, config, errp)) {
+            return;
+        }
+    }
+
     /*
      * Having preliminary checks for uri and channel
      */
diff --git a/migration/migration.h b/migration/migration.h
index 993d51aedd..49761f4699 100644
--- a/migration/migration.h
+++ b/migration/migration.h
@@ -319,6 +319,7 @@ struct MigrationState {
 
     /* params from 'migrate-set-parameters' */
     MigrationParameters parameters;
+    MigrationParameters defaults;
 
     MigrationStatus state;
 
diff --git a/migration/options.c b/migration/options.c
index fa3f7035c8..dd2288187d 100644
--- a/migration/options.c
+++ b/migration/options.c
@@ -1333,6 +1333,36 @@ static void migrate_params_apply(MigrationParameters *params)
                                            params->block_bitmap_mapping);
 }
 
+void migrate_params_store_defaults(MigrationState *s)
+{
+    /*
+     * The defaults set for each qdev property in migration_properties
+     * will be stored as the default values for each migration
+     * parameter. For debugging, using -global can override the
+     * defaults.
+     */
+    QAPI_CLONE_MEMBERS(MigrationParameters, &s->defaults, &s->parameters);
+}
+
+bool migrate_params_override(MigrationState *s, MigrationParameters *new,
+                             Error **errp)
+{
+    ERRP_GUARD();
+
+    assert(bql_locked());
+
+    /* reset to default parameters */
+    migrate_params_apply(&s->defaults);
+
+    /* overwrite with the new ones */
+    qmp_migrate_set_parameters(new, errp);
+    if (*errp) {
+        return false;
+    }
+
+    return true;
+}
+
 void qmp_migrate_set_parameters(MigrationParameters *params, Error **errp)
 {
     MigrationParameters *tmp = g_new0(MigrationParameters, 1);
diff --git a/migration/options.h b/migration/options.h
index fcfd120cd7..3630c2a0dd 100644
--- a/migration/options.h
+++ b/migration/options.h
@@ -83,4 +83,7 @@ void migrate_capability_set_compat(MigrationParameters *params, int i,
 void migrate_capabilities_set_compat(MigrationParameters *params,
                                      MigrationCapabilityStatusList *caps);
 bool migrate_caps_check(MigrationParameters *new, Error **errp);
+void migrate_params_store_defaults(MigrationState *s);
+bool migrate_params_override(MigrationState *s, MigrationParameters *new,
+                             Error **errp);
 #endif
diff --git a/qapi/migration.json b/qapi/migration.json
index 7282e4b9eb..64a92d8d28 100644
--- a/qapi/migration.json
+++ b/qapi/migration.json
@@ -1474,9 +1474,16 @@
 #
 # @resume: resume one paused migration, default "off".  (since 3.0)
 #
+# @config: migration configuration options, previously set via
+#     @migrate-set-parameters and @migrate-set-capabilities.  (since
+#     10.1)
+#
 # Features:
 #
 # @deprecated: Argument @detach is deprecated.
+# @config: Indicates this command can receive the entire migration
+# configuration via the @config field, dispensing the use of
+# @migrate-set-parameters.
 #
 # Since: 0.14
 #
@@ -1538,7 +1545,9 @@
   'data': {'*uri': 'str',
            '*channels': [ 'MigrationChannel' ],
            '*detach': { 'type': 'bool', 'features': [ 'deprecated' ] },
-           '*resume': 'bool' } }
+           '*config': 'MigrationParameters',
+           '*resume': 'bool' },
+  'features': [ 'config' ] }
 
 ##
 # @migrate-incoming:
@@ -1557,6 +1566,16 @@
 #     error details could be retrieved with query-migrate.
 #     (since 9.1)
 #
+# @config: migration configuration options, previously set via
+#     @migrate-set-parameters and @migrate-set-capabilities.  (since
+#     10.1)
+#
+# Features:
+#
+# @config: Indicates this command can receive the entire migration
+# configuration via the @config field, dispensing the use of
+# @migrate-set-parameters.
+#
 # Since: 2.3
 #
 # .. admonition:: Notes
@@ -1610,7 +1629,9 @@
 { 'command': 'migrate-incoming',
              'data': {'*uri': 'str',
                       '*channels': [ 'MigrationChannel' ],
-                      '*exit-on-error': 'bool' } }
+                      '*config': 'MigrationParameters',
+                      '*exit-on-error': 'bool' },
+             'features': [ 'config' ] }
 
 ##
 # @xen-save-devices-state:
diff --git a/system/vl.c b/system/vl.c
index 3b7057e6c6..b29fd24d08 100644
--- a/system/vl.c
+++ b/system/vl.c
@@ -2823,7 +2823,8 @@ void qmp_x_exit_preconfig(Error **errp)
                 g_new0(MigrationChannelList, 1);
 
             channels->value = incoming_channels[MIGRATION_CHANNEL_TYPE_MAIN];
-            qmp_migrate_incoming(NULL, true, channels, true, true, &local_err);
+            qmp_migrate_incoming(NULL, true, channels, NULL, true, true,
+                                 &local_err);
             if (local_err) {
                 error_reportf_err(local_err, "-incoming %s: ", incoming);
                 exit(1);
-- 
2.35.3



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

* [PATCH 20/21] libqtest: Add a function to check whether a QMP command supports a feature
  2025-06-03  1:37 [PATCH 00/21] migration: Unify capabilities and parameters Fabiano Rosas
                   ` (18 preceding siblings ...)
  2025-06-03  1:38 ` [PATCH 19/21] migration: Allow migrate commands to provide the migration config Fabiano Rosas
@ 2025-06-03  1:38 ` Fabiano Rosas
  2025-06-03  1:38 ` [PATCH 21/21] tests/qtest/migration: Add a test for config passing Fabiano Rosas
  2025-06-12  6:41 ` [PATCH 00/21] migration: Unify capabilities and parameters Mario Casquero
  21 siblings, 0 replies; 83+ messages in thread
From: Fabiano Rosas @ 2025-06-03  1:38 UTC (permalink / raw)
  To: qemu-devel; +Cc: Peter Xu, Markus Armbruster, Daniel P . Berrangé

Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
 tests/qtest/libqtest.c | 42 ++++++++++++++++++++++++++++++++++++++++++
 tests/qtest/libqtest.h | 12 ++++++++++++
 2 files changed, 54 insertions(+)

diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c
index 94526b7f9c..7ee4831841 100644
--- a/tests/qtest/libqtest.c
+++ b/tests/qtest/libqtest.c
@@ -34,6 +34,9 @@
 #include "qemu/ctype.h"
 #include "qemu/cutils.h"
 #include "qemu/sockets.h"
+#include "qapi/error.h"
+#include "qapi/qapi-visit-introspect.h"
+#include "qapi/qobject-input-visitor.h"
 #include "qobject/qdict.h"
 #include "qobject/qjson.h"
 #include "qobject/qlist.h"
@@ -2091,3 +2094,42 @@ bool mkimg(const char *file, const char *fmt, unsigned size_mb)
 
     return ret && !err;
 }
+
+bool qtest_qmp_cmd_has_feature(QTestState *qts, const char *cmd,
+                               const char *feature)
+{
+    QDict *resp;
+    Visitor *qiv;
+    SchemaInfoList *tail;
+    SchemaInfo *si;
+    strList *str;
+
+    resp = qtest_qmp(qts, "{ 'execute': 'query-qmp-schema' }");
+
+    qiv = qobject_input_visitor_new(qdict_get(resp, "return"));
+    visit_type_SchemaInfoList(qiv, NULL, &tail, &error_abort);
+    visit_free(qiv);
+    qobject_unref(resp);
+
+    for (; tail; tail = tail->next) {
+        si = tail->value;
+
+        if (si->meta_type != SCHEMA_META_TYPE_COMMAND) {
+            continue;
+        }
+
+        if (g_str_equal(si->name, cmd)) {
+            break;
+        }
+    }
+
+    if (tail && si->has_features) {
+        for (str = si->features; str; str = str->next) {
+            if (g_str_equal(str->value, feature)) {
+                return true;
+            }
+        }
+    }
+
+    return false;
+}
diff --git a/tests/qtest/libqtest.h b/tests/qtest/libqtest.h
index b3f2e7fbef..c609316223 100644
--- a/tests/qtest/libqtest.h
+++ b/tests/qtest/libqtest.h
@@ -1153,4 +1153,16 @@ bool have_qemu_img(void);
  */
 bool mkimg(const char *file, const char *fmt, unsigned size_mb);
 
+/**
+ * qtest_qmp_cmd_has_feature:
+ * @qts: QTestState instance
+ * @cmd: The QMP command being introspected
+ * @feature: Name of the feature to be checked
+ *
+ * Returns: true if the QMP command @cmd supports @feature, false otherwise.
+ */
+bool qtest_qmp_cmd_has_feature(QTestState *qts, const char *cmd,
+                               const char *feature);
+
+
 #endif
-- 
2.35.3



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

* [PATCH 21/21] tests/qtest/migration: Add a test for config passing
  2025-06-03  1:37 [PATCH 00/21] migration: Unify capabilities and parameters Fabiano Rosas
                   ` (19 preceding siblings ...)
  2025-06-03  1:38 ` [PATCH 20/21] libqtest: Add a function to check whether a QMP command supports a feature Fabiano Rosas
@ 2025-06-03  1:38 ` Fabiano Rosas
  2025-06-12  6:41 ` [PATCH 00/21] migration: Unify capabilities and parameters Mario Casquero
  21 siblings, 0 replies; 83+ messages in thread
From: Fabiano Rosas @ 2025-06-03  1:38 UTC (permalink / raw)
  To: qemu-devel; +Cc: Peter Xu, Markus Armbruster, Daniel P . Berrangé

Pass the config directly via the migrate/incoming command.

Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
 tests/qtest/migration/framework.h  |  2 ++
 tests/qtest/migration/misc-tests.c | 39 ++++++++++++++++++++++++++++++
 2 files changed, 41 insertions(+)

diff --git a/tests/qtest/migration/framework.h b/tests/qtest/migration/framework.h
index 01e425e64e..e55cd33fa8 100644
--- a/tests/qtest/migration/framework.h
+++ b/tests/qtest/migration/framework.h
@@ -145,6 +145,8 @@ typedef struct {
     /* Optional: fine tune start parameters */
     MigrateStart start;
 
+    const char *config;
+
     /* Required: the URI for the dst QEMU to listen on */
     const char *listen_uri;
 
diff --git a/tests/qtest/migration/misc-tests.c b/tests/qtest/migration/misc-tests.c
index 54995256d8..e835865aee 100644
--- a/tests/qtest/migration/misc-tests.c
+++ b/tests/qtest/migration/misc-tests.c
@@ -256,6 +256,42 @@ static void test_validate_uri_channels_none_set(void)
     do_test_validate_uri_channel(&args);
 }
 
+static void test_config_migrate(void)
+{
+    QTestState *from, *to;
+    MigrateStart args = {
+        .hide_stderr = false,
+    };
+    const char *config = "{"
+        "'events':true, "
+        "'multifd':true, "
+        "'multifd-channels': 4"
+        "}";
+
+    if (migrate_start(&from, &to, "defer", &args)) {
+        return;
+    }
+
+    assert(qtest_qmp_cmd_has_feature(from, "migrate", "config"));
+
+    wait_for_serial("src_serial");
+    qtest_qmp_assert_success(from, "{ 'execute' : 'stop'}");
+    migrate_ensure_converge(from);
+
+    migrate_incoming_qmp(to, "tcp:127.0.0.1:0", NULL,
+                         "{ 'exit-on-error': false, 'config': %p }",
+                         qobject_from_json(config, &error_abort));
+
+    migrate_qmp(from, to, migrate_get_connect_uri(to), NULL,
+                "{ 'config': %p }",
+                qobject_from_json(config, &error_abort));
+
+    wait_for_migration_complete(from);
+    wait_for_migration_complete(to);
+
+    migrate_end(from, to, false);
+}
+
 static void migration_test_add_misc_smoke(MigrationTestEnv *env)
 {
 #ifndef _WIN32
@@ -294,4 +330,7 @@ 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/config/migrate",
+                       test_config_migrate);
 }
-- 
2.35.3



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

* Re: [PATCH 16/21] qapi/migration: Mark that query/set-migrate-parameters support capabilities
  2025-06-03  1:38 ` [PATCH 16/21] qapi/migration: Mark that query/set-migrate-parameters support capabilities Fabiano Rosas
@ 2025-06-03  9:01   ` Daniel P. Berrangé
  2025-06-06 13:53     ` Fabiano Rosas
  0 siblings, 1 reply; 83+ messages in thread
From: Daniel P. Berrangé @ 2025-06-03  9:01 UTC (permalink / raw)
  To: Fabiano Rosas; +Cc: qemu-devel, Peter Xu, Markus Armbruster

On Mon, Jun 02, 2025 at 10:38:05PM -0300, Fabiano Rosas wrote:
> Add a QAPI command feature "capabilities" that can be queried by the
> client to check that the parameters commands now also support
> capabilities.
> 
> Signed-off-by: Fabiano Rosas <farosas@suse.de>
> ---
>  qapi/migration.json | 20 ++++++++++++++++++--
>  1 file changed, 18 insertions(+), 2 deletions(-)
> 
> diff --git a/qapi/migration.json b/qapi/migration.json
> index 5942622ba7..557a9c523e 100644
> --- a/qapi/migration.json
> +++ b/qapi/migration.json
> @@ -776,6 +776,13 @@
>  #
>  # Set various migration parameters.
>  #
> +# Features:
> +#
> +# @capabilities: Indicates this command supports setting the set of
> +# parameters previously known as capabilities.  This means this
> +# command can (and should) be used instead of the depreacated
> +# @migrate-set-capabilities.
> +#
>  # Since: 2.4
>  #
>  # .. qmp-example::
> @@ -785,7 +792,8 @@
>  #     <- { "return": {} }
>  ##
>  { 'command': 'migrate-set-parameters', 'boxed': true,
> -  'data': 'MigrationParameters' }
> +  'data': 'MigrationParameters',
> +  'features': [ 'capabilities' ] }
>  
>  ##
>  # @MigrationParameters:
> @@ -1110,6 +1118,13 @@
>  #
>  # Returns: @MigrationParameters
>  #
> +# Features:
> +#
> +# @capabilities: Indicates this command supports setting the set of
> +# parameters previously known as capabilities.  This means this
> +# command can (and should) be used instead of the depreacated
> +# @migrate-set-capabilities.
> +#
>  # Since: 2.4
>  #
>  # .. qmp-example::
> @@ -1125,7 +1140,8 @@
>  #        }
>  ##
>  { 'command': 'query-migrate-parameters',
> -  'returns': 'MigrationParameters' }
> +  'returns': 'MigrationParameters',
> +  'features': [ 'capabilities' ] }
>

Adding QAPI "features" is only needed if there is no other viable
way to detect existence of the feature. In this case, apps can
trivially detect the feature by querying the QAPI schema and
identifying that the MigrationParameters struct has gained a
load of new fields. So IMHO this patch can be dropped.


With regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|



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

* Re: [PATCH 19/21] migration: Allow migrate commands to provide the migration config
  2025-06-03  1:38 ` [PATCH 19/21] migration: Allow migrate commands to provide the migration config Fabiano Rosas
@ 2025-06-03  9:03   ` Daniel P. Berrangé
  2025-06-06 19:28   ` Peter Xu
  1 sibling, 0 replies; 83+ messages in thread
From: Daniel P. Berrangé @ 2025-06-03  9:03 UTC (permalink / raw)
  To: Fabiano Rosas; +Cc: qemu-devel, Peter Xu, Markus Armbruster

On Mon, Jun 02, 2025 at 10:38:08PM -0300, Fabiano Rosas wrote:
> Allow the migrate and migrate_incoming commands to pass the migration
> configuration options all at once, dispensing the use of
> migrate-set-parameters and migrate-set-capabilities.
> 
> The motivation of this is to simplify the interface with the
> management layer and avoid the usage of several command invocations to
> configure a migration. It also avoids stale parameters from a previous
> migration to influence the current migration.
> 
> The options that are changed during the migration can still be set
> with the existing commands.
> 
> The order of precedence is:
> 
> 'config' argument > -global cmdline > defaults (migration_properties)
> 
> I.e. the config takes precedence over all, values not present in the
> config assume the default values. The (debug) -global command line
> option allows the defaults to be overridden.
> 
> Signed-off-by: Fabiano Rosas <farosas@suse.de>
> ---
>  migration/migration-hmp-cmds.c |  5 +++--
>  migration/migration.c          | 29 ++++++++++++++++++++++++++---
>  migration/migration.h          |  1 +
>  migration/options.c            | 30 ++++++++++++++++++++++++++++++
>  migration/options.h            |  3 +++
>  qapi/migration.json            | 25 +++++++++++++++++++++++--
>  system/vl.c                    |  3 ++-
>  7 files changed, 88 insertions(+), 8 deletions(-)

> diff --git a/qapi/migration.json b/qapi/migration.json
> index 7282e4b9eb..64a92d8d28 100644
> --- a/qapi/migration.json
> +++ b/qapi/migration.json
> @@ -1474,9 +1474,16 @@
>  #
>  # @resume: resume one paused migration, default "off".  (since 3.0)
>  #
> +# @config: migration configuration options, previously set via
> +#     @migrate-set-parameters and @migrate-set-capabilities.  (since
> +#     10.1)
> +#
>  # Features:
>  #
>  # @deprecated: Argument @detach is deprecated.
> +# @config: Indicates this command can receive the entire migration
> +# configuration via the @config field, dispensing the use of
> +# @migrate-set-parameters.

There is no need to add this feature - mgmt apps can identify
this simply by the fact that the 'config' parameter now exists
in the QAPI schema.

>  #
>  # Since: 0.14
>  #
> @@ -1538,7 +1545,9 @@
>    'data': {'*uri': 'str',
>             '*channels': [ 'MigrationChannel' ],
>             '*detach': { 'type': 'bool', 'features': [ 'deprecated' ] },
> -           '*resume': 'bool' } }
> +           '*config': 'MigrationParameters',
> +           '*resume': 'bool' },
> +  'features': [ 'config' ] }
>  
>  ##
>  # @migrate-incoming:
> @@ -1557,6 +1566,16 @@
>  #     error details could be retrieved with query-migrate.
>  #     (since 9.1)
>  #
> +# @config: migration configuration options, previously set via
> +#     @migrate-set-parameters and @migrate-set-capabilities.  (since
> +#     10.1)
> +#
> +# Features:
> +#
> +# @config: Indicates this command can receive the entire migration
> +# configuration via the @config field, dispensing the use of
> +# @migrate-set-parameters.

Likewise redundant.

> +#
>  # Since: 2.3
>  #
>  # .. admonition:: Notes
> @@ -1610,7 +1629,9 @@
>  { 'command': 'migrate-incoming',
>               'data': {'*uri': 'str',
>                        '*channels': [ 'MigrationChannel' ],
> -                      '*exit-on-error': 'bool' } }
> +                      '*config': 'MigrationParameters',
> +                      '*exit-on-error': 'bool' },
> +             'features': [ 'config' ] }
>  
>  ##
>  # @xen-save-devices-state:

With regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|



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

* Re: [PATCH 01/21] migration: Normalize tls arguments
  2025-06-03  1:37 ` [PATCH 01/21] migration: Normalize tls arguments Fabiano Rosas
@ 2025-06-05 20:51   ` Peter Xu
  2025-06-06 13:48     ` Fabiano Rosas
  2025-06-25  9:41   ` Markus Armbruster
  1 sibling, 1 reply; 83+ messages in thread
From: Peter Xu @ 2025-06-05 20:51 UTC (permalink / raw)
  To: Fabiano Rosas; +Cc: qemu-devel, Markus Armbruster, Daniel P . Berrangé

On Mon, Jun 02, 2025 at 10:37:50PM -0300, Fabiano Rosas wrote:
> The migration parameters tls_creds, tls_authz and tls_hostname
> currently have a non-uniform handling. When used as arguments to
> migrate-set-parameters, their type is StrOrNull and when used as
> return value from query-migrate-parameters, their type is a plain
> string.
> 
> Not only having to convert between the types is cumbersome, but it
> also creates the issue of requiring two different QAPI types to be
> used, one for each command. MigrateSetParameters is used for
> migrate-set-parameters with the TLS arguments as StrOrNull while
> MigrationParameters is used for query-migrate-parameters with the TLS
> arguments as str.
> 
> Since StrOrNull could be considered a superset of str, change the type
> of the TLS arguments in MigrationParameters to StrOrNull and add a
> helper to ensure they're never actually used as QTYPE_QNULL.
> 
> This will allow the type duplication to be removed in the next
> patches.

Definitely a progress if we can finally merge the two!

> 
> Signed-off-by: Fabiano Rosas <farosas@suse.de>
> ---
>  migration/migration-hmp-cmds.c |   8 +-
>  migration/migration.c          |   2 +
>  migration/options.c            | 149 ++++++++++++++++++++-------------
>  migration/options.h            |   1 +
>  migration/tls.c                |   2 +-
>  qapi/migration.json            |   6 +-
>  6 files changed, 99 insertions(+), 69 deletions(-)
> 
> diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c
> index e8a563c7d8..bc8179c582 100644
> --- a/migration/migration-hmp-cmds.c
> +++ b/migration/migration-hmp-cmds.c
> @@ -276,14 +276,12 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict)
>          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);
> -        assert(params->tls_hostname);
> +                       params->tls_creds ? params->tls_creds->u.s : "");
>          monitor_printf(mon, "%s: '%s'\n",
>              MigrationParameter_str(MIGRATION_PARAMETER_TLS_HOSTNAME),
> -            params->tls_hostname);
> +                       params->tls_hostname ? params->tls_hostname->u.s : "");
>          assert(params->has_max_bandwidth);
>          monitor_printf(mon, "%s: %" PRIu64 " bytes/second\n",
>              MigrationParameter_str(MIGRATION_PARAMETER_MAX_BANDWIDTH),
> @@ -319,7 +317,7 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict)
>              params->max_postcopy_bandwidth);
>          monitor_printf(mon, "%s: '%s'\n",
>              MigrationParameter_str(MIGRATION_PARAMETER_TLS_AUTHZ),
> -            params->tls_authz);
> +                       params->tls_authz ? params->tls_authz->u.s : "");
>  
>          if (params->has_block_bitmap_mapping) {
>              const BitmapMigrationNodeAliasList *bmnal;
> diff --git a/migration/migration.c b/migration/migration.c
> index 4697732bef..f65cb81b6d 100644
> --- a/migration/migration.c
> +++ b/migration/migration.c
> @@ -4053,6 +4053,8 @@ static void migration_instance_finalize(Object *obj)
>  {
>      MigrationState *ms = MIGRATION_OBJ(obj);
>  
> +    migrate_tls_opts_free(&ms->parameters);

Does this line imply that maybe it's time we make MigrationState.parameters
a pointer instead of embeded struct anymore?

We already used to have tls_* strings hence that idea should be already
applicable even before.. but this work just made me think about it more.

So instead of freeing sub-fields if we add more to MigrationParameters, we
could then do:

  g_clear_pointer(&ms->parameters, qapi_free_MigrationParameters);

> +
>      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 162c72cda4..45a95dc6da 100644
> --- a/migration/options.c
> +++ b/migration/options.c
> @@ -162,9 +162,11 @@ const Property migration_properties[] = {
>      DEFINE_PROP_SIZE("announce-step", MigrationState,
>                        parameters.announce_step,
>                        DEFAULT_MIGRATE_ANNOUNCE_STEP),
> -    DEFINE_PROP_STRING("tls-creds", MigrationState, parameters.tls_creds),
> -    DEFINE_PROP_STRING("tls-hostname", MigrationState, parameters.tls_hostname),
> -    DEFINE_PROP_STRING("tls-authz", MigrationState, parameters.tls_authz),
> +    /*
> +     * tls-creds, tls-hostname and tls-authz are of type StrOrNull,
> +     * which can't be easily handled (if at all) by qdev. So these
> +     * will not be exposed as global migration options (-global).
> +     */
>      DEFINE_PROP_UINT64("x-vcpu-dirty-limit-period", MigrationState,
>                         parameters.x_vcpu_dirty_limit_period,
>                         DEFAULT_MIGRATE_VCPU_DIRTY_LIMIT_PERIOD),
> @@ -379,13 +381,6 @@ bool migrate_rdma(void)
>      return s->rdma_migration;
>  }
>  
> -bool migrate_tls(void)
> -{
> -    MigrationState *s = migrate_get_current();
> -
> -    return s->parameters.tls_creds && *s->parameters.tls_creds;
> -}
> -
>  typedef enum WriteTrackingSupport {
>      WT_SUPPORT_UNKNOWN = 0,
>      WT_SUPPORT_ABSENT,
> @@ -834,21 +829,44 @@ const char *migrate_tls_authz(void)
>  {
>      MigrationState *s = migrate_get_current();
>  
> -    return s->parameters.tls_authz;
> +    if (s->parameters.tls_authz &&
> +        s->parameters.tls_authz->type == QTYPE_QSTRING &&
> +        *s->parameters.tls_authz->u.s) {
> +        return s->parameters.tls_authz->u.s;
> +    }

Nitpick: some deduplication would be nice?  E.g.

  bool StrOrNull_str_valid(StrOrNull *obj)
  {
      return obj && obj->type == QTYPE_QSTRING && *obj->u.s;
  }

  const char *StrOrNull_str_get(StrOrNull *obj)
  {
      assert(obj && obj->type == QTYPE_QSTRING);
      return obj->u.s;
  }

  const char *StrOrNull_to_str(StrOrNull *obj)
  {
      return StrOrNull_str_valid(obj) ? StrOrNull_str_get(obj) : NULL;
  }

Then:

const char *migrate_tls_authz(void)
{
    return StrOrNull_to_str(s->parameters.tls_authz);
}

Then apply below.

> +
> +    return NULL;
>  }
>  
>  const char *migrate_tls_creds(void)
>  {
>      MigrationState *s = migrate_get_current();
>  
> -    return s->parameters.tls_creds;
> +    if (s->parameters.tls_creds &&
> +        s->parameters.tls_creds->type == QTYPE_QSTRING &&
> +        *s->parameters.tls_creds->u.s) {
> +        return s->parameters.tls_creds->u.s;
> +    }
> +
> +    return NULL;
>  }
>  
>  const char *migrate_tls_hostname(void)
>  {
>      MigrationState *s = migrate_get_current();
>  
> -    return s->parameters.tls_hostname;
> +    if (s->parameters.tls_hostname &&
> +        s->parameters.tls_hostname->type == QTYPE_QSTRING &&
> +        *s->parameters.tls_hostname->u.s) {
> +        return s->parameters.tls_hostname->u.s;
> +    }
> +
> +    return NULL;
> +}
> +
> +bool migrate_tls(void)
> +{
> +    return !!migrate_tls_creds();
>  }
>  
>  uint64_t migrate_vcpu_dirty_limit_period(void)
> @@ -888,6 +906,36 @@ AnnounceParameters *migrate_announce_params(void)
>      return &ap;
>  }
>  
> +void migrate_tls_opts_free(MigrationParameters *params)
> +{
> +    qapi_free_StrOrNull(params->tls_creds);
> +    qapi_free_StrOrNull(params->tls_hostname);
> +    qapi_free_StrOrNull(params->tls_authz);
> +}
> +
> +/* needs BQL if dst is part of s->parameters */
> +static void tls_option_set_str(StrOrNull **dstp, StrOrNull *src)

Nitpick: set_str made me think that the internal objects will be stolen.
Maybe tls_option_copy()?

> +{
> +    StrOrNull *dst = *dstp;
> +
> +    assert(!dst);
> +
> +    dst = *dstp = g_new0(StrOrNull, 1);
> +    dst->type = QTYPE_QSTRING;
> +
> +    if (!src) {
> +        dst->u.s = g_strdup("");
> +        return;
> +    }
> +
> +    if (src->type == QTYPE_QSTRING) {
> +        dst->u.s = g_strdup(src->u.s);
> +    } else {
> +        assert(src->type == QTYPE_QNULL);
> +        dst->u.s = g_strdup("");
> +    }
> +}
> +
>  MigrationParameters *qmp_query_migrate_parameters(Error **errp)
>  {
>      MigrationParameters *params;
> @@ -903,10 +951,11 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp)
>      params->cpu_throttle_increment = s->parameters.cpu_throttle_increment;
>      params->has_cpu_throttle_tailslow = true;
>      params->cpu_throttle_tailslow = s->parameters.cpu_throttle_tailslow;
> -    params->tls_creds = g_strdup(s->parameters.tls_creds);
> -    params->tls_hostname = g_strdup(s->parameters.tls_hostname);
> -    params->tls_authz = g_strdup(s->parameters.tls_authz ?
> -                                 s->parameters.tls_authz : "");
> +
> +    tls_option_set_str(&params->tls_creds, s->parameters.tls_creds);
> +    tls_option_set_str(&params->tls_hostname, s->parameters.tls_hostname);
> +    tls_option_set_str(&params->tls_authz, s->parameters.tls_authz);
> +
>      params->has_max_bandwidth = true;
>      params->max_bandwidth = s->parameters.max_bandwidth;
>      params->has_avail_switchover_bandwidth = true;
> @@ -963,9 +1012,6 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp)
>  
>  void migrate_params_init(MigrationParameters *params)
>  {
> -    params->tls_hostname = g_strdup("");
> -    params->tls_creds = g_strdup("");
> -
>      /* Set has_* up only for parameter checks */
>      params->has_throttle_trigger_threshold = true;
>      params->has_cpu_throttle_initial = true;
> @@ -1142,7 +1188,8 @@ bool migrate_params_check(MigrationParameters *params, Error **errp)
>  #ifdef CONFIG_LINUX
>      if (migrate_zero_copy_send() &&
>          ((params->has_multifd_compression && params->multifd_compression) ||
> -         (params->tls_creds && *params->tls_creds))) {
> +         (params->tls_creds && params->tls_creds->type == QTYPE_QSTRING &&
> +          *params->tls_creds->u.s))) {

Nitpick: StrOrNull_str_valid() candidate.

>          error_setg(errp,
>                     "Zero copy only available for non-compressed non-TLS multifd migration");
>          return false;
> @@ -1204,18 +1251,24 @@ static void migrate_params_test_apply(MigrateSetParameters *params,
>      }
>  
>      if (params->tls_creds) {
> -        assert(params->tls_creds->type == QTYPE_QSTRING);
> -        dest->tls_creds = params->tls_creds->u.s;
> +        tls_option_set_str(&dest->tls_creds, params->tls_creds);
> +    } else {
> +        /* drop the reference, it's owned by s->parameters */
> +        dest->tls_creds = NULL;
>      }

This looks ok, but smells tricky, because before this line, "params" can
contain something that it doesn't own..

If above idea of "making MigrationState->parameters a real QAPI object"
works out, here IIUC we can also make "params" a real QAPI object by
QAPI_CLONE(MigrationState->parameters), then when reaching here:

       if (params->tls_creds) {
          qapi_free_StrOrNull(params->tls_creds);
          params->tls_creds = QAPI_CLONE(params->tls_creds);
       }

It avoids the tricky "else" where we need to remember to clear something it
doesn't ever own.  Same to the rest.

>  
>      if (params->tls_hostname) {
> -        assert(params->tls_hostname->type == QTYPE_QSTRING);
> -        dest->tls_hostname = params->tls_hostname->u.s;
> +        tls_option_set_str(&dest->tls_hostname, params->tls_hostname);
> +    } else {
> +        /* drop the reference, it's owned by s->parameters */
> +        dest->tls_hostname = NULL;
>      }
>  
>      if (params->tls_authz) {
> -        assert(params->tls_authz->type == QTYPE_QSTRING);
> -        dest->tls_authz = params->tls_authz->u.s;
> +        tls_option_set_str(&dest->tls_authz, params->tls_authz);
> +    } else {
> +        /* drop the reference, it's owned by s->parameters */
> +        dest->tls_authz = NULL;
>      }
>  
>      if (params->has_max_bandwidth) {
> @@ -1320,21 +1373,18 @@ static void migrate_params_apply(MigrateSetParameters *params, Error **errp)
>      }
>  
>      if (params->tls_creds) {
> -        g_free(s->parameters.tls_creds);
> -        assert(params->tls_creds->type == QTYPE_QSTRING);
> -        s->parameters.tls_creds = g_strdup(params->tls_creds->u.s);
> +        qapi_free_StrOrNull(s->parameters.tls_creds);
> +        tls_option_set_str(&s->parameters.tls_creds, params->tls_creds);

Yeah, exactly like this one..

>      }
>  
>      if (params->tls_hostname) {
> -        g_free(s->parameters.tls_hostname);
> -        assert(params->tls_hostname->type == QTYPE_QSTRING);
> -        s->parameters.tls_hostname = g_strdup(params->tls_hostname->u.s);
> +        qapi_free_StrOrNull(s->parameters.tls_hostname);
> +        tls_option_set_str(&s->parameters.tls_hostname, params->tls_hostname);
>      }
>  
>      if (params->tls_authz) {
> -        g_free(s->parameters.tls_authz);
> -        assert(params->tls_authz->type == QTYPE_QSTRING);
> -        s->parameters.tls_authz = g_strdup(params->tls_authz->u.s);
> +        qapi_free_StrOrNull(s->parameters.tls_authz);
> +        tls_option_set_str(&s->parameters.tls_authz, params->tls_authz);
>      }
>  
>      if (params->has_max_bandwidth) {
> @@ -1433,32 +1483,11 @@ void qmp_migrate_set_parameters(MigrateSetParameters *params, Error **errp)
>  {
>      MigrationParameters tmp;
>  
> -    /* TODO Rewrite "" to null instead for all three tls_* parameters */
> -    if (params->tls_creds
> -        && params->tls_creds->type == QTYPE_QNULL) {
> -        qobject_unref(params->tls_creds->u.n);
> -        params->tls_creds->type = QTYPE_QSTRING;
> -        params->tls_creds->u.s = strdup("");
> -    }
> -    if (params->tls_hostname
> -        && params->tls_hostname->type == QTYPE_QNULL) {
> -        qobject_unref(params->tls_hostname->u.n);
> -        params->tls_hostname->type = QTYPE_QSTRING;
> -        params->tls_hostname->u.s = strdup("");
> -    }
> -    if (params->tls_authz
> -        && params->tls_authz->type == QTYPE_QNULL) {
> -        qobject_unref(params->tls_authz->u.n);
> -        params->tls_authz->type = QTYPE_QSTRING;
> -        params->tls_authz->u.s = strdup("");
> -    }
> -
>      migrate_params_test_apply(params, &tmp);
>  
> -    if (!migrate_params_check(&tmp, errp)) {
> -        /* Invalid parameter */
> -        return;
> +    if (migrate_params_check(&tmp, errp)) {
> +        migrate_params_apply(params, errp);
>      }
>  
> -    migrate_params_apply(params, errp);
> +    migrate_tls_opts_free(&tmp);
>  }
> diff --git a/migration/options.h b/migration/options.h
> index 82d839709e..999eee6f3b 100644
> --- a/migration/options.h
> +++ b/migration/options.h
> @@ -91,4 +91,5 @@ ZeroPageDetection migrate_zero_page_detection(void);
>  
>  bool migrate_params_check(MigrationParameters *params, Error **errp);
>  void migrate_params_init(MigrationParameters *params);
> +void migrate_tls_opts_free(MigrationParameters *params);
>  #endif
> diff --git a/migration/tls.c b/migration/tls.c
> index 5cbf952383..8a89d3f767 100644
> --- a/migration/tls.c
> +++ b/migration/tls.c
> @@ -126,7 +126,7 @@ QIOChannelTLS *migration_tls_client_create(QIOChannel *ioc,
>      }
>  
>      const char *tls_hostname = migrate_tls_hostname();
> -    if (tls_hostname && *tls_hostname) {
> +    if (tls_hostname) {
>          hostname = tls_hostname;
>      }
>  
> diff --git a/qapi/migration.json b/qapi/migration.json
> index 41826bde45..fa42d94810 100644
> --- a/qapi/migration.json
> +++ b/qapi/migration.json
> @@ -1293,9 +1293,9 @@
>              '*cpu-throttle-initial': 'uint8',
>              '*cpu-throttle-increment': 'uint8',
>              '*cpu-throttle-tailslow': 'bool',
> -            '*tls-creds': 'str',
> -            '*tls-hostname': 'str',
> -            '*tls-authz': 'str',
> +            '*tls-creds': 'StrOrNull',
> +            '*tls-hostname': 'StrOrNull',
> +            '*tls-authz': 'StrOrNull',
>              '*max-bandwidth': 'size',
>              '*avail-switchover-bandwidth': 'size',
>              '*downtime-limit': 'uint64',
> -- 
> 2.35.3
> 

-- 
Peter Xu



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

* Re: [PATCH 02/21] migration: Remove MigrateSetParameters
  2025-06-03  1:37 ` [PATCH 02/21] migration: Remove MigrateSetParameters Fabiano Rosas
@ 2025-06-05 20:58   ` Peter Xu
  2025-06-25 11:31   ` Markus Armbruster
  1 sibling, 0 replies; 83+ messages in thread
From: Peter Xu @ 2025-06-05 20:58 UTC (permalink / raw)
  To: Fabiano Rosas; +Cc: qemu-devel, Markus Armbruster, Daniel P . Berrangé

On Mon, Jun 02, 2025 at 10:37:51PM -0300, Fabiano Rosas wrote:
> Now that the TLS options have been made the same between
> migrate-set-parameters and query-migrate-parameters, a single type can
> be used. Remove MigrateSetParameters.
> 
> The TLS options documentation from MigrationParameters were replaced
> with the ones from MigrateSetParameters which was more complete.
> 
> I'm choosing to somewhat ignore any ambiguity between "query" and
> "set" because other options' docs are already ambiguous in that
> regard.
> 
> Signed-off-by: Fabiano Rosas <farosas@suse.de>

It's like some wish is going to come true..

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

-- 
Peter Xu



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

* Re: [PATCH 03/21] qapi/migration: Don't document MigrationParameter
  2025-06-03  1:37 ` [PATCH 03/21] qapi/migration: Don't document MigrationParameter Fabiano Rosas
@ 2025-06-05 21:00   ` Peter Xu
  2025-06-25 12:04   ` Markus Armbruster
  1 sibling, 0 replies; 83+ messages in thread
From: Peter Xu @ 2025-06-05 21:00 UTC (permalink / raw)
  To: Fabiano Rosas; +Cc: qemu-devel, Markus Armbruster, Daniel P . Berrangé

On Mon, Jun 02, 2025 at 10:37:52PM -0300, Fabiano Rosas wrote:
> The MigrationParameter (singular) enumeration is not part of the
> migration QMP API, it's only used for nicely converting HMP strings
> into MigrationParameters (plural) members and for providing readline
> completion.
> 
> Documenting this enum only serves to duplicate documentation between
> MigrationParameter and MigrationParameters.
> 
> Add an exception to QAPIs pragma.json and stop documenting it.
> 
> Signed-off-by: Fabiano Rosas <farosas@suse.de>

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

-- 
Peter Xu



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

* Re: [PATCH 01/21] migration: Normalize tls arguments
  2025-06-05 20:51   ` Peter Xu
@ 2025-06-06 13:48     ` Fabiano Rosas
  0 siblings, 0 replies; 83+ messages in thread
From: Fabiano Rosas @ 2025-06-06 13:48 UTC (permalink / raw)
  To: Peter Xu; +Cc: qemu-devel, Markus Armbruster, Daniel P . Berrangé

Peter Xu <peterx@redhat.com> writes:

> On Mon, Jun 02, 2025 at 10:37:50PM -0300, Fabiano Rosas wrote:
>> The migration parameters tls_creds, tls_authz and tls_hostname
>> currently have a non-uniform handling. When used as arguments to
>> migrate-set-parameters, their type is StrOrNull and when used as
>> return value from query-migrate-parameters, their type is a plain
>> string.
>> 
>> Not only having to convert between the types is cumbersome, but it
>> also creates the issue of requiring two different QAPI types to be
>> used, one for each command. MigrateSetParameters is used for
>> migrate-set-parameters with the TLS arguments as StrOrNull while
>> MigrationParameters is used for query-migrate-parameters with the TLS
>> arguments as str.
>> 
>> Since StrOrNull could be considered a superset of str, change the type
>> of the TLS arguments in MigrationParameters to StrOrNull and add a
>> helper to ensure they're never actually used as QTYPE_QNULL.
>> 
>> This will allow the type duplication to be removed in the next
>> patches.
>
> Definitely a progress if we can finally merge the two!
>
>> 
>> Signed-off-by: Fabiano Rosas <farosas@suse.de>
>> ---
>>  migration/migration-hmp-cmds.c |   8 +-
>>  migration/migration.c          |   2 +
>>  migration/options.c            | 149 ++++++++++++++++++++-------------
>>  migration/options.h            |   1 +
>>  migration/tls.c                |   2 +-
>>  qapi/migration.json            |   6 +-
>>  6 files changed, 99 insertions(+), 69 deletions(-)
>> 
>> diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c
>> index e8a563c7d8..bc8179c582 100644
>> --- a/migration/migration-hmp-cmds.c
>> +++ b/migration/migration-hmp-cmds.c
>> @@ -276,14 +276,12 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict)
>>          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);
>> -        assert(params->tls_hostname);
>> +                       params->tls_creds ? params->tls_creds->u.s : "");
>>          monitor_printf(mon, "%s: '%s'\n",
>>              MigrationParameter_str(MIGRATION_PARAMETER_TLS_HOSTNAME),
>> -            params->tls_hostname);
>> +                       params->tls_hostname ? params->tls_hostname->u.s : "");
>>          assert(params->has_max_bandwidth);
>>          monitor_printf(mon, "%s: %" PRIu64 " bytes/second\n",
>>              MigrationParameter_str(MIGRATION_PARAMETER_MAX_BANDWIDTH),
>> @@ -319,7 +317,7 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict)
>>              params->max_postcopy_bandwidth);
>>          monitor_printf(mon, "%s: '%s'\n",
>>              MigrationParameter_str(MIGRATION_PARAMETER_TLS_AUTHZ),
>> -            params->tls_authz);
>> +                       params->tls_authz ? params->tls_authz->u.s : "");
>>  
>>          if (params->has_block_bitmap_mapping) {
>>              const BitmapMigrationNodeAliasList *bmnal;
>> diff --git a/migration/migration.c b/migration/migration.c
>> index 4697732bef..f65cb81b6d 100644
>> --- a/migration/migration.c
>> +++ b/migration/migration.c
>> @@ -4053,6 +4053,8 @@ static void migration_instance_finalize(Object *obj)
>>  {
>>      MigrationState *ms = MIGRATION_OBJ(obj);
>>  
>> +    migrate_tls_opts_free(&ms->parameters);
>
> Does this line imply that maybe it's time we make MigrationState.parameters
> a pointer instead of embeded struct anymore?
>
> We already used to have tls_* strings hence that idea should be already
> applicable even before.. but this work just made me think about it more.
>
> So instead of freeing sub-fields if we add more to MigrationParameters, we
> could then do:
>
>   g_clear_pointer(&ms->parameters, qapi_free_MigrationParameters);
>

I don't see why not. I can add a patch at the end doing the conversion
globally.

>> +
>>      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 162c72cda4..45a95dc6da 100644
>> --- a/migration/options.c
>> +++ b/migration/options.c
>> @@ -162,9 +162,11 @@ const Property migration_properties[] = {
>>      DEFINE_PROP_SIZE("announce-step", MigrationState,
>>                        parameters.announce_step,
>>                        DEFAULT_MIGRATE_ANNOUNCE_STEP),
>> -    DEFINE_PROP_STRING("tls-creds", MigrationState, parameters.tls_creds),
>> -    DEFINE_PROP_STRING("tls-hostname", MigrationState, parameters.tls_hostname),
>> -    DEFINE_PROP_STRING("tls-authz", MigrationState, parameters.tls_authz),
>> +    /*
>> +     * tls-creds, tls-hostname and tls-authz are of type StrOrNull,
>> +     * which can't be easily handled (if at all) by qdev. So these
>> +     * will not be exposed as global migration options (-global).
>> +     */
>>      DEFINE_PROP_UINT64("x-vcpu-dirty-limit-period", MigrationState,
>>                         parameters.x_vcpu_dirty_limit_period,
>>                         DEFAULT_MIGRATE_VCPU_DIRTY_LIMIT_PERIOD),
>> @@ -379,13 +381,6 @@ bool migrate_rdma(void)
>>      return s->rdma_migration;
>>  }
>>  
>> -bool migrate_tls(void)
>> -{
>> -    MigrationState *s = migrate_get_current();
>> -
>> -    return s->parameters.tls_creds && *s->parameters.tls_creds;
>> -}
>> -
>>  typedef enum WriteTrackingSupport {
>>      WT_SUPPORT_UNKNOWN = 0,
>>      WT_SUPPORT_ABSENT,
>> @@ -834,21 +829,44 @@ const char *migrate_tls_authz(void)
>>  {
>>      MigrationState *s = migrate_get_current();
>>  
>> -    return s->parameters.tls_authz;
>> +    if (s->parameters.tls_authz &&
>> +        s->parameters.tls_authz->type == QTYPE_QSTRING &&
>> +        *s->parameters.tls_authz->u.s) {
>> +        return s->parameters.tls_authz->u.s;
>> +    }
>
> Nitpick: some deduplication would be nice?  E.g.
>
>   bool StrOrNull_str_valid(StrOrNull *obj)
>   {
>       return obj && obj->type == QTYPE_QSTRING && *obj->u.s;
>   }
>
>   const char *StrOrNull_str_get(StrOrNull *obj)
>   {
>       assert(obj && obj->type == QTYPE_QSTRING);
>       return obj->u.s;
>   }
>
>   const char *StrOrNull_to_str(StrOrNull *obj)
>   {
>       return StrOrNull_str_valid(obj) ? StrOrNull_str_get(obj) : NULL;
>   }
>
> Then:
>
> const char *migrate_tls_authz(void)
> {
>     return StrOrNull_to_str(s->parameters.tls_authz);
> }
>
> Then apply below.
>

Sure.

>> +
>> +    return NULL;
>>  }
>>  
>>  const char *migrate_tls_creds(void)
>>  {
>>      MigrationState *s = migrate_get_current();
>>  
>> -    return s->parameters.tls_creds;
>> +    if (s->parameters.tls_creds &&
>> +        s->parameters.tls_creds->type == QTYPE_QSTRING &&
>> +        *s->parameters.tls_creds->u.s) {
>> +        return s->parameters.tls_creds->u.s;
>> +    }
>> +
>> +    return NULL;
>>  }
>>  
>>  const char *migrate_tls_hostname(void)
>>  {
>>      MigrationState *s = migrate_get_current();
>>  
>> -    return s->parameters.tls_hostname;
>> +    if (s->parameters.tls_hostname &&
>> +        s->parameters.tls_hostname->type == QTYPE_QSTRING &&
>> +        *s->parameters.tls_hostname->u.s) {
>> +        return s->parameters.tls_hostname->u.s;
>> +    }
>> +
>> +    return NULL;
>> +}
>> +
>> +bool migrate_tls(void)
>> +{
>> +    return !!migrate_tls_creds();
>>  }
>>  
>>  uint64_t migrate_vcpu_dirty_limit_period(void)
>> @@ -888,6 +906,36 @@ AnnounceParameters *migrate_announce_params(void)
>>      return &ap;
>>  }
>>  
>> +void migrate_tls_opts_free(MigrationParameters *params)
>> +{
>> +    qapi_free_StrOrNull(params->tls_creds);
>> +    qapi_free_StrOrNull(params->tls_hostname);
>> +    qapi_free_StrOrNull(params->tls_authz);
>> +}
>> +
>> +/* needs BQL if dst is part of s->parameters */
>> +static void tls_option_set_str(StrOrNull **dstp, StrOrNull *src)
>
> Nitpick: set_str made me think that the internal objects will be stolen.
> Maybe tls_option_copy()?
>

Yeah, the idea was "set the TLS option and make sure it's a string".

>> +{
>> +    StrOrNull *dst = *dstp;
>> +
>> +    assert(!dst);
>> +
>> +    dst = *dstp = g_new0(StrOrNull, 1);
>> +    dst->type = QTYPE_QSTRING;
>> +
>> +    if (!src) {
>> +        dst->u.s = g_strdup("");
>> +        return;
>> +    }
>> +
>> +    if (src->type == QTYPE_QSTRING) {
>> +        dst->u.s = g_strdup(src->u.s);
>> +    } else {
>> +        assert(src->type == QTYPE_QNULL);
>> +        dst->u.s = g_strdup("");
>> +    }
>> +}
>> +
>>  MigrationParameters *qmp_query_migrate_parameters(Error **errp)
>>  {
>>      MigrationParameters *params;
>> @@ -903,10 +951,11 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp)
>>      params->cpu_throttle_increment = s->parameters.cpu_throttle_increment;
>>      params->has_cpu_throttle_tailslow = true;
>>      params->cpu_throttle_tailslow = s->parameters.cpu_throttle_tailslow;
>> -    params->tls_creds = g_strdup(s->parameters.tls_creds);
>> -    params->tls_hostname = g_strdup(s->parameters.tls_hostname);
>> -    params->tls_authz = g_strdup(s->parameters.tls_authz ?
>> -                                 s->parameters.tls_authz : "");
>> +
>> +    tls_option_set_str(&params->tls_creds, s->parameters.tls_creds);
>> +    tls_option_set_str(&params->tls_hostname, s->parameters.tls_hostname);
>> +    tls_option_set_str(&params->tls_authz, s->parameters.tls_authz);
>> +
>>      params->has_max_bandwidth = true;
>>      params->max_bandwidth = s->parameters.max_bandwidth;
>>      params->has_avail_switchover_bandwidth = true;
>> @@ -963,9 +1012,6 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp)
>>  
>>  void migrate_params_init(MigrationParameters *params)
>>  {
>> -    params->tls_hostname = g_strdup("");
>> -    params->tls_creds = g_strdup("");
>> -
>>      /* Set has_* up only for parameter checks */
>>      params->has_throttle_trigger_threshold = true;
>>      params->has_cpu_throttle_initial = true;
>> @@ -1142,7 +1188,8 @@ bool migrate_params_check(MigrationParameters *params, Error **errp)
>>  #ifdef CONFIG_LINUX
>>      if (migrate_zero_copy_send() &&
>>          ((params->has_multifd_compression && params->multifd_compression) ||
>> -         (params->tls_creds && *params->tls_creds))) {
>> +         (params->tls_creds && params->tls_creds->type == QTYPE_QSTRING &&
>> +          *params->tls_creds->u.s))) {
>
> Nitpick: StrOrNull_str_valid() candidate.
>
>>          error_setg(errp,
>>                     "Zero copy only available for non-compressed non-TLS multifd migration");
>>          return false;
>> @@ -1204,18 +1251,24 @@ static void migrate_params_test_apply(MigrateSetParameters *params,
>>      }
>>  
>>      if (params->tls_creds) {
>> -        assert(params->tls_creds->type == QTYPE_QSTRING);
>> -        dest->tls_creds = params->tls_creds->u.s;
>> +        tls_option_set_str(&dest->tls_creds, params->tls_creds);
>> +    } else {
>> +        /* drop the reference, it's owned by s->parameters */
>> +        dest->tls_creds = NULL;
>>      }
>
> This looks ok, but smells tricky, because before this line, "params" can
> contain something that it doesn't own..
>

This else actually goes away in patch 11. I'm taking small steps to get
there to make the diffs easier to review.

> If above idea of "making MigrationState->parameters a real QAPI object"
> works out, here IIUC we can also make "params" a real QAPI object by
> QAPI_CLONE(MigrationState->parameters), then when reaching here:
>
>        if (params->tls_creds) {
>           qapi_free_StrOrNull(params->tls_creds);
>           params->tls_creds = QAPI_CLONE(params->tls_creds);
>        }
>

Patches 10-13 clean this situation up and use QAPI_CLONE* all over.

> It avoids the tricky "else" where we need to remember to clear something it
> doesn't ever own.  Same to the rest.
>
>>  
>>      if (params->tls_hostname) {
>> -        assert(params->tls_hostname->type == QTYPE_QSTRING);
>> -        dest->tls_hostname = params->tls_hostname->u.s;
>> +        tls_option_set_str(&dest->tls_hostname, params->tls_hostname);
>> +    } else {
>> +        /* drop the reference, it's owned by s->parameters */
>> +        dest->tls_hostname = NULL;
>>      }
>>  
>>      if (params->tls_authz) {
>> -        assert(params->tls_authz->type == QTYPE_QSTRING);
>> -        dest->tls_authz = params->tls_authz->u.s;
>> +        tls_option_set_str(&dest->tls_authz, params->tls_authz);
>> +    } else {
>> +        /* drop the reference, it's owned by s->parameters */
>> +        dest->tls_authz = NULL;
>>      }
>>  
>>      if (params->has_max_bandwidth) {
>> @@ -1320,21 +1373,18 @@ static void migrate_params_apply(MigrateSetParameters *params, Error **errp)
>>      }
>>  
>>      if (params->tls_creds) {
>> -        g_free(s->parameters.tls_creds);
>> -        assert(params->tls_creds->type == QTYPE_QSTRING);
>> -        s->parameters.tls_creds = g_strdup(params->tls_creds->u.s);
>> +        qapi_free_StrOrNull(s->parameters.tls_creds);
>> +        tls_option_set_str(&s->parameters.tls_creds, params->tls_creds);
>
> Yeah, exactly like this one..
>
>>      }
>>  
>>      if (params->tls_hostname) {
>> -        g_free(s->parameters.tls_hostname);
>> -        assert(params->tls_hostname->type == QTYPE_QSTRING);
>> -        s->parameters.tls_hostname = g_strdup(params->tls_hostname->u.s);
>> +        qapi_free_StrOrNull(s->parameters.tls_hostname);
>> +        tls_option_set_str(&s->parameters.tls_hostname, params->tls_hostname);
>>      }
>>  
>>      if (params->tls_authz) {
>> -        g_free(s->parameters.tls_authz);
>> -        assert(params->tls_authz->type == QTYPE_QSTRING);
>> -        s->parameters.tls_authz = g_strdup(params->tls_authz->u.s);
>> +        qapi_free_StrOrNull(s->parameters.tls_authz);
>> +        tls_option_set_str(&s->parameters.tls_authz, params->tls_authz);
>>      }
>>  
>>      if (params->has_max_bandwidth) {
>> @@ -1433,32 +1483,11 @@ void qmp_migrate_set_parameters(MigrateSetParameters *params, Error **errp)
>>  {
>>      MigrationParameters tmp;
>>  
>> -    /* TODO Rewrite "" to null instead for all three tls_* parameters */
>> -    if (params->tls_creds
>> -        && params->tls_creds->type == QTYPE_QNULL) {
>> -        qobject_unref(params->tls_creds->u.n);
>> -        params->tls_creds->type = QTYPE_QSTRING;
>> -        params->tls_creds->u.s = strdup("");
>> -    }
>> -    if (params->tls_hostname
>> -        && params->tls_hostname->type == QTYPE_QNULL) {
>> -        qobject_unref(params->tls_hostname->u.n);
>> -        params->tls_hostname->type = QTYPE_QSTRING;
>> -        params->tls_hostname->u.s = strdup("");
>> -    }
>> -    if (params->tls_authz
>> -        && params->tls_authz->type == QTYPE_QNULL) {
>> -        qobject_unref(params->tls_authz->u.n);
>> -        params->tls_authz->type = QTYPE_QSTRING;
>> -        params->tls_authz->u.s = strdup("");
>> -    }
>> -
>>      migrate_params_test_apply(params, &tmp);
>>  
>> -    if (!migrate_params_check(&tmp, errp)) {
>> -        /* Invalid parameter */
>> -        return;
>> +    if (migrate_params_check(&tmp, errp)) {
>> +        migrate_params_apply(params, errp);
>>      }
>>  
>> -    migrate_params_apply(params, errp);
>> +    migrate_tls_opts_free(&tmp);
>>  }
>> diff --git a/migration/options.h b/migration/options.h
>> index 82d839709e..999eee6f3b 100644
>> --- a/migration/options.h
>> +++ b/migration/options.h
>> @@ -91,4 +91,5 @@ ZeroPageDetection migrate_zero_page_detection(void);
>>  
>>  bool migrate_params_check(MigrationParameters *params, Error **errp);
>>  void migrate_params_init(MigrationParameters *params);
>> +void migrate_tls_opts_free(MigrationParameters *params);
>>  #endif
>> diff --git a/migration/tls.c b/migration/tls.c
>> index 5cbf952383..8a89d3f767 100644
>> --- a/migration/tls.c
>> +++ b/migration/tls.c
>> @@ -126,7 +126,7 @@ QIOChannelTLS *migration_tls_client_create(QIOChannel *ioc,
>>      }
>>  
>>      const char *tls_hostname = migrate_tls_hostname();
>> -    if (tls_hostname && *tls_hostname) {
>> +    if (tls_hostname) {
>>          hostname = tls_hostname;
>>      }
>>  
>> diff --git a/qapi/migration.json b/qapi/migration.json
>> index 41826bde45..fa42d94810 100644
>> --- a/qapi/migration.json
>> +++ b/qapi/migration.json
>> @@ -1293,9 +1293,9 @@
>>              '*cpu-throttle-initial': 'uint8',
>>              '*cpu-throttle-increment': 'uint8',
>>              '*cpu-throttle-tailslow': 'bool',
>> -            '*tls-creds': 'str',
>> -            '*tls-hostname': 'str',
>> -            '*tls-authz': 'str',
>> +            '*tls-creds': 'StrOrNull',
>> +            '*tls-hostname': 'StrOrNull',
>> +            '*tls-authz': 'StrOrNull',
>>              '*max-bandwidth': 'size',
>>              '*avail-switchover-bandwidth': 'size',
>>              '*downtime-limit': 'uint64',
>> -- 
>> 2.35.3
>> 


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

* Re: [PATCH 16/21] qapi/migration: Mark that query/set-migrate-parameters support capabilities
  2025-06-03  9:01   ` Daniel P. Berrangé
@ 2025-06-06 13:53     ` Fabiano Rosas
  0 siblings, 0 replies; 83+ messages in thread
From: Fabiano Rosas @ 2025-06-06 13:53 UTC (permalink / raw)
  To: Daniel P. Berrangé; +Cc: qemu-devel, Peter Xu, Markus Armbruster

Daniel P. Berrangé <berrange@redhat.com> writes:

> On Mon, Jun 02, 2025 at 10:38:05PM -0300, Fabiano Rosas wrote:
>> Add a QAPI command feature "capabilities" that can be queried by the
>> client to check that the parameters commands now also support
>> capabilities.
>> 
>> Signed-off-by: Fabiano Rosas <farosas@suse.de>
>> ---
>>  qapi/migration.json | 20 ++++++++++++++++++--
>>  1 file changed, 18 insertions(+), 2 deletions(-)
>> 
>> diff --git a/qapi/migration.json b/qapi/migration.json
>> index 5942622ba7..557a9c523e 100644
>> --- a/qapi/migration.json
>> +++ b/qapi/migration.json
>> @@ -776,6 +776,13 @@
>>  #
>>  # Set various migration parameters.
>>  #
>> +# Features:
>> +#
>> +# @capabilities: Indicates this command supports setting the set of
>> +# parameters previously known as capabilities.  This means this
>> +# command can (and should) be used instead of the depreacated
>> +# @migrate-set-capabilities.
>> +#
>>  # Since: 2.4
>>  #
>>  # .. qmp-example::
>> @@ -785,7 +792,8 @@
>>  #     <- { "return": {} }
>>  ##
>>  { 'command': 'migrate-set-parameters', 'boxed': true,
>> -  'data': 'MigrationParameters' }
>> +  'data': 'MigrationParameters',
>> +  'features': [ 'capabilities' ] }
>>  
>>  ##
>>  # @MigrationParameters:
>> @@ -1110,6 +1118,13 @@
>>  #
>>  # Returns: @MigrationParameters
>>  #
>> +# Features:
>> +#
>> +# @capabilities: Indicates this command supports setting the set of
>> +# parameters previously known as capabilities.  This means this
>> +# command can (and should) be used instead of the depreacated
>> +# @migrate-set-capabilities.
>> +#
>>  # Since: 2.4
>>  #
>>  # .. qmp-example::
>> @@ -1125,7 +1140,8 @@
>>  #        }
>>  ##
>>  { 'command': 'query-migrate-parameters',
>> -  'returns': 'MigrationParameters' }
>> +  'returns': 'MigrationParameters',
>> +  'features': [ 'capabilities' ] }
>>
>
> Adding QAPI "features" is only needed if there is no other viable
> way to detect existence of the feature. In this case, apps can
> trivially detect the feature by querying the QAPI schema and
> identifying that the MigrationParameters struct has gained a
> load of new fields. So IMHO this patch can be dropped.
>

Oh, ok. I was thinking this would be friendlier to the management layer,
but if it's all the same I'll drop it. Same for the other one in patch
19.

Thanks


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

* Re: [PATCH 05/21] migration: Add a flag to track block-bitmap-mapping input
  2025-06-03  1:37 ` [PATCH 05/21] migration: Add a flag to track block-bitmap-mapping input Fabiano Rosas
@ 2025-06-06 15:03   ` Peter Xu
  2025-06-06 15:43     ` Fabiano Rosas
  0 siblings, 1 reply; 83+ messages in thread
From: Peter Xu @ 2025-06-06 15:03 UTC (permalink / raw)
  To: Fabiano Rosas; +Cc: qemu-devel, Markus Armbruster, Daniel P . Berrangé

On Mon, Jun 02, 2025 at 10:37:54PM -0300, Fabiano Rosas wrote:
> The QAPI converts an empty list on the block-bitmap-mapping input into
> a NULL BitmapMigrationNodeAliasList. The empty list is a valid input
> for the block-bitmap-mapping option, so commit 3cba22c9ad ("migration:
> Fix block_bitmap_mapping migration") started using the
> s->parameters.has_block_bitmap_mapping field to tell when the user has
> passed in an empty list vs. when no list has been passed at all.
> 
> However, using the has_block_bitmap_mapping field of s->parameters is
> only possible because MigrationParameters has had its members made
> optional due to historical reasons.
> 
> In order to make improvements to the way configuration options are set
> for a migration, we'd like to reduce the usage of the has_* fields of
> the global configuration object (s->parameters).
> 
> Add a separate boolean to track the status of the block_bitmap_mapping
> option.
> 
> (this was verified to not regress iotest 300, which is the test that
> 3cba22c9ad refers to)
> 
> Signed-off-by: Fabiano Rosas <farosas@suse.de>
> ---
>  migration/migration.h | 7 +++++++
>  migration/options.c   | 6 +++---
>  2 files changed, 10 insertions(+), 3 deletions(-)
> 
> diff --git a/migration/migration.h b/migration/migration.h
> index d53f7cad84..ab797540b0 100644
> --- a/migration/migration.h
> +++ b/migration/migration.h
> @@ -510,6 +510,13 @@ struct MigrationState {
>      bool rdma_migration;
>  
>      GSource *hup_source;
> +
> +    /*
> +     * The block-bitmap-mapping option is allowed to be an emtpy list,
> +     * therefore we need a way to know wheter the user has given
> +     * anything as input.
> +     */
> +    bool has_block_bitmap_mapping;
>  };
>  
>  void migrate_set_state(MigrationStatus *state, MigrationStatus old_state,
> diff --git a/migration/options.c b/migration/options.c
> index f64e141394..cf77826204 100644
> --- a/migration/options.c
> +++ b/migration/options.c
> @@ -685,7 +685,7 @@ bool migrate_has_block_bitmap_mapping(void)
>  {
>      MigrationState *s = migrate_get_current();
>  
> -    return s->parameters.has_block_bitmap_mapping;
> +    return s->has_block_bitmap_mapping;
>  }
>  
>  uint32_t migrate_checkpoint_delay(void)
> @@ -989,7 +989,7 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp)
>      params->has_announce_step = true;
>      params->announce_step = s->parameters.announce_step;
>  
> -    if (s->parameters.has_block_bitmap_mapping) {
> +    if (s->has_block_bitmap_mapping) {
>          params->has_block_bitmap_mapping = true;
>          params->block_bitmap_mapping =
>              QAPI_CLONE(BitmapMigrationNodeAliasList,
> @@ -1469,7 +1469,7 @@ static void migrate_params_apply(MigrationParameters *params)
>          qapi_free_BitmapMigrationNodeAliasList(
>              s->parameters.block_bitmap_mapping);
>  
> -        s->parameters.has_block_bitmap_mapping = true;
> +        s->has_block_bitmap_mapping = true;
>          s->parameters.block_bitmap_mapping =
>              QAPI_CLONE(BitmapMigrationNodeAliasList,
>                         params->block_bitmap_mapping);
> -- 
> 2.35.3
> 

This is definitely unfortunate, and I'm still scratching my head on
understanding why it's necessary.

E.g. I tried to revert this patch manually and iotest 300 passed, with:

===8<===
From a952479805d8bdfe532ad4e0c0092f758991af08 Mon Sep 17 00:00:00 2001
From: Peter Xu <peterx@redhat.com>
Date: Fri, 6 Jun 2025 10:44:37 -0400
Subject: [PATCH] Revert "migration: Add a flag to track block-bitmap-mapping
 input"

This reverts commit fd755a53c0e4ce9739d20d7cdd69400b2a37102c.

Signed-off-by: Peter Xu <peterx@redhat.com>
---
 migration/migration.h | 7 -------
 migration/options.c   | 4 ++--
 2 files changed, 2 insertions(+), 9 deletions(-)

diff --git a/migration/migration.h b/migration/migration.h
index 49761f4699..e710c421f8 100644
--- a/migration/migration.h
+++ b/migration/migration.h
@@ -510,13 +510,6 @@ struct MigrationState {
     bool rdma_migration;
 
     GSource *hup_source;
-
-    /*
-     * The block-bitmap-mapping option is allowed to be an emtpy list,
-     * therefore we need a way to know wheter the user has given
-     * anything as input.
-     */
-    bool has_block_bitmap_mapping;
 };
 
 void migrate_set_state(MigrationStatus *state, MigrationStatus old_state,
diff --git a/migration/options.c b/migration/options.c
index dd2288187d..e71a57764d 100644
--- a/migration/options.c
+++ b/migration/options.c
@@ -765,7 +765,7 @@ bool migrate_has_block_bitmap_mapping(void)
 {
     MigrationState *s = migrate_get_current();
 
-    return s->has_block_bitmap_mapping;
+    return s->parameters.has_block_bitmap_mapping;
 }
 
 uint32_t migrate_checkpoint_delay(void)
@@ -1376,7 +1376,7 @@ void qmp_migrate_set_parameters(MigrationParameters *params, Error **errp)
      * params structure with the user input around.
      */
     if (params->has_block_bitmap_mapping) {
-        migrate_get_current()->has_block_bitmap_mapping = true;
+        migrate_get_current()->parameters.has_block_bitmap_mapping = true;
     }
 
     if (migrate_params_check(tmp, errp)) {
-- 
2.49.0
===8<===

I'm staring at commit 3cba22c9ad now, looks like what it wants to do is
making sure construct_alias_map() will be invoked even if the block bitmap
mapping is NULL itself.  But then right below the code, it has:

static int init_dirty_bitmap_migration(DBMSaveState *s, Error **errp)
{
    ...
    if (migrate_has_block_bitmap_mapping()) {
        alias_map = construct_alias_map(migrate_block_bitmap_mapping(), true,
                                        &error_abort);
    }
    ...
    if (!alias_map) {
    ...
    }
}

Looks like it's also ready for !alias_map anyway.  I'm definitely puzzled
by this code.

Even if so, IIUC the question can still be asked on whether we can always
assume has_block_bitmap_mapping to be always true, then here instead of:

    if (migrate_has_block_bitmap_mapping()) {
        alias_map = construct_alias_map(migrate_block_bitmap_mapping(), true,
                                        &error_abort);
    }

We do:

    alias_map = construct_alias_map(migrate_block_bitmap_mapping(), true,
                                    &error_abort);

After all it looks like construct_alias_map() takes NULL too..

-- 
Peter Xu



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

* Re: [PATCH 06/21] migration: Remove checks for s->parameters has_* fields
  2025-06-03  1:37 ` [PATCH 06/21] migration: Remove checks for s->parameters has_* fields Fabiano Rosas
@ 2025-06-06 15:13   ` Peter Xu
  0 siblings, 0 replies; 83+ messages in thread
From: Peter Xu @ 2025-06-06 15:13 UTC (permalink / raw)
  To: Fabiano Rosas; +Cc: qemu-devel, Markus Armbruster, Daniel P . Berrangé

On Mon, Jun 02, 2025 at 10:37:55PM -0300, Fabiano Rosas wrote:
> The migration parameters validation produces a temporary structure
> which is the merge of the current parameter values (s->parameters,
> MigrationParameters) with the new parameters set by the user
> (MigrateSetParameters).
> 
> When copying the values from s->parameters into the temporary
> structure, the has_* fields are copied along, but when merging the
> user-input values (MigrateSetParameters) they are not.
> 
> During migrate_params_check(), only the parameters that have the
> corresponding has_* field will be checked, so only the parameters that
> were initialized in migrate_params_init() will be validated.
> 
> This causes (almost) all of the migration parameters to be validated
> every time a parameter is set, regardless of which fields the user
> touched, but it also skips validation of any values that are not set
> in migrate_params_init().
> 
> It's not clear what was the intention of the original code, whether to
> validate all fields always, or only validate what the user input
> changed. Since the current situation is closer to the former option,
> make the choice of validating all parameters by removing the checks
> for the has_* fields when validating.
> 
> Note that bringing the user input into the temporary structure for
> validation still needs to look at the has_* fields, otherwise any
> parameters not set by the user (i.e. 0) would override the
> corresponding value in s->parameters.
> 
> Signed-off-by: Fabiano Rosas <farosas@suse.de>

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

-- 
Peter Xu



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

* Re: [PATCH 08/21] migration: Do away with usage of QERR_INVALID_PARAMETER_VALUE
  2025-06-03  1:37 ` [PATCH 08/21] migration: Do away with usage of QERR_INVALID_PARAMETER_VALUE Fabiano Rosas
@ 2025-06-06 15:17   ` Peter Xu
  0 siblings, 0 replies; 83+ messages in thread
From: Peter Xu @ 2025-06-06 15:17 UTC (permalink / raw)
  To: Fabiano Rosas; +Cc: qemu-devel, Markus Armbruster, Daniel P . Berrangé

On Mon, Jun 02, 2025 at 10:37:57PM -0300, Fabiano Rosas wrote:
> The QERR_INVALID_PARAMETER_VALUE macro is documented as not to be used
> in new code. Remove the usage from migration/options.c.
> 
> Signed-off-by: Fabiano Rosas <farosas@suse.de>

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

-- 
Peter Xu



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

* Re: [PATCH 09/21] migration: Extract code to mark all parameters as present
  2025-06-03  1:37 ` [PATCH 09/21] migration: Extract code to mark all parameters as present Fabiano Rosas
@ 2025-06-06 15:26   ` Peter Xu
  2025-06-06 15:51     ` Fabiano Rosas
  0 siblings, 1 reply; 83+ messages in thread
From: Peter Xu @ 2025-06-06 15:26 UTC (permalink / raw)
  To: Fabiano Rosas; +Cc: qemu-devel, Markus Armbruster, Daniel P . Berrangé

On Mon, Jun 02, 2025 at 10:37:58PM -0300, Fabiano Rosas wrote:
> MigrationParameters needs to have all of its has_* fields marked as
> true when used as the return of query_migrate_parameters because the
> corresponding QMP command has all of its members non-optional by
> design, despite them being marked as optional in migration.json.
> 
> Extract this code into a function and make it assert if any field is
> missing. With this we ensure future changes will not inadvertently
> leave any parameters missing.
> 
> Also assert that s->parameters _does not_ have any of its has_* fields
> set. This structure is internal to the migration code and it should
> not rely on the QAPI-generate has_* fields. We might want to store
> migration parameters differently in the future.
> 
> Signed-off-by: Fabiano Rosas <farosas@suse.de>
> ---
>  migration/options.c | 74 ++++++++++++++++++++++++++++-----------------
>  1 file changed, 46 insertions(+), 28 deletions(-)
> 
> diff --git a/migration/options.c b/migration/options.c
> index e2e3ab717f..dd62e726cb 100644
> --- a/migration/options.c
> +++ b/migration/options.c
> @@ -936,6 +936,40 @@ static void tls_option_set_str(StrOrNull **dstp, StrOrNull *src)
>      }
>  }
>  
> +static void migrate_mark_all_params_present(MigrationParameters *p)
> +{
> +    int len, n_str_args = 3; /* tls-creds, tls-hostname, tls-authz */

Could you remind me why we don't set has_*=true for these three?

> +    bool *has_fields[] = {
> +        &p->has_throttle_trigger_threshold, &p->has_cpu_throttle_initial,
> +        &p->has_cpu_throttle_increment, &p->has_cpu_throttle_tailslow,
> +        &p->has_max_bandwidth, &p->has_avail_switchover_bandwidth,
> +        &p->has_downtime_limit, &p->has_x_checkpoint_delay,
> +        &p->has_multifd_channels, &p->has_multifd_compression,
> +        &p->has_multifd_zlib_level, &p->has_multifd_qatzip_level,
> +        &p->has_multifd_zstd_level, &p->has_xbzrle_cache_size,
> +        &p->has_max_postcopy_bandwidth, &p->has_max_cpu_throttle,
> +        &p->has_announce_initial, &p->has_announce_max, &p->has_announce_rounds,
> +        &p->has_announce_step, &p->has_block_bitmap_mapping,
> +        &p->has_x_vcpu_dirty_limit_period, &p->has_vcpu_dirty_limit,
> +        &p->has_mode, &p->has_zero_page_detection, &p->has_direct_io,
> +    };
> +
> +    /*
> +     * The has_* fields of MigrationParameters are used by QAPI to
> +     * inform whether an optional struct member is present. Keep this
> +     * decoupled from the internal usage (not QAPI) by leaving the
> +     * has_* fields of s->parameters unused.
> +     */
> +    assert(p != &(migrate_get_current())->parameters);

This is OK, I'm not sure whether we're over-cautious though.. but..

> +
> +    len = ARRAY_SIZE(has_fields);
> +    assert(len + n_str_args == MIGRATION_PARAMETER__MAX);

.. I definitely like this assert.

> +
> +    for (int i = 0; i < len; i++) {
> +        *has_fields[i] = true;
> +    }
> +}
> +
>  MigrationParameters *qmp_query_migrate_parameters(Error **errp)
>  {
>      MigrationParameters *params;
> @@ -943,68 +977,52 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp)
>  
>      /* TODO use QAPI_CLONE() instead of duplicating it inline */
>      params = g_malloc0(sizeof(*params));
> -    params->has_throttle_trigger_threshold = true;
> +
>      params->throttle_trigger_threshold = s->parameters.throttle_trigger_threshold;
> -    params->has_cpu_throttle_initial = true;
>      params->cpu_throttle_initial = s->parameters.cpu_throttle_initial;
> -    params->has_cpu_throttle_increment = true;
>      params->cpu_throttle_increment = s->parameters.cpu_throttle_increment;
> -    params->has_cpu_throttle_tailslow = true;
>      params->cpu_throttle_tailslow = s->parameters.cpu_throttle_tailslow;
>  
>      tls_option_set_str(&params->tls_creds, s->parameters.tls_creds);
>      tls_option_set_str(&params->tls_hostname, s->parameters.tls_hostname);
>      tls_option_set_str(&params->tls_authz, s->parameters.tls_authz);
>  
> -    params->has_max_bandwidth = true;
>      params->max_bandwidth = s->parameters.max_bandwidth;
> -    params->has_avail_switchover_bandwidth = true;
>      params->avail_switchover_bandwidth = s->parameters.avail_switchover_bandwidth;
> -    params->has_downtime_limit = true;
>      params->downtime_limit = s->parameters.downtime_limit;
> -    params->has_x_checkpoint_delay = true;
>      params->x_checkpoint_delay = s->parameters.x_checkpoint_delay;
> -    params->has_multifd_channels = true;
>      params->multifd_channels = s->parameters.multifd_channels;
> -    params->has_multifd_compression = true;
>      params->multifd_compression = s->parameters.multifd_compression;
> -    params->has_multifd_zlib_level = true;
>      params->multifd_zlib_level = s->parameters.multifd_zlib_level;
> -    params->has_multifd_qatzip_level = true;
>      params->multifd_qatzip_level = s->parameters.multifd_qatzip_level;
> -    params->has_multifd_zstd_level = true;
>      params->multifd_zstd_level = s->parameters.multifd_zstd_level;
> -    params->has_xbzrle_cache_size = true;
>      params->xbzrle_cache_size = s->parameters.xbzrle_cache_size;
> -    params->has_max_postcopy_bandwidth = true;
>      params->max_postcopy_bandwidth = s->parameters.max_postcopy_bandwidth;
> -    params->has_max_cpu_throttle = true;
>      params->max_cpu_throttle = s->parameters.max_cpu_throttle;
> -    params->has_announce_initial = true;
>      params->announce_initial = s->parameters.announce_initial;
> -    params->has_announce_max = true;
>      params->announce_max = s->parameters.announce_max;
> -    params->has_announce_rounds = true;
>      params->announce_rounds = s->parameters.announce_rounds;
> -    params->has_announce_step = true;
>      params->announce_step = s->parameters.announce_step;
> -
> -    params->has_block_bitmap_mapping = true;
>      params->block_bitmap_mapping =
>          QAPI_CLONE(BitmapMigrationNodeAliasList,
>                     s->parameters.block_bitmap_mapping);
> -
> -    params->has_x_vcpu_dirty_limit_period = true;
>      params->x_vcpu_dirty_limit_period = s->parameters.x_vcpu_dirty_limit_period;
> -    params->has_vcpu_dirty_limit = true;
>      params->vcpu_dirty_limit = s->parameters.vcpu_dirty_limit;
> -    params->has_mode = true;
>      params->mode = s->parameters.mode;
> -    params->has_zero_page_detection = true;
>      params->zero_page_detection = s->parameters.zero_page_detection;
> -    params->has_direct_io = true;
>      params->direct_io = s->parameters.direct_io;
>  
> +    /*
> +     * query-migrate-parameters expects all members of
> +     * MigrationParameters to be present, but we cannot mark them
> +     * non-optional in QAPI because the structure is also used for
> +     * migrate-set-parameters, which needs the optionality. Force all
> +     * parameters to be seen as present now. Note that this depends on
> +     * some form of default being set for every member of
> +     * MigrationParameters, currently done during qdev init using
> +     * migration_properties defined in this file.
> +     */
> +    migrate_mark_all_params_present(params);
>      return params;
>  }
>  
> -- 
> 2.35.3
> 

-- 
Peter Xu



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

* Re: [PATCH 10/21] migration: Use QAPI_CLONE_MEMBERS in query_migrate_parameters
  2025-06-03  1:37 ` [PATCH 10/21] migration: Use QAPI_CLONE_MEMBERS in query_migrate_parameters Fabiano Rosas
@ 2025-06-06 15:29   ` Peter Xu
  2025-06-06 15:53     ` Fabiano Rosas
  0 siblings, 1 reply; 83+ messages in thread
From: Peter Xu @ 2025-06-06 15:29 UTC (permalink / raw)
  To: Fabiano Rosas; +Cc: qemu-devel, Markus Armbruster, Daniel P . Berrangé

On Mon, Jun 02, 2025 at 10:37:59PM -0300, Fabiano Rosas wrote:
> QAPI_CLONE_MEMBERS is a better option than copying parameters one by
> one because it operates on the entire struct and follows pointers. It
> also avoids the need to alter this function every time a new parameter
> is added.
> 
> Note, since this is a deep clone, now we must free the TLS strings
> before assignment.
> 
> Signed-off-by: Fabiano Rosas <farosas@suse.de>
> ---
>  migration/options.c | 31 ++++---------------------------
>  1 file changed, 4 insertions(+), 27 deletions(-)
> 
> diff --git a/migration/options.c b/migration/options.c
> index dd62e726cb..0a2a3050ec 100644
> --- a/migration/options.c
> +++ b/migration/options.c
> @@ -918,7 +918,9 @@ static void tls_option_set_str(StrOrNull **dstp, StrOrNull *src)
>  {
>      StrOrNull *dst = *dstp;
>  
> -    assert(!dst);
> +    if (dst) {
> +        qapi_free_StrOrNull(dst);
> +    }
>  
>      dst = *dstp = g_new0(StrOrNull, 1);
>      dst->type = QTYPE_QSTRING;
> @@ -975,42 +977,17 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp)
>      MigrationParameters *params;
>      MigrationState *s = migrate_get_current();
>  
> -    /* TODO use QAPI_CLONE() instead of duplicating it inline */
>      params = g_malloc0(sizeof(*params));
>  
> -    params->throttle_trigger_threshold = s->parameters.throttle_trigger_threshold;
> -    params->cpu_throttle_initial = s->parameters.cpu_throttle_initial;
> -    params->cpu_throttle_increment = s->parameters.cpu_throttle_increment;
> -    params->cpu_throttle_tailslow = s->parameters.cpu_throttle_tailslow;
> +    QAPI_CLONE_MEMBERS(MigrationParameters, params, &s->parameters);
>  
>      tls_option_set_str(&params->tls_creds, s->parameters.tls_creds);
>      tls_option_set_str(&params->tls_hostname, s->parameters.tls_hostname);
>      tls_option_set_str(&params->tls_authz, s->parameters.tls_authz);
>  
> -    params->max_bandwidth = s->parameters.max_bandwidth;
> -    params->avail_switchover_bandwidth = s->parameters.avail_switchover_bandwidth;
> -    params->downtime_limit = s->parameters.downtime_limit;
> -    params->x_checkpoint_delay = s->parameters.x_checkpoint_delay;
> -    params->multifd_channels = s->parameters.multifd_channels;
> -    params->multifd_compression = s->parameters.multifd_compression;
> -    params->multifd_zlib_level = s->parameters.multifd_zlib_level;
> -    params->multifd_qatzip_level = s->parameters.multifd_qatzip_level;
> -    params->multifd_zstd_level = s->parameters.multifd_zstd_level;
> -    params->xbzrle_cache_size = s->parameters.xbzrle_cache_size;
> -    params->max_postcopy_bandwidth = s->parameters.max_postcopy_bandwidth;
> -    params->max_cpu_throttle = s->parameters.max_cpu_throttle;
> -    params->announce_initial = s->parameters.announce_initial;
> -    params->announce_max = s->parameters.announce_max;
> -    params->announce_rounds = s->parameters.announce_rounds;
> -    params->announce_step = s->parameters.announce_step;
>      params->block_bitmap_mapping =
>          QAPI_CLONE(BitmapMigrationNodeAliasList,
>                     s->parameters.block_bitmap_mapping);

Wouldn't the QAPI_CLONE_MEMBERS() have deep cloned this too?

> -    params->x_vcpu_dirty_limit_period = s->parameters.x_vcpu_dirty_limit_period;
> -    params->vcpu_dirty_limit = s->parameters.vcpu_dirty_limit;
> -    params->mode = s->parameters.mode;
> -    params->zero_page_detection = s->parameters.zero_page_detection;
> -    params->direct_io = s->parameters.direct_io;
>  
>      /*
>       * query-migrate-parameters expects all members of
> -- 
> 2.35.3
> 

-- 
Peter Xu



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

* Re: [PATCH 05/21] migration: Add a flag to track block-bitmap-mapping input
  2025-06-06 15:03   ` Peter Xu
@ 2025-06-06 15:43     ` Fabiano Rosas
  2025-06-06 17:44       ` Peter Xu
  0 siblings, 1 reply; 83+ messages in thread
From: Fabiano Rosas @ 2025-06-06 15:43 UTC (permalink / raw)
  To: Peter Xu; +Cc: qemu-devel, Markus Armbruster, Daniel P . Berrangé

Peter Xu <peterx@redhat.com> writes:

> On Mon, Jun 02, 2025 at 10:37:54PM -0300, Fabiano Rosas wrote:
>> The QAPI converts an empty list on the block-bitmap-mapping input into
>> a NULL BitmapMigrationNodeAliasList. The empty list is a valid input
>> for the block-bitmap-mapping option, so commit 3cba22c9ad ("migration:
>> Fix block_bitmap_mapping migration") started using the
>> s->parameters.has_block_bitmap_mapping field to tell when the user has
>> passed in an empty list vs. when no list has been passed at all.
>> 
>> However, using the has_block_bitmap_mapping field of s->parameters is
>> only possible because MigrationParameters has had its members made
>> optional due to historical reasons.
>> 
>> In order to make improvements to the way configuration options are set
>> for a migration, we'd like to reduce the usage of the has_* fields of
>> the global configuration object (s->parameters).
>> 
>> Add a separate boolean to track the status of the block_bitmap_mapping
>> option.
>> 
>> (this was verified to not regress iotest 300, which is the test that
>> 3cba22c9ad refers to)
>> 
>> Signed-off-by: Fabiano Rosas <farosas@suse.de>
>> ---
>>  migration/migration.h | 7 +++++++
>>  migration/options.c   | 6 +++---
>>  2 files changed, 10 insertions(+), 3 deletions(-)
>> 
>> diff --git a/migration/migration.h b/migration/migration.h
>> index d53f7cad84..ab797540b0 100644
>> --- a/migration/migration.h
>> +++ b/migration/migration.h
>> @@ -510,6 +510,13 @@ struct MigrationState {
>>      bool rdma_migration;
>>  
>>      GSource *hup_source;
>> +
>> +    /*
>> +     * The block-bitmap-mapping option is allowed to be an emtpy list,
>> +     * therefore we need a way to know wheter the user has given
>> +     * anything as input.
>> +     */
>> +    bool has_block_bitmap_mapping;
>>  };
>>  
>>  void migrate_set_state(MigrationStatus *state, MigrationStatus old_state,
>> diff --git a/migration/options.c b/migration/options.c
>> index f64e141394..cf77826204 100644
>> --- a/migration/options.c
>> +++ b/migration/options.c
>> @@ -685,7 +685,7 @@ bool migrate_has_block_bitmap_mapping(void)
>>  {
>>      MigrationState *s = migrate_get_current();
>>  
>> -    return s->parameters.has_block_bitmap_mapping;
>> +    return s->has_block_bitmap_mapping;
>>  }
>>  
>>  uint32_t migrate_checkpoint_delay(void)
>> @@ -989,7 +989,7 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp)
>>      params->has_announce_step = true;
>>      params->announce_step = s->parameters.announce_step;
>>  
>> -    if (s->parameters.has_block_bitmap_mapping) {
>> +    if (s->has_block_bitmap_mapping) {
>>          params->has_block_bitmap_mapping = true;
>>          params->block_bitmap_mapping =
>>              QAPI_CLONE(BitmapMigrationNodeAliasList,
>> @@ -1469,7 +1469,7 @@ static void migrate_params_apply(MigrationParameters *params)
>>          qapi_free_BitmapMigrationNodeAliasList(
>>              s->parameters.block_bitmap_mapping);
>>  
>> -        s->parameters.has_block_bitmap_mapping = true;
>> +        s->has_block_bitmap_mapping = true;
>>          s->parameters.block_bitmap_mapping =
>>              QAPI_CLONE(BitmapMigrationNodeAliasList,
>>                         params->block_bitmap_mapping);
>> -- 
>> 2.35.3
>> 
>
> This is definitely unfortunate, and I'm still scratching my head on
> understanding why it's necessary.
>
> E.g. I tried to revert this patch manually and iotest 300 passed, with:

This (mine) patch is not needed per-se. I want it so we stop using
s->parameters.has_* altogether. If we think we need a flag to track
whether the user has passed some value or not, then we add one to some
migration specific state, say MigrationState.

This decouples the migration internal usage from the QAPI. Today we use
MigrationParameters as defined by the QAPI, we might in the future want
something else. And that something else might not come with has_*
fields. So it's simple enough now to add this one flag to the
MigrationState and be able to me completely independent from the
QAPI-generated has_ fields.

>
> ===8<===
> From a952479805d8bdfe532ad4e0c0092f758991af08 Mon Sep 17 00:00:00 2001
> From: Peter Xu <peterx@redhat.com>
> Date: Fri, 6 Jun 2025 10:44:37 -0400
> Subject: [PATCH] Revert "migration: Add a flag to track block-bitmap-mapping
>  input"
>
> This reverts commit fd755a53c0e4ce9739d20d7cdd69400b2a37102c.
>
> Signed-off-by: Peter Xu <peterx@redhat.com>
> ---
>  migration/migration.h | 7 -------
>  migration/options.c   | 4 ++--
>  2 files changed, 2 insertions(+), 9 deletions(-)
>
> diff --git a/migration/migration.h b/migration/migration.h
> index 49761f4699..e710c421f8 100644
> --- a/migration/migration.h
> +++ b/migration/migration.h
> @@ -510,13 +510,6 @@ struct MigrationState {
>      bool rdma_migration;
>  
>      GSource *hup_source;
> -
> -    /*
> -     * The block-bitmap-mapping option is allowed to be an emtpy list,
> -     * therefore we need a way to know wheter the user has given
> -     * anything as input.
> -     */
> -    bool has_block_bitmap_mapping;
>  };
>  
>  void migrate_set_state(MigrationStatus *state, MigrationStatus old_state,
> diff --git a/migration/options.c b/migration/options.c
> index dd2288187d..e71a57764d 100644
> --- a/migration/options.c
> +++ b/migration/options.c
> @@ -765,7 +765,7 @@ bool migrate_has_block_bitmap_mapping(void)
>  {
>      MigrationState *s = migrate_get_current();
>  
> -    return s->has_block_bitmap_mapping;
> +    return s->parameters.has_block_bitmap_mapping;
>  }
>  
>  uint32_t migrate_checkpoint_delay(void)
> @@ -1376,7 +1376,7 @@ void qmp_migrate_set_parameters(MigrationParameters *params, Error **errp)
>       * params structure with the user input around.
>       */
>      if (params->has_block_bitmap_mapping) {
> -        migrate_get_current()->has_block_bitmap_mapping = true;
> +        migrate_get_current()->parameters.has_block_bitmap_mapping = true;
>      }
>  
>      if (migrate_params_check(tmp, errp)) {
> -- 
> 2.49.0
> ===8<===
>
> I'm staring at commit 3cba22c9ad now, looks like what it wants to do is
> making sure construct_alias_map() will be invoked even if the block bitmap
> mapping is NULL itself.  But then right below the code, it has:
>
> static int init_dirty_bitmap_migration(DBMSaveState *s, Error **errp)
> {
>     ...
>     if (migrate_has_block_bitmap_mapping()) {
>         alias_map = construct_alias_map(migrate_block_bitmap_mapping(), true,
>                                         &error_abort);
>     }
>     ...
>     if (!alias_map) {
>     ...
>     }
> }
>
> Looks like it's also ready for !alias_map anyway.  I'm definitely puzzled
> by this code.
>
> Even if so, IIUC the question can still be asked on whether we can always
> assume has_block_bitmap_mapping to be always true, then here instead of:
>
>     if (migrate_has_block_bitmap_mapping()) {
>         alias_map = construct_alias_map(migrate_block_bitmap_mapping(), true,
>                                         &error_abort);
>     }
>
> We do:
>
>     alias_map = construct_alias_map(migrate_block_bitmap_mapping(), true,
>                                     &error_abort);
>
> After all it looks like construct_alias_map() takes NULL too..

The point is that construct_alias_map always returns a hashtable. It
might be empty if the user passes [], and that's ok according to
3cba22c9ad. So they needed some flag to say: "the user has tried to use
block-bitmap-mapping".

I don't know why it needs to be like that and I honestly don't want to
go into details of block migration just to be able to do a
refactoring. All I want is that this code stop using s->parameters.has_*
so we can do nice tricks with QAPI_CLONE later on and not bother about
this.

I fully support we chase this, but keep in mind this patch (mine) is
just gingerly moving the problem to the side so we can make progress
with this series.


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

* Re: [PATCH 09/21] migration: Extract code to mark all parameters as present
  2025-06-06 15:26   ` Peter Xu
@ 2025-06-06 15:51     ` Fabiano Rosas
  2025-06-06 17:48       ` Peter Xu
  0 siblings, 1 reply; 83+ messages in thread
From: Fabiano Rosas @ 2025-06-06 15:51 UTC (permalink / raw)
  To: Peter Xu; +Cc: qemu-devel, Markus Armbruster, Daniel P . Berrangé

Peter Xu <peterx@redhat.com> writes:

> On Mon, Jun 02, 2025 at 10:37:58PM -0300, Fabiano Rosas wrote:
>> MigrationParameters needs to have all of its has_* fields marked as
>> true when used as the return of query_migrate_parameters because the
>> corresponding QMP command has all of its members non-optional by
>> design, despite them being marked as optional in migration.json.
>> 
>> Extract this code into a function and make it assert if any field is
>> missing. With this we ensure future changes will not inadvertently
>> leave any parameters missing.
>> 
>> Also assert that s->parameters _does not_ have any of its has_* fields
>> set. This structure is internal to the migration code and it should
>> not rely on the QAPI-generate has_* fields. We might want to store
>> migration parameters differently in the future.
>> 
>> Signed-off-by: Fabiano Rosas <farosas@suse.de>
>> ---
>>  migration/options.c | 74 ++++++++++++++++++++++++++++-----------------
>>  1 file changed, 46 insertions(+), 28 deletions(-)
>> 
>> diff --git a/migration/options.c b/migration/options.c
>> index e2e3ab717f..dd62e726cb 100644
>> --- a/migration/options.c
>> +++ b/migration/options.c
>> @@ -936,6 +936,40 @@ static void tls_option_set_str(StrOrNull **dstp, StrOrNull *src)
>>      }
>>  }
>>  
>> +static void migrate_mark_all_params_present(MigrationParameters *p)
>> +{
>> +    int len, n_str_args = 3; /* tls-creds, tls-hostname, tls-authz */
>
> Could you remind me why we don't set has_*=true for these three?
>

I doesn't exist. These are StrOrNull so their presence is supposed to be
determined by checking against NULL pointer.

>> +    bool *has_fields[] = {
>> +        &p->has_throttle_trigger_threshold, &p->has_cpu_throttle_initial,
>> +        &p->has_cpu_throttle_increment, &p->has_cpu_throttle_tailslow,
>> +        &p->has_max_bandwidth, &p->has_avail_switchover_bandwidth,
>> +        &p->has_downtime_limit, &p->has_x_checkpoint_delay,
>> +        &p->has_multifd_channels, &p->has_multifd_compression,
>> +        &p->has_multifd_zlib_level, &p->has_multifd_qatzip_level,
>> +        &p->has_multifd_zstd_level, &p->has_xbzrle_cache_size,
>> +        &p->has_max_postcopy_bandwidth, &p->has_max_cpu_throttle,
>> +        &p->has_announce_initial, &p->has_announce_max, &p->has_announce_rounds,
>> +        &p->has_announce_step, &p->has_block_bitmap_mapping,
>> +        &p->has_x_vcpu_dirty_limit_period, &p->has_vcpu_dirty_limit,
>> +        &p->has_mode, &p->has_zero_page_detection, &p->has_direct_io,
>> +    };
>> +
>> +    /*
>> +     * The has_* fields of MigrationParameters are used by QAPI to
>> +     * inform whether an optional struct member is present. Keep this
>> +     * decoupled from the internal usage (not QAPI) by leaving the
>> +     * has_* fields of s->parameters unused.
>> +     */
>> +    assert(p != &(migrate_get_current())->parameters);
>
> This is OK, I'm not sure whether we're over-cautious though.. but..
>

Hopefully after this series the code will be encapsulated enough that we
don't need to think about this, but before this series the situation is
definitely confusing enough that we need to know which fields are used
for what.

I don't want to see people passing s->parameters into here thinking it's
all the same, because it isn't. The has_* fields should be used only for
QAPI stuff, user input validation, etc, while s->parameters is the thing
that stores all that after validation and there's not reason to be
messing with has_* since we know that's just an consequence of the fact
that we're choosing to use the same QAPI type for user input/output and
internal storage.

I guess what I'm trying to do is take the pain points where I got
confused while working on the current code and introduce some hard rules
to it.

>> +
>> +    len = ARRAY_SIZE(has_fields);
>> +    assert(len + n_str_args == MIGRATION_PARAMETER__MAX);
>
> .. I definitely like this assert.
>

Yep, we can't at the moment get rid of the enum because HMP needs it, so
let's put it to good use. I think this is specially useful for new
contributors, so they don't need to guess what needs to be changed when
adding a new parameter.



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

* Re: [PATCH 10/21] migration: Use QAPI_CLONE_MEMBERS in query_migrate_parameters
  2025-06-06 15:29   ` Peter Xu
@ 2025-06-06 15:53     ` Fabiano Rosas
  2025-06-12 20:58       ` Fabiano Rosas
  0 siblings, 1 reply; 83+ messages in thread
From: Fabiano Rosas @ 2025-06-06 15:53 UTC (permalink / raw)
  To: Peter Xu; +Cc: qemu-devel, Markus Armbruster, Daniel P . Berrangé

Peter Xu <peterx@redhat.com> writes:

> On Mon, Jun 02, 2025 at 10:37:59PM -0300, Fabiano Rosas wrote:
>> QAPI_CLONE_MEMBERS is a better option than copying parameters one by
>> one because it operates on the entire struct and follows pointers. It
>> also avoids the need to alter this function every time a new parameter
>> is added.
>> 
>> Note, since this is a deep clone, now we must free the TLS strings
>> before assignment.
>> 
>> Signed-off-by: Fabiano Rosas <farosas@suse.de>
>> ---
>>  migration/options.c | 31 ++++---------------------------
>>  1 file changed, 4 insertions(+), 27 deletions(-)
>> 
>> diff --git a/migration/options.c b/migration/options.c
>> index dd62e726cb..0a2a3050ec 100644
>> --- a/migration/options.c
>> +++ b/migration/options.c
>> @@ -918,7 +918,9 @@ static void tls_option_set_str(StrOrNull **dstp, StrOrNull *src)
>>  {
>>      StrOrNull *dst = *dstp;
>>  
>> -    assert(!dst);
>> +    if (dst) {
>> +        qapi_free_StrOrNull(dst);
>> +    }
>>  
>>      dst = *dstp = g_new0(StrOrNull, 1);
>>      dst->type = QTYPE_QSTRING;
>> @@ -975,42 +977,17 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp)
>>      MigrationParameters *params;
>>      MigrationState *s = migrate_get_current();
>>  
>> -    /* TODO use QAPI_CLONE() instead of duplicating it inline */
>>      params = g_malloc0(sizeof(*params));
>>  
>> -    params->throttle_trigger_threshold = s->parameters.throttle_trigger_threshold;
>> -    params->cpu_throttle_initial = s->parameters.cpu_throttle_initial;
>> -    params->cpu_throttle_increment = s->parameters.cpu_throttle_increment;
>> -    params->cpu_throttle_tailslow = s->parameters.cpu_throttle_tailslow;
>> +    QAPI_CLONE_MEMBERS(MigrationParameters, params, &s->parameters);
>>  
>>      tls_option_set_str(&params->tls_creds, s->parameters.tls_creds);
>>      tls_option_set_str(&params->tls_hostname, s->parameters.tls_hostname);
>>      tls_option_set_str(&params->tls_authz, s->parameters.tls_authz);
>>  
>> -    params->max_bandwidth = s->parameters.max_bandwidth;
>> -    params->avail_switchover_bandwidth = s->parameters.avail_switchover_bandwidth;
>> -    params->downtime_limit = s->parameters.downtime_limit;
>> -    params->x_checkpoint_delay = s->parameters.x_checkpoint_delay;
>> -    params->multifd_channels = s->parameters.multifd_channels;
>> -    params->multifd_compression = s->parameters.multifd_compression;
>> -    params->multifd_zlib_level = s->parameters.multifd_zlib_level;
>> -    params->multifd_qatzip_level = s->parameters.multifd_qatzip_level;
>> -    params->multifd_zstd_level = s->parameters.multifd_zstd_level;
>> -    params->xbzrle_cache_size = s->parameters.xbzrle_cache_size;
>> -    params->max_postcopy_bandwidth = s->parameters.max_postcopy_bandwidth;
>> -    params->max_cpu_throttle = s->parameters.max_cpu_throttle;
>> -    params->announce_initial = s->parameters.announce_initial;
>> -    params->announce_max = s->parameters.announce_max;
>> -    params->announce_rounds = s->parameters.announce_rounds;
>> -    params->announce_step = s->parameters.announce_step;
>>      params->block_bitmap_mapping =
>>          QAPI_CLONE(BitmapMigrationNodeAliasList,
>>                     s->parameters.block_bitmap_mapping);
>
> Wouldn't the QAPI_CLONE_MEMBERS() have deep cloned this too?
>

Hmm, I think it should. But it definitely broke something without this
line. I'll double check.

>> -    params->x_vcpu_dirty_limit_period = s->parameters.x_vcpu_dirty_limit_period;
>> -    params->vcpu_dirty_limit = s->parameters.vcpu_dirty_limit;
>> -    params->mode = s->parameters.mode;
>> -    params->zero_page_detection = s->parameters.zero_page_detection;
>> -    params->direct_io = s->parameters.direct_io;
>>  
>>      /*
>>       * query-migrate-parameters expects all members of
>> -- 
>> 2.35.3
>> 


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

* Re: [PATCH 05/21] migration: Add a flag to track block-bitmap-mapping input
  2025-06-06 15:43     ` Fabiano Rosas
@ 2025-06-06 17:44       ` Peter Xu
  2025-06-06 18:38         ` Fabiano Rosas
  0 siblings, 1 reply; 83+ messages in thread
From: Peter Xu @ 2025-06-06 17:44 UTC (permalink / raw)
  To: Fabiano Rosas; +Cc: qemu-devel, Markus Armbruster, Daniel P . Berrangé

On Fri, Jun 06, 2025 at 12:43:04PM -0300, Fabiano Rosas wrote:
> Peter Xu <peterx@redhat.com> writes:
> 
> > On Mon, Jun 02, 2025 at 10:37:54PM -0300, Fabiano Rosas wrote:
> >> The QAPI converts an empty list on the block-bitmap-mapping input into
> >> a NULL BitmapMigrationNodeAliasList. The empty list is a valid input
> >> for the block-bitmap-mapping option, so commit 3cba22c9ad ("migration:
> >> Fix block_bitmap_mapping migration") started using the
> >> s->parameters.has_block_bitmap_mapping field to tell when the user has
> >> passed in an empty list vs. when no list has been passed at all.
> >> 
> >> However, using the has_block_bitmap_mapping field of s->parameters is
> >> only possible because MigrationParameters has had its members made
> >> optional due to historical reasons.
> >> 
> >> In order to make improvements to the way configuration options are set
> >> for a migration, we'd like to reduce the usage of the has_* fields of
> >> the global configuration object (s->parameters).
> >> 
> >> Add a separate boolean to track the status of the block_bitmap_mapping
> >> option.
> >> 
> >> (this was verified to not regress iotest 300, which is the test that
> >> 3cba22c9ad refers to)
> >> 
> >> Signed-off-by: Fabiano Rosas <farosas@suse.de>
> >> ---
> >>  migration/migration.h | 7 +++++++
> >>  migration/options.c   | 6 +++---
> >>  2 files changed, 10 insertions(+), 3 deletions(-)
> >> 
> >> diff --git a/migration/migration.h b/migration/migration.h
> >> index d53f7cad84..ab797540b0 100644
> >> --- a/migration/migration.h
> >> +++ b/migration/migration.h
> >> @@ -510,6 +510,13 @@ struct MigrationState {
> >>      bool rdma_migration;
> >>  
> >>      GSource *hup_source;
> >> +
> >> +    /*
> >> +     * The block-bitmap-mapping option is allowed to be an emtpy list,
> >> +     * therefore we need a way to know wheter the user has given
> >> +     * anything as input.
> >> +     */
> >> +    bool has_block_bitmap_mapping;
> >>  };
> >>  
> >>  void migrate_set_state(MigrationStatus *state, MigrationStatus old_state,
> >> diff --git a/migration/options.c b/migration/options.c
> >> index f64e141394..cf77826204 100644
> >> --- a/migration/options.c
> >> +++ b/migration/options.c
> >> @@ -685,7 +685,7 @@ bool migrate_has_block_bitmap_mapping(void)
> >>  {
> >>      MigrationState *s = migrate_get_current();
> >>  
> >> -    return s->parameters.has_block_bitmap_mapping;
> >> +    return s->has_block_bitmap_mapping;
> >>  }
> >>  
> >>  uint32_t migrate_checkpoint_delay(void)
> >> @@ -989,7 +989,7 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp)
> >>      params->has_announce_step = true;
> >>      params->announce_step = s->parameters.announce_step;
> >>  
> >> -    if (s->parameters.has_block_bitmap_mapping) {
> >> +    if (s->has_block_bitmap_mapping) {
> >>          params->has_block_bitmap_mapping = true;
> >>          params->block_bitmap_mapping =
> >>              QAPI_CLONE(BitmapMigrationNodeAliasList,
> >> @@ -1469,7 +1469,7 @@ static void migrate_params_apply(MigrationParameters *params)
> >>          qapi_free_BitmapMigrationNodeAliasList(
> >>              s->parameters.block_bitmap_mapping);
> >>  
> >> -        s->parameters.has_block_bitmap_mapping = true;
> >> +        s->has_block_bitmap_mapping = true;
> >>          s->parameters.block_bitmap_mapping =
> >>              QAPI_CLONE(BitmapMigrationNodeAliasList,
> >>                         params->block_bitmap_mapping);
> >> -- 
> >> 2.35.3
> >> 
> >
> > This is definitely unfortunate, and I'm still scratching my head on
> > understanding why it's necessary.
> >
> > E.g. I tried to revert this patch manually and iotest 300 passed, with:
> 
> This (mine) patch is not needed per-se. I want it so we stop using
> s->parameters.has_* altogether. If we think we need a flag to track
> whether the user has passed some value or not, then we add one to some
> migration specific state, say MigrationState.
> 
> This decouples the migration internal usage from the QAPI. Today we use
> MigrationParameters as defined by the QAPI, we might in the future want
> something else. And that something else might not come with has_*
> fields. So it's simple enough now to add this one flag to the
> MigrationState and be able to me completely independent from the
> QAPI-generated has_ fields.
> 
> >
> > ===8<===
> > From a952479805d8bdfe532ad4e0c0092f758991af08 Mon Sep 17 00:00:00 2001
> > From: Peter Xu <peterx@redhat.com>
> > Date: Fri, 6 Jun 2025 10:44:37 -0400
> > Subject: [PATCH] Revert "migration: Add a flag to track block-bitmap-mapping
> >  input"
> >
> > This reverts commit fd755a53c0e4ce9739d20d7cdd69400b2a37102c.
> >
> > Signed-off-by: Peter Xu <peterx@redhat.com>
> > ---
> >  migration/migration.h | 7 -------
> >  migration/options.c   | 4 ++--
> >  2 files changed, 2 insertions(+), 9 deletions(-)
> >
> > diff --git a/migration/migration.h b/migration/migration.h
> > index 49761f4699..e710c421f8 100644
> > --- a/migration/migration.h
> > +++ b/migration/migration.h
> > @@ -510,13 +510,6 @@ struct MigrationState {
> >      bool rdma_migration;
> >  
> >      GSource *hup_source;
> > -
> > -    /*
> > -     * The block-bitmap-mapping option is allowed to be an emtpy list,
> > -     * therefore we need a way to know wheter the user has given
> > -     * anything as input.
> > -     */
> > -    bool has_block_bitmap_mapping;
> >  };
> >  
> >  void migrate_set_state(MigrationStatus *state, MigrationStatus old_state,
> > diff --git a/migration/options.c b/migration/options.c
> > index dd2288187d..e71a57764d 100644
> > --- a/migration/options.c
> > +++ b/migration/options.c
> > @@ -765,7 +765,7 @@ bool migrate_has_block_bitmap_mapping(void)
> >  {
> >      MigrationState *s = migrate_get_current();
> >  
> > -    return s->has_block_bitmap_mapping;
> > +    return s->parameters.has_block_bitmap_mapping;
> >  }
> >  
> >  uint32_t migrate_checkpoint_delay(void)
> > @@ -1376,7 +1376,7 @@ void qmp_migrate_set_parameters(MigrationParameters *params, Error **errp)
> >       * params structure with the user input around.
> >       */
> >      if (params->has_block_bitmap_mapping) {
> > -        migrate_get_current()->has_block_bitmap_mapping = true;
> > +        migrate_get_current()->parameters.has_block_bitmap_mapping = true;
> >      }
> >  
> >      if (migrate_params_check(tmp, errp)) {
> > -- 
> > 2.49.0
> > ===8<===
> >
> > I'm staring at commit 3cba22c9ad now, looks like what it wants to do is
> > making sure construct_alias_map() will be invoked even if the block bitmap
> > mapping is NULL itself.  But then right below the code, it has:
> >
> > static int init_dirty_bitmap_migration(DBMSaveState *s, Error **errp)
> > {
> >     ...
> >     if (migrate_has_block_bitmap_mapping()) {
> >         alias_map = construct_alias_map(migrate_block_bitmap_mapping(), true,
> >                                         &error_abort);
> >     }
> >     ...
> >     if (!alias_map) {
> >     ...
> >     }
> > }
> >
> > Looks like it's also ready for !alias_map anyway.  I'm definitely puzzled
> > by this code.
> >
> > Even if so, IIUC the question can still be asked on whether we can always
> > assume has_block_bitmap_mapping to be always true, then here instead of:
> >
> >     if (migrate_has_block_bitmap_mapping()) {
> >         alias_map = construct_alias_map(migrate_block_bitmap_mapping(), true,
> >                                         &error_abort);
> >     }
> >
> > We do:
> >
> >     alias_map = construct_alias_map(migrate_block_bitmap_mapping(), true,
> >                                     &error_abort);
> >
> > After all it looks like construct_alias_map() takes NULL too..
> 
> The point is that construct_alias_map always returns a hashtable. It
> might be empty if the user passes [], and that's ok according to
> 3cba22c9ad. So they needed some flag to say: "the user has tried to use
> block-bitmap-mapping".
> 
> I don't know why it needs to be like that and I honestly don't want to
> go into details of block migration just to be able to do a
> refactoring. All I want is that this code stop using s->parameters.has_*
> so we can do nice tricks with QAPI_CLONE later on and not bother about
> this.
> 
> I fully support we chase this, but keep in mind this patch (mine) is
> just gingerly moving the problem to the side so we can make progress
> with this series.

Yep that makes sense.

I'm thinking whether we have other better ways to move on without digging
another hole for ourselves, e.g. make migrate_has_block_bitmap_mapping() to
constantly return true?  We can cc the block people on that patch, assuming
we'd always better copy them when touching this part, including the current
patch.

AFAIU, as long as it takes NULL for the real parameter it'll just work.

Then if all tests can pass and no one is unhappy, we go with that.  We can
always add this var back when someone reports a break, then we at least
know this is needed and why.

That's what I'll do, but feel free to choose yours.  In all cases, I'd
still suggest we copy block developers on similar changes.

-- 
Peter Xu



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

* Re: [PATCH 09/21] migration: Extract code to mark all parameters as present
  2025-06-06 15:51     ` Fabiano Rosas
@ 2025-06-06 17:48       ` Peter Xu
  0 siblings, 0 replies; 83+ messages in thread
From: Peter Xu @ 2025-06-06 17:48 UTC (permalink / raw)
  To: Fabiano Rosas; +Cc: qemu-devel, Markus Armbruster, Daniel P . Berrangé

On Fri, Jun 06, 2025 at 12:51:58PM -0300, Fabiano Rosas wrote:
> Peter Xu <peterx@redhat.com> writes:
> 
> > On Mon, Jun 02, 2025 at 10:37:58PM -0300, Fabiano Rosas wrote:
> >> MigrationParameters needs to have all of its has_* fields marked as
> >> true when used as the return of query_migrate_parameters because the
> >> corresponding QMP command has all of its members non-optional by
> >> design, despite them being marked as optional in migration.json.
> >> 
> >> Extract this code into a function and make it assert if any field is
> >> missing. With this we ensure future changes will not inadvertently
> >> leave any parameters missing.
> >> 
> >> Also assert that s->parameters _does not_ have any of its has_* fields
> >> set. This structure is internal to the migration code and it should
> >> not rely on the QAPI-generate has_* fields. We might want to store
> >> migration parameters differently in the future.
> >> 
> >> Signed-off-by: Fabiano Rosas <farosas@suse.de>
> >> ---
> >>  migration/options.c | 74 ++++++++++++++++++++++++++++-----------------
> >>  1 file changed, 46 insertions(+), 28 deletions(-)
> >> 
> >> diff --git a/migration/options.c b/migration/options.c
> >> index e2e3ab717f..dd62e726cb 100644
> >> --- a/migration/options.c
> >> +++ b/migration/options.c
> >> @@ -936,6 +936,40 @@ static void tls_option_set_str(StrOrNull **dstp, StrOrNull *src)
> >>      }
> >>  }
> >>  
> >> +static void migrate_mark_all_params_present(MigrationParameters *p)
> >> +{
> >> +    int len, n_str_args = 3; /* tls-creds, tls-hostname, tls-authz */
> >
> > Could you remind me why we don't set has_*=true for these three?
> >
> 
> I doesn't exist. These are StrOrNull so their presence is supposed to be
> determined by checking against NULL pointer.
> 
> >> +    bool *has_fields[] = {
> >> +        &p->has_throttle_trigger_threshold, &p->has_cpu_throttle_initial,
> >> +        &p->has_cpu_throttle_increment, &p->has_cpu_throttle_tailslow,
> >> +        &p->has_max_bandwidth, &p->has_avail_switchover_bandwidth,
> >> +        &p->has_downtime_limit, &p->has_x_checkpoint_delay,
> >> +        &p->has_multifd_channels, &p->has_multifd_compression,
> >> +        &p->has_multifd_zlib_level, &p->has_multifd_qatzip_level,
> >> +        &p->has_multifd_zstd_level, &p->has_xbzrle_cache_size,
> >> +        &p->has_max_postcopy_bandwidth, &p->has_max_cpu_throttle,
> >> +        &p->has_announce_initial, &p->has_announce_max, &p->has_announce_rounds,
> >> +        &p->has_announce_step, &p->has_block_bitmap_mapping,
> >> +        &p->has_x_vcpu_dirty_limit_period, &p->has_vcpu_dirty_limit,
> >> +        &p->has_mode, &p->has_zero_page_detection, &p->has_direct_io,
> >> +    };
> >> +
> >> +    /*
> >> +     * The has_* fields of MigrationParameters are used by QAPI to
> >> +     * inform whether an optional struct member is present. Keep this
> >> +     * decoupled from the internal usage (not QAPI) by leaving the
> >> +     * has_* fields of s->parameters unused.
> >> +     */
> >> +    assert(p != &(migrate_get_current())->parameters);
> >
> > This is OK, I'm not sure whether we're over-cautious though.. but..
> >
> 
> Hopefully after this series the code will be encapsulated enough that we
> don't need to think about this, but before this series the situation is
> definitely confusing enough that we need to know which fields are used
> for what.
> 
> I don't want to see people passing s->parameters into here thinking it's
> all the same, because it isn't. The has_* fields should be used only for
> QAPI stuff, user input validation, etc, while s->parameters is the thing
> that stores all that after validation and there's not reason to be
> messing with has_* since we know that's just an consequence of the fact
> that we're choosing to use the same QAPI type for user input/output and
> internal storage.
> 
> I guess what I'm trying to do is take the pain points where I got
> confused while working on the current code and introduce some hard rules
> to it.

Yes, this makes sense.

-- 
Peter Xu



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

* Re: [PATCH 05/21] migration: Add a flag to track block-bitmap-mapping input
  2025-06-06 17:44       ` Peter Xu
@ 2025-06-06 18:38         ` Fabiano Rosas
  0 siblings, 0 replies; 83+ messages in thread
From: Fabiano Rosas @ 2025-06-06 18:38 UTC (permalink / raw)
  To: Peter Xu; +Cc: qemu-devel, Markus Armbruster, Daniel P . Berrangé

Peter Xu <peterx@redhat.com> writes:

> On Fri, Jun 06, 2025 at 12:43:04PM -0300, Fabiano Rosas wrote:
>> Peter Xu <peterx@redhat.com> writes:
>> 
>> > On Mon, Jun 02, 2025 at 10:37:54PM -0300, Fabiano Rosas wrote:
>> >> The QAPI converts an empty list on the block-bitmap-mapping input into
>> >> a NULL BitmapMigrationNodeAliasList. The empty list is a valid input
>> >> for the block-bitmap-mapping option, so commit 3cba22c9ad ("migration:
>> >> Fix block_bitmap_mapping migration") started using the
>> >> s->parameters.has_block_bitmap_mapping field to tell when the user has
>> >> passed in an empty list vs. when no list has been passed at all.
>> >> 
>> >> However, using the has_block_bitmap_mapping field of s->parameters is
>> >> only possible because MigrationParameters has had its members made
>> >> optional due to historical reasons.
>> >> 
>> >> In order to make improvements to the way configuration options are set
>> >> for a migration, we'd like to reduce the usage of the has_* fields of
>> >> the global configuration object (s->parameters).
>> >> 
>> >> Add a separate boolean to track the status of the block_bitmap_mapping
>> >> option.
>> >> 
>> >> (this was verified to not regress iotest 300, which is the test that
>> >> 3cba22c9ad refers to)
>> >> 
>> >> Signed-off-by: Fabiano Rosas <farosas@suse.de>
>> >> ---
>> >>  migration/migration.h | 7 +++++++
>> >>  migration/options.c   | 6 +++---
>> >>  2 files changed, 10 insertions(+), 3 deletions(-)
>> >> 
>> >> diff --git a/migration/migration.h b/migration/migration.h
>> >> index d53f7cad84..ab797540b0 100644
>> >> --- a/migration/migration.h
>> >> +++ b/migration/migration.h
>> >> @@ -510,6 +510,13 @@ struct MigrationState {
>> >>      bool rdma_migration;
>> >>  
>> >>      GSource *hup_source;
>> >> +
>> >> +    /*
>> >> +     * The block-bitmap-mapping option is allowed to be an emtpy list,
>> >> +     * therefore we need a way to know wheter the user has given
>> >> +     * anything as input.
>> >> +     */
>> >> +    bool has_block_bitmap_mapping;
>> >>  };
>> >>  
>> >>  void migrate_set_state(MigrationStatus *state, MigrationStatus old_state,
>> >> diff --git a/migration/options.c b/migration/options.c
>> >> index f64e141394..cf77826204 100644
>> >> --- a/migration/options.c
>> >> +++ b/migration/options.c
>> >> @@ -685,7 +685,7 @@ bool migrate_has_block_bitmap_mapping(void)
>> >>  {
>> >>      MigrationState *s = migrate_get_current();
>> >>  
>> >> -    return s->parameters.has_block_bitmap_mapping;
>> >> +    return s->has_block_bitmap_mapping;
>> >>  }
>> >>  
>> >>  uint32_t migrate_checkpoint_delay(void)
>> >> @@ -989,7 +989,7 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp)
>> >>      params->has_announce_step = true;
>> >>      params->announce_step = s->parameters.announce_step;
>> >>  
>> >> -    if (s->parameters.has_block_bitmap_mapping) {
>> >> +    if (s->has_block_bitmap_mapping) {
>> >>          params->has_block_bitmap_mapping = true;
>> >>          params->block_bitmap_mapping =
>> >>              QAPI_CLONE(BitmapMigrationNodeAliasList,
>> >> @@ -1469,7 +1469,7 @@ static void migrate_params_apply(MigrationParameters *params)
>> >>          qapi_free_BitmapMigrationNodeAliasList(
>> >>              s->parameters.block_bitmap_mapping);
>> >>  
>> >> -        s->parameters.has_block_bitmap_mapping = true;
>> >> +        s->has_block_bitmap_mapping = true;
>> >>          s->parameters.block_bitmap_mapping =
>> >>              QAPI_CLONE(BitmapMigrationNodeAliasList,
>> >>                         params->block_bitmap_mapping);
>> >> -- 
>> >> 2.35.3
>> >> 
>> >
>> > This is definitely unfortunate, and I'm still scratching my head on
>> > understanding why it's necessary.
>> >
>> > E.g. I tried to revert this patch manually and iotest 300 passed, with:
>> 
>> This (mine) patch is not needed per-se. I want it so we stop using
>> s->parameters.has_* altogether. If we think we need a flag to track
>> whether the user has passed some value or not, then we add one to some
>> migration specific state, say MigrationState.
>> 
>> This decouples the migration internal usage from the QAPI. Today we use
>> MigrationParameters as defined by the QAPI, we might in the future want
>> something else. And that something else might not come with has_*
>> fields. So it's simple enough now to add this one flag to the
>> MigrationState and be able to me completely independent from the
>> QAPI-generated has_ fields.
>> 
>> >
>> > ===8<===
>> > From a952479805d8bdfe532ad4e0c0092f758991af08 Mon Sep 17 00:00:00 2001
>> > From: Peter Xu <peterx@redhat.com>
>> > Date: Fri, 6 Jun 2025 10:44:37 -0400
>> > Subject: [PATCH] Revert "migration: Add a flag to track block-bitmap-mapping
>> >  input"
>> >
>> > This reverts commit fd755a53c0e4ce9739d20d7cdd69400b2a37102c.
>> >
>> > Signed-off-by: Peter Xu <peterx@redhat.com>
>> > ---
>> >  migration/migration.h | 7 -------
>> >  migration/options.c   | 4 ++--
>> >  2 files changed, 2 insertions(+), 9 deletions(-)
>> >
>> > diff --git a/migration/migration.h b/migration/migration.h
>> > index 49761f4699..e710c421f8 100644
>> > --- a/migration/migration.h
>> > +++ b/migration/migration.h
>> > @@ -510,13 +510,6 @@ struct MigrationState {
>> >      bool rdma_migration;
>> >  
>> >      GSource *hup_source;
>> > -
>> > -    /*
>> > -     * The block-bitmap-mapping option is allowed to be an emtpy list,
>> > -     * therefore we need a way to know wheter the user has given
>> > -     * anything as input.
>> > -     */
>> > -    bool has_block_bitmap_mapping;
>> >  };
>> >  
>> >  void migrate_set_state(MigrationStatus *state, MigrationStatus old_state,
>> > diff --git a/migration/options.c b/migration/options.c
>> > index dd2288187d..e71a57764d 100644
>> > --- a/migration/options.c
>> > +++ b/migration/options.c
>> > @@ -765,7 +765,7 @@ bool migrate_has_block_bitmap_mapping(void)
>> >  {
>> >      MigrationState *s = migrate_get_current();
>> >  
>> > -    return s->has_block_bitmap_mapping;
>> > +    return s->parameters.has_block_bitmap_mapping;
>> >  }
>> >  
>> >  uint32_t migrate_checkpoint_delay(void)
>> > @@ -1376,7 +1376,7 @@ void qmp_migrate_set_parameters(MigrationParameters *params, Error **errp)
>> >       * params structure with the user input around.
>> >       */
>> >      if (params->has_block_bitmap_mapping) {
>> > -        migrate_get_current()->has_block_bitmap_mapping = true;
>> > +        migrate_get_current()->parameters.has_block_bitmap_mapping = true;
>> >      }
>> >  
>> >      if (migrate_params_check(tmp, errp)) {
>> > -- 
>> > 2.49.0
>> > ===8<===
>> >
>> > I'm staring at commit 3cba22c9ad now, looks like what it wants to do is
>> > making sure construct_alias_map() will be invoked even if the block bitmap
>> > mapping is NULL itself.  But then right below the code, it has:
>> >
>> > static int init_dirty_bitmap_migration(DBMSaveState *s, Error **errp)
>> > {
>> >     ...
>> >     if (migrate_has_block_bitmap_mapping()) {
>> >         alias_map = construct_alias_map(migrate_block_bitmap_mapping(), true,
>> >                                         &error_abort);
>> >     }
>> >     ...
>> >     if (!alias_map) {
>> >     ...
>> >     }
>> > }
>> >
>> > Looks like it's also ready for !alias_map anyway.  I'm definitely puzzled
>> > by this code.
>> >
>> > Even if so, IIUC the question can still be asked on whether we can always
>> > assume has_block_bitmap_mapping to be always true, then here instead of:
>> >
>> >     if (migrate_has_block_bitmap_mapping()) {
>> >         alias_map = construct_alias_map(migrate_block_bitmap_mapping(), true,
>> >                                         &error_abort);
>> >     }
>> >
>> > We do:
>> >
>> >     alias_map = construct_alias_map(migrate_block_bitmap_mapping(), true,
>> >                                     &error_abort);
>> >
>> > After all it looks like construct_alias_map() takes NULL too..
>> 
>> The point is that construct_alias_map always returns a hashtable. It
>> might be empty if the user passes [], and that's ok according to
>> 3cba22c9ad. So they needed some flag to say: "the user has tried to use
>> block-bitmap-mapping".
>> 
>> I don't know why it needs to be like that and I honestly don't want to
>> go into details of block migration just to be able to do a
>> refactoring. All I want is that this code stop using s->parameters.has_*
>> so we can do nice tricks with QAPI_CLONE later on and not bother about
>> this.
>> 
>> I fully support we chase this, but keep in mind this patch (mine) is
>> just gingerly moving the problem to the side so we can make progress
>> with this series.
>
> Yep that makes sense.
>
> I'm thinking whether we have other better ways to move on without digging
> another hole for ourselves, e.g. make migrate_has_block_bitmap_mapping() to
> constantly return true?

Your concept of what it takes to dig a hole is quite different from
mine.

> We can cc the block people on that patch, assuming
> we'd always better copy them when touching this part, including the current
> patch.

I think I messed up the get_maintainers usage.

>
> AFAIU, as long as it takes NULL for the real parameter it'll just work.
>

But that's what 3cba22c9ad was fixing. I belive the !alias_map is the
key, it'll be NULL if has_block_bitmap is false, no matter the actual
value of the parameters.

> Then if all tests can pass and no one is unhappy, we go with that.  We can
> always add this var back when someone reports a break, then we at least
> know this is needed and why.
>

Ok, this part is a sticking point of the series indeed. I'll try to
clear this up. Let's not make this another "TLS options" situation.

> That's what I'll do, but feel free to choose yours.  In all cases, I'd
> still suggest we copy block developers on similar changes.


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

* Re: [PATCH 14/21] migration: Cleanup hmp_info_migrate_parameters
  2025-06-03  1:38 ` [PATCH 14/21] migration: Cleanup hmp_info_migrate_parameters Fabiano Rosas
@ 2025-06-06 18:52   ` Peter Xu
  0 siblings, 0 replies; 83+ messages in thread
From: Peter Xu @ 2025-06-06 18:52 UTC (permalink / raw)
  To: Fabiano Rosas; +Cc: qemu-devel, Markus Armbruster, Daniel P . Berrangé

On Mon, Jun 02, 2025 at 10:38:03PM -0300, Fabiano Rosas wrote:
> Do a cleanup of hmp_info_migrate_parameters() before adding more lines
> into it:
> 
> - Make sure every parameter asserts that the has_* field is
>   set. qmp_query_migrate_parameters should have set them all.
> 
> - Remove the if (params), qmp_query_migrate_parameters never returns
>   NULL.
> 
> - Add a macro to encapsulate boilerplate.
> 
> - Line breaks for legibility.
> 
> Signed-off-by: Fabiano Rosas <farosas@suse.de>

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

-- 
Peter Xu



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

* Re: [PATCH 15/21] migration: Add capabilities into MigrationParameters
  2025-06-03  1:38 ` [PATCH 15/21] migration: Add capabilities into MigrationParameters Fabiano Rosas
@ 2025-06-06 19:01   ` Peter Xu
  0 siblings, 0 replies; 83+ messages in thread
From: Peter Xu @ 2025-06-06 19:01 UTC (permalink / raw)
  To: Fabiano Rosas; +Cc: qemu-devel, Markus Armbruster, Daniel P . Berrangé

On Mon, Jun 02, 2025 at 10:38:04PM -0300, Fabiano Rosas wrote:
> Add capabilities to MigrationParameters. This structure will hold all
> migration options. Capabilities will go away in the next patch.
> 
> Also add capabilities to MigrationParameter as the enum needs to be
> kept in sync with MigrationParameters. This affects the parsing of
> migration HMP commands so make the necessary additions there too.
> 
> 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.
> 
> Signed-off-by: Fabiano Rosas <farosas@suse.de>

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

-- 
Peter Xu



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

* Re: [PATCH 17/21] migration: Remove s->capabilities
  2025-06-03  1:38 ` [PATCH 17/21] migration: Remove s->capabilities Fabiano Rosas
@ 2025-06-06 19:16   ` Peter Xu
  0 siblings, 0 replies; 83+ messages in thread
From: Peter Xu @ 2025-06-06 19:16 UTC (permalink / raw)
  To: Fabiano Rosas; +Cc: qemu-devel, Markus Armbruster, Daniel P . Berrangé

On Mon, Jun 02, 2025 at 10:38:06PM -0300, Fabiano Rosas wrote:
> 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.
> 
> Signed-off-by: Fabiano Rosas <farosas@suse.de>

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

-- 
Peter Xu



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

* Re: [PATCH 18/21] qapi/migration: Deprecate capabilities commands
  2025-06-03  1:38 ` [PATCH 18/21] qapi/migration: Deprecate capabilities commands Fabiano Rosas
@ 2025-06-06 19:16   ` Peter Xu
  0 siblings, 0 replies; 83+ messages in thread
From: Peter Xu @ 2025-06-06 19:16 UTC (permalink / raw)
  To: Fabiano Rosas; +Cc: qemu-devel, Markus Armbruster, Daniel P . Berrangé

On Mon, Jun 02, 2025 at 10:38:07PM -0300, Fabiano Rosas wrote:
> 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
> 
> Signed-off-by: Fabiano Rosas <farosas@suse.de>

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

-- 
Peter Xu



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

* Re: [PATCH 19/21] migration: Allow migrate commands to provide the migration config
  2025-06-03  1:38 ` [PATCH 19/21] migration: Allow migrate commands to provide the migration config Fabiano Rosas
  2025-06-03  9:03   ` Daniel P. Berrangé
@ 2025-06-06 19:28   ` Peter Xu
  2025-06-06 20:23     ` Fabiano Rosas
  1 sibling, 1 reply; 83+ messages in thread
From: Peter Xu @ 2025-06-06 19:28 UTC (permalink / raw)
  To: Fabiano Rosas; +Cc: qemu-devel, Markus Armbruster, Daniel P . Berrangé

On Mon, Jun 02, 2025 at 10:38:08PM -0300, Fabiano Rosas wrote:
> Allow the migrate and migrate_incoming commands to pass the migration
> configuration options all at once, dispensing the use of
> migrate-set-parameters and migrate-set-capabilities.
> 
> The motivation of this is to simplify the interface with the
> management layer and avoid the usage of several command invocations to
> configure a migration. It also avoids stale parameters from a previous
> migration to influence the current migration.
> 
> The options that are changed during the migration can still be set
> with the existing commands.
> 
> The order of precedence is:
> 
> 'config' argument > -global cmdline > defaults (migration_properties)

Could we still keep the QMP migrate-set-parameters values?

  'config' argument > QMP setups using migrate-set-parameters >
    -global cmdline > defaults (migration_properties)

I asked this before, maybe I forgot the answer..

> 
> I.e. the config takes precedence over all, values not present in the
> config assume the default values. The (debug) -global command line
> option allows the defaults to be overridden.
> 
> Signed-off-by: Fabiano Rosas <farosas@suse.de>
> ---
>  migration/migration-hmp-cmds.c |  5 +++--
>  migration/migration.c          | 29 ++++++++++++++++++++++++++---
>  migration/migration.h          |  1 +
>  migration/options.c            | 30 ++++++++++++++++++++++++++++++
>  migration/options.h            |  3 +++
>  qapi/migration.json            | 25 +++++++++++++++++++++++--
>  system/vl.c                    |  3 ++-
>  7 files changed, 88 insertions(+), 8 deletions(-)
> 
> diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c
> index a8c3515e9d..38b289e8d8 100644
> --- a/migration/migration-hmp-cmds.c
> +++ b/migration/migration-hmp-cmds.c
> @@ -575,7 +575,7 @@ void hmp_migrate_incoming(Monitor *mon, const QDict *qdict)
>      }
>      QAPI_LIST_PREPEND(caps, g_steal_pointer(&channel));
>  
> -    qmp_migrate_incoming(NULL, true, caps, true, false, &err);
> +    qmp_migrate_incoming(NULL, true, caps, NULL, true, false, &err);
>      qapi_free_MigrationChannelList(caps);
>  
>  end:
> @@ -952,7 +952,8 @@ void hmp_migrate(Monitor *mon, const QDict *qdict)
>      }
>      QAPI_LIST_PREPEND(caps, g_steal_pointer(&channel));
>  
> -    qmp_migrate(NULL, true, caps, false, false, true, resume, &err);
> +    qmp_migrate(NULL, true, caps, NULL, false, false, true, resume,
> +                &err);
>      if (hmp_handle_error(mon, err)) {
>          return;
>      }
> diff --git a/migration/migration.c b/migration/migration.c
> index 75c4ec9a95..7b450b8836 100644
> --- a/migration/migration.c
> +++ b/migration/migration.c
> @@ -335,6 +335,7 @@ void migration_object_init(void)
>      current_incoming->exit_on_error = INMIGRATE_DEFAULT_EXIT_ON_ERROR;
>  
>      migration_object_check(current_migration, &error_fatal);
> +    migrate_params_store_defaults(current_migration);
>  
>      ram_mig_init();
>      dirty_bitmap_mig_init();
> @@ -1916,13 +1917,24 @@ void migrate_del_blocker(Error **reasonp)
>  
>  void qmp_migrate_incoming(const char *uri, bool has_channels,
>                            MigrationChannelList *channels,
> -                          bool has_exit_on_error, bool exit_on_error,
> -                          Error **errp)
> +                          MigrationParameters *config, bool has_exit_on_error,
> +                          bool exit_on_error, Error **errp)
>  {
>      Error *local_err = NULL;
>      static bool once = true;
> +    MigrationState *s = migrate_get_current();
>      MigrationIncomingState *mis = migration_incoming_get_current();
>  
> +    if (config) {
> +        /*
> +         * If a config was provided, all options set previously get
> +         * ignored.
> +         */
> +        if (!migrate_params_override(s, config, errp)) {
> +            return;
> +        }
> +    }
> +
>      if (!once) {
>          error_setg(errp, "The incoming migration has already been started");
>          return;
> @@ -2182,7 +2194,8 @@ static gboolean qmp_migrate_finish_cb(QIOChannel *channel,
>  }
>  
>  void qmp_migrate(const char *uri, bool has_channels,
> -                 MigrationChannelList *channels, bool has_detach, bool detach,
> +                 MigrationChannelList *channels,
> +                 bool has_detach, bool detach, MigrationParameters *config,
>                   bool has_resume, bool resume, Error **errp)
>  {
>      bool resume_requested;
> @@ -2193,6 +2206,16 @@ void qmp_migrate(const char *uri, bool has_channels,
>      MigrationChannel *channelv[MIGRATION_CHANNEL_TYPE__MAX] = { NULL };
>      MigrationChannel *cpr_channel = NULL;
>  
> +    if (config) {
> +        /*
> +         * If a config was provided, all options set previously get
> +         * ignored.
> +         */
> +        if (!migrate_params_override(s, config, errp)) {
> +            return;
> +        }
> +    }
> +
>      /*
>       * Having preliminary checks for uri and channel
>       */
> diff --git a/migration/migration.h b/migration/migration.h
> index 993d51aedd..49761f4699 100644
> --- a/migration/migration.h
> +++ b/migration/migration.h
> @@ -319,6 +319,7 @@ struct MigrationState {
>  
>      /* params from 'migrate-set-parameters' */
>      MigrationParameters parameters;
> +    MigrationParameters defaults;

This is also prone to be a pointer; I still think embeded qapi objects are
too error prone.  Since it's new, make it a pointer from start?

>  
>      MigrationStatus state;
>  
> diff --git a/migration/options.c b/migration/options.c
> index fa3f7035c8..dd2288187d 100644
> --- a/migration/options.c
> +++ b/migration/options.c
> @@ -1333,6 +1333,36 @@ static void migrate_params_apply(MigrationParameters *params)
>                                             params->block_bitmap_mapping);
>  }
>  
> +void migrate_params_store_defaults(MigrationState *s)
> +{
> +    /*
> +     * The defaults set for each qdev property in migration_properties
> +     * will be stored as the default values for each migration
> +     * parameter. For debugging, using -global can override the
> +     * defaults.
> +     */
> +    QAPI_CLONE_MEMBERS(MigrationParameters, &s->defaults, &s->parameters);
> +}
> +
> +bool migrate_params_override(MigrationState *s, MigrationParameters *new,
> +                             Error **errp)
> +{
> +    ERRP_GUARD();
> +
> +    assert(bql_locked());
> +
> +    /* reset to default parameters */
> +    migrate_params_apply(&s->defaults);
> +
> +    /* overwrite with the new ones */
> +    qmp_migrate_set_parameters(new, errp);
> +    if (*errp) {
> +        return false;
> +    }
> +
> +    return true;
> +}
> +
>  void qmp_migrate_set_parameters(MigrationParameters *params, Error **errp)
>  {
>      MigrationParameters *tmp = g_new0(MigrationParameters, 1);
> diff --git a/migration/options.h b/migration/options.h
> index fcfd120cd7..3630c2a0dd 100644
> --- a/migration/options.h
> +++ b/migration/options.h
> @@ -83,4 +83,7 @@ void migrate_capability_set_compat(MigrationParameters *params, int i,
>  void migrate_capabilities_set_compat(MigrationParameters *params,
>                                       MigrationCapabilityStatusList *caps);
>  bool migrate_caps_check(MigrationParameters *new, Error **errp);
> +void migrate_params_store_defaults(MigrationState *s);
> +bool migrate_params_override(MigrationState *s, MigrationParameters *new,
> +                             Error **errp);
>  #endif
> diff --git a/qapi/migration.json b/qapi/migration.json
> index 7282e4b9eb..64a92d8d28 100644
> --- a/qapi/migration.json
> +++ b/qapi/migration.json
> @@ -1474,9 +1474,16 @@
>  #
>  # @resume: resume one paused migration, default "off".  (since 3.0)
>  #
> +# @config: migration configuration options, previously set via
> +#     @migrate-set-parameters and @migrate-set-capabilities.  (since
> +#     10.1)
> +#
>  # Features:
>  #
>  # @deprecated: Argument @detach is deprecated.
> +# @config: Indicates this command can receive the entire migration
> +# configuration via the @config field, dispensing the use of
> +# @migrate-set-parameters.
>  #
>  # Since: 0.14
>  #
> @@ -1538,7 +1545,9 @@
>    'data': {'*uri': 'str',
>             '*channels': [ 'MigrationChannel' ],
>             '*detach': { 'type': 'bool', 'features': [ 'deprecated' ] },
> -           '*resume': 'bool' } }
> +           '*config': 'MigrationParameters',
> +           '*resume': 'bool' },
> +  'features': [ 'config' ] }
>  
>  ##
>  # @migrate-incoming:
> @@ -1557,6 +1566,16 @@
>  #     error details could be retrieved with query-migrate.
>  #     (since 9.1)
>  #
> +# @config: migration configuration options, previously set via
> +#     @migrate-set-parameters and @migrate-set-capabilities.  (since
> +#     10.1)
> +#
> +# Features:
> +#
> +# @config: Indicates this command can receive the entire migration
> +# configuration via the @config field, dispensing the use of
> +# @migrate-set-parameters.
> +#
>  # Since: 2.3
>  #
>  # .. admonition:: Notes
> @@ -1610,7 +1629,9 @@
>  { 'command': 'migrate-incoming',
>               'data': {'*uri': 'str',
>                        '*channels': [ 'MigrationChannel' ],
> -                      '*exit-on-error': 'bool' } }
> +                      '*config': 'MigrationParameters',
> +                      '*exit-on-error': 'bool' },
> +             'features': [ 'config' ] }
>  
>  ##
>  # @xen-save-devices-state:
> diff --git a/system/vl.c b/system/vl.c
> index 3b7057e6c6..b29fd24d08 100644
> --- a/system/vl.c
> +++ b/system/vl.c
> @@ -2823,7 +2823,8 @@ void qmp_x_exit_preconfig(Error **errp)
>                  g_new0(MigrationChannelList, 1);
>  
>              channels->value = incoming_channels[MIGRATION_CHANNEL_TYPE_MAIN];
> -            qmp_migrate_incoming(NULL, true, channels, true, true, &local_err);
> +            qmp_migrate_incoming(NULL, true, channels, NULL, true, true,
> +                                 &local_err);
>              if (local_err) {
>                  error_reportf_err(local_err, "-incoming %s: ", incoming);
>                  exit(1);
> -- 
> 2.35.3
> 

-- 
Peter Xu



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

* Re: [PATCH 19/21] migration: Allow migrate commands to provide the migration config
  2025-06-06 19:28   ` Peter Xu
@ 2025-06-06 20:23     ` Fabiano Rosas
  2025-06-06 20:50       ` Peter Xu
  0 siblings, 1 reply; 83+ messages in thread
From: Fabiano Rosas @ 2025-06-06 20:23 UTC (permalink / raw)
  To: Peter Xu; +Cc: qemu-devel, Markus Armbruster, Daniel P . Berrangé

Peter Xu <peterx@redhat.com> writes:

> On Mon, Jun 02, 2025 at 10:38:08PM -0300, Fabiano Rosas wrote:
>> Allow the migrate and migrate_incoming commands to pass the migration
>> configuration options all at once, dispensing the use of
>> migrate-set-parameters and migrate-set-capabilities.
>> 
>> The motivation of this is to simplify the interface with the
>> management layer and avoid the usage of several command invocations to
>> configure a migration. It also avoids stale parameters from a previous
>> migration to influence the current migration.
>> 
>> The options that are changed during the migration can still be set
>> with the existing commands.
>> 
>> The order of precedence is:
>> 
>> 'config' argument > -global cmdline > defaults (migration_properties)
>
> Could we still keep the QMP migrate-set-parameters values?
>
>   'config' argument > QMP setups using migrate-set-parameters >
>     -global cmdline > defaults (migration_properties)
>

That's the case. I failed to mention it in the commit message. IOW it
behaves just like today, but the new 'config' way takes precedence over
all.

> I asked this before, maybe I forgot the answer..
>
>> 
>> I.e. the config takes precedence over all, values not present in the
>> config assume the default values. The (debug) -global command line
>> option allows the defaults to be overridden.
>> 
>> Signed-off-by: Fabiano Rosas <farosas@suse.de>
>> ---
>>  migration/migration-hmp-cmds.c |  5 +++--
>>  migration/migration.c          | 29 ++++++++++++++++++++++++++---
>>  migration/migration.h          |  1 +
>>  migration/options.c            | 30 ++++++++++++++++++++++++++++++
>>  migration/options.h            |  3 +++
>>  qapi/migration.json            | 25 +++++++++++++++++++++++--
>>  system/vl.c                    |  3 ++-
>>  7 files changed, 88 insertions(+), 8 deletions(-)
>> 
>> diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c
>> index a8c3515e9d..38b289e8d8 100644
>> --- a/migration/migration-hmp-cmds.c
>> +++ b/migration/migration-hmp-cmds.c
>> @@ -575,7 +575,7 @@ void hmp_migrate_incoming(Monitor *mon, const QDict *qdict)
>>      }
>>      QAPI_LIST_PREPEND(caps, g_steal_pointer(&channel));
>>  
>> -    qmp_migrate_incoming(NULL, true, caps, true, false, &err);
>> +    qmp_migrate_incoming(NULL, true, caps, NULL, true, false, &err);
>>      qapi_free_MigrationChannelList(caps);
>>  
>>  end:
>> @@ -952,7 +952,8 @@ void hmp_migrate(Monitor *mon, const QDict *qdict)
>>      }
>>      QAPI_LIST_PREPEND(caps, g_steal_pointer(&channel));
>>  
>> -    qmp_migrate(NULL, true, caps, false, false, true, resume, &err);
>> +    qmp_migrate(NULL, true, caps, NULL, false, false, true, resume,
>> +                &err);
>>      if (hmp_handle_error(mon, err)) {
>>          return;
>>      }
>> diff --git a/migration/migration.c b/migration/migration.c
>> index 75c4ec9a95..7b450b8836 100644
>> --- a/migration/migration.c
>> +++ b/migration/migration.c
>> @@ -335,6 +335,7 @@ void migration_object_init(void)
>>      current_incoming->exit_on_error = INMIGRATE_DEFAULT_EXIT_ON_ERROR;
>>  
>>      migration_object_check(current_migration, &error_fatal);
>> +    migrate_params_store_defaults(current_migration);
>>  
>>      ram_mig_init();
>>      dirty_bitmap_mig_init();
>> @@ -1916,13 +1917,24 @@ void migrate_del_blocker(Error **reasonp)
>>  
>>  void qmp_migrate_incoming(const char *uri, bool has_channels,
>>                            MigrationChannelList *channels,
>> -                          bool has_exit_on_error, bool exit_on_error,
>> -                          Error **errp)
>> +                          MigrationParameters *config, bool has_exit_on_error,
>> +                          bool exit_on_error, Error **errp)
>>  {
>>      Error *local_err = NULL;
>>      static bool once = true;
>> +    MigrationState *s = migrate_get_current();
>>      MigrationIncomingState *mis = migration_incoming_get_current();
>>  
>> +    if (config) {
>> +        /*
>> +         * If a config was provided, all options set previously get
>> +         * ignored.
>> +         */
>> +        if (!migrate_params_override(s, config, errp)) {
>> +            return;
>> +        }
>> +    }
>> +
>>      if (!once) {
>>          error_setg(errp, "The incoming migration has already been started");
>>          return;
>> @@ -2182,7 +2194,8 @@ static gboolean qmp_migrate_finish_cb(QIOChannel *channel,
>>  }
>>  
>>  void qmp_migrate(const char *uri, bool has_channels,
>> -                 MigrationChannelList *channels, bool has_detach, bool detach,
>> +                 MigrationChannelList *channels,
>> +                 bool has_detach, bool detach, MigrationParameters *config,
>>                   bool has_resume, bool resume, Error **errp)
>>  {
>>      bool resume_requested;
>> @@ -2193,6 +2206,16 @@ void qmp_migrate(const char *uri, bool has_channels,
>>      MigrationChannel *channelv[MIGRATION_CHANNEL_TYPE__MAX] = { NULL };
>>      MigrationChannel *cpr_channel = NULL;
>>  
>> +    if (config) {
>> +        /*
>> +         * If a config was provided, all options set previously get
>> +         * ignored.
>> +         */
>> +        if (!migrate_params_override(s, config, errp)) {
>> +            return;
>> +        }
>> +    }
>> +
>>      /*
>>       * Having preliminary checks for uri and channel
>>       */
>> diff --git a/migration/migration.h b/migration/migration.h
>> index 993d51aedd..49761f4699 100644
>> --- a/migration/migration.h
>> +++ b/migration/migration.h
>> @@ -319,6 +319,7 @@ struct MigrationState {
>>  
>>      /* params from 'migrate-set-parameters' */
>>      MigrationParameters parameters;
>> +    MigrationParameters defaults;
>
> This is also prone to be a pointer; I still think embeded qapi objects are
> too error prone.  Since it's new, make it a pointer from start?
>

Ok.

>>  
>>      MigrationStatus state;
>>  
>> diff --git a/migration/options.c b/migration/options.c
>> index fa3f7035c8..dd2288187d 100644
>> --- a/migration/options.c
>> +++ b/migration/options.c
>> @@ -1333,6 +1333,36 @@ static void migrate_params_apply(MigrationParameters *params)
>>                                             params->block_bitmap_mapping);
>>  }
>>  
>> +void migrate_params_store_defaults(MigrationState *s)
>> +{
>> +    /*
>> +     * The defaults set for each qdev property in migration_properties
>> +     * will be stored as the default values for each migration
>> +     * parameter. For debugging, using -global can override the
>> +     * defaults.
>> +     */
>> +    QAPI_CLONE_MEMBERS(MigrationParameters, &s->defaults, &s->parameters);
>> +}
>> +
>> +bool migrate_params_override(MigrationState *s, MigrationParameters *new,
>> +                             Error **errp)
>> +{
>> +    ERRP_GUARD();
>> +
>> +    assert(bql_locked());
>> +
>> +    /* reset to default parameters */
>> +    migrate_params_apply(&s->defaults);
>> +
>> +    /* overwrite with the new ones */
>> +    qmp_migrate_set_parameters(new, errp);
>> +    if (*errp) {
>> +        return false;
>> +    }
>> +
>> +    return true;
>> +}
>> +
>>  void qmp_migrate_set_parameters(MigrationParameters *params, Error **errp)
>>  {
>>      MigrationParameters *tmp = g_new0(MigrationParameters, 1);
>> diff --git a/migration/options.h b/migration/options.h
>> index fcfd120cd7..3630c2a0dd 100644
>> --- a/migration/options.h
>> +++ b/migration/options.h
>> @@ -83,4 +83,7 @@ void migrate_capability_set_compat(MigrationParameters *params, int i,
>>  void migrate_capabilities_set_compat(MigrationParameters *params,
>>                                       MigrationCapabilityStatusList *caps);
>>  bool migrate_caps_check(MigrationParameters *new, Error **errp);
>> +void migrate_params_store_defaults(MigrationState *s);
>> +bool migrate_params_override(MigrationState *s, MigrationParameters *new,
>> +                             Error **errp);
>>  #endif
>> diff --git a/qapi/migration.json b/qapi/migration.json
>> index 7282e4b9eb..64a92d8d28 100644
>> --- a/qapi/migration.json
>> +++ b/qapi/migration.json
>> @@ -1474,9 +1474,16 @@
>>  #
>>  # @resume: resume one paused migration, default "off".  (since 3.0)
>>  #
>> +# @config: migration configuration options, previously set via
>> +#     @migrate-set-parameters and @migrate-set-capabilities.  (since
>> +#     10.1)
>> +#
>>  # Features:
>>  #
>>  # @deprecated: Argument @detach is deprecated.
>> +# @config: Indicates this command can receive the entire migration
>> +# configuration via the @config field, dispensing the use of
>> +# @migrate-set-parameters.
>>  #
>>  # Since: 0.14
>>  #
>> @@ -1538,7 +1545,9 @@
>>    'data': {'*uri': 'str',
>>             '*channels': [ 'MigrationChannel' ],
>>             '*detach': { 'type': 'bool', 'features': [ 'deprecated' ] },
>> -           '*resume': 'bool' } }
>> +           '*config': 'MigrationParameters',
>> +           '*resume': 'bool' },
>> +  'features': [ 'config' ] }
>>  
>>  ##
>>  # @migrate-incoming:
>> @@ -1557,6 +1566,16 @@
>>  #     error details could be retrieved with query-migrate.
>>  #     (since 9.1)
>>  #
>> +# @config: migration configuration options, previously set via
>> +#     @migrate-set-parameters and @migrate-set-capabilities.  (since
>> +#     10.1)
>> +#
>> +# Features:
>> +#
>> +# @config: Indicates this command can receive the entire migration
>> +# configuration via the @config field, dispensing the use of
>> +# @migrate-set-parameters.
>> +#
>>  # Since: 2.3
>>  #
>>  # .. admonition:: Notes
>> @@ -1610,7 +1629,9 @@
>>  { 'command': 'migrate-incoming',
>>               'data': {'*uri': 'str',
>>                        '*channels': [ 'MigrationChannel' ],
>> -                      '*exit-on-error': 'bool' } }
>> +                      '*config': 'MigrationParameters',
>> +                      '*exit-on-error': 'bool' },
>> +             'features': [ 'config' ] }
>>  
>>  ##
>>  # @xen-save-devices-state:
>> diff --git a/system/vl.c b/system/vl.c
>> index 3b7057e6c6..b29fd24d08 100644
>> --- a/system/vl.c
>> +++ b/system/vl.c
>> @@ -2823,7 +2823,8 @@ void qmp_x_exit_preconfig(Error **errp)
>>                  g_new0(MigrationChannelList, 1);
>>  
>>              channels->value = incoming_channels[MIGRATION_CHANNEL_TYPE_MAIN];
>> -            qmp_migrate_incoming(NULL, true, channels, true, true, &local_err);
>> +            qmp_migrate_incoming(NULL, true, channels, NULL, true, true,
>> +                                 &local_err);
>>              if (local_err) {
>>                  error_reportf_err(local_err, "-incoming %s: ", incoming);
>>                  exit(1);
>> -- 
>> 2.35.3
>> 


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

* Re: [PATCH 19/21] migration: Allow migrate commands to provide the migration config
  2025-06-06 20:23     ` Fabiano Rosas
@ 2025-06-06 20:50       ` Peter Xu
  2025-06-09 14:37         ` Fabiano Rosas
  2025-06-09 15:03         ` Daniel P. Berrangé
  0 siblings, 2 replies; 83+ messages in thread
From: Peter Xu @ 2025-06-06 20:50 UTC (permalink / raw)
  To: Fabiano Rosas; +Cc: qemu-devel, Markus Armbruster, Daniel P . Berrangé

On Fri, Jun 06, 2025 at 05:23:18PM -0300, Fabiano Rosas wrote:
> Peter Xu <peterx@redhat.com> writes:
> 
> > On Mon, Jun 02, 2025 at 10:38:08PM -0300, Fabiano Rosas wrote:
> >> Allow the migrate and migrate_incoming commands to pass the migration
> >> configuration options all at once, dispensing the use of
> >> migrate-set-parameters and migrate-set-capabilities.
> >> 
> >> The motivation of this is to simplify the interface with the
> >> management layer and avoid the usage of several command invocations to
> >> configure a migration. It also avoids stale parameters from a previous
> >> migration to influence the current migration.
> >> 
> >> The options that are changed during the migration can still be set
> >> with the existing commands.
> >> 
> >> The order of precedence is:
> >> 
> >> 'config' argument > -global cmdline > defaults (migration_properties)
> >
> > Could we still keep the QMP migrate-set-parameters values?
> >
> >   'config' argument > QMP setups using migrate-set-parameters >
> >     -global cmdline > defaults (migration_properties)
> >
> 
> That's the case. I failed to mention it in the commit message. IOW it
> behaves just like today, but the new 'config' way takes precedence over
> all.

Referring to below chunk of code:

[...]

> >> +bool migrate_params_override(MigrationState *s, MigrationParameters *new,
> >> +                             Error **errp)
> >> +{
> >> +    ERRP_GUARD();
> >> +
> >> +    assert(bql_locked());
> >> +
> >> +    /* reset to default parameters */
> >> +    migrate_params_apply(&s->defaults);

IIUC here it'll reset all global parameters using the initial defaults
first, then apply the "config" specified in "migrate" QMP command?

I think there're actually two separate questions to be asked, to make it
clearer, they are:

  (1) Whether we should allow QMP "migrate" 'config' parameter to overwrite
      global setup?

  (2) Whether we should allow previous QMP global setup to be used even if
      QMP "migrate" provided 'config' parameter?

So IIUC the patch does (1) YES (2) NO, while what I think might be more
intuitive is (1) NO (2) YES.

> >> +
> >> +    /* overwrite with the new ones */
> >> +    qmp_migrate_set_parameters(new, errp);
> >> +    if (*errp) {
> >> +        return false;
> >> +    }
> >> +
> >> +    return true;
> >> +}

-- 
Peter Xu



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

* Re: [PATCH 19/21] migration: Allow migrate commands to provide the migration config
  2025-06-06 20:50       ` Peter Xu
@ 2025-06-09 14:37         ` Fabiano Rosas
  2025-06-09 15:51           ` Peter Xu
  2025-06-09 15:03         ` Daniel P. Berrangé
  1 sibling, 1 reply; 83+ messages in thread
From: Fabiano Rosas @ 2025-06-09 14:37 UTC (permalink / raw)
  To: Peter Xu; +Cc: qemu-devel, Markus Armbruster, Daniel P . Berrangé

Peter Xu <peterx@redhat.com> writes:

> On Fri, Jun 06, 2025 at 05:23:18PM -0300, Fabiano Rosas wrote:
>> Peter Xu <peterx@redhat.com> writes:
>> 
>> > On Mon, Jun 02, 2025 at 10:38:08PM -0300, Fabiano Rosas wrote:
>> >> Allow the migrate and migrate_incoming commands to pass the migration
>> >> configuration options all at once, dispensing the use of
>> >> migrate-set-parameters and migrate-set-capabilities.
>> >> 
>> >> The motivation of this is to simplify the interface with the
>> >> management layer and avoid the usage of several command invocations to
>> >> configure a migration. It also avoids stale parameters from a previous
>> >> migration to influence the current migration.
>> >> 
>> >> The options that are changed during the migration can still be set
>> >> with the existing commands.
>> >> 
>> >> The order of precedence is:
>> >> 
>> >> 'config' argument > -global cmdline > defaults (migration_properties)
>> >
>> > Could we still keep the QMP migrate-set-parameters values?
>> >
>> >   'config' argument > QMP setups using migrate-set-parameters >
>> >     -global cmdline > defaults (migration_properties)
>> >
>> 
>> That's the case. I failed to mention it in the commit message. IOW it
>> behaves just like today, but the new 'config' way takes precedence over
>> all.
>
> Referring to below chunk of code:
>
> [...]
>
>> >> +bool migrate_params_override(MigrationState *s, MigrationParameters *new,
>> >> +                             Error **errp)
>> >> +{
>> >> +    ERRP_GUARD();
>> >> +
>> >> +    assert(bql_locked());
>> >> +
>> >> +    /* reset to default parameters */
>> >> +    migrate_params_apply(&s->defaults);
>
> IIUC here it'll reset all global parameters using the initial defaults
> first, then apply the "config" specified in "migrate" QMP command?
>

Yes, this is so any previously set parameter via migrate-set-parameter
gets erased. I think what we want (but feel free to disagree) is to have
the migrate-set-parameter _eventually_ only handle parameters that need
to be modifed during migration runtime. Anything else can be done via
passing config to qmp_migrate.

For -global, I don't have a preference. Having -global take precedence
over all would require a way to know which options were present in the
command-line and which are just the defaults seet in
migration_properties. I currently don't know how to do that. If it is at
all possible (within reason) we could make the change, no worries.

> I think there're actually two separate questions to be asked, to make it
> clearer, they are:

Here it got ambiguous when you say "global", I've been using -global to
refer to the cmdline -global migration.foo, but others have used global
to mean s->parameters (which has an extended lifetime). Could you
clarify?

>
>   (1) Whether we should allow QMP "migrate" 'config' parameter to overwrite
>       global setup?
>
>   (2) Whether we should allow previous QMP global setup to be used even if
>       QMP "migrate" provided 'config' parameter?
>
> So IIUC the patch does (1) YES (2) NO, while what I think might be more
> intuitive is (1) NO (2) YES.
>
>> >> +
>> >> +    /* overwrite with the new ones */
>> >> +    qmp_migrate_set_parameters(new, errp);
>> >> +    if (*errp) {
>> >> +        return false;
>> >> +    }
>> >> +
>> >> +    return true;
>> >> +}


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

* Re: [PATCH 19/21] migration: Allow migrate commands to provide the migration config
  2025-06-06 20:50       ` Peter Xu
  2025-06-09 14:37         ` Fabiano Rosas
@ 2025-06-09 15:03         ` Daniel P. Berrangé
  2025-06-09 15:33           ` Peter Xu
  1 sibling, 1 reply; 83+ messages in thread
From: Daniel P. Berrangé @ 2025-06-09 15:03 UTC (permalink / raw)
  To: Peter Xu; +Cc: Fabiano Rosas, qemu-devel, Markus Armbruster

On Fri, Jun 06, 2025 at 04:50:54PM -0400, Peter Xu wrote:
> On Fri, Jun 06, 2025 at 05:23:18PM -0300, Fabiano Rosas wrote:
> > Peter Xu <peterx@redhat.com> writes:
> > 
> > > On Mon, Jun 02, 2025 at 10:38:08PM -0300, Fabiano Rosas wrote:
> > >> Allow the migrate and migrate_incoming commands to pass the migration
> > >> configuration options all at once, dispensing the use of
> > >> migrate-set-parameters and migrate-set-capabilities.
> > >> 
> > >> The motivation of this is to simplify the interface with the
> > >> management layer and avoid the usage of several command invocations to
> > >> configure a migration. It also avoids stale parameters from a previous
> > >> migration to influence the current migration.
> > >> 
> > >> The options that are changed during the migration can still be set
> > >> with the existing commands.
> > >> 
> > >> The order of precedence is:
> > >> 
> > >> 'config' argument > -global cmdline > defaults (migration_properties)
> > >
> > > Could we still keep the QMP migrate-set-parameters values?
> > >
> > >   'config' argument > QMP setups using migrate-set-parameters >
> > >     -global cmdline > defaults (migration_properties)
> > >
> > 
> > That's the case. I failed to mention it in the commit message. IOW it
> > behaves just like today, but the new 'config' way takes precedence over
> > all.
> 
> Referring to below chunk of code:
> 
> [...]
> 
> > >> +bool migrate_params_override(MigrationState *s, MigrationParameters *new,
> > >> +                             Error **errp)
> > >> +{
> > >> +    ERRP_GUARD();
> > >> +
> > >> +    assert(bql_locked());
> > >> +
> > >> +    /* reset to default parameters */
> > >> +    migrate_params_apply(&s->defaults);
> 
> IIUC here it'll reset all global parameters using the initial defaults
> first, then apply the "config" specified in "migrate" QMP command?
> 
> I think there're actually two separate questions to be asked, to make it
> clearer, they are:
> 
>   (1) Whether we should allow QMP "migrate" 'config' parameter to overwrite
>       global setup?
> 
>   (2) Whether we should allow previous QMP global setup to be used even if
>       QMP "migrate" provided 'config' parameter?
> 
> So IIUC the patch does (1) YES (2) NO, while what I think might be more
> intuitive is (1) NO (2) YES.

The point of the 'config' parameter to the 'migrate' command is to
enable the mgmt app to fully specify what it wants the configuration
to be, such that there is no previously set state will will cause
it surprises. Allowing -global to have an effect undermines the
predictibility in the same way that migrate-set-parameter undermines
the predictibility.

With regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|



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

* Re: [PATCH 19/21] migration: Allow migrate commands to provide the migration config
  2025-06-09 15:03         ` Daniel P. Berrangé
@ 2025-06-09 15:33           ` Peter Xu
  2025-06-09 15:43             ` Daniel P. Berrangé
  0 siblings, 1 reply; 83+ messages in thread
From: Peter Xu @ 2025-06-09 15:33 UTC (permalink / raw)
  To: Daniel P. Berrangé; +Cc: Fabiano Rosas, qemu-devel, Markus Armbruster

On Mon, Jun 09, 2025 at 04:03:01PM +0100, Daniel P. Berrangé wrote:
> On Fri, Jun 06, 2025 at 04:50:54PM -0400, Peter Xu wrote:
> > On Fri, Jun 06, 2025 at 05:23:18PM -0300, Fabiano Rosas wrote:
> > > Peter Xu <peterx@redhat.com> writes:
> > > 
> > > > On Mon, Jun 02, 2025 at 10:38:08PM -0300, Fabiano Rosas wrote:
> > > >> Allow the migrate and migrate_incoming commands to pass the migration
> > > >> configuration options all at once, dispensing the use of
> > > >> migrate-set-parameters and migrate-set-capabilities.
> > > >> 
> > > >> The motivation of this is to simplify the interface with the
> > > >> management layer and avoid the usage of several command invocations to
> > > >> configure a migration. It also avoids stale parameters from a previous
> > > >> migration to influence the current migration.
> > > >> 
> > > >> The options that are changed during the migration can still be set
> > > >> with the existing commands.
> > > >> 
> > > >> The order of precedence is:
> > > >> 
> > > >> 'config' argument > -global cmdline > defaults (migration_properties)
> > > >
> > > > Could we still keep the QMP migrate-set-parameters values?
> > > >
> > > >   'config' argument > QMP setups using migrate-set-parameters >
> > > >     -global cmdline > defaults (migration_properties)
> > > >
> > > 
> > > That's the case. I failed to mention it in the commit message. IOW it
> > > behaves just like today, but the new 'config' way takes precedence over
> > > all.
> > 
> > Referring to below chunk of code:
> > 
> > [...]
> > 
> > > >> +bool migrate_params_override(MigrationState *s, MigrationParameters *new,
> > > >> +                             Error **errp)
> > > >> +{
> > > >> +    ERRP_GUARD();
> > > >> +
> > > >> +    assert(bql_locked());
> > > >> +
> > > >> +    /* reset to default parameters */
> > > >> +    migrate_params_apply(&s->defaults);
> > 
> > IIUC here it'll reset all global parameters using the initial defaults
> > first, then apply the "config" specified in "migrate" QMP command?
> > 
> > I think there're actually two separate questions to be asked, to make it
> > clearer, they are:
> > 
> >   (1) Whether we should allow QMP "migrate" 'config' parameter to overwrite
> >       global setup?
> > 
> >   (2) Whether we should allow previous QMP global setup to be used even if
> >       QMP "migrate" provided 'config' parameter?
> > 
> > So IIUC the patch does (1) YES (2) NO, while what I think might be more
> > intuitive is (1) NO (2) YES.
> 
> The point of the 'config' parameter to the 'migrate' command is to
> enable the mgmt app to fully specify what it wants the configuration
> to be, such that there is no previously set state will will cause
> it surprises. Allowing -global to have an effect undermines the
> predictibility in the same way that migrate-set-parameter undermines
> the predictibility.

Now I think I know part of what I've missed: I used to think the "config"
of per-QMP-migrate-command can be totally temporary for a specific
migration request, but then we need another MigrationState.parameters_2 to
cache the old or vice versa; that's probably not necessary.  Now I think it
makes sense to overwrite any settings directly, hence I think I changed my
mind on question (1), YES is fine here.

For (2), why it would introduce any uncertainty for mgmt?

If the mgmt app can both: (1) query from qapi schema knowing all the
parameters supported, then (2) specify all the parameters in QMP migrate's
"option" parameter.  Then it's literally overwritting all the parameters,
so it's predictable with or without completely removing global settings as
an idea?  To me, the "option" is the key to make QMP migrate command and
parameter/cap setup in one "atomic-like" operation, and that provides the
predictability if the command succeeded and if all the parameters are
specified (otherwise it'll fail saying migration in progress, internally
protected by BQL or whatever lock QMP monitor holds).

Thanks,

-- 
Peter Xu



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

* Re: [PATCH 19/21] migration: Allow migrate commands to provide the migration config
  2025-06-09 15:33           ` Peter Xu
@ 2025-06-09 15:43             ` Daniel P. Berrangé
  2025-06-09 15:53               ` Peter Xu
  0 siblings, 1 reply; 83+ messages in thread
From: Daniel P. Berrangé @ 2025-06-09 15:43 UTC (permalink / raw)
  To: Peter Xu; +Cc: Fabiano Rosas, qemu-devel, Markus Armbruster

On Mon, Jun 09, 2025 at 11:33:14AM -0400, Peter Xu wrote:
> 
> Now I think I know part of what I've missed: I used to think the "config"
> of per-QMP-migrate-command can be totally temporary for a specific
> migration request, but then we need another MigrationState.parameters_2 to
> cache the old or vice versa; that's probably not necessary.  Now I think it
> makes sense to overwrite any settings directly, hence I think I changed my
> mind on question (1), YES is fine here.
> 
> For (2), why it would introduce any uncertainty for mgmt?
> 
> If the mgmt app can both: (1) query from qapi schema knowing all the
> parameters supported, then (2) specify all the parameters in QMP migrate's
> "option" parameter.  Then it's literally overwritting all the parameters,
> so it's predictable with or without completely removing global settings as
> an idea?

That is relying on the mgmt app specifiying absolutely every config
parameter that exists. If they miss anything, then the behaviour is
not well defined, as external global state still affects things.

This is the same situation we already have with migrate-set-parameter,
where mgmt apps have to know to call migrate-set-parameter over & over
with every possible parameter to get back to a well known starting point.

The command needs to run with the parameters provided in 'config' and
no external global state, whether from -global or any prior call of
migrate-set-parameter

With regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|



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

* Re: [PATCH 19/21] migration: Allow migrate commands to provide the migration config
  2025-06-09 14:37         ` Fabiano Rosas
@ 2025-06-09 15:51           ` Peter Xu
  2025-06-09 16:13             ` Daniel P. Berrangé
  2025-06-09 18:02             ` Fabiano Rosas
  0 siblings, 2 replies; 83+ messages in thread
From: Peter Xu @ 2025-06-09 15:51 UTC (permalink / raw)
  To: Fabiano Rosas; +Cc: qemu-devel, Markus Armbruster, Daniel P . Berrangé

On Mon, Jun 09, 2025 at 11:37:02AM -0300, Fabiano Rosas wrote:
> Peter Xu <peterx@redhat.com> writes:
> 
> > On Fri, Jun 06, 2025 at 05:23:18PM -0300, Fabiano Rosas wrote:
> >> Peter Xu <peterx@redhat.com> writes:
> >> 
> >> > On Mon, Jun 02, 2025 at 10:38:08PM -0300, Fabiano Rosas wrote:
> >> >> Allow the migrate and migrate_incoming commands to pass the migration
> >> >> configuration options all at once, dispensing the use of
> >> >> migrate-set-parameters and migrate-set-capabilities.
> >> >> 
> >> >> The motivation of this is to simplify the interface with the
> >> >> management layer and avoid the usage of several command invocations to
> >> >> configure a migration. It also avoids stale parameters from a previous
> >> >> migration to influence the current migration.
> >> >> 
> >> >> The options that are changed during the migration can still be set
> >> >> with the existing commands.
> >> >> 
> >> >> The order of precedence is:
> >> >> 
> >> >> 'config' argument > -global cmdline > defaults (migration_properties)
> >> >
> >> > Could we still keep the QMP migrate-set-parameters values?
> >> >
> >> >   'config' argument > QMP setups using migrate-set-parameters >
> >> >     -global cmdline > defaults (migration_properties)
> >> >
> >> 
> >> That's the case. I failed to mention it in the commit message. IOW it
> >> behaves just like today, but the new 'config' way takes precedence over
> >> all.
> >
> > Referring to below chunk of code:
> >
> > [...]
> >
> >> >> +bool migrate_params_override(MigrationState *s, MigrationParameters *new,
> >> >> +                             Error **errp)
> >> >> +{
> >> >> +    ERRP_GUARD();
> >> >> +
> >> >> +    assert(bql_locked());
> >> >> +
> >> >> +    /* reset to default parameters */
> >> >> +    migrate_params_apply(&s->defaults);
> >
> > IIUC here it'll reset all global parameters using the initial defaults
> > first, then apply the "config" specified in "migrate" QMP command?
> >
> 
> Yes, this is so any previously set parameter via migrate-set-parameter
> gets erased. I think what we want (but feel free to disagree) is to have
> the migrate-set-parameter _eventually_ only handle parameters that need
> to be modifed during migration runtime. Anything else can be done via
> passing config to qmp_migrate.
> 
> For -global, I don't have a preference. Having -global take precedence
> over all would require a way to know which options were present in the
> command-line and which are just the defaults seet in
> migration_properties. I currently don't know how to do that. If it is at
> all possible (within reason) we could make the change, no worries.
> 
> > I think there're actually two separate questions to be asked, to make it
> > clearer, they are:
> 
> Here it got ambiguous when you say "global", I've been using -global to
> refer to the cmdline -global migration.foo, but others have used global
> to mean s->parameters (which has an extended lifetime). Could you
> clarify?

I meant the -global, and the global setups via migrate-set-parameters.

As replied to Dan in the other email, I changed my mind on question (1); I
think it makes sense to have it YES.  I left my pure question on (2) there
too.

Do we really want to disable migrate-set-parameters setting most of the
parameters, and only allow it to be set during migration on a few things
like bandwidth or so?

I just don't really see the major benefit of that yet.  I would think it
make more sense if we don't need to change any parameters in migration,
then provide that in one shot in QMP migrate "config".  Maybe making more
sense if migration is not heavily thread-based but having its aiocontext so
we could even move to Jobs.

Now after all we'll need to allow setting something like bandwidth even
during migration alive, and we have all the things ready allowing to set
before migration starts, I'm not 100% sure whether we need to bother even
if it does look cleaner, because we'll still break mgmt used to be working
for years.. I could be over-cautious on breaking things, but I still want
to understand better on the benefits.

One step back, on this "allow migrate to specify 'config'" request: I think
we can definitely do that as it still provides some kind of atomicity.  But
frankly speaking I never see it a "real problem" - do we really have report
or use case showing that Libvirt can trigger "migrate" with some global
settings touched by other apps at all?

To me, it was yet an illutionary problem, I never know the answer of that.
If Libvirt is still the owner of QEMU instance via the QMP channel, I
actually don't really see why the atomicity would even help, even though we
can still provide that as it's pretty easy as something optional; like what
this patch does without too much hassle.

Then if to move one step further to remove all global settings, we face
breaking debugging scripts, and breaking of any old libvirt and non-libvirt
mgmt apps.  Frankly I really don't yet know whether it's a good idea.  I
could miss some important reasoning of why we want to do it - it needs to
be something not relevant to "making the code cleaner", IMHO..

-- 
Peter Xu



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

* Re: [PATCH 19/21] migration: Allow migrate commands to provide the migration config
  2025-06-09 15:43             ` Daniel P. Berrangé
@ 2025-06-09 15:53               ` Peter Xu
  2025-06-09 15:58                 ` Peter Xu
  2025-06-09 16:15                 ` Daniel P. Berrangé
  0 siblings, 2 replies; 83+ messages in thread
From: Peter Xu @ 2025-06-09 15:53 UTC (permalink / raw)
  To: Daniel P. Berrangé; +Cc: Fabiano Rosas, qemu-devel, Markus Armbruster

On Mon, Jun 09, 2025 at 04:43:40PM +0100, Daniel P. Berrangé wrote:
> On Mon, Jun 09, 2025 at 11:33:14AM -0400, Peter Xu wrote:
> > 
> > Now I think I know part of what I've missed: I used to think the "config"
> > of per-QMP-migrate-command can be totally temporary for a specific
> > migration request, but then we need another MigrationState.parameters_2 to
> > cache the old or vice versa; that's probably not necessary.  Now I think it
> > makes sense to overwrite any settings directly, hence I think I changed my
> > mind on question (1), YES is fine here.
> > 
> > For (2), why it would introduce any uncertainty for mgmt?
> > 
> > If the mgmt app can both: (1) query from qapi schema knowing all the
> > parameters supported, then (2) specify all the parameters in QMP migrate's
> > "option" parameter.  Then it's literally overwritting all the parameters,
> > so it's predictable with or without completely removing global settings as
> > an idea?
> 
> That is relying on the mgmt app specifiying absolutely every config
> parameter that exists. If they miss anything, then the behaviour is
> not well defined, as external global state still affects things.
> 
> This is the same situation we already have with migrate-set-parameter,
> where mgmt apps have to know to call migrate-set-parameter over & over
> with every possible parameter to get back to a well known starting point.
> 
> The command needs to run with the parameters provided in 'config' and
> no external global state, whether from -global or any prior call of
> migrate-set-parameter

So libvirt does not probe the qapi schema for all possible parameters?  Why
not do that once on QEMU boot up, then when migration is needed use a
sequence of commands to make sure everything will be setup before
"migrate"?  It'll definitely take a few rounds of QMP commands, but the
core issue is whether there can be any real atomic issues of that.

Just to say, I still think having "option" is a fine idea at least, but I'm
really curious on whether there's any real issue even without it.

Thanks,

-- 
Peter Xu



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

* Re: [PATCH 19/21] migration: Allow migrate commands to provide the migration config
  2025-06-09 15:53               ` Peter Xu
@ 2025-06-09 15:58                 ` Peter Xu
  2025-06-09 16:15                 ` Daniel P. Berrangé
  1 sibling, 0 replies; 83+ messages in thread
From: Peter Xu @ 2025-06-09 15:58 UTC (permalink / raw)
  To: Daniel P. Berrangé; +Cc: Fabiano Rosas, qemu-devel, Markus Armbruster

On Mon, Jun 09, 2025 at 11:53:47AM -0400, Peter Xu wrote:
> On Mon, Jun 09, 2025 at 04:43:40PM +0100, Daniel P. Berrangé wrote:
> > On Mon, Jun 09, 2025 at 11:33:14AM -0400, Peter Xu wrote:
> > > 
> > > Now I think I know part of what I've missed: I used to think the "config"
> > > of per-QMP-migrate-command can be totally temporary for a specific
> > > migration request, but then we need another MigrationState.parameters_2 to
> > > cache the old or vice versa; that's probably not necessary.  Now I think it
> > > makes sense to overwrite any settings directly, hence I think I changed my
> > > mind on question (1), YES is fine here.
> > > 
> > > For (2), why it would introduce any uncertainty for mgmt?
> > > 
> > > If the mgmt app can both: (1) query from qapi schema knowing all the
> > > parameters supported, then (2) specify all the parameters in QMP migrate's
> > > "option" parameter.  Then it's literally overwritting all the parameters,
> > > so it's predictable with or without completely removing global settings as
> > > an idea?
> > 
> > That is relying on the mgmt app specifiying absolutely every config
> > parameter that exists. If they miss anything, then the behaviour is
> > not well defined, as external global state still affects things.
> > 
> > This is the same situation we already have with migrate-set-parameter,
> > where mgmt apps have to know to call migrate-set-parameter over & over
> > with every possible parameter to get back to a well known starting point.
> > 
> > The command needs to run with the parameters provided in 'config' and
> > no external global state, whether from -global or any prior call of
> > migrate-set-parameter
> 
> So libvirt does not probe the qapi schema for all possible parameters?  Why
> not do that once on QEMU boot up, then when migration is needed use a
> sequence of commands to make sure everything will be setup before
> "migrate"?  It'll definitely take a few rounds of QMP commands, but the
> core issue is whether there can be any real atomic issues of that.
> 
> Just to say, I still think having "option" is a fine idea at least, but I'm
> really curious on whether there's any real issue even without it.

Btw, one more thing to mention: IIUC libvirt shouldn't need to specify all
parameters either.  We have default parameters provided, now with Fabiano's
work we can even have default caps being enabled now, used to controversial
when being a cap.  Hence, I would expect Libvirt doesn't need to specify
all parameters (with/without "config") but whatever parameters it doesn't
want to use the default (e.g. being specified by the user).

When we can have more sane default parameters (I do plan to turn on preempt
and blocktime caps on by default for postcopy at some point, for example),
I think it's better libvirt uses the default, or e.g. blocktime may have
been disabled there.

Thanks,

-- 
Peter Xu



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

* Re: [PATCH 19/21] migration: Allow migrate commands to provide the migration config
  2025-06-09 15:51           ` Peter Xu
@ 2025-06-09 16:13             ` Daniel P. Berrangé
  2025-06-09 16:49               ` Peter Xu
  2025-06-09 18:02             ` Fabiano Rosas
  1 sibling, 1 reply; 83+ messages in thread
From: Daniel P. Berrangé @ 2025-06-09 16:13 UTC (permalink / raw)
  To: Peter Xu; +Cc: Fabiano Rosas, qemu-devel, Markus Armbruster

On Mon, Jun 09, 2025 at 11:51:17AM -0400, Peter Xu wrote:
> On Mon, Jun 09, 2025 at 11:37:02AM -0300, Fabiano Rosas wrote:
> > Peter Xu <peterx@redhat.com> writes:
> > 
> > > On Fri, Jun 06, 2025 at 05:23:18PM -0300, Fabiano Rosas wrote:
> > >> Peter Xu <peterx@redhat.com> writes:
> > >> 
> > >> > On Mon, Jun 02, 2025 at 10:38:08PM -0300, Fabiano Rosas wrote:
> > >> >> Allow the migrate and migrate_incoming commands to pass the migration
> > >> >> configuration options all at once, dispensing the use of
> > >> >> migrate-set-parameters and migrate-set-capabilities.
> > >> >> 
> > >> >> The motivation of this is to simplify the interface with the
> > >> >> management layer and avoid the usage of several command invocations to
> > >> >> configure a migration. It also avoids stale parameters from a previous
> > >> >> migration to influence the current migration.
> > >> >> 
> > >> >> The options that are changed during the migration can still be set
> > >> >> with the existing commands.
> > >> >> 
> > >> >> The order of precedence is:
> > >> >> 
> > >> >> 'config' argument > -global cmdline > defaults (migration_properties)
> > >> >
> > >> > Could we still keep the QMP migrate-set-parameters values?
> > >> >
> > >> >   'config' argument > QMP setups using migrate-set-parameters >
> > >> >     -global cmdline > defaults (migration_properties)
> > >> >
> > >> 
> > >> That's the case. I failed to mention it in the commit message. IOW it
> > >> behaves just like today, but the new 'config' way takes precedence over
> > >> all.
> > >
> > > Referring to below chunk of code:
> > >
> > > [...]
> > >
> > >> >> +bool migrate_params_override(MigrationState *s, MigrationParameters *new,
> > >> >> +                             Error **errp)
> > >> >> +{
> > >> >> +    ERRP_GUARD();
> > >> >> +
> > >> >> +    assert(bql_locked());
> > >> >> +
> > >> >> +    /* reset to default parameters */
> > >> >> +    migrate_params_apply(&s->defaults);
> > >
> > > IIUC here it'll reset all global parameters using the initial defaults
> > > first, then apply the "config" specified in "migrate" QMP command?
> > >
> > 
> > Yes, this is so any previously set parameter via migrate-set-parameter
> > gets erased. I think what we want (but feel free to disagree) is to have
> > the migrate-set-parameter _eventually_ only handle parameters that need
> > to be modifed during migration runtime. Anything else can be done via
> > passing config to qmp_migrate.
> > 
> > For -global, I don't have a preference. Having -global take precedence
> > over all would require a way to know which options were present in the
> > command-line and which are just the defaults seet in
> > migration_properties. I currently don't know how to do that. If it is at
> > all possible (within reason) we could make the change, no worries.
> > 
> > > I think there're actually two separate questions to be asked, to make it
> > > clearer, they are:
> > 
> > Here it got ambiguous when you say "global", I've been using -global to
> > refer to the cmdline -global migration.foo, but others have used global
> > to mean s->parameters (which has an extended lifetime). Could you
> > clarify?
> 
> I meant the -global, and the global setups via migrate-set-parameters.
> 
> As replied to Dan in the other email, I changed my mind on question (1); I
> think it makes sense to have it YES.  I left my pure question on (2) there
> too.
> 
> Do we really want to disable migrate-set-parameters setting most of the
> parameters, and only allow it to be set during migration on a few things
> like bandwidth or so?

Yes, that's the whole point of the exercise IMHO. 

> I just don't really see the major benefit of that yet.  I would think it
> make more sense if we don't need to change any parameters in migration,
> then provide that in one shot in QMP migrate "config".  Maybe making more
> sense if migration is not heavily thread-based but having its aiocontext so
> we could even move to Jobs.

The benefit is that it brings 'migrate' into line with all other
QMP commands, such that the data provided with the command is
precisely what controls behaviour, giving predictable behaviour. 

> One step back, on this "allow migrate to specify 'config'" request: I think
> we can definitely do that as it still provides some kind of atomicity.  But
> frankly speaking I never see it a "real problem" - do we really have report
> or use case showing that Libvirt can trigger "migrate" with some global
> settings touched by other apps at all?

atomicity is only one of the goals - being free from side effects
of externally set global state is the more important aspect.

> To me, it was yet an illutionary problem, I never know the answer of that.
> If Libvirt is still the owner of QEMU instance via the QMP channel, I
> actually don't really see why the atomicity would even help, even though we
> can still provide that as it's pretty easy as something optional; like what
> this patch does without too much hassle.

Even if only a single mgmt app is involved this is still beneficial
because the migration infrastructure is used for distinct use cases
inside QEMU - live migration, CPR, save/restore, and savevm/loadvm.
Any time code any one of those uses cases starts using a new parameter,
apps have to make sure they don't inadvertantly have its effects apply
to the other use cases.

> Then if to move one step further to remove all global settings, we face
> breaking debugging scripts, and breaking of any old libvirt and non-libvirt
> mgmt apps.  Frankly I really don't yet know whether it's a good idea.  I
> could miss some important reasoning of why we want to do it - it needs to
> be something not relevant to "making the code cleaner", IMHO..

Again, it makes the behaviour predictable as the QMP command fully expresses
what action is going to be peformed, free with side effets of any previously
set state.

With regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|



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

* Re: [PATCH 19/21] migration: Allow migrate commands to provide the migration config
  2025-06-09 15:53               ` Peter Xu
  2025-06-09 15:58                 ` Peter Xu
@ 2025-06-09 16:15                 ` Daniel P. Berrangé
  2025-06-09 16:41                   ` Peter Xu
  1 sibling, 1 reply; 83+ messages in thread
From: Daniel P. Berrangé @ 2025-06-09 16:15 UTC (permalink / raw)
  To: Peter Xu; +Cc: Fabiano Rosas, qemu-devel, Markus Armbruster

On Mon, Jun 09, 2025 at 11:53:47AM -0400, Peter Xu wrote:
> On Mon, Jun 09, 2025 at 04:43:40PM +0100, Daniel P. Berrangé wrote:
> > On Mon, Jun 09, 2025 at 11:33:14AM -0400, Peter Xu wrote:
> > > 
> > > Now I think I know part of what I've missed: I used to think the "config"
> > > of per-QMP-migrate-command can be totally temporary for a specific
> > > migration request, but then we need another MigrationState.parameters_2 to
> > > cache the old or vice versa; that's probably not necessary.  Now I think it
> > > makes sense to overwrite any settings directly, hence I think I changed my
> > > mind on question (1), YES is fine here.
> > > 
> > > For (2), why it would introduce any uncertainty for mgmt?
> > > 
> > > If the mgmt app can both: (1) query from qapi schema knowing all the
> > > parameters supported, then (2) specify all the parameters in QMP migrate's
> > > "option" parameter.  Then it's literally overwritting all the parameters,
> > > so it's predictable with or without completely removing global settings as
> > > an idea?
> > 
> > That is relying on the mgmt app specifiying absolutely every config
> > parameter that exists. If they miss anything, then the behaviour is
> > not well defined, as external global state still affects things.
> > 
> > This is the same situation we already have with migrate-set-parameter,
> > where mgmt apps have to know to call migrate-set-parameter over & over
> > with every possible parameter to get back to a well known starting point.
> > 
> > The command needs to run with the parameters provided in 'config' and
> > no external global state, whether from -global or any prior call of
> > migrate-set-parameter
> 
> So libvirt does not probe the qapi schema for all possible parameters?  Why
> not do that once on QEMU boot up, then when migration is needed use a
> sequence of commands to make sure everything will be setup before
> "migrate"?  It'll definitely take a few rounds of QMP commands, but the
> core issue is whether there can be any real atomic issues of that.

Probing the QAPI schema tells you what parameters exist. It does not tell
you what values you should set for parameters, if you don't already know
what the semantics of that parameter are. Such a requirement to probe
all parameters & set them all manually is again making migration into a
special case that is not following the normal QMP design, and there's
no justification for that other than the historical design mistakes in
migration QMP which were copied from HMP.

With regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|



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

* Re: [PATCH 19/21] migration: Allow migrate commands to provide the migration config
  2025-06-09 16:15                 ` Daniel P. Berrangé
@ 2025-06-09 16:41                   ` Peter Xu
  0 siblings, 0 replies; 83+ messages in thread
From: Peter Xu @ 2025-06-09 16:41 UTC (permalink / raw)
  To: Daniel P. Berrangé; +Cc: Fabiano Rosas, qemu-devel, Markus Armbruster

On Mon, Jun 09, 2025 at 05:15:43PM +0100, Daniel P. Berrangé wrote:
> On Mon, Jun 09, 2025 at 11:53:47AM -0400, Peter Xu wrote:
> > On Mon, Jun 09, 2025 at 04:43:40PM +0100, Daniel P. Berrangé wrote:
> > > On Mon, Jun 09, 2025 at 11:33:14AM -0400, Peter Xu wrote:
> > > > 
> > > > Now I think I know part of what I've missed: I used to think the "config"
> > > > of per-QMP-migrate-command can be totally temporary for a specific
> > > > migration request, but then we need another MigrationState.parameters_2 to
> > > > cache the old or vice versa; that's probably not necessary.  Now I think it
> > > > makes sense to overwrite any settings directly, hence I think I changed my
> > > > mind on question (1), YES is fine here.
> > > > 
> > > > For (2), why it would introduce any uncertainty for mgmt?
> > > > 
> > > > If the mgmt app can both: (1) query from qapi schema knowing all the
> > > > parameters supported, then (2) specify all the parameters in QMP migrate's
> > > > "option" parameter.  Then it's literally overwritting all the parameters,
> > > > so it's predictable with or without completely removing global settings as
> > > > an idea?
> > > 
> > > That is relying on the mgmt app specifiying absolutely every config
> > > parameter that exists. If they miss anything, then the behaviour is
> > > not well defined, as external global state still affects things.
> > > 
> > > This is the same situation we already have with migrate-set-parameter,
> > > where mgmt apps have to know to call migrate-set-parameter over & over
> > > with every possible parameter to get back to a well known starting point.
> > > 
> > > The command needs to run with the parameters provided in 'config' and
> > > no external global state, whether from -global or any prior call of
> > > migrate-set-parameter
> > 
> > So libvirt does not probe the qapi schema for all possible parameters?  Why
> > not do that once on QEMU boot up, then when migration is needed use a
> > sequence of commands to make sure everything will be setup before
> > "migrate"?  It'll definitely take a few rounds of QMP commands, but the
> > core issue is whether there can be any real atomic issues of that.
> 
> Probing the QAPI schema tells you what parameters exist. It does not tell
> you what values you should set for parameters, if you don't already know

If Libvirt is looking for some suggested value to set a parameter, it
should just leave it empty, using the default provided by QEMU?  I was
expecting Libvirt to only specify anything it explicitly knows the answer.

> what the semantics of that parameter are. Such a requirement to probe
> all parameters & set them all manually is again making migration into a
> special case that is not following the normal QMP design, and there's
> no justification for that other than the historical design mistakes in
> migration QMP which were copied from HMP.

I agree migration is special cased.. I also agree if we design the
interface today it may not be like that.  I suppose it means it's only the
"API cleaness" issue.  That matches my understanding, even if I wished I
missed something else..

That'll be a hassle for all mgmt for sure whenever an old libvirt might
still have a chance to run on a newer QEMU.

That'll also be a hassle for any downstream if some Y+1 branch starts to
drop the global-set way completely then downstream might need to take care
of keeping that instead for the major release until the last Y, otherwise
if someone installs some X.Y+1 package on X.Y it might break similarly.
All that for "let's make the interface look better".

I sincerely could be wrong, but I keep my skeptical view of this whole
effort; it's only about after this series (while this series still makes
sense to me to have caps being able to set as params, and the "config" in
general).  I would say we could at least prioritize and invest other more
important things, for example on handshakes, which could provide functional
differences (removing src/dst param dependency, removing hackish channel
establishments all over the places, early failure of device state mismatch
rather than late failure on converge, etc.).

Thanks,

-- 
Peter Xu



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

* Re: [PATCH 19/21] migration: Allow migrate commands to provide the migration config
  2025-06-09 16:13             ` Daniel P. Berrangé
@ 2025-06-09 16:49               ` Peter Xu
  2025-06-09 18:17                 ` Fabiano Rosas
  0 siblings, 1 reply; 83+ messages in thread
From: Peter Xu @ 2025-06-09 16:49 UTC (permalink / raw)
  To: Daniel P. Berrangé; +Cc: Fabiano Rosas, qemu-devel, Markus Armbruster

(I had a reply in the other thread, that might have covered most of the
 points but maybe not this one..)

On Mon, Jun 09, 2025 at 05:13:00PM +0100, Daniel P. Berrangé wrote:
> Even if only a single mgmt app is involved this is still beneficial
> because the migration infrastructure is used for distinct use cases
> inside QEMU - live migration, CPR, save/restore, and savevm/loadvm.

I assume CPR is save/restore, so indeed we have 3 ways to use migration
core.

> Any time code any one of those uses cases starts using a new parameter,
> apps have to make sure they don't inadvertantly have its effects apply
> to the other use cases.

AFAICT, that's not affected by "whether we allow global settings", that is
still a concern internally as long as they use migration core.

One thing to mention is CPR is really a fine citizen here, AFAICT it is
exactly live migration using all the proper caps/params.  We _could_ split
it as many things do not apply like postcopy, but we could still just reuse
everything and ignoring the rest.  It'll be again a cleaness issue to me,
and even if CPR reuses everything it looks still clean enough, especially
comparing to savevm/loadvm.

savevm/loadvm is another story.. however afaiu if we want to decouple it,
it should be done not from the interface level, but internally first.
E.g., we should allow taking parameters as a temp pointer passing to
migration core, so that will be passed over by savevm setting all caps off,
for example, ignoring the global config.  The interface alone should, IMHO,
be done only later.

Meanwhile, even if that, IMO we can't avoid the need to think any new param
affecting savevm, as long as it's still using migration core.  I don't know
whether we need to do that one step even further to decouple savevm: I
would think the other way round to obsolete savevm completely if necessary
when we have fine "file:" migrations now, especially with mapped-ram.

Thanks,

-- 
Peter Xu



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

* Re: [PATCH 19/21] migration: Allow migrate commands to provide the migration config
  2025-06-09 15:51           ` Peter Xu
  2025-06-09 16:13             ` Daniel P. Berrangé
@ 2025-06-09 18:02             ` Fabiano Rosas
  2025-06-09 19:05               ` Peter Xu
  1 sibling, 1 reply; 83+ messages in thread
From: Fabiano Rosas @ 2025-06-09 18:02 UTC (permalink / raw)
  To: Peter Xu; +Cc: qemu-devel, Markus Armbruster, Daniel P . Berrangé

Peter Xu <peterx@redhat.com> writes:

> On Mon, Jun 09, 2025 at 11:37:02AM -0300, Fabiano Rosas wrote:
>> Peter Xu <peterx@redhat.com> writes:
>> 
>> > On Fri, Jun 06, 2025 at 05:23:18PM -0300, Fabiano Rosas wrote:
>> >> Peter Xu <peterx@redhat.com> writes:
>> >> 
>> >> > On Mon, Jun 02, 2025 at 10:38:08PM -0300, Fabiano Rosas wrote:
>> >> >> Allow the migrate and migrate_incoming commands to pass the migration
>> >> >> configuration options all at once, dispensing the use of
>> >> >> migrate-set-parameters and migrate-set-capabilities.
>> >> >> 
>> >> >> The motivation of this is to simplify the interface with the
>> >> >> management layer and avoid the usage of several command invocations to
>> >> >> configure a migration. It also avoids stale parameters from a previous
>> >> >> migration to influence the current migration.
>> >> >> 
>> >> >> The options that are changed during the migration can still be set
>> >> >> with the existing commands.
>> >> >> 
>> >> >> The order of precedence is:
>> >> >> 
>> >> >> 'config' argument > -global cmdline > defaults (migration_properties)
>> >> >
>> >> > Could we still keep the QMP migrate-set-parameters values?
>> >> >
>> >> >   'config' argument > QMP setups using migrate-set-parameters >
>> >> >     -global cmdline > defaults (migration_properties)
>> >> >
>> >> 
>> >> That's the case. I failed to mention it in the commit message. IOW it
>> >> behaves just like today, but the new 'config' way takes precedence over
>> >> all.
>> >
>> > Referring to below chunk of code:
>> >
>> > [...]
>> >
>> >> >> +bool migrate_params_override(MigrationState *s, MigrationParameters *new,
>> >> >> +                             Error **errp)
>> >> >> +{
>> >> >> +    ERRP_GUARD();
>> >> >> +
>> >> >> +    assert(bql_locked());
>> >> >> +
>> >> >> +    /* reset to default parameters */
>> >> >> +    migrate_params_apply(&s->defaults);
>> >
>> > IIUC here it'll reset all global parameters using the initial defaults
>> > first, then apply the "config" specified in "migrate" QMP command?
>> >
>> 
>> Yes, this is so any previously set parameter via migrate-set-parameter
>> gets erased. I think what we want (but feel free to disagree) is to have
>> the migrate-set-parameter _eventually_ only handle parameters that need
>> to be modifed during migration runtime. Anything else can be done via
>> passing config to qmp_migrate.
>> 
>> For -global, I don't have a preference. Having -global take precedence
>> over all would require a way to know which options were present in the
>> command-line and which are just the defaults seet in
>> migration_properties. I currently don't know how to do that. If it is at
>> all possible (within reason) we could make the change, no worries.
>> 
>> > I think there're actually two separate questions to be asked, to make it
>> > clearer, they are:
>> 
>> Here it got ambiguous when you say "global", I've been using -global to
>> refer to the cmdline -global migration.foo, but others have used global
>> to mean s->parameters (which has an extended lifetime). Could you
>> clarify?
>
> I meant the -global, and the global setups via migrate-set-parameters.
>
> As replied to Dan in the other email, I changed my mind on question (1); I
> think it makes sense to have it YES.  I left my pure question on (2) there
> too.
>
> Do we really want to disable migrate-set-parameters setting most of the
> parameters, and only allow it to be set during migration on a few things
> like bandwidth or so?
>

Well, if we decide we have reasons to introduce the "config" concept,
then I think we should not present two ways of doing the same
thing. User calls qmp_migrate with its arguments and that's the
migration. No other ways of setting parameters.

Since we do have parameters that are set in "runtime" I though of
keeping migrate-set-parameters around to minimize the interface
change. Maybe those should have been separate knobs on their own after
all... But in any case, we can't reject migrate-set-parameters because
it might happen way earlier than the actual migration command. So I
don't think anything changes regarding the API.

> I just don't really see the major benefit of that yet.  I would think it
> make more sense if we don't need to change any parameters in migration,
> then provide that in one shot in QMP migrate "config".  Maybe making more
> sense if migration is not heavily thread-based but having its aiocontext so
> we could even move to Jobs.
>
> Now after all we'll need to allow setting something like bandwidth even
> during migration alive, and we have all the things ready allowing to set
> before migration starts, I'm not 100% sure whether we need to bother even
> if it does look cleaner, because we'll still break mgmt used to be working
> for years.. I could be over-cautious on breaking things, but I still want
> to understand better on the benefits.
>

Makes sense. We'd say either use the old way or the new way. If both are
mixed, then the new way takes precedence. That keeps older apps working
and allows new code to transition into the new way.

> One step back, on this "allow migrate to specify 'config'" request: I
> think we can definitely do that as it still provides some kind of
> atomicity.  But frankly speaking I never see it a "real problem" - do
> we really have report or use case showing that Libvirt can trigger
> "migrate" with some global settings touched by other apps at all?
>

I don't think other apps is the problem, but libvirt itself maybe
attempting two migrations in sequence after one of them fails.

There always the possibility that the user is poking around, which of
course is not advisable, but if a weird migration bug shows up it's
difficult to confirm that other app/user hasn't changed the parameters.

> To me, it was yet an illutionary problem, I never know the answer of that.
> If Libvirt is still the owner of QEMU instance via the QMP channel, I
> actually don't really see why the atomicity would even help, even though we
> can still provide that as it's pretty easy as something optional; like what
> this patch does without too much hassle.
>

We can provide it, but I'd rather not unless we agree that is the way
forward. We don't need another way of doing the same as existing
commands.

> Then if to move one step further to remove all global settings, we face
> breaking debugging scripts, and breaking of any old libvirt and non-libvirt
> mgmt apps.  Frankly I really don't yet know whether it's a good idea.  I
> could miss some important reasoning of why we want to do it - it needs to
> be something not relevant to "making the code cleaner", IMHO..

I don't see it as breaking the old stuff. Because any old users would
still be using migrate-set-parameters as usual. So I think your concern
is about calling migrate the new way and also keeping -global
working. As I said, personally I don't mind if put some ifs around to
keep -global working.

Could we add another parameter that says allow-globals (or w/e) and make
everyone happy?



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

* Re: [PATCH 19/21] migration: Allow migrate commands to provide the migration config
  2025-06-09 16:49               ` Peter Xu
@ 2025-06-09 18:17                 ` Fabiano Rosas
  0 siblings, 0 replies; 83+ messages in thread
From: Fabiano Rosas @ 2025-06-09 18:17 UTC (permalink / raw)
  To: Peter Xu, Daniel P. Berrangé; +Cc: qemu-devel, Markus Armbruster

Peter Xu <peterx@redhat.com> writes:

> (I had a reply in the other thread, that might have covered most of the
>  points but maybe not this one..)
>
> On Mon, Jun 09, 2025 at 05:13:00PM +0100, Daniel P. Berrangé wrote:
>> Even if only a single mgmt app is involved this is still beneficial
>> because the migration infrastructure is used for distinct use cases
>> inside QEMU - live migration, CPR, save/restore, and savevm/loadvm.
>
> I assume CPR is save/restore, so indeed we have 3 ways to use migration
> core.
>
>> Any time code any one of those uses cases starts using a new parameter,
>> apps have to make sure they don't inadvertantly have its effects apply
>> to the other use cases.
>
> AFAICT, that's not affected by "whether we allow global settings", that is
> still a concern internally as long as they use migration core.
>
> One thing to mention is CPR is really a fine citizen here, AFAICT it is
> exactly live migration using all the proper caps/params.  We _could_ split
> it as many things do not apply like postcopy, but we could still just reuse
> everything and ignoring the rest.  It'll be again a cleaness issue to me,
> and even if CPR reuses everything it looks still clean enough, especially
> comparing to savevm/loadvm.
>
> savevm/loadvm is another story.. however afaiu if we want to decouple it,
> it should be done not from the interface level, but internally first.
> E.g., we should allow taking parameters as a temp pointer passing to
> migration core, so that will be passed over by savevm setting all caps off,
> for example, ignoring the global config.  The interface alone should, IMHO,
> be done only later.
>

This is simple to do, just reset all of s->parameters to (the new)
s->defaults. We never decided if any migration parameters do make sense
to use with savevm. If some of them does or is added later, then
snapshot_save would gain a "config" argument.

> Meanwhile, even if that, IMO we can't avoid the need to think any new param
> affecting savevm, as long as it's still using migration core.  I don't know
> whether we need to do that one step even further to decouple savevm: I
> would think the other way round to obsolete savevm completely if necessary
> when we have fine "file:" migrations now, especially with mapped-ram.

savevm is a weird case. It supports a wider range of setups than regular
migration. I don't know what to make of this. I would also like to make
it "just migration" but it will need a bunch of special-casing. Anyway,
we can discuss, but that's definitely for another day.

>
> Thanks,


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

* Re: [PATCH 19/21] migration: Allow migrate commands to provide the migration config
  2025-06-09 18:02             ` Fabiano Rosas
@ 2025-06-09 19:05               ` Peter Xu
  2025-06-09 19:41                 ` Fabiano Rosas
  0 siblings, 1 reply; 83+ messages in thread
From: Peter Xu @ 2025-06-09 19:05 UTC (permalink / raw)
  To: Fabiano Rosas; +Cc: qemu-devel, Markus Armbruster, Daniel P . Berrangé

On Mon, Jun 09, 2025 at 03:02:06PM -0300, Fabiano Rosas wrote:
> Peter Xu <peterx@redhat.com> writes:
> 
> > On Mon, Jun 09, 2025 at 11:37:02AM -0300, Fabiano Rosas wrote:
> >> Peter Xu <peterx@redhat.com> writes:
> >> 
> >> > On Fri, Jun 06, 2025 at 05:23:18PM -0300, Fabiano Rosas wrote:
> >> >> Peter Xu <peterx@redhat.com> writes:
> >> >> 
> >> >> > On Mon, Jun 02, 2025 at 10:38:08PM -0300, Fabiano Rosas wrote:
> >> >> >> Allow the migrate and migrate_incoming commands to pass the migration
> >> >> >> configuration options all at once, dispensing the use of
> >> >> >> migrate-set-parameters and migrate-set-capabilities.
> >> >> >> 
> >> >> >> The motivation of this is to simplify the interface with the
> >> >> >> management layer and avoid the usage of several command invocations to
> >> >> >> configure a migration. It also avoids stale parameters from a previous
> >> >> >> migration to influence the current migration.
> >> >> >> 
> >> >> >> The options that are changed during the migration can still be set
> >> >> >> with the existing commands.
> >> >> >> 
> >> >> >> The order of precedence is:
> >> >> >> 
> >> >> >> 'config' argument > -global cmdline > defaults (migration_properties)
> >> >> >
> >> >> > Could we still keep the QMP migrate-set-parameters values?
> >> >> >
> >> >> >   'config' argument > QMP setups using migrate-set-parameters >
> >> >> >     -global cmdline > defaults (migration_properties)
> >> >> >
> >> >> 
> >> >> That's the case. I failed to mention it in the commit message. IOW it
> >> >> behaves just like today, but the new 'config' way takes precedence over
> >> >> all.
> >> >
> >> > Referring to below chunk of code:
> >> >
> >> > [...]
> >> >
> >> >> >> +bool migrate_params_override(MigrationState *s, MigrationParameters *new,
> >> >> >> +                             Error **errp)
> >> >> >> +{
> >> >> >> +    ERRP_GUARD();
> >> >> >> +
> >> >> >> +    assert(bql_locked());
> >> >> >> +
> >> >> >> +    /* reset to default parameters */
> >> >> >> +    migrate_params_apply(&s->defaults);
> >> >
> >> > IIUC here it'll reset all global parameters using the initial defaults
> >> > first, then apply the "config" specified in "migrate" QMP command?
> >> >
> >> 
> >> Yes, this is so any previously set parameter via migrate-set-parameter
> >> gets erased. I think what we want (but feel free to disagree) is to have
> >> the migrate-set-parameter _eventually_ only handle parameters that need
> >> to be modifed during migration runtime. Anything else can be done via
> >> passing config to qmp_migrate.
> >> 
> >> For -global, I don't have a preference. Having -global take precedence
> >> over all would require a way to know which options were present in the
> >> command-line and which are just the defaults seet in
> >> migration_properties. I currently don't know how to do that. If it is at
> >> all possible (within reason) we could make the change, no worries.
> >> 
> >> > I think there're actually two separate questions to be asked, to make it
> >> > clearer, they are:
> >> 
> >> Here it got ambiguous when you say "global", I've been using -global to
> >> refer to the cmdline -global migration.foo, but others have used global
> >> to mean s->parameters (which has an extended lifetime). Could you
> >> clarify?
> >
> > I meant the -global, and the global setups via migrate-set-parameters.
> >
> > As replied to Dan in the other email, I changed my mind on question (1); I
> > think it makes sense to have it YES.  I left my pure question on (2) there
> > too.
> >
> > Do we really want to disable migrate-set-parameters setting most of the
> > parameters, and only allow it to be set during migration on a few things
> > like bandwidth or so?
> >
> 
> Well, if we decide we have reasons to introduce the "config" concept,
> then I think we should not present two ways of doing the same
> thing. User calls qmp_migrate with its arguments and that's the
> migration. No other ways of setting parameters.
> 
> Since we do have parameters that are set in "runtime" I though of
> keeping migrate-set-parameters around to minimize the interface
> change. Maybe those should have been separate knobs on their own after
> all... But in any case, we can't reject migrate-set-parameters because
> it might happen way earlier than the actual migration command. So I
> don't think anything changes regarding the API.
> 
> > I just don't really see the major benefit of that yet.  I would think it
> > make more sense if we don't need to change any parameters in migration,
> > then provide that in one shot in QMP migrate "config".  Maybe making more
> > sense if migration is not heavily thread-based but having its aiocontext so
> > we could even move to Jobs.
> >
> > Now after all we'll need to allow setting something like bandwidth even
> > during migration alive, and we have all the things ready allowing to set
> > before migration starts, I'm not 100% sure whether we need to bother even
> > if it does look cleaner, because we'll still break mgmt used to be working
> > for years.. I could be over-cautious on breaking things, but I still want
> > to understand better on the benefits.
> >
> 
> Makes sense. We'd say either use the old way or the new way. If both are
> mixed, then the new way takes precedence. That keeps older apps working
> and allows new code to transition into the new way.

Fair enough.  Yes whenever the new way is chosen it can work in anyway we
define it.

It's just that if the global list of parameters will still be around then
it seems to have no good reason to not build the migration parameters on
top of the global list of parameters.  After all, anything can be
overwritten in the QMP migrate if needed.

> 
> > One step back, on this "allow migrate to specify 'config'" request: I
> > think we can definitely do that as it still provides some kind of
> > atomicity.  But frankly speaking I never see it a "real problem" - do
> > we really have report or use case showing that Libvirt can trigger
> > "migrate" with some global settings touched by other apps at all?
> >
> 
> I don't think other apps is the problem, but libvirt itself maybe
> attempting two migrations in sequence after one of them fails.
> 
> There always the possibility that the user is poking around, which of
> course is not advisable, but if a weird migration bug shows up it's
> difficult to confirm that other app/user hasn't changed the parameters.

That's almost what I think the current patch is useful on providing some
kind of atomicity.

If we want to make debugging easy, we could also consider returning the
finalized migration setup in the response of QMP "migrate" with all
parameters, by defining "returns" for QMP "migrate".

> 
> > To me, it was yet an illutionary problem, I never know the answer of that.
> > If Libvirt is still the owner of QEMU instance via the QMP channel, I
> > actually don't really see why the atomicity would even help, even though we
> > can still provide that as it's pretty easy as something optional; like what
> > this patch does without too much hassle.
> >
> 
> We can provide it, but I'd rather not unless we agree that is the way
> forward. We don't need another way of doing the same as existing
> commands.

OK, it might be me that misunderstood the request initially.

> 
> > Then if to move one step further to remove all global settings, we face
> > breaking debugging scripts, and breaking of any old libvirt and non-libvirt
> > mgmt apps.  Frankly I really don't yet know whether it's a good idea.  I
> > could miss some important reasoning of why we want to do it - it needs to
> > be something not relevant to "making the code cleaner", IMHO..
> 
> I don't see it as breaking the old stuff. Because any old users would
> still be using migrate-set-parameters as usual. So I think your concern
> is about calling migrate the new way and also keeping -global
> working. As I said, personally I don't mind if put some ifs around to
> keep -global working.
> 
> Could we add another parameter that says allow-globals (or w/e) and make
> everyone happy?

That's not needed if it's about making me happy. :) My happiness alone
isn't that important, I can change any of my script, and I'm OK whatever
ABI changes, but as long as the downstream won't be a mess..

If we want to either do nothing or making it a bundle, then we can decide
what's the bundle now.

For example, do we plan to have this, then drop migrate-set-parameters &
capabilities finally (or at least failing non-runtime-modifi-able ones)?

How fast do we want to do this, and how do we manage downstream to not be
affected by having new QEMU's migrate-set-* commands completely gone, would
be the follow up questions..

Maybe I worked much enough on Linux so I pay a lot of attention trying to
think such trade-off, then if I see not much benefit normally I'll try to
not break any ABI.  But if that's everyone's wish (except myself.. even if
it only makes the interface better..) then we can discuss before moving on.

-- 
Peter Xu



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

* Re: [PATCH 19/21] migration: Allow migrate commands to provide the migration config
  2025-06-09 19:05               ` Peter Xu
@ 2025-06-09 19:41                 ` Fabiano Rosas
  2025-06-09 20:35                   ` Peter Xu
  0 siblings, 1 reply; 83+ messages in thread
From: Fabiano Rosas @ 2025-06-09 19:41 UTC (permalink / raw)
  To: Peter Xu; +Cc: qemu-devel, Markus Armbruster, Daniel P . Berrangé

Peter Xu <peterx@redhat.com> writes:

> On Mon, Jun 09, 2025 at 03:02:06PM -0300, Fabiano Rosas wrote:
>> Peter Xu <peterx@redhat.com> writes:
>> 
>> > On Mon, Jun 09, 2025 at 11:37:02AM -0300, Fabiano Rosas wrote:
>> >> Peter Xu <peterx@redhat.com> writes:
>> >> 
>> >> > On Fri, Jun 06, 2025 at 05:23:18PM -0300, Fabiano Rosas wrote:
>> >> >> Peter Xu <peterx@redhat.com> writes:
>> >> >> 
>> >> >> > On Mon, Jun 02, 2025 at 10:38:08PM -0300, Fabiano Rosas wrote:
>> >> >> >> Allow the migrate and migrate_incoming commands to pass the migration
>> >> >> >> configuration options all at once, dispensing the use of
>> >> >> >> migrate-set-parameters and migrate-set-capabilities.
>> >> >> >> 
>> >> >> >> The motivation of this is to simplify the interface with the
>> >> >> >> management layer and avoid the usage of several command invocations to
>> >> >> >> configure a migration. It also avoids stale parameters from a previous
>> >> >> >> migration to influence the current migration.
>> >> >> >> 
>> >> >> >> The options that are changed during the migration can still be set
>> >> >> >> with the existing commands.
>> >> >> >> 
>> >> >> >> The order of precedence is:
>> >> >> >> 
>> >> >> >> 'config' argument > -global cmdline > defaults (migration_properties)
>> >> >> >
>> >> >> > Could we still keep the QMP migrate-set-parameters values?
>> >> >> >
>> >> >> >   'config' argument > QMP setups using migrate-set-parameters >
>> >> >> >     -global cmdline > defaults (migration_properties)
>> >> >> >
>> >> >> 
>> >> >> That's the case. I failed to mention it in the commit message. IOW it
>> >> >> behaves just like today, but the new 'config' way takes precedence over
>> >> >> all.
>> >> >
>> >> > Referring to below chunk of code:
>> >> >
>> >> > [...]
>> >> >
>> >> >> >> +bool migrate_params_override(MigrationState *s, MigrationParameters *new,
>> >> >> >> +                             Error **errp)
>> >> >> >> +{
>> >> >> >> +    ERRP_GUARD();
>> >> >> >> +
>> >> >> >> +    assert(bql_locked());
>> >> >> >> +
>> >> >> >> +    /* reset to default parameters */
>> >> >> >> +    migrate_params_apply(&s->defaults);
>> >> >
>> >> > IIUC here it'll reset all global parameters using the initial defaults
>> >> > first, then apply the "config" specified in "migrate" QMP command?
>> >> >
>> >> 
>> >> Yes, this is so any previously set parameter via migrate-set-parameter
>> >> gets erased. I think what we want (but feel free to disagree) is to have
>> >> the migrate-set-parameter _eventually_ only handle parameters that need
>> >> to be modifed during migration runtime. Anything else can be done via
>> >> passing config to qmp_migrate.
>> >> 
>> >> For -global, I don't have a preference. Having -global take precedence
>> >> over all would require a way to know which options were present in the
>> >> command-line and which are just the defaults seet in
>> >> migration_properties. I currently don't know how to do that. If it is at
>> >> all possible (within reason) we could make the change, no worries.
>> >> 
>> >> > I think there're actually two separate questions to be asked, to make it
>> >> > clearer, they are:
>> >> 
>> >> Here it got ambiguous when you say "global", I've been using -global to
>> >> refer to the cmdline -global migration.foo, but others have used global
>> >> to mean s->parameters (which has an extended lifetime). Could you
>> >> clarify?
>> >
>> > I meant the -global, and the global setups via migrate-set-parameters.
>> >
>> > As replied to Dan in the other email, I changed my mind on question (1); I
>> > think it makes sense to have it YES.  I left my pure question on (2) there
>> > too.
>> >
>> > Do we really want to disable migrate-set-parameters setting most of the
>> > parameters, and only allow it to be set during migration on a few things
>> > like bandwidth or so?
>> >
>> 
>> Well, if we decide we have reasons to introduce the "config" concept,
>> then I think we should not present two ways of doing the same
>> thing. User calls qmp_migrate with its arguments and that's the
>> migration. No other ways of setting parameters.
>> 
>> Since we do have parameters that are set in "runtime" I though of
>> keeping migrate-set-parameters around to minimize the interface
>> change. Maybe those should have been separate knobs on their own after
>> all... But in any case, we can't reject migrate-set-parameters because
>> it might happen way earlier than the actual migration command. So I
>> don't think anything changes regarding the API.
>> 
>> > I just don't really see the major benefit of that yet.  I would think it
>> > make more sense if we don't need to change any parameters in migration,
>> > then provide that in one shot in QMP migrate "config".  Maybe making more
>> > sense if migration is not heavily thread-based but having its aiocontext so
>> > we could even move to Jobs.
>> >
>> > Now after all we'll need to allow setting something like bandwidth even
>> > during migration alive, and we have all the things ready allowing to set
>> > before migration starts, I'm not 100% sure whether we need to bother even
>> > if it does look cleaner, because we'll still break mgmt used to be working
>> > for years.. I could be over-cautious on breaking things, but I still want
>> > to understand better on the benefits.
>> >
>> 
>> Makes sense. We'd say either use the old way or the new way. If both are
>> mixed, then the new way takes precedence. That keeps older apps working
>> and allows new code to transition into the new way.
>
> Fair enough.  Yes whenever the new way is chosen it can work in anyway we
> define it.
>
> It's just that if the global list of parameters will still be around then
> it seems to have no good reason to not build the migration parameters on
> top of the global list of parameters.  After all, anything can be
> overwritten in the QMP migrate if needed.
>

If we had a way to detect that the user has modified some parameters via
the cmdline, then we could merge that with the s->defaults and restore
it before applying config, that would achieve what you want. I'm in
favor, -global should only be used for debugging, I think it's fine if
we let it go through. But anything set by migrate-set-parameters
definitely needs to be reset. I just need a way to differentiate between
"default parameter" vs. "default parameter that got overwritten by
-global". I'll try to figure something out.

>> 
>> > One step back, on this "allow migrate to specify 'config'" request: I
>> > think we can definitely do that as it still provides some kind of
>> > atomicity.  But frankly speaking I never see it a "real problem" - do
>> > we really have report or use case showing that Libvirt can trigger
>> > "migrate" with some global settings touched by other apps at all?
>> >
>> 
>> I don't think other apps is the problem, but libvirt itself maybe
>> attempting two migrations in sequence after one of them fails.
>> 
>> There always the possibility that the user is poking around, which of
>> course is not advisable, but if a weird migration bug shows up it's
>> difficult to confirm that other app/user hasn't changed the parameters.
>
> That's almost what I think the current patch is useful on providing some
> kind of atomicity.
>
> If we want to make debugging easy, we could also consider returning the
> finalized migration setup in the response of QMP "migrate" with all
> parameters, by defining "returns" for QMP "migrate".
>

Isn't query-migrate-parameters that already?

>> 
>> > To me, it was yet an illutionary problem, I never know the answer of that.
>> > If Libvirt is still the owner of QEMU instance via the QMP channel, I
>> > actually don't really see why the atomicity would even help, even though we
>> > can still provide that as it's pretty easy as something optional; like what
>> > this patch does without too much hassle.
>> >
>> 
>> We can provide it, but I'd rather not unless we agree that is the way
>> forward. We don't need another way of doing the same as existing
>> commands.
>
> OK, it might be me that misunderstood the request initially.
>
>> 
>> > Then if to move one step further to remove all global settings, we face
>> > breaking debugging scripts, and breaking of any old libvirt and non-libvirt
>> > mgmt apps.  Frankly I really don't yet know whether it's a good idea.  I
>> > could miss some important reasoning of why we want to do it - it needs to
>> > be something not relevant to "making the code cleaner", IMHO..
>> 
>> I don't see it as breaking the old stuff. Because any old users would
>> still be using migrate-set-parameters as usual. So I think your concern
>> is about calling migrate the new way and also keeping -global
>> working. As I said, personally I don't mind if put some ifs around to
>> keep -global working.
>> 
>> Could we add another parameter that says allow-globals (or w/e) and make
>> everyone happy?
>
> That's not needed if it's about making me happy. :) My happiness alone
> isn't that important, I can change any of my script, and I'm OK whatever
> ABI changes, but as long as the downstream won't be a mess..
>

At this point you've probably done more migrations than any single
person. So of course your use-case is important. I don't think an extra
knob is too much to ask. Could even be -global only.

However, if we're going to keep both requirements working: 1) overwrite
migrate-set-params; 2) do not overwrite -global; as I said we need a way
to detect a parameter changed via -global... and then we don't need a
new knob because that would already tell us.

> If we want to either do nothing or making it a bundle, then we can decide
> what's the bundle now.
>
> For example, do we plan to have this, then drop migrate-set-parameters &
> capabilities finally (or at least failing non-runtime-modifi-able ones)?
>

Good question, I don't think we've decided. Those last few patches could
have kept the RFC tag. I say we:

- Merge params+caps and deprecate migrate-set-capabilities now.

  One is an internal change and the other is a normal command
  deprecation, AFAIK.

- Make sure we agree on how config is going to work and introduce it
  then (possibly this release).

  We also need to make sure this is the right thing for savevm, cpr,
  etc. It would be good to have all of them uniformly prepared to take
  (or not) the config parameter.

- Leave migrate-set-parameters as is.
  (this wasn't my original intention, but this discussion changed my
  mind)

  As I said, we can't predict whether the user will call
  migrate-set-parameters before calling migrate. So I don't think we can
  say:

    "Only use this if you're not using the new 'config' option OR if
     you're using the new 'config' option plus setting runtime parameters"

  It also gives us the ability to say that nothing changes with
  migrate-set-parameter except that it now takes caps as well.

> How fast do we want to do this, and how do we manage downstream to not be
> affected by having new QEMU's migrate-set-* commands completely gone, would
> be the follow up questions..
>

- The migrate-set-parameters change is just an addition of options. We
  do this anytime a new feature is added and I don't think there are
  issues downstream. Are there?

- Dropping migrate-set-capabilities would be covered by the deprecation
  period. We shouldn't have to think about it. But let's say we _are_
  going to think about it: a libvirt from before the removal of the
  command would have trouble with a QEMU from after the
  removal. Converting between the two may not be trivial because caps
  today take a MigrationCapabilityList which is a more complex data
  structure than just key=value as MigrationParameters.

- The 'config' change is supported by keeping migrate-set-parameters
  around, so it wouldn't affect anything. Just don't use the new API.

I think that's it? Am I being too simplistic?

> Maybe I worked much enough on Linux so I pay a lot of attention trying to
> think such trade-off, then if I see not much benefit normally I'll try to
> not break any ABI.  But if that's everyone's wish (except myself.. even if
> it only makes the interface better..) then we can discuss before
> moving on.

I think we can do this without breaking anything. We could bring more
people into the discussion to double-check. Let's just agree between
ourselves on some of these other details.

I don't discard the possibility of simply dropping it. But I see Daniel
(and Markus on the previous version) making compelling points.


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

* Re: [PATCH 19/21] migration: Allow migrate commands to provide the migration config
  2025-06-09 19:41                 ` Fabiano Rosas
@ 2025-06-09 20:35                   ` Peter Xu
  2025-06-10 20:55                     ` Fabiano Rosas
  0 siblings, 1 reply; 83+ messages in thread
From: Peter Xu @ 2025-06-09 20:35 UTC (permalink / raw)
  To: Fabiano Rosas; +Cc: qemu-devel, Markus Armbruster, Daniel P . Berrangé

On Mon, Jun 09, 2025 at 04:41:06PM -0300, Fabiano Rosas wrote:
> Peter Xu <peterx@redhat.com> writes:
> 
> > On Mon, Jun 09, 2025 at 03:02:06PM -0300, Fabiano Rosas wrote:
> >> Peter Xu <peterx@redhat.com> writes:
> >> 
> >> > On Mon, Jun 09, 2025 at 11:37:02AM -0300, Fabiano Rosas wrote:
> >> >> Peter Xu <peterx@redhat.com> writes:
> >> >> 
> >> >> > On Fri, Jun 06, 2025 at 05:23:18PM -0300, Fabiano Rosas wrote:
> >> >> >> Peter Xu <peterx@redhat.com> writes:
> >> >> >> 
> >> >> >> > On Mon, Jun 02, 2025 at 10:38:08PM -0300, Fabiano Rosas wrote:
> >> >> >> >> Allow the migrate and migrate_incoming commands to pass the migration
> >> >> >> >> configuration options all at once, dispensing the use of
> >> >> >> >> migrate-set-parameters and migrate-set-capabilities.
> >> >> >> >> 
> >> >> >> >> The motivation of this is to simplify the interface with the
> >> >> >> >> management layer and avoid the usage of several command invocations to
> >> >> >> >> configure a migration. It also avoids stale parameters from a previous
> >> >> >> >> migration to influence the current migration.
> >> >> >> >> 
> >> >> >> >> The options that are changed during the migration can still be set
> >> >> >> >> with the existing commands.
> >> >> >> >> 
> >> >> >> >> The order of precedence is:
> >> >> >> >> 
> >> >> >> >> 'config' argument > -global cmdline > defaults (migration_properties)
> >> >> >> >
> >> >> >> > Could we still keep the QMP migrate-set-parameters values?
> >> >> >> >
> >> >> >> >   'config' argument > QMP setups using migrate-set-parameters >
> >> >> >> >     -global cmdline > defaults (migration_properties)
> >> >> >> >
> >> >> >> 
> >> >> >> That's the case. I failed to mention it in the commit message. IOW it
> >> >> >> behaves just like today, but the new 'config' way takes precedence over
> >> >> >> all.
> >> >> >
> >> >> > Referring to below chunk of code:
> >> >> >
> >> >> > [...]
> >> >> >
> >> >> >> >> +bool migrate_params_override(MigrationState *s, MigrationParameters *new,
> >> >> >> >> +                             Error **errp)
> >> >> >> >> +{
> >> >> >> >> +    ERRP_GUARD();
> >> >> >> >> +
> >> >> >> >> +    assert(bql_locked());
> >> >> >> >> +
> >> >> >> >> +    /* reset to default parameters */
> >> >> >> >> +    migrate_params_apply(&s->defaults);
> >> >> >
> >> >> > IIUC here it'll reset all global parameters using the initial defaults
> >> >> > first, then apply the "config" specified in "migrate" QMP command?
> >> >> >
> >> >> 
> >> >> Yes, this is so any previously set parameter via migrate-set-parameter
> >> >> gets erased. I think what we want (but feel free to disagree) is to have
> >> >> the migrate-set-parameter _eventually_ only handle parameters that need
> >> >> to be modifed during migration runtime. Anything else can be done via
> >> >> passing config to qmp_migrate.
> >> >> 
> >> >> For -global, I don't have a preference. Having -global take precedence
> >> >> over all would require a way to know which options were present in the
> >> >> command-line and which are just the defaults seet in
> >> >> migration_properties. I currently don't know how to do that. If it is at
> >> >> all possible (within reason) we could make the change, no worries.
> >> >> 
> >> >> > I think there're actually two separate questions to be asked, to make it
> >> >> > clearer, they are:
> >> >> 
> >> >> Here it got ambiguous when you say "global", I've been using -global to
> >> >> refer to the cmdline -global migration.foo, but others have used global
> >> >> to mean s->parameters (which has an extended lifetime). Could you
> >> >> clarify?
> >> >
> >> > I meant the -global, and the global setups via migrate-set-parameters.
> >> >
> >> > As replied to Dan in the other email, I changed my mind on question (1); I
> >> > think it makes sense to have it YES.  I left my pure question on (2) there
> >> > too.
> >> >
> >> > Do we really want to disable migrate-set-parameters setting most of the
> >> > parameters, and only allow it to be set during migration on a few things
> >> > like bandwidth or so?
> >> >
> >> 
> >> Well, if we decide we have reasons to introduce the "config" concept,
> >> then I think we should not present two ways of doing the same
> >> thing. User calls qmp_migrate with its arguments and that's the
> >> migration. No other ways of setting parameters.
> >> 
> >> Since we do have parameters that are set in "runtime" I though of
> >> keeping migrate-set-parameters around to minimize the interface
> >> change. Maybe those should have been separate knobs on their own after
> >> all... But in any case, we can't reject migrate-set-parameters because
> >> it might happen way earlier than the actual migration command. So I
> >> don't think anything changes regarding the API.
> >> 
> >> > I just don't really see the major benefit of that yet.  I would think it
> >> > make more sense if we don't need to change any parameters in migration,
> >> > then provide that in one shot in QMP migrate "config".  Maybe making more
> >> > sense if migration is not heavily thread-based but having its aiocontext so
> >> > we could even move to Jobs.
> >> >
> >> > Now after all we'll need to allow setting something like bandwidth even
> >> > during migration alive, and we have all the things ready allowing to set
> >> > before migration starts, I'm not 100% sure whether we need to bother even
> >> > if it does look cleaner, because we'll still break mgmt used to be working
> >> > for years.. I could be over-cautious on breaking things, but I still want
> >> > to understand better on the benefits.
> >> >
> >> 
> >> Makes sense. We'd say either use the old way or the new way. If both are
> >> mixed, then the new way takes precedence. That keeps older apps working
> >> and allows new code to transition into the new way.
> >
> > Fair enough.  Yes whenever the new way is chosen it can work in anyway we
> > define it.
> >
> > It's just that if the global list of parameters will still be around then
> > it seems to have no good reason to not build the migration parameters on
> > top of the global list of parameters.  After all, anything can be
> > overwritten in the QMP migrate if needed.
> >
> 
> If we had a way to detect that the user has modified some parameters via
> the cmdline, then we could merge that with the s->defaults and restore
> it before applying config, that would achieve what you want. I'm in
> favor, -global should only be used for debugging, I think it's fine if
> we let it go through. But anything set by migrate-set-parameters
> definitely needs to be reset. I just need a way to differentiate between
> "default parameter" vs. "default parameter that got overwritten by
> -global". I'll try to figure something out.

I think I see what you meant.  Ignoring -global is ok.  I agree with you
that should be pure debugging, and feel free to keep it like that if you
can't find anything to persist it - it may not justify your time spent if
it grows too much.

> 
> >> 
> >> > One step back, on this "allow migrate to specify 'config'" request: I
> >> > think we can definitely do that as it still provides some kind of
> >> > atomicity.  But frankly speaking I never see it a "real problem" - do
> >> > we really have report or use case showing that Libvirt can trigger
> >> > "migrate" with some global settings touched by other apps at all?
> >> >
> >> 
> >> I don't think other apps is the problem, but libvirt itself maybe
> >> attempting two migrations in sequence after one of them fails.
> >> 
> >> There always the possibility that the user is poking around, which of
> >> course is not advisable, but if a weird migration bug shows up it's
> >> difficult to confirm that other app/user hasn't changed the parameters.
> >
> > That's almost what I think the current patch is useful on providing some
> > kind of atomicity.
> >
> > If we want to make debugging easy, we could also consider returning the
> > finalized migration setup in the response of QMP "migrate" with all
> > parameters, by defining "returns" for QMP "migrate".
> >
> 
> Isn't query-migrate-parameters that already?

The important part is still "atomicity".  Consider right after Libvirt
sends a "migrate" command someone quickly cancelled it and invoked another
one using another "config".  Yes there will still be events generated to
Libvirt but I think that'll be asynchronous anyway so its arrival might
have been after the other one migrating VM again.

Attach to that to "returns" provides atomicity making sure if Libvirt
invoking a "migrate" command and get the returns, if that succeeded Libvirt
knows the returned setup is 100% the one that is running now.  It might be
cancelled too but the finalized setup will match what Libvirt triggers.

Said that, not that I think any of such would ever happen.. but that idea
does match with atomicity provided by QMP "migrate" with "config".

> 
> >> 
> >> > To me, it was yet an illutionary problem, I never know the answer of that.
> >> > If Libvirt is still the owner of QEMU instance via the QMP channel, I
> >> > actually don't really see why the atomicity would even help, even though we
> >> > can still provide that as it's pretty easy as something optional; like what
> >> > this patch does without too much hassle.
> >> >
> >> 
> >> We can provide it, but I'd rather not unless we agree that is the way
> >> forward. We don't need another way of doing the same as existing
> >> commands.
> >
> > OK, it might be me that misunderstood the request initially.
> >
> >> 
> >> > Then if to move one step further to remove all global settings, we face
> >> > breaking debugging scripts, and breaking of any old libvirt and non-libvirt
> >> > mgmt apps.  Frankly I really don't yet know whether it's a good idea.  I
> >> > could miss some important reasoning of why we want to do it - it needs to
> >> > be something not relevant to "making the code cleaner", IMHO..
> >> 
> >> I don't see it as breaking the old stuff. Because any old users would
> >> still be using migrate-set-parameters as usual. So I think your concern
> >> is about calling migrate the new way and also keeping -global
> >> working. As I said, personally I don't mind if put some ifs around to
> >> keep -global working.
> >> 
> >> Could we add another parameter that says allow-globals (or w/e) and make
> >> everyone happy?
> >
> > That's not needed if it's about making me happy. :) My happiness alone
> > isn't that important, I can change any of my script, and I'm OK whatever
> > ABI changes, but as long as the downstream won't be a mess..
> >
> 
> At this point you've probably done more migrations than any single
> person. So of course your use-case is important. I don't think an extra
> knob is too much to ask. Could even be -global only.
> 
> However, if we're going to keep both requirements working: 1) overwrite
> migrate-set-params; 2) do not overwrite -global; as I said we need a way
> to detect a parameter changed via -global... and then we don't need a
> new knob because that would already tell us.

Let's not bother; I'm totally OK ignoring -global.  When one's testing
manually, one won't be using "config" in "migrate" normally so it's fine.

> 
> > If we want to either do nothing or making it a bundle, then we can decide
> > what's the bundle now.
> >
> > For example, do we plan to have this, then drop migrate-set-parameters &
> > capabilities finally (or at least failing non-runtime-modifi-able ones)?
> >
> 
> Good question, I don't think we've decided. Those last few patches could
> have kept the RFC tag. I say we:
> 
> - Merge params+caps and deprecate migrate-set-capabilities now.
> 
>   One is an internal change and the other is a normal command
>   deprecation, AFAIK.

Agree.  Maybe we could still keep the interface for more than two releases.
Maybe we don't need to rush removing the support, and keep it deprecate for
long enough, until we figure out when it's safe.

> 
> - Make sure we agree on how config is going to work and introduce it
>   then (possibly this release).
> 
>   We also need to make sure this is the right thing for savevm, cpr,
>   etc. It would be good to have all of them uniformly prepared to take
>   (or not) the config parameter.

CPR is still live migration, I hope we can reach consensus.  It uses
exactly the caps/params it needs, but it's migrating the same as live
migration would do, except it migrates some more fds.

It could be more special if it was based on fork(), there're tons of
uncertainties over fork() with a multi-threaded app, but now we're going
scm rights, much better I'd say. Same reason, it is still live migration
when it's using generic unix sockets.

savevm - we don't plan to yet support any migration cap/param on it, right?

I remember the other use case for enabling mapped-ram, but per my memory
there is much better way to go for that use case rather than building it on
top of savevm, so I'd still think savevm doesn't need any extension, and it
should take zero parameters even in the near future.

What it can do is reset all parameters right before start, then "recover"
the parameters right after, taking BQL for the whole process.  Logically if
we know it's a new libvirt we don't even need to bother on the "recover"
part, but we may still want to consider the old libvirts too as long as
there's compat concerns.

> 
> - Leave migrate-set-parameters as is.
>   (this wasn't my original intention, but this discussion changed my
>   mind)
> 
>   As I said, we can't predict whether the user will call
>   migrate-set-parameters before calling migrate. So I don't think we can
>   say:
> 
>     "Only use this if you're not using the new 'config' option OR if
>      you're using the new 'config' option plus setting runtime parameters"
> 
>   It also gives us the ability to say that nothing changes with
>   migrate-set-parameter except that it now takes caps as well.

Yep.  For this one keeping it as-is is simpler.  We can wait for Dan/Markus
and others to chime in when there's other opinions.

> 
> > How fast do we want to do this, and how do we manage downstream to not be
> > affected by having new QEMU's migrate-set-* commands completely gone, would
> > be the follow up questions..
> >
> 
> - The migrate-set-parameters change is just an addition of options. We
>   do this anytime a new feature is added and I don't think there are
>   issues downstream. Are there?

Adding caps into it is all fine; I don't see anything would break.

> 
> - Dropping migrate-set-capabilities would be covered by the deprecation
>   period. We shouldn't have to think about it. But let's say we _are_
>   going to think about it: a libvirt from before the removal of the
>   command would have trouble with a QEMU from after the
>   removal. Converting between the two may not be trivial because caps
>   today take a MigrationCapabilityList which is a more complex data
>   structure than just key=value as MigrationParameters.

Yes, this is discussed above as well.  The worst case is we can keep the
deprecation for as long as how we deprecate machine types.  Logically
that was defined partly as "max major release cycle on guaranteed ABI",
then we may assume whatever to be rebased on the current release in any
downstream would have new libvirt ready.  Again, we can discuss this later,
marking deprecation can be done first.

> 
> - The 'config' change is supported by keeping migrate-set-parameters
>   around, so it wouldn't affect anything. Just don't use the new API.
> 
> I think that's it? Am I being too simplistic?

Nop; so far so good to me.

> 
> > Maybe I worked much enough on Linux so I pay a lot of attention trying to
> > think such trade-off, then if I see not much benefit normally I'll try to
> > not break any ABI.  But if that's everyone's wish (except myself.. even if
> > it only makes the interface better..) then we can discuss before
> > moving on.
> 
> I think we can do this without breaking anything. We could bring more
> people into the discussion to double-check. Let's just agree between
> ourselves on some of these other details.
> 
> I don't discard the possibility of simply dropping it. But I see Daniel
> (and Markus on the previous version) making compelling points.

Yeah I was absent for quite a while, and I may have missed some points.  We
can wait for some more inputs.

-- 
Peter Xu



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

* Re: [PATCH 19/21] migration: Allow migrate commands to provide the migration config
  2025-06-09 20:35                   ` Peter Xu
@ 2025-06-10 20:55                     ` Fabiano Rosas
  2025-06-10 21:27                       ` Peter Xu
  0 siblings, 1 reply; 83+ messages in thread
From: Fabiano Rosas @ 2025-06-10 20:55 UTC (permalink / raw)
  To: Peter Xu; +Cc: qemu-devel, Markus Armbruster, Daniel P . Berrangé

Peter Xu <peterx@redhat.com> writes:

> On Mon, Jun 09, 2025 at 04:41:06PM -0300, Fabiano Rosas wrote:
>> Peter Xu <peterx@redhat.com> writes:
>> 
>> > On Mon, Jun 09, 2025 at 03:02:06PM -0300, Fabiano Rosas wrote:
>> >> Peter Xu <peterx@redhat.com> writes:
>> >> 
>> >> > On Mon, Jun 09, 2025 at 11:37:02AM -0300, Fabiano Rosas wrote:
>> >> >> Peter Xu <peterx@redhat.com> writes:
>> >> >> 
>> >> >> > On Fri, Jun 06, 2025 at 05:23:18PM -0300, Fabiano Rosas wrote:
>> >> >> >> Peter Xu <peterx@redhat.com> writes:
>> >> >> >> 
>> >> >> >> > On Mon, Jun 02, 2025 at 10:38:08PM -0300, Fabiano Rosas wrote:
>> >> >> >> >> Allow the migrate and migrate_incoming commands to pass the migration
>> >> >> >> >> configuration options all at once, dispensing the use of
>> >> >> >> >> migrate-set-parameters and migrate-set-capabilities.
>> >> >> >> >> 
>> >> >> >> >> The motivation of this is to simplify the interface with the
>> >> >> >> >> management layer and avoid the usage of several command invocations to
>> >> >> >> >> configure a migration. It also avoids stale parameters from a previous
>> >> >> >> >> migration to influence the current migration.
>> >> >> >> >> 
>> >> >> >> >> The options that are changed during the migration can still be set
>> >> >> >> >> with the existing commands.
>> >> >> >> >> 
>> >> >> >> >> The order of precedence is:
>> >> >> >> >> 
>> >> >> >> >> 'config' argument > -global cmdline > defaults (migration_properties)
>> >> >> >> >
>> >> >> >> > Could we still keep the QMP migrate-set-parameters values?
>> >> >> >> >
>> >> >> >> >   'config' argument > QMP setups using migrate-set-parameters >
>> >> >> >> >     -global cmdline > defaults (migration_properties)
>> >> >> >> >
>> >> >> >> 
>> >> >> >> That's the case. I failed to mention it in the commit message. IOW it
>> >> >> >> behaves just like today, but the new 'config' way takes precedence over
>> >> >> >> all.
>> >> >> >
>> >> >> > Referring to below chunk of code:
>> >> >> >
>> >> >> > [...]
>> >> >> >
>> >> >> >> >> +bool migrate_params_override(MigrationState *s, MigrationParameters *new,
>> >> >> >> >> +                             Error **errp)
>> >> >> >> >> +{
>> >> >> >> >> +    ERRP_GUARD();
>> >> >> >> >> +
>> >> >> >> >> +    assert(bql_locked());
>> >> >> >> >> +
>> >> >> >> >> +    /* reset to default parameters */
>> >> >> >> >> +    migrate_params_apply(&s->defaults);
>> >> >> >
>> >> >> > IIUC here it'll reset all global parameters using the initial defaults
>> >> >> > first, then apply the "config" specified in "migrate" QMP command?
>> >> >> >
>> >> >> 
>> >> >> Yes, this is so any previously set parameter via migrate-set-parameter
>> >> >> gets erased. I think what we want (but feel free to disagree) is to have
>> >> >> the migrate-set-parameter _eventually_ only handle parameters that need
>> >> >> to be modifed during migration runtime. Anything else can be done via
>> >> >> passing config to qmp_migrate.
>> >> >> 
>> >> >> For -global, I don't have a preference. Having -global take precedence
>> >> >> over all would require a way to know which options were present in the
>> >> >> command-line and which are just the defaults seet in
>> >> >> migration_properties. I currently don't know how to do that. If it is at
>> >> >> all possible (within reason) we could make the change, no worries.
>> >> >> 
>> >> >> > I think there're actually two separate questions to be asked, to make it
>> >> >> > clearer, they are:
>> >> >> 
>> >> >> Here it got ambiguous when you say "global", I've been using -global to
>> >> >> refer to the cmdline -global migration.foo, but others have used global
>> >> >> to mean s->parameters (which has an extended lifetime). Could you
>> >> >> clarify?
>> >> >
>> >> > I meant the -global, and the global setups via migrate-set-parameters.
>> >> >
>> >> > As replied to Dan in the other email, I changed my mind on question (1); I
>> >> > think it makes sense to have it YES.  I left my pure question on (2) there
>> >> > too.
>> >> >
>> >> > Do we really want to disable migrate-set-parameters setting most of the
>> >> > parameters, and only allow it to be set during migration on a few things
>> >> > like bandwidth or so?
>> >> >
>> >> 
>> >> Well, if we decide we have reasons to introduce the "config" concept,
>> >> then I think we should not present two ways of doing the same
>> >> thing. User calls qmp_migrate with its arguments and that's the
>> >> migration. No other ways of setting parameters.
>> >> 
>> >> Since we do have parameters that are set in "runtime" I though of
>> >> keeping migrate-set-parameters around to minimize the interface
>> >> change. Maybe those should have been separate knobs on their own after
>> >> all... But in any case, we can't reject migrate-set-parameters because
>> >> it might happen way earlier than the actual migration command. So I
>> >> don't think anything changes regarding the API.
>> >> 
>> >> > I just don't really see the major benefit of that yet.  I would think it
>> >> > make more sense if we don't need to change any parameters in migration,
>> >> > then provide that in one shot in QMP migrate "config".  Maybe making more
>> >> > sense if migration is not heavily thread-based but having its aiocontext so
>> >> > we could even move to Jobs.
>> >> >
>> >> > Now after all we'll need to allow setting something like bandwidth even
>> >> > during migration alive, and we have all the things ready allowing to set
>> >> > before migration starts, I'm not 100% sure whether we need to bother even
>> >> > if it does look cleaner, because we'll still break mgmt used to be working
>> >> > for years.. I could be over-cautious on breaking things, but I still want
>> >> > to understand better on the benefits.
>> >> >
>> >> 
>> >> Makes sense. We'd say either use the old way or the new way. If both are
>> >> mixed, then the new way takes precedence. That keeps older apps working
>> >> and allows new code to transition into the new way.
>> >
>> > Fair enough.  Yes whenever the new way is chosen it can work in anyway we
>> > define it.
>> >
>> > It's just that if the global list of parameters will still be around then
>> > it seems to have no good reason to not build the migration parameters on
>> > top of the global list of parameters.  After all, anything can be
>> > overwritten in the QMP migrate if needed.
>> >
>> 
>> If we had a way to detect that the user has modified some parameters via
>> the cmdline, then we could merge that with the s->defaults and restore
>> it before applying config, that would achieve what you want. I'm in
>> favor, -global should only be used for debugging, I think it's fine if
>> we let it go through. But anything set by migrate-set-parameters
>> definitely needs to be reset. I just need a way to differentiate between
>> "default parameter" vs. "default parameter that got overwritten by
>> -global". I'll try to figure something out.
>
> I think I see what you meant.  Ignoring -global is ok.  I agree with you
> that should be pure debugging, and feel free to keep it like that if you
> can't find anything to persist it - it may not justify your time spent if
> it grows too much.
>

I think I caused some confusion here. I wrote migrate_params_override()
last thing on a friday and forgot it did the right thing from the
beginning:

    migrate_params_apply(&s->defaults);
    qmp_migrate_set_parameters(new, errp);

This s->defaults is poorly named and is actualy already the merge of
defaults + globals, because qdev does it for us. migrate_params_apply()
will then copy that to s->parameters and qmp_migrate_set_parameters()
will apply the 'new' params from 'config' on top s->parameters. An
example:

Setting multifd-channels (default 2) using various methods and querying
both QMP and HMP:

a) global overrides default:

 $ ./qemu-system-x86_64 -global migration.multifd-channels=4 ...
 => QMP: "multifd-channels": 4, HMP: multifd-channels: 4

b) migrate-set-parameter overrides global:

 { 'execute': 'migrate-set-parameters', 'arguments': { 'multifd-channels': 8 } }
 => QMP: "multifd-channels": 8, HMP: multifd-channels: 8

c) config not touching the parameter, value is reset to global:

 { 'execute': 'migrate', 'arguments': { ..., 'config': { 'multifd': true } } }
 => QMP: "multifd-channels": 4, HMP: multifd-channels: 4

d) config overrides all:

 { 'execute': 'migrate', 'arguments': { ..., 'config': {'multifd-channels': 16 } } }
 => QMP: "multifd-channels": 16, HMP: multifd-channels: 16

Without global:

e) default is set initially

 $ ./qemu-system-x86_64 ...
 => QMP: "multifd-channels": 2, HMP: multifd-channels: 2

f) migrate-set-parameter overrides default:

 { 'execute': 'migrate-set-parameters', 'arguments': { 'multifd-channels': 8 } }
 => QMP: "multifd-channels": 8, HMP: multifd-channels: 8

g) config not touching the parameter, value is reset to default:

 { 'execute': 'migrate', 'arguments': { ..., 'config': { 'multifd': true } } }
 => "multifd-channels": 2, HMP: multifd-channels: 2

h) config overrides all:

 { 'execute': 'migrate', 'arguments': { ..., 'config': {'multifd-channels': 16 } } }
 => QMP: "multifd-channels": 16, HMP: multifd-channels: 16

I'll update the variable names and code comments to be more
precise. Sorry for the noise.


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

* Re: [PATCH 19/21] migration: Allow migrate commands to provide the migration config
  2025-06-10 20:55                     ` Fabiano Rosas
@ 2025-06-10 21:27                       ` Peter Xu
  0 siblings, 0 replies; 83+ messages in thread
From: Peter Xu @ 2025-06-10 21:27 UTC (permalink / raw)
  To: Fabiano Rosas; +Cc: qemu-devel, Markus Armbruster, Daniel P . Berrangé

On Tue, Jun 10, 2025 at 05:55:31PM -0300, Fabiano Rosas wrote:
> I think I caused some confusion here. I wrote migrate_params_override()
> last thing on a friday and forgot it did the right thing from the
> beginning:
> 
>     migrate_params_apply(&s->defaults);
>     qmp_migrate_set_parameters(new, errp);
> 
> This s->defaults is poorly named and is actualy already the merge of
> defaults + globals, because qdev does it for us. migrate_params_apply()

Ha!  I didn't remember this part of details when reading, but then I
followed with that idea it won't apply to &defaults.

> will then copy that to s->parameters and qmp_migrate_set_parameters()
> will apply the 'new' params from 'config' on top s->parameters. An
> example:
> 
> Setting multifd-channels (default 2) using various methods and querying
> both QMP and HMP:
> 
> a) global overrides default:
> 
>  $ ./qemu-system-x86_64 -global migration.multifd-channels=4 ...
>  => QMP: "multifd-channels": 4, HMP: multifd-channels: 4
> 
> b) migrate-set-parameter overrides global:
> 
>  { 'execute': 'migrate-set-parameters', 'arguments': { 'multifd-channels': 8 } }
>  => QMP: "multifd-channels": 8, HMP: multifd-channels: 8
> 
> c) config not touching the parameter, value is reset to global:
> 
>  { 'execute': 'migrate', 'arguments': { ..., 'config': { 'multifd': true } } }
>  => QMP: "multifd-channels": 4, HMP: multifd-channels: 4
> 
> d) config overrides all:
> 
>  { 'execute': 'migrate', 'arguments': { ..., 'config': {'multifd-channels': 16 } } }
>  => QMP: "multifd-channels": 16, HMP: multifd-channels: 16
> 
> Without global:
> 
> e) default is set initially
> 
>  $ ./qemu-system-x86_64 ...
>  => QMP: "multifd-channels": 2, HMP: multifd-channels: 2
> 
> f) migrate-set-parameter overrides default:
> 
>  { 'execute': 'migrate-set-parameters', 'arguments': { 'multifd-channels': 8 } }
>  => QMP: "multifd-channels": 8, HMP: multifd-channels: 8
> 
> g) config not touching the parameter, value is reset to default:
> 
>  { 'execute': 'migrate', 'arguments': { ..., 'config': { 'multifd': true } } }
>  => "multifd-channels": 2, HMP: multifd-channels: 2
> 
> h) config overrides all:
> 
>  { 'execute': 'migrate', 'arguments': { ..., 'config': {'multifd-channels': 16 } } }
>  => QMP: "multifd-channels": 16, HMP: multifd-channels: 16
> 
> I'll update the variable names and code comments to be more
> precise. Sorry for the noise.

Good to know it's even working.  Thanks for digging it.

-- 
Peter Xu



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

* Re: [PATCH 00/21] migration: Unify capabilities and parameters
  2025-06-03  1:37 [PATCH 00/21] migration: Unify capabilities and parameters Fabiano Rosas
                   ` (20 preceding siblings ...)
  2025-06-03  1:38 ` [PATCH 21/21] tests/qtest/migration: Add a test for config passing Fabiano Rosas
@ 2025-06-12  6:41 ` Mario Casquero
  21 siblings, 0 replies; 83+ messages in thread
From: Mario Casquero @ 2025-06-12  6:41 UTC (permalink / raw)
  To: Fabiano Rosas
  Cc: qemu-devel, Peter Xu, Markus Armbruster, Daniel P . Berrangé

This series has been successfully tested. Boot up two VMs, one in
source and the other in destination with -incoming defer. Using the
migrate_set_parameter, enable the postcopy-ram, after that check with
info migrate_parameters this is correct.

(qemu) migrate_set_parameter postcopy-ram on
(qemu) info migrate_parameters
...
events: off
postcopy-ram: on
x-colo: off
...

Do migration plus the postcopy and check with info migrate there's a
real postcopy process while migrating

(qemu) migrate -d tcp:${DST_IP}:8888
(qemu) info migrate
Status: postcopy-active
Time (ms): total=3410, setup=34, down=53
RAM info:
  Throughput (Mbps): 1000.82
  Sizes (KiB): pagesize=4, total=16798280,
    transferred=357703, remain=6293884,
    precopy=128792, multifd=0, postcopy=228566
  Pages: normal=88199, zero=440747, rate_per_sec=34875
  Others: dirty_syncs=2, postcopy_req=644

(qemu) info migrate -a
Status: postcopy-active
Sockets: [
tcp::::8888
]
Globals:
  store-global-state: on
  only-migratable: off
  send-configuration: on
  send-section-footer: on
  send-switchover-start: on
  clear-bitmap-shift: 18

Finally migration ends seamlessly.
(qemu) info migrate
Status: completed

Tested-by: Mario Casquero <mcasquer@redhat.com>

On Tue, Jun 3, 2025 at 3:39 AM Fabiano Rosas <farosas@suse.de> wrote:
>
> Hi,
>
> Thanks for the reviews in the last round. Your feedback has allowed me
> to reduce the complexity of the series and the code considerably.
>
> Changes in this v2:
>
> - Followed the suggestion to unify the TLS strings as StrOrNull. This
>   allows the complete removal of the MigrateSetParameters
>   type. Therefore, no need for playing tricks with base types.
>
> - I figured out that block_bitmap_mapping was set as optional in
>   qmp_query_migrate_parameters in error. Fixing that allows to
>   simplify that function somewhat.
>
> - The suggestion of not checking the has_* fields when setting the
>   parameters also led to great simplification. Now there's no need to
>   open-code the setting of every single parameter.
>
> - Deprecated the capabilities commands.
>
> - Removed some redundant documentation from migration.json. Now
>   there's only 1 (one) place where migration parameters need to be
>   documented.
>
> The series:
> - passes CI: https://gitlab.com/farosas/qemu/-/pipelines/1849885920
> - passes the migration tests in the ASAN build.
> - passes the migration compat tests against each of the 3 last QEMU versions.
> - passes the iotest 300 (related to block_bitmap_mapping).
>
> v1:
> https://lore.kernel.org/r/20250411191443.22565-1-farosas@suse.de
>
> Fabiano Rosas (21):
>   migration: Normalize tls arguments
>   migration: Remove MigrateSetParameters
>   qapi/migration: Don't document MigrationParameter
>   migration: Run a post update routine after setting parameters
>   migration: Add a flag to track block-bitmap-mapping input
>   migration: Remove checks for s->parameters has_* fields
>   migration: Set block_bitmap_mapping unconditionally in
>     query-migrate-parameters
>   migration: Do away with usage of QERR_INVALID_PARAMETER_VALUE
>   migration: Extract code to mark all parameters as present
>   migration: Use QAPI_CLONE_MEMBERS in query_migrate_parameters
>   migration: Use QAPI_CLONE_MEMBERS in migrate_params_test_apply
>   migration: Use QAPI_CLONE_MEMBERS in migrate_params_apply
>   migration: Use visitors in migrate_params_test_apply
>   migration: Cleanup hmp_info_migrate_parameters
>   migration: Add capabilities into MigrationParameters
>   qapi/migration: Mark that query/set-migrate-parameters support
>     capabilities
>   migration: Remove s->capabilities
>   qapi/migration: Deprecate capabilities commands
>   migration: Allow migrate commands to provide the migration config
>   libqtest: Add a function to check whether a QMP command supports a
>     feature
>   tests/qtest/migration: Add a test for config passing
>
>  docs/about/deprecated.rst          |   12 +
>  migration/migration-hmp-cmds.c     |  484 ++++++++----
>  migration/migration.c              |   50 +-
>  migration/migration.h              |    9 +-
>  migration/options.c                | 1090 +++++++++++++---------------
>  migration/options.h                |   29 +-
>  migration/page_cache.c             |    6 +-
>  migration/ram.c                    |    5 +-
>  migration/savevm.c                 |    8 +-
>  migration/tls.c                    |    2 +-
>  qapi/migration.json                |  573 ++++++---------
>  qapi/pragma.json                   |    3 +-
>  system/vl.c                        |    3 +-
>  tests/qtest/libqtest.c             |   42 ++
>  tests/qtest/libqtest.h             |   12 +
>  tests/qtest/migration/framework.h  |    2 +
>  tests/qtest/migration/misc-tests.c |   39 +
>  17 files changed, 1243 insertions(+), 1126 deletions(-)
>
> --
> 2.35.3
>
>



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

* Re: [PATCH 10/21] migration: Use QAPI_CLONE_MEMBERS in query_migrate_parameters
  2025-06-06 15:53     ` Fabiano Rosas
@ 2025-06-12 20:58       ` Fabiano Rosas
  2025-06-12 21:27         ` Peter Xu
  0 siblings, 1 reply; 83+ messages in thread
From: Fabiano Rosas @ 2025-06-12 20:58 UTC (permalink / raw)
  To: Peter Xu; +Cc: qemu-devel, Markus Armbruster, Daniel P . Berrangé

Fabiano Rosas <farosas@suse.de> writes:

> Peter Xu <peterx@redhat.com> writes:
>
>> On Mon, Jun 02, 2025 at 10:37:59PM -0300, Fabiano Rosas wrote:
>>> QAPI_CLONE_MEMBERS is a better option than copying parameters one by
>>> one because it operates on the entire struct and follows pointers. It
>>> also avoids the need to alter this function every time a new parameter
>>> is added.
>>> 
>>> Note, since this is a deep clone, now we must free the TLS strings
>>> before assignment.
>>> 
>>> Signed-off-by: Fabiano Rosas <farosas@suse.de>
>>> ---
>>>  migration/options.c | 31 ++++---------------------------
>>>  1 file changed, 4 insertions(+), 27 deletions(-)
>>> 
>>> diff --git a/migration/options.c b/migration/options.c
>>> index dd62e726cb..0a2a3050ec 100644
>>> --- a/migration/options.c
>>> +++ b/migration/options.c
>>> @@ -918,7 +918,9 @@ static void tls_option_set_str(StrOrNull **dstp, StrOrNull *src)
>>>  {
>>>      StrOrNull *dst = *dstp;
>>>  
>>> -    assert(!dst);
>>> +    if (dst) {
>>> +        qapi_free_StrOrNull(dst);
>>> +    }
>>>  
>>>      dst = *dstp = g_new0(StrOrNull, 1);
>>>      dst->type = QTYPE_QSTRING;
>>> @@ -975,42 +977,17 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp)
>>>      MigrationParameters *params;
>>>      MigrationState *s = migrate_get_current();
>>>  
>>> -    /* TODO use QAPI_CLONE() instead of duplicating it inline */
>>>      params = g_malloc0(sizeof(*params));
>>>  
>>> -    params->throttle_trigger_threshold = s->parameters.throttle_trigger_threshold;
>>> -    params->cpu_throttle_initial = s->parameters.cpu_throttle_initial;
>>> -    params->cpu_throttle_increment = s->parameters.cpu_throttle_increment;
>>> -    params->cpu_throttle_tailslow = s->parameters.cpu_throttle_tailslow;
>>> +    QAPI_CLONE_MEMBERS(MigrationParameters, params, &s->parameters);
>>>  
>>>      tls_option_set_str(&params->tls_creds, s->parameters.tls_creds);
>>>      tls_option_set_str(&params->tls_hostname, s->parameters.tls_hostname);
>>>      tls_option_set_str(&params->tls_authz, s->parameters.tls_authz);
>>>  
>>> -    params->max_bandwidth = s->parameters.max_bandwidth;
>>> -    params->avail_switchover_bandwidth = s->parameters.avail_switchover_bandwidth;
>>> -    params->downtime_limit = s->parameters.downtime_limit;
>>> -    params->x_checkpoint_delay = s->parameters.x_checkpoint_delay;
>>> -    params->multifd_channels = s->parameters.multifd_channels;
>>> -    params->multifd_compression = s->parameters.multifd_compression;
>>> -    params->multifd_zlib_level = s->parameters.multifd_zlib_level;
>>> -    params->multifd_qatzip_level = s->parameters.multifd_qatzip_level;
>>> -    params->multifd_zstd_level = s->parameters.multifd_zstd_level;
>>> -    params->xbzrle_cache_size = s->parameters.xbzrle_cache_size;
>>> -    params->max_postcopy_bandwidth = s->parameters.max_postcopy_bandwidth;
>>> -    params->max_cpu_throttle = s->parameters.max_cpu_throttle;
>>> -    params->announce_initial = s->parameters.announce_initial;
>>> -    params->announce_max = s->parameters.announce_max;
>>> -    params->announce_rounds = s->parameters.announce_rounds;
>>> -    params->announce_step = s->parameters.announce_step;
>>>      params->block_bitmap_mapping =
>>>          QAPI_CLONE(BitmapMigrationNodeAliasList,
>>>                     s->parameters.block_bitmap_mapping);
>>
>> Wouldn't the QAPI_CLONE_MEMBERS() have deep cloned this too?
>>
>
> Hmm, I think it should. But it definitely broke something without this
> line. I'll double check.
>

Thanks for the question, this was indeed wrong. QAPI_CLONE_MEMBERS
depend on the has_* fields on src, otherwise it's just a glorified
assignment (*dst = src). The reason I got this wrong is that I was using
the TLS strings to test and they have a different handling in QAPI:

visit_type_MigrationParameters_members():

    bool has_tls_creds = !!obj->tls_creds;

So the code was working for them, but not for block_bitmap_mapping, for
which the QAPI has:

if (visit_optional(v, "block-bitmap-mapping", &obj->has_block_bitmap_mapping)) {
                                                    ^
    if (!visit_type_BitmapMigrationNodeAliasList(v, "block-bitmap-mapping",
        &obj->block_bitmap_mapping, errp)) {
        return false;
    }
}

IOW, the QAPI_CLONE routines depend on the has_ fields (in retrospect:
obviously).

That assert you didn't like will have to go then and s->parameters will
have to have all has_* fields permanently set. Not a huge deal, but it
undermines my argument of keeping it free from QAPI details.

>>> -    params->x_vcpu_dirty_limit_period = s->parameters.x_vcpu_dirty_limit_period;
>>> -    params->vcpu_dirty_limit = s->parameters.vcpu_dirty_limit;
>>> -    params->mode = s->parameters.mode;
>>> -    params->zero_page_detection = s->parameters.zero_page_detection;
>>> -    params->direct_io = s->parameters.direct_io;
>>>  
>>>      /*
>>>       * query-migrate-parameters expects all members of
>>> -- 
>>> 2.35.3
>>> 


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

* Re: [PATCH 10/21] migration: Use QAPI_CLONE_MEMBERS in query_migrate_parameters
  2025-06-12 20:58       ` Fabiano Rosas
@ 2025-06-12 21:27         ` Peter Xu
  2025-06-13 12:30           ` Fabiano Rosas
  0 siblings, 1 reply; 83+ messages in thread
From: Peter Xu @ 2025-06-12 21:27 UTC (permalink / raw)
  To: Fabiano Rosas; +Cc: qemu-devel, Markus Armbruster, Daniel P . Berrangé

On Thu, Jun 12, 2025 at 05:58:14PM -0300, Fabiano Rosas wrote:
> Fabiano Rosas <farosas@suse.de> writes:
> 
> > Peter Xu <peterx@redhat.com> writes:
> >
> >> On Mon, Jun 02, 2025 at 10:37:59PM -0300, Fabiano Rosas wrote:
> >>> QAPI_CLONE_MEMBERS is a better option than copying parameters one by
> >>> one because it operates on the entire struct and follows pointers. It
> >>> also avoids the need to alter this function every time a new parameter
> >>> is added.
> >>> 
> >>> Note, since this is a deep clone, now we must free the TLS strings
> >>> before assignment.
> >>> 
> >>> Signed-off-by: Fabiano Rosas <farosas@suse.de>
> >>> ---
> >>>  migration/options.c | 31 ++++---------------------------
> >>>  1 file changed, 4 insertions(+), 27 deletions(-)
> >>> 
> >>> diff --git a/migration/options.c b/migration/options.c
> >>> index dd62e726cb..0a2a3050ec 100644
> >>> --- a/migration/options.c
> >>> +++ b/migration/options.c
> >>> @@ -918,7 +918,9 @@ static void tls_option_set_str(StrOrNull **dstp, StrOrNull *src)
> >>>  {
> >>>      StrOrNull *dst = *dstp;
> >>>  
> >>> -    assert(!dst);
> >>> +    if (dst) {
> >>> +        qapi_free_StrOrNull(dst);
> >>> +    }
> >>>  
> >>>      dst = *dstp = g_new0(StrOrNull, 1);
> >>>      dst->type = QTYPE_QSTRING;
> >>> @@ -975,42 +977,17 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp)
> >>>      MigrationParameters *params;
> >>>      MigrationState *s = migrate_get_current();
> >>>  
> >>> -    /* TODO use QAPI_CLONE() instead of duplicating it inline */
> >>>      params = g_malloc0(sizeof(*params));
> >>>  
> >>> -    params->throttle_trigger_threshold = s->parameters.throttle_trigger_threshold;
> >>> -    params->cpu_throttle_initial = s->parameters.cpu_throttle_initial;
> >>> -    params->cpu_throttle_increment = s->parameters.cpu_throttle_increment;
> >>> -    params->cpu_throttle_tailslow = s->parameters.cpu_throttle_tailslow;
> >>> +    QAPI_CLONE_MEMBERS(MigrationParameters, params, &s->parameters);
> >>>  
> >>>      tls_option_set_str(&params->tls_creds, s->parameters.tls_creds);
> >>>      tls_option_set_str(&params->tls_hostname, s->parameters.tls_hostname);
> >>>      tls_option_set_str(&params->tls_authz, s->parameters.tls_authz);

[1]

> >>>  
> >>> -    params->max_bandwidth = s->parameters.max_bandwidth;
> >>> -    params->avail_switchover_bandwidth = s->parameters.avail_switchover_bandwidth;
> >>> -    params->downtime_limit = s->parameters.downtime_limit;
> >>> -    params->x_checkpoint_delay = s->parameters.x_checkpoint_delay;
> >>> -    params->multifd_channels = s->parameters.multifd_channels;
> >>> -    params->multifd_compression = s->parameters.multifd_compression;
> >>> -    params->multifd_zlib_level = s->parameters.multifd_zlib_level;
> >>> -    params->multifd_qatzip_level = s->parameters.multifd_qatzip_level;
> >>> -    params->multifd_zstd_level = s->parameters.multifd_zstd_level;
> >>> -    params->xbzrle_cache_size = s->parameters.xbzrle_cache_size;
> >>> -    params->max_postcopy_bandwidth = s->parameters.max_postcopy_bandwidth;
> >>> -    params->max_cpu_throttle = s->parameters.max_cpu_throttle;
> >>> -    params->announce_initial = s->parameters.announce_initial;
> >>> -    params->announce_max = s->parameters.announce_max;
> >>> -    params->announce_rounds = s->parameters.announce_rounds;
> >>> -    params->announce_step = s->parameters.announce_step;
> >>>      params->block_bitmap_mapping =
> >>>          QAPI_CLONE(BitmapMigrationNodeAliasList,
> >>>                     s->parameters.block_bitmap_mapping);
> >>
> >> Wouldn't the QAPI_CLONE_MEMBERS() have deep cloned this too?
> >>
> >
> > Hmm, I think it should. But it definitely broke something without this
> > line. I'll double check.
> >
> 
> Thanks for the question, this was indeed wrong. QAPI_CLONE_MEMBERS
> depend on the has_* fields on src, otherwise it's just a glorified
> assignment (*dst = src). The reason I got this wrong is that I was using
> the TLS strings to test and they have a different handling in QAPI:
> 
> visit_type_MigrationParameters_members():
> 
>     bool has_tls_creds = !!obj->tls_creds;

[2]

> 
> So the code was working for them, but not for block_bitmap_mapping, for
> which the QAPI has:
> 
> if (visit_optional(v, "block-bitmap-mapping", &obj->has_block_bitmap_mapping)) {
>                                                     ^
>     if (!visit_type_BitmapMigrationNodeAliasList(v, "block-bitmap-mapping",
>         &obj->block_bitmap_mapping, errp)) {
>         return false;
>     }
> }
> 
> IOW, the QAPI_CLONE routines depend on the has_ fields (in retrospect:
> obviously).
> 
> That assert you didn't like will have to go then and s->parameters will
> have to have all has_* fields permanently set. Not a huge deal, but it
> undermines my argument of keeping it free from QAPI details.

Oops, indeed.  Now you have that function to set all has_*, hopefully this
is trivial now to still do so.

Since you mentioned tls_* won't have has_*, but they will get properly
cloned IIUC as you mentioned above [2].  Does it mean we can also drop the
three lines at [1] too?

In general, I am curious why we can't already use QAPI_CLONE() like:

  params = QAPI_CLONE(&s->parameters);

And if my wish came true once more on having it a pointer (meanwhile if it
even happened before this patch):

  params = QAPI_CLONE(s->parameters);

I thought with that, any of "g_malloc0(), copying of tls_*, copying of
block_bitmap things" are all not needed?

-- 
Peter Xu



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

* Re: [PATCH 10/21] migration: Use QAPI_CLONE_MEMBERS in query_migrate_parameters
  2025-06-12 21:27         ` Peter Xu
@ 2025-06-13 12:30           ` Fabiano Rosas
  0 siblings, 0 replies; 83+ messages in thread
From: Fabiano Rosas @ 2025-06-13 12:30 UTC (permalink / raw)
  To: Peter Xu; +Cc: qemu-devel, Markus Armbruster, Daniel P . Berrangé

Peter Xu <peterx@redhat.com> writes:

> On Thu, Jun 12, 2025 at 05:58:14PM -0300, Fabiano Rosas wrote:
>> Fabiano Rosas <farosas@suse.de> writes:
>> 
>> > Peter Xu <peterx@redhat.com> writes:
>> >
>> >> On Mon, Jun 02, 2025 at 10:37:59PM -0300, Fabiano Rosas wrote:
>> >>> QAPI_CLONE_MEMBERS is a better option than copying parameters one by
>> >>> one because it operates on the entire struct and follows pointers. It
>> >>> also avoids the need to alter this function every time a new parameter
>> >>> is added.
>> >>> 
>> >>> Note, since this is a deep clone, now we must free the TLS strings
>> >>> before assignment.
>> >>> 
>> >>> Signed-off-by: Fabiano Rosas <farosas@suse.de>
>> >>> ---
>> >>>  migration/options.c | 31 ++++---------------------------
>> >>>  1 file changed, 4 insertions(+), 27 deletions(-)
>> >>> 
>> >>> diff --git a/migration/options.c b/migration/options.c
>> >>> index dd62e726cb..0a2a3050ec 100644
>> >>> --- a/migration/options.c
>> >>> +++ b/migration/options.c
>> >>> @@ -918,7 +918,9 @@ static void tls_option_set_str(StrOrNull **dstp, StrOrNull *src)
>> >>>  {
>> >>>      StrOrNull *dst = *dstp;
>> >>>  
>> >>> -    assert(!dst);
>> >>> +    if (dst) {
>> >>> +        qapi_free_StrOrNull(dst);
>> >>> +    }
>> >>>  
>> >>>      dst = *dstp = g_new0(StrOrNull, 1);
>> >>>      dst->type = QTYPE_QSTRING;
>> >>> @@ -975,42 +977,17 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp)
>> >>>      MigrationParameters *params;
>> >>>      MigrationState *s = migrate_get_current();
>> >>>  
>> >>> -    /* TODO use QAPI_CLONE() instead of duplicating it inline */
>> >>>      params = g_malloc0(sizeof(*params));
>> >>>  
>> >>> -    params->throttle_trigger_threshold = s->parameters.throttle_trigger_threshold;
>> >>> -    params->cpu_throttle_initial = s->parameters.cpu_throttle_initial;
>> >>> -    params->cpu_throttle_increment = s->parameters.cpu_throttle_increment;
>> >>> -    params->cpu_throttle_tailslow = s->parameters.cpu_throttle_tailslow;
>> >>> +    QAPI_CLONE_MEMBERS(MigrationParameters, params, &s->parameters);
>> >>>  
>> >>>      tls_option_set_str(&params->tls_creds, s->parameters.tls_creds);
>> >>>      tls_option_set_str(&params->tls_hostname, s->parameters.tls_hostname);
>> >>>      tls_option_set_str(&params->tls_authz, s->parameters.tls_authz);
>
> [1]
>
>> >>>  
>> >>> -    params->max_bandwidth = s->parameters.max_bandwidth;
>> >>> -    params->avail_switchover_bandwidth = s->parameters.avail_switchover_bandwidth;
>> >>> -    params->downtime_limit = s->parameters.downtime_limit;
>> >>> -    params->x_checkpoint_delay = s->parameters.x_checkpoint_delay;
>> >>> -    params->multifd_channels = s->parameters.multifd_channels;
>> >>> -    params->multifd_compression = s->parameters.multifd_compression;
>> >>> -    params->multifd_zlib_level = s->parameters.multifd_zlib_level;
>> >>> -    params->multifd_qatzip_level = s->parameters.multifd_qatzip_level;
>> >>> -    params->multifd_zstd_level = s->parameters.multifd_zstd_level;
>> >>> -    params->xbzrle_cache_size = s->parameters.xbzrle_cache_size;
>> >>> -    params->max_postcopy_bandwidth = s->parameters.max_postcopy_bandwidth;
>> >>> -    params->max_cpu_throttle = s->parameters.max_cpu_throttle;
>> >>> -    params->announce_initial = s->parameters.announce_initial;
>> >>> -    params->announce_max = s->parameters.announce_max;
>> >>> -    params->announce_rounds = s->parameters.announce_rounds;
>> >>> -    params->announce_step = s->parameters.announce_step;
>> >>>      params->block_bitmap_mapping =
>> >>>          QAPI_CLONE(BitmapMigrationNodeAliasList,
>> >>>                     s->parameters.block_bitmap_mapping);
>> >>
>> >> Wouldn't the QAPI_CLONE_MEMBERS() have deep cloned this too?
>> >>
>> >
>> > Hmm, I think it should. But it definitely broke something without this
>> > line. I'll double check.
>> >
>> 
>> Thanks for the question, this was indeed wrong. QAPI_CLONE_MEMBERS
>> depend on the has_* fields on src, otherwise it's just a glorified
>> assignment (*dst = src). The reason I got this wrong is that I was using
>> the TLS strings to test and they have a different handling in QAPI:
>> 
>> visit_type_MigrationParameters_members():
>> 
>>     bool has_tls_creds = !!obj->tls_creds;
>
> [2]
>
>> 
>> So the code was working for them, but not for block_bitmap_mapping, for
>> which the QAPI has:
>> 
>> if (visit_optional(v, "block-bitmap-mapping", &obj->has_block_bitmap_mapping)) {
>>                                                     ^
>>     if (!visit_type_BitmapMigrationNodeAliasList(v, "block-bitmap-mapping",
>>         &obj->block_bitmap_mapping, errp)) {
>>         return false;
>>     }
>> }
>> 
>> IOW, the QAPI_CLONE routines depend on the has_ fields (in retrospect:
>> obviously).
>> 
>> That assert you didn't like will have to go then and s->parameters will
>> have to have all has_* fields permanently set. Not a huge deal, but it
>> undermines my argument of keeping it free from QAPI details.
>
> Oops, indeed.  Now you have that function to set all has_*, hopefully this
> is trivial now to still do so.
>

Yes.

> Since you mentioned tls_* won't have has_*, but they will get properly
> cloned IIUC as you mentioned above [2].  Does it mean we can also drop the
> three lines at [1] too?
>

I'm thinking yes as well, still woking on it.

> In general, I am curious why we can't already use QAPI_CLONE() like:
>
>   params = QAPI_CLONE(&s->parameters);
>
> And if my wish came true once more on having it a pointer (meanwhile if it
> even happened before this patch):
>
>   params = QAPI_CLONE(s->parameters);
>
> I thought with that, any of "g_malloc0(), copying of tls_*, copying of
> block_bitmap things" are all not needed?

Same here.


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

* Re: [PATCH 01/21] migration: Normalize tls arguments
  2025-06-03  1:37 ` [PATCH 01/21] migration: Normalize tls arguments Fabiano Rosas
  2025-06-05 20:51   ` Peter Xu
@ 2025-06-25  9:41   ` Markus Armbruster
  2025-06-25 17:17     ` Fabiano Rosas
  1 sibling, 1 reply; 83+ messages in thread
From: Markus Armbruster @ 2025-06-25  9:41 UTC (permalink / raw)
  To: Fabiano Rosas; +Cc: qemu-devel, Peter Xu, Daniel P . Berrangé

Fabiano Rosas <farosas@suse.de> writes:

> The migration parameters tls_creds, tls_authz and tls_hostname
> currently have a non-uniform handling. When used as arguments to
> migrate-set-parameters, their type is StrOrNull and when used as
> return value from query-migrate-parameters, their type is a plain
> string.
>
> Not only having to convert between the types is cumbersome, but it
> also creates the issue of requiring two different QAPI types to be
> used, one for each command. MigrateSetParameters is used for
> migrate-set-parameters with the TLS arguments as StrOrNull while
> MigrationParameters is used for query-migrate-parameters with the TLS
> arguments as str.
>
> Since StrOrNull could be considered a superset of str, change the type
> of the TLS arguments in MigrationParameters to StrOrNull and add a
> helper to ensure they're never actually used as QTYPE_QNULL.

The type of @tls_creds, @tls-hostname, @tls-authz changes from str to
StrOrNull in introspection query-migrate-parameters.  Loss of precision.
Introspection is already imprecise: it shows the members optional even
though they aren't.  We accept the loss of precision to enable
de-duplication.

This should be worked into the commit message.

> This will allow the type duplication to be removed in the next
> patches.
>
> Signed-off-by: Fabiano Rosas <farosas@suse.de>
> ---
>  migration/migration-hmp-cmds.c |   8 +-
>  migration/migration.c          |   2 +
>  migration/options.c            | 149 ++++++++++++++++++++-------------
>  migration/options.h            |   1 +
>  migration/tls.c                |   2 +-
>  qapi/migration.json            |   6 +-
>  6 files changed, 99 insertions(+), 69 deletions(-)
>
> diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c
> index e8a563c7d8..bc8179c582 100644
> --- a/migration/migration-hmp-cmds.c
> +++ b/migration/migration-hmp-cmds.c
> @@ -276,14 +276,12 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict)
>          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);
> -        assert(params->tls_hostname);
> +                       params->tls_creds ? params->tls_creds->u.s : "");
>          monitor_printf(mon, "%s: '%s'\n",
>              MigrationParameter_str(MIGRATION_PARAMETER_TLS_HOSTNAME),
> -            params->tls_hostname);
> +                       params->tls_hostname ? params->tls_hostname->u.s : "");
>          assert(params->has_max_bandwidth);
>          monitor_printf(mon, "%s: %" PRIu64 " bytes/second\n",
>              MigrationParameter_str(MIGRATION_PARAMETER_MAX_BANDWIDTH),
> @@ -319,7 +317,7 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict)
>              params->max_postcopy_bandwidth);
>          monitor_printf(mon, "%s: '%s'\n",
>              MigrationParameter_str(MIGRATION_PARAMETER_TLS_AUTHZ),
> -            params->tls_authz);
> +                       params->tls_authz ? params->tls_authz->u.s : "");
>  
>          if (params->has_block_bitmap_mapping) {
>              const BitmapMigrationNodeAliasList *bmnal;

Before, the code assumes ->tls_creds, ->tls_hostname, and ->tls_authz
are non-null.  It assert its assumption for the first two.

Afterwards, it maps null to "".  Why is that necessary?  Hmm, see below.

> diff --git a/migration/migration.c b/migration/migration.c
> index 4697732bef..f65cb81b6d 100644
> --- a/migration/migration.c
> +++ b/migration/migration.c
> @@ -4053,6 +4053,8 @@ static void migration_instance_finalize(Object *obj)
>  {
>      MigrationState *ms = MIGRATION_OBJ(obj);
>  
> +    migrate_tls_opts_free(&ms->parameters);

Is this a bug fix?

As far as I can tell, the object gets destroyed only on QEMU shutdown.
Freeing resources then is unnecessary, except it may help leak detection
tools.

> +
>      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 162c72cda4..45a95dc6da 100644
> --- a/migration/options.c
> +++ b/migration/options.c
> @@ -162,9 +162,11 @@ const Property migration_properties[] = {
>      DEFINE_PROP_SIZE("announce-step", MigrationState,
>                        parameters.announce_step,
>                        DEFAULT_MIGRATE_ANNOUNCE_STEP),
> -    DEFINE_PROP_STRING("tls-creds", MigrationState, parameters.tls_creds),
> -    DEFINE_PROP_STRING("tls-hostname", MigrationState, parameters.tls_hostname),
> -    DEFINE_PROP_STRING("tls-authz", MigrationState, parameters.tls_authz),
> +    /*
> +     * tls-creds, tls-hostname and tls-authz are of type StrOrNull,
> +     * which can't be easily handled (if at all) by qdev. So these
> +     * will not be exposed as global migration options (-global).
> +     */

This is a compatibility break.

The orthodox way to break it is deprecate, let the grace period expire,
break.  Record in docs/about/deprecated.rst at the beginning, move the
record to docs/about/removed-features.rst at the end.

An argument could be made that the interface in question is
accidental[*], not actually used by anything, and therefore breaking it
without a grace period is fine.  But even then we should record the
breakage in docs/about/removed-features.rst.

Aside: the interface in question is a hack (making the migration object
a device) piled onto a hack (the way compat properties work, and how
they spill into -global).  Past sins catching up with us...

>      DEFINE_PROP_UINT64("x-vcpu-dirty-limit-period", MigrationState,
>                         parameters.x_vcpu_dirty_limit_period,
>                         DEFAULT_MIGRATE_VCPU_DIRTY_LIMIT_PERIOD),
> @@ -379,13 +381,6 @@ bool migrate_rdma(void)
>      return s->rdma_migration;
>  }
>  
> -bool migrate_tls(void)
> -{
> -    MigrationState *s = migrate_get_current();
> -
> -    return s->parameters.tls_creds && *s->parameters.tls_creds;
> -}
> -
>  typedef enum WriteTrackingSupport {
>      WT_SUPPORT_UNKNOWN = 0,
>      WT_SUPPORT_ABSENT,
> @@ -834,21 +829,44 @@ const char *migrate_tls_authz(void)
>  {
>      MigrationState *s = migrate_get_current();
>  
> -    return s->parameters.tls_authz;
> +    if (s->parameters.tls_authz &&
> +        s->parameters.tls_authz->type == QTYPE_QSTRING &&
> +        *s->parameters.tls_authz->u.s) {
> +        return s->parameters.tls_authz->u.s;
> +    }
> +
> +    return NULL;
>  }
>  
>  const char *migrate_tls_creds(void)
>  {
>      MigrationState *s = migrate_get_current();
>  
> -    return s->parameters.tls_creds;
> +    if (s->parameters.tls_creds &&
> +        s->parameters.tls_creds->type == QTYPE_QSTRING &&
> +        *s->parameters.tls_creds->u.s) {
> +        return s->parameters.tls_creds->u.s;
> +    }
> +
> +    return NULL;
>  }
>  
>  const char *migrate_tls_hostname(void)
>  {
>      MigrationState *s = migrate_get_current();
>  
> -    return s->parameters.tls_hostname;
> +    if (s->parameters.tls_hostname &&
> +        s->parameters.tls_hostname->type == QTYPE_QSTRING &&
> +        *s->parameters.tls_hostname->u.s) {
> +        return s->parameters.tls_hostname->u.s;
> +    }
> +
> +    return NULL;
> +}

Again, the code changes to cope with null.  Why is that necessary?
Again, see below.

> +
> +bool migrate_tls(void)
> +{
> +    return !!migrate_tls_creds();
>  }
>  
>  uint64_t migrate_vcpu_dirty_limit_period(void)
> @@ -888,6 +906,36 @@ AnnounceParameters *migrate_announce_params(void)
>      return &ap;
>  }
>  
> +void migrate_tls_opts_free(MigrationParameters *params)
> +{
> +    qapi_free_StrOrNull(params->tls_creds);
> +    qapi_free_StrOrNull(params->tls_hostname);
> +    qapi_free_StrOrNull(params->tls_authz);
> +}
> +
> +/* needs BQL if dst is part of s->parameters */
> +static void tls_option_set_str(StrOrNull **dstp, StrOrNull *src)
> +{
> +    StrOrNull *dst = *dstp;
> +
> +    assert(!dst);
> +
> +    dst = *dstp = g_new0(StrOrNull, 1);
> +    dst->type = QTYPE_QSTRING;
> +
> +    if (!src) {
> +        dst->u.s = g_strdup("");
> +        return;
> +    }
> +
> +    if (src->type == QTYPE_QSTRING) {
> +        dst->u.s = g_strdup(src->u.s);
> +    } else {
> +        assert(src->type == QTYPE_QNULL);
> +        dst->u.s = g_strdup("");
> +    }
> +}

Postcondition: dstp points to a StrOrNull containing a str,
i.e. QTYPE_QSTRING.  Makes sense.

I'd prefer something like

       StrOrNull *dst = g_new0(StrOrNull, 1);

       ... fill in members ...

       assert(!*dstp);
       *dstp = dst;

> +
>  MigrationParameters *qmp_query_migrate_parameters(Error **errp)
>  {
>      MigrationParameters *params;
> @@ -903,10 +951,11 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp)
>      params->cpu_throttle_increment = s->parameters.cpu_throttle_increment;
>      params->has_cpu_throttle_tailslow = true;
>      params->cpu_throttle_tailslow = s->parameters.cpu_throttle_tailslow;
> -    params->tls_creds = g_strdup(s->parameters.tls_creds);
> -    params->tls_hostname = g_strdup(s->parameters.tls_hostname);
> -    params->tls_authz = g_strdup(s->parameters.tls_authz ?
> -                                 s->parameters.tls_authz : "");
> +
> +    tls_option_set_str(&params->tls_creds, s->parameters.tls_creds);
> +    tls_option_set_str(&params->tls_hostname, s->parameters.tls_hostname);
> +    tls_option_set_str(&params->tls_authz, s->parameters.tls_authz);
> +
>      params->has_max_bandwidth = true;
>      params->max_bandwidth = s->parameters.max_bandwidth;
>      params->has_avail_switchover_bandwidth = true;
> @@ -963,9 +1012,6 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp)
>  
>  void migrate_params_init(MigrationParameters *params)
>  {
> -    params->tls_hostname = g_strdup("");
> -    params->tls_creds = g_strdup("");
> -

Is this the reason why the code now needs to deal with null?

I'm not objecting, just pointing out that the commit message didn't
prepare me for such a change.

>      /* Set has_* up only for parameter checks */
>      params->has_throttle_trigger_threshold = true;
>      params->has_cpu_throttle_initial = true;

I'm stopping here to ask: how exactly does the patch change quasi-global
state, namely current_migration->parameters->tls_*?

[...]


[*] We have oh so many accidental external interfaces.



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

* Re: [PATCH 02/21] migration: Remove MigrateSetParameters
  2025-06-03  1:37 ` [PATCH 02/21] migration: Remove MigrateSetParameters Fabiano Rosas
  2025-06-05 20:58   ` Peter Xu
@ 2025-06-25 11:31   ` Markus Armbruster
  2025-06-25 17:21     ` Fabiano Rosas
  1 sibling, 1 reply; 83+ messages in thread
From: Markus Armbruster @ 2025-06-25 11:31 UTC (permalink / raw)
  To: Fabiano Rosas; +Cc: qemu-devel, Peter Xu, Daniel P . Berrangé

Fabiano Rosas <farosas@suse.de> writes:

> Now that the TLS options have been made the same between
> migrate-set-parameters and query-migrate-parameters, a single type can
> be used. Remove MigrateSetParameters.
>
> The TLS options documentation from MigrationParameters were replaced
> with the ones from MigrateSetParameters which was more complete.
>
> I'm choosing to somewhat ignore any ambiguity between "query" and
> "set" because other options' docs are already ambiguous in that
> regard.
>
> Signed-off-by: Fabiano Rosas <farosas@suse.de>
> ---
>  migration/migration-hmp-cmds.c |   4 +-
>  migration/options.c            |   6 +-
>  qapi/migration.json            | 221 +++------------------------------
>  3 files changed, 20 insertions(+), 211 deletions(-)
>
> diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c
> index bc8179c582..aacffdc532 100644
> --- a/migration/migration-hmp-cmds.c
> +++ b/migration/migration-hmp-cmds.c
> @@ -490,7 +490,7 @@ void hmp_migrate_set_parameter(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);
> -    MigrateSetParameters *p = g_new0(MigrateSetParameters, 1);
> +    MigrationParameters *p = g_new0(MigrationParameters, 1);
>      uint64_t valuebw = 0;
>      uint64_t cache_size;
>      Error *err = NULL;
> @@ -656,7 +656,7 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict)
>      qmp_migrate_set_parameters(p, &err);
>  
>   cleanup:
> -    qapi_free_MigrateSetParameters(p);
> +    qapi_free_MigrationParameters(p);
>      visit_free(v);
>      hmp_handle_error(mon, err);
>  }
> diff --git a/migration/options.c b/migration/options.c
> index 45a95dc6da..e49d584a99 100644
> --- a/migration/options.c
> +++ b/migration/options.c
> @@ -1227,7 +1227,7 @@ bool migrate_params_check(MigrationParameters *params, Error **errp)
>      return true;
>  }
>  
> -static void migrate_params_test_apply(MigrateSetParameters *params,
> +static void migrate_params_test_apply(MigrationParameters *params,
>                                        MigrationParameters *dest)
>  {
>      *dest = migrate_get_current()->parameters;
> @@ -1350,7 +1350,7 @@ static void migrate_params_test_apply(MigrateSetParameters *params,
>      }
>  }
>  
> -static void migrate_params_apply(MigrateSetParameters *params, Error **errp)
> +static void migrate_params_apply(MigrationParameters *params, Error **errp)
>  {
>      MigrationState *s = migrate_get_current();
>  
> @@ -1479,7 +1479,7 @@ static void migrate_params_apply(MigrateSetParameters *params, Error **errp)
>      }
>  }
>  
> -void qmp_migrate_set_parameters(MigrateSetParameters *params, Error **errp)
> +void qmp_migrate_set_parameters(MigrationParameters *params, Error **errp)
>  {
>      MigrationParameters tmp;
>  
> diff --git a/qapi/migration.json b/qapi/migration.json
> index fa42d94810..080968993a 100644
> --- a/qapi/migration.json
> +++ b/qapi/migration.json
> @@ -914,202 +914,6 @@
>             'zero-page-detection',
>             'direct-io'] }
>  
> -##
> -# @MigrateSetParameters:

Only use is argument type of migrate-set-parameters.  You're replacing
it by MigrationParameters there.  Let's compare the deleted docs to
their replacement.  I'll quote replacement docs exactly where they
differ.

   # @MigrationParameters:
   #
   # Migration parameters. Optional members are optional when used with
   # an input command, otherwise mandatory.

Figuring out which commands are input commands is left to the reader.
Why not simply "optional with migrate-set-parameters"?

However, it doesn't end there.  The paragraph creates a problem with
John Snow's "inliner", which I hope to merge later this year.  Let me
explain.

Generated command documentation normally looks like this:

    Command migrate-set-capabilities (Since: 1.2)

       Enable/Disable the following migration capabilities (like xbzrle)

       Arguments:
          * **capabilities** ("[""MigrationCapabilityStatus""]") -- json
            array of capability modifications to make

Except when we happen to use a named type for the arguments.  This
should be an implementation detail, and it is, except for generated
documentation, which looks like

    Command migrate-set-parameters (Since: 2.4)

       Set various migration parameters.

       Arguments:
          * The members of "MigrationParameters".

The arguments are hidden behind a link.  The "inliner" will show the
them normally *always*, for better usability.  It will not, however,
inline the introductory paragraph above.  I can explain why if
necessary.

To compensate for the loss of that paragraph, we'll have to add suitable
text to migrate-set-parameters's doc comment.

I think we could just as well do that *now*: scratch the paragraph here,
add a suitable paragraph there.

> -#
> -# @announce-initial: Initial delay (in milliseconds) before sending
> -#     the first announce (Since 4.0)
> -#
> -# @announce-max: Maximum delay (in milliseconds) between packets in
> -#     the announcement (Since 4.0)
> -#
> -# @announce-rounds: Number of self-announce packets sent after
> -#     migration (Since 4.0)
> -#
> -# @announce-step: Increase in delay (in milliseconds) between
> -#     subsequent packets in the announcement (Since 4.0)
> -#
> -# @throttle-trigger-threshold: The ratio of bytes_dirty_period and
> -#     bytes_xfer_period to trigger throttling.  It is expressed as
> -#     percentage.  The default value is 50.  (Since 5.0)
> -#
> -# @cpu-throttle-initial: Initial percentage of time guest cpus are
> -#     throttled when migration auto-converge is activated.  The
> -#     default value is 20.  (Since 2.7)
> -#

   # @cpu-throttle-initial: Initial percentage of time guest cpus are
   #     throttled when migration auto-converge is activated.  (Since
   #     2.7)

We no longer document the default value.

> -# @cpu-throttle-increment: throttle percentage increase each time
> -#     auto-converge detects that migration is not making progress.
> -#     The default value is 10.  (Since 2.7)

   # @cpu-throttle-increment: throttle percentage increase each time
   #     auto-converge detects that migration is not making progress.
   #     (Since 2.7)

Likewise.

> -#
> -# @cpu-throttle-tailslow: Make CPU throttling slower at tail stage At
> -#     the tail stage of throttling, the Guest is very sensitive to CPU
> -#     percentage while the @cpu-throttle -increment is excessive
> -#     usually at tail stage.  If this parameter is true, we will
> -#     compute the ideal CPU percentage used by the Guest, which may
> -#     exactly make the dirty rate match the dirty rate threshold.
> -#     Then we will choose a smaller throttle increment between the one
> -#     specified by @cpu-throttle-increment and the one generated by
> -#     ideal CPU percentage.  Therefore, it is compatible to
> -#     traditional throttling, meanwhile the throttle increment won't
> -#     be excessive at tail stage.  The default value is false.  (Since
> -#     5.1)
> -#
> -# @tls-creds: ID of the 'tls-creds' object that provides credentials
> -#     for establishing a TLS connection over the migration data
> -#     channel.  On the outgoing side of the migration, the credentials
> -#     must be for a 'client' endpoint, while for the incoming side the
> -#     credentials must be for a 'server' endpoint.  Setting this to a
> -#     non-empty string enables TLS for all migrations.  An empty
> -#     string means that QEMU will use plain text mode for migration,
> -#     rather than TLS.  This is the default.  (Since 2.7)
> -#
> -# @tls-hostname: migration target's hostname for validating the
> -#     server's x509 certificate identity.  If empty, QEMU will use the
> -#     hostname from the migration URI, if any.  A non-empty value is
> -#     required when using x509 based TLS credentials and the migration
> -#     URI does not include a hostname, such as fd: or exec: based
> -#     migration.  (Since 2.7)
> -#
> -#     Note: empty value works only since 2.9.
> -#
> -# @tls-authz: ID of the 'authz' object subclass that provides access
> -#     control checking of the TLS x509 certificate distinguished name.
> -#     This object is only resolved at time of use, so can be deleted
> -#     and recreated on the fly while the migration server is active.
> -#     If missing, it will default to denying access (Since 4.0)
> -#
> -# @max-bandwidth: maximum speed for migration, in bytes per second.
> -#     (Since 2.8)
> -#
> -# @avail-switchover-bandwidth: to set the available bandwidth that
> -#     migration can use during switchover phase.  NOTE!  This does not
> -#     limit the bandwidth during switchover, but only for calculations
> -#     when making decisions to switchover.  By default, this value is
> -#     zero, which means QEMU will estimate the bandwidth
> -#     automatically.  This can be set when the estimated value is not
> -#     accurate, while the user is able to guarantee such bandwidth is
> -#     available when switching over.  When specified correctly, this
> -#     can make the switchover decision much more accurate.
> -#     (Since 8.2)
> -#
> -# @downtime-limit: set maximum tolerated downtime for migration.
> -#     maximum downtime in milliseconds (Since 2.8)
> -#
> -# @x-checkpoint-delay: The delay time (in ms) between two COLO
> -#     checkpoints in periodic mode.  (Since 2.8)

   # @x-checkpoint-delay: the delay time between two COLO checkpoints.
   #     (Since 2.8)

We no longer mention periodic mode.

> -#
> -# @multifd-channels: Number of channels used to migrate data in
> -#     parallel.  This is the same number that the number of sockets
> -#     used for migration.  The default value is 2 (since 4.0)
> -#
> -# @xbzrle-cache-size: cache size to be used by XBZRLE migration.  It
> -#     needs to be a multiple of the target page size and a power of 2
> -#     (Since 2.11)
> -#
> -# @max-postcopy-bandwidth: Background transfer bandwidth during
> -#     postcopy.  Defaults to 0 (unlimited).  In bytes per second.
> -#     (Since 3.0)
> -#
> -# @max-cpu-throttle: maximum cpu throttle percentage.  Defaults to 99.
> -#     (Since 3.1)
> -#
> -# @multifd-compression: Which compression method to use.  Defaults to
> -#     none.  (Since 5.0)
> -#
> -# @multifd-zlib-level: Set the compression level to be used in live
> -#     migration, the compression level is an integer between 0 and 9,
> -#     where 0 means no compression, 1 means the best compression
> -#     speed, and 9 means best compression ratio which will consume
> -#     more CPU.  Defaults to 1.  (Since 5.0)
> -#
> -# @multifd-qatzip-level: Set the compression level to be used in live
> -#     migration. The level is an integer between 1 and 9, where 1 means
> -#     the best compression speed, and 9 means the best compression
> -#     ratio which will consume more CPU. Defaults to 1.  (Since 9.2)
> -#
> -# @multifd-zstd-level: Set the compression level to be used in live
> -#     migration, the compression level is an integer between 0 and 20,
> -#     where 0 means no compression, 1 means the best compression
> -#     speed, and 20 means best compression ratio which will consume
> -#     more CPU.  Defaults to 1.  (Since 5.0)
> -#
> -# @block-bitmap-mapping: Maps block nodes and bitmaps on them to
> -#     aliases for the purpose of dirty bitmap migration.  Such aliases
> -#     may for example be the corresponding names on the opposite site.
> -#     The mapping must be one-to-one, but not necessarily complete: On
> -#     the source, unmapped bitmaps and all bitmaps on unmapped nodes
> -#     will be ignored.  On the destination, encountering an unmapped
> -#     alias in the incoming migration stream will result in a report,
> -#     and all further bitmap migration data will then be discarded.
> -#     Note that the destination does not know about bitmaps it does
> -#     not receive, so there is no limitation or requirement regarding
> -#     the number of bitmaps received, or how they are named, or on
> -#     which nodes they are placed.  By default (when this parameter
> -#     has never been set), bitmap names are mapped to themselves.
> -#     Nodes are mapped to their block device name if there is one, and
> -#     to their node name otherwise.  (Since 5.2)
> -#
> -# @x-vcpu-dirty-limit-period: Periodic time (in milliseconds) of dirty
> -#     limit during live migration.  Should be in the range 1 to
> -#     1000ms.  Defaults to 1000ms.  (Since 8.1)
> -#
> -# @vcpu-dirty-limit: Dirtyrate limit (MB/s) during live migration.
> -#     Defaults to 1.  (Since 8.1)
> -#
> -# @mode: Migration mode.  See description in @MigMode.  Default is
> -#     'normal'.  (Since 8.2)
> -#
> -# @zero-page-detection: Whether and how to detect zero pages.
> -#     See description in @ZeroPageDetection.  Default is 'multifd'.
> -#     (since 9.0)
> -#
> -# @direct-io: Open migration files with O_DIRECT when possible.  This
> -#     only has effect if the @mapped-ram capability is enabled.
> -#     (Since 9.1)
> -#
> -# Features:
> -#
> -# @unstable: Members @x-checkpoint-delay and
> -#     @x-vcpu-dirty-limit-period are experimental.
> -#
> -# TODO: either fuse back into MigrationParameters, or make
> -#     MigrationParameters members mandatory

The TODO is gone.  Makes sense.

> -#
> -# Since: 2.4
> -##
> -{ 'struct': 'MigrateSetParameters',
> -  'data': { '*announce-initial': 'size',
> -            '*announce-max': 'size',
> -            '*announce-rounds': 'size',
> -            '*announce-step': 'size',
> -            '*throttle-trigger-threshold': 'uint8',
> -            '*cpu-throttle-initial': 'uint8',
> -            '*cpu-throttle-increment': 'uint8',
> -            '*cpu-throttle-tailslow': 'bool',
> -            '*tls-creds': 'StrOrNull',
> -            '*tls-hostname': 'StrOrNull',
> -            '*tls-authz': 'StrOrNull',
> -            '*max-bandwidth': 'size',
> -            '*avail-switchover-bandwidth': 'size',
> -            '*downtime-limit': 'uint64',
> -            '*x-checkpoint-delay': { 'type': 'uint32',
> -                                     'features': [ 'unstable' ] },
> -            '*multifd-channels': 'uint8',
> -            '*xbzrle-cache-size': 'size',
> -            '*max-postcopy-bandwidth': 'size',
> -            '*max-cpu-throttle': 'uint8',
> -            '*multifd-compression': 'MultiFDCompression',
> -            '*multifd-zlib-level': 'uint8',
> -            '*multifd-qatzip-level': 'uint8',
> -            '*multifd-zstd-level': 'uint8',
> -            '*block-bitmap-mapping': [ 'BitmapMigrationNodeAlias' ],
> -            '*x-vcpu-dirty-limit-period': { 'type': 'uint64',
> -                                            'features': [ 'unstable' ] },
> -            '*vcpu-dirty-limit': 'uint64',
> -            '*mode': 'MigMode',
> -            '*zero-page-detection': 'ZeroPageDetection',
> -            '*direct-io': 'bool' } }
> -
>  ##
>  # @migrate-set-parameters:
>  #
> @@ -1124,12 +928,13 @@
>  #     <- { "return": {} }
>  ##
>  { 'command': 'migrate-set-parameters', 'boxed': true,
> -  'data': 'MigrateSetParameters' }
> +  'data': 'MigrationParameters' }
>  
>  ##
>  # @MigrationParameters:
>  #
> -# The optional members aren't actually optional.
> +# Migration parameters. Optional members are optional when used with
> +# an input command, otherwise mandatory.
>  #
>  # @announce-initial: Initial delay (in milliseconds) before sending
>  #     the first announce (Since 4.0)
> @@ -1172,21 +977,25 @@
>  #     for establishing a TLS connection over the migration data
>  #     channel.  On the outgoing side of the migration, the credentials
>  #     must be for a 'client' endpoint, while for the incoming side the
> -#     credentials must be for a 'server' endpoint.  An empty string
> -#     means that QEMU will use plain text mode for migration, rather
> -#     than TLS.  (Since 2.7)
> -#
> -#     Note: 2.8 omits empty @tls-creds instead.
> +#     credentials must be for a 'server' endpoint.  Setting this to a
> +#     non-empty string enables TLS for all migrations.  An empty
> +#     string means that QEMU will use plain text mode for migration,
> +#     rather than TLS.  This is the default.  (Since 2.7)
>  #
>  # @tls-hostname: migration target's hostname for validating the
>  #     server's x509 certificate identity.  If empty, QEMU will use the
> -#     hostname from the migration URI, if any.  (Since 2.7)
> +#     hostname from the migration URI, if any.  A non-empty value is
> +#     required when using x509 based TLS credentials and the migration
> +#     URI does not include a hostname, such as fd: or exec: based
> +#     migration.  (Since 2.7)
>  #
> -#     Note: 2.8 omits empty @tls-hostname instead.
> +#     Note: empty value works only since 2.9.
>  #
>  # @tls-authz: ID of the 'authz' object subclass that provides access
>  #     control checking of the TLS x509 certificate distinguished name.
> -#     (Since 4.0)
> +#     This object is only resolved at time of use, so can be deleted
> +#     and recreated on the fly while the migration server is active.
> +#     If missing, it will default to denying access (Since 4.0)
>  #
>  # @max-bandwidth: maximum speed for migration, in bytes per second.
>  #     (Since 2.8)



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

* Re: [PATCH 03/21] qapi/migration: Don't document MigrationParameter
  2025-06-03  1:37 ` [PATCH 03/21] qapi/migration: Don't document MigrationParameter Fabiano Rosas
  2025-06-05 21:00   ` Peter Xu
@ 2025-06-25 12:04   ` Markus Armbruster
  2025-06-25 12:22     ` Markus Armbruster
  1 sibling, 1 reply; 83+ messages in thread
From: Markus Armbruster @ 2025-06-25 12:04 UTC (permalink / raw)
  To: Fabiano Rosas
  Cc: qemu-devel, Peter Xu, Markus Armbruster, Daniel P . Berrangé

Fabiano Rosas <farosas@suse.de> writes:

> The MigrationParameter (singular) enumeration is not part of the
> migration QMP API, it's only used for nicely converting HMP strings
> into MigrationParameters (plural) members and for providing readline
> completion.



> Documenting this enum only serves to duplicate documentation between
> MigrationParameter and MigrationParameters.
>
> Add an exception to QAPIs pragma.json and stop documenting it.
>
> Signed-off-by: Fabiano Rosas <farosas@suse.de>
> ---
>  qapi/migration.json | 152 +-------------------------------------------
>  qapi/pragma.json    |   3 +-
>  2 files changed, 3 insertions(+), 152 deletions(-)
>
> diff --git a/qapi/migration.json b/qapi/migration.json
> index 080968993a..452e6dedaa 100644
> --- a/qapi/migration.json
> +++ b/qapi/migration.json
> @@ -734,157 +734,7 @@
>  ##
>  # @MigrationParameter:
>  #
> -# Migration parameters enumeration
> -#
> -# @announce-initial: Initial delay (in milliseconds) before sending
> -#     the first announce (Since 4.0)

[...]

> -# @direct-io: Open migration files with O_DIRECT when possible.  This
> -#     only has effect if the @mapped-ram capability is enabled.
> -#     (Since 9.1)
> +# Migration parameters enumeration. See @MigrationParameters for more info.

Suggest something like

   # The enumeration values mirror the members of MigrationParameters,
   # which see.

I could compare the deleted docs with MigrationParameters docs, but I
doubt it's worthwhile: the type is only ever used internally.  That it
appears in the QEMU QMP Reference Manual anyway is a defect.  There are
quite a few more like it (list appended).

If I remember correctly, John Snow's doc generator work will fix this
defect.

>  #
>  # Features:
>  #
> diff --git a/qapi/pragma.json b/qapi/pragma.json
> index 023a2ef7bc..58133907b6 100644
> --- a/qapi/pragma.json
> +++ b/qapi/pragma.json
> @@ -76,7 +76,8 @@
>          'X86CPURegister32',
>          'XDbgBlockGraph',
>          'YankInstanceType',
> -        'blockdev-reopen' ],
> +        'blockdev-reopen',
> +        'MigrationParameter'],

Please keep this list sorted alphabetically.

>      # Externally visible types whose member names may use uppercase
>      'member-name-exceptions': [     # visible in:
>          'ACPISlotType',             # query-acpi-ospm-status


Types documented in the QEMU QMP Reference Manual that don't occur in
QMP introspection:

AcpiTableOptions
BiosAtaTranslation
BlockChildInfo
BlockGraphInfo
BlockMeasureInfo
BootConfiguration
COLOMessage
CXLFMWProperties
CXLFixedMemoryWindowOptions
CacheLevelAndType
CompatPolicy
CompatPolicyInput
CompatPolicyOutput
CompressionStats
CpuTopologyLevel
DummyBlockCoreForceArrays
DummyForceArrays
DummyVirtioForceArrays
EndianMode
FailoverStatus
FloppyDriveType
GranuleMode
IOThreadVirtQueueMapping
ImageCheck
JobVerb
LostTickPolicy
MapEntry
MemorySizeConfiguration
MigrationParameter
MonitorMode
MonitorOptions
NbdServerOptions
NotifyVmexitOption
OffAutoPCIBAR
OnOffSplit
PCIELinkSpeed
PCIELinkWidth
QCryptoAkCipherAlgo
QCryptoAkCipherKeyType
QCryptoAkCipherOptions
QCryptoAkCipherOptionsRSA
QCryptoBlockInfo
QCryptoBlockOpenOptions
QCryptoRSAPaddingAlgo
QCryptodevBackendAlgoType
QapiErrorClass
SMPConfiguration
SgxEPC
SgxEPCProperties
SmbiosEntryPointType
SmpCacheProperties
SmpCachePropertiesWrapper
UefiVarStore
UefiVariable
VMAppleVirtioBlkVariant
X86CPUFeatureWordInfo
X86CPURegister32



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

* Re: [PATCH 03/21] qapi/migration: Don't document MigrationParameter
  2025-06-25 12:04   ` Markus Armbruster
@ 2025-06-25 12:22     ` Markus Armbruster
  2025-06-25 17:29       ` Fabiano Rosas
  0 siblings, 1 reply; 83+ messages in thread
From: Markus Armbruster @ 2025-06-25 12:22 UTC (permalink / raw)
  To: Fabiano Rosas; +Cc: qemu-devel, Peter Xu, Daniel P . Berrangé

Markus Armbruster <armbru@redhat.com> writes:

> Fabiano Rosas <farosas@suse.de> writes:
>
>> The MigrationParameter (singular) enumeration is not part of the
>> migration QMP API, it's only used for nicely converting HMP strings
>> into MigrationParameters (plural) members and for providing readline
>> completion.
>
>
>
>> Documenting this enum only serves to duplicate documentation between
>> MigrationParameter and MigrationParameters.
>>
>> Add an exception to QAPIs pragma.json and stop documenting it.
>>
>> Signed-off-by: Fabiano Rosas <farosas@suse.de>
>> ---
>>  qapi/migration.json | 152 +-------------------------------------------
>>  qapi/pragma.json    |   3 +-
>>  2 files changed, 3 insertions(+), 152 deletions(-)
>>
>> diff --git a/qapi/migration.json b/qapi/migration.json
>> index 080968993a..452e6dedaa 100644
>> --- a/qapi/migration.json
>> +++ b/qapi/migration.json
>> @@ -734,157 +734,7 @@
>>  ##
>>  # @MigrationParameter:
>>  #
>> -# Migration parameters enumeration
>> -#
>> -# @announce-initial: Initial delay (in milliseconds) before sending
>> -#     the first announce (Since 4.0)
>
> [...]
>
>> -# @direct-io: Open migration files with O_DIRECT when possible.  This
>> -#     only has effect if the @mapped-ram capability is enabled.
>> -#     (Since 9.1)
>> +# Migration parameters enumeration. See @MigrationParameters for more info.
>
> Suggest something like
>
>    # The enumeration values mirror the members of MigrationParameters,
>    # which see.
>
> I could compare the deleted docs with MigrationParameters docs, but I
> doubt it's worthwhile: the type is only ever used internally.  That it
> appears in the QEMU QMP Reference Manual anyway is a defect.  There are
> quite a few more like it (list appended).
>
> If I remember correctly, John Snow's doc generator work will fix this
> defect.

Until then, this patch has a drawback: the manual now shows all the
members as "Not documented" .  Ugly and a bit embarrassing.  Maybe even
confusing.

[...]



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

* Re: [PATCH 01/21] migration: Normalize tls arguments
  2025-06-25  9:41   ` Markus Armbruster
@ 2025-06-25 17:17     ` Fabiano Rosas
  2025-06-26  9:38       ` Markus Armbruster
  0 siblings, 1 reply; 83+ messages in thread
From: Fabiano Rosas @ 2025-06-25 17:17 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel, Peter Xu, Daniel P . Berrangé

Markus Armbruster <armbru@redhat.com> writes:

> Fabiano Rosas <farosas@suse.de> writes:
>
>> The migration parameters tls_creds, tls_authz and tls_hostname
>> currently have a non-uniform handling. When used as arguments to
>> migrate-set-parameters, their type is StrOrNull and when used as
>> return value from query-migrate-parameters, their type is a plain
>> string.
>>
>> Not only having to convert between the types is cumbersome, but it
>> also creates the issue of requiring two different QAPI types to be
>> used, one for each command. MigrateSetParameters is used for
>> migrate-set-parameters with the TLS arguments as StrOrNull while
>> MigrationParameters is used for query-migrate-parameters with the TLS
>> arguments as str.
>>
>> Since StrOrNull could be considered a superset of str, change the type
>> of the TLS arguments in MigrationParameters to StrOrNull and add a
>> helper to ensure they're never actually used as QTYPE_QNULL.
>
> The type of @tls_creds, @tls-hostname, @tls-authz changes from str to
> StrOrNull in introspection query-migrate-parameters.  Loss of precision.
> Introspection is already imprecise: it shows the members optional even
> though they aren't.  We accept the loss of precision to enable
> de-duplication.
>
> This should be worked into the commit message.
>

Ack.

>> This will allow the type duplication to be removed in the next
>> patches.
>>
>> Signed-off-by: Fabiano Rosas <farosas@suse.de>
>> ---
>>  migration/migration-hmp-cmds.c |   8 +-
>>  migration/migration.c          |   2 +
>>  migration/options.c            | 149 ++++++++++++++++++++-------------
>>  migration/options.h            |   1 +
>>  migration/tls.c                |   2 +-
>>  qapi/migration.json            |   6 +-
>>  6 files changed, 99 insertions(+), 69 deletions(-)
>>
>> diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c
>> index e8a563c7d8..bc8179c582 100644
>> --- a/migration/migration-hmp-cmds.c
>> +++ b/migration/migration-hmp-cmds.c
>> @@ -276,14 +276,12 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict)
>>          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);
>> -        assert(params->tls_hostname);
>> +                       params->tls_creds ? params->tls_creds->u.s : "");
>>          monitor_printf(mon, "%s: '%s'\n",
>>              MigrationParameter_str(MIGRATION_PARAMETER_TLS_HOSTNAME),
>> -            params->tls_hostname);
>> +                       params->tls_hostname ? params->tls_hostname->u.s : "");
>>          assert(params->has_max_bandwidth);
>>          monitor_printf(mon, "%s: %" PRIu64 " bytes/second\n",
>>              MigrationParameter_str(MIGRATION_PARAMETER_MAX_BANDWIDTH),
>> @@ -319,7 +317,7 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict)
>>              params->max_postcopy_bandwidth);
>>          monitor_printf(mon, "%s: '%s'\n",
>>              MigrationParameter_str(MIGRATION_PARAMETER_TLS_AUTHZ),
>> -            params->tls_authz);
>> +                       params->tls_authz ? params->tls_authz->u.s : "");
>>  
>>          if (params->has_block_bitmap_mapping) {
>>              const BitmapMigrationNodeAliasList *bmnal;
>
> Before, the code assumes ->tls_creds, ->tls_hostname, and ->tls_authz
> are non-null.  It assert its assumption for the first two.
>
> Afterwards, it maps null to "".  Why is that necessary?  Hmm, see below.
>

Maps NULL to "" because the intermediate type, MigrationParameters, has
been changed from str to StrOrNull. For the purposes of info
migrate_parameters and query-migrate-parameters the only valid values
are a non-empty string or an empty string.

>> diff --git a/migration/migration.c b/migration/migration.c
>> index 4697732bef..f65cb81b6d 100644
>> --- a/migration/migration.c
>> +++ b/migration/migration.c
>> @@ -4053,6 +4053,8 @@ static void migration_instance_finalize(Object *obj)
>>  {
>>      MigrationState *ms = MIGRATION_OBJ(obj);
>>  
>> +    migrate_tls_opts_free(&ms->parameters);
>
> Is this a bug fix?
>

My new version is a little different, but I can make it a separate patch
if that happens to be the case.

> As far as I can tell, the object gets destroyed only on QEMU shutdown.
> Freeing resources then is unnecessary, except it may help leak detection
> tools.
>

From a maintenance perspective I consider any leak as a bug because I
don't have control over what kinds of leak detection tools are ran on
the code. To avoid spending time looking at such bug reports I have ASAN
automated in my testing and I fix early any leaks that it founds.

>> +
>>      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 162c72cda4..45a95dc6da 100644
>> --- a/migration/options.c
>> +++ b/migration/options.c
>> @@ -162,9 +162,11 @@ const Property migration_properties[] = {
>>      DEFINE_PROP_SIZE("announce-step", MigrationState,
>>                        parameters.announce_step,
>>                        DEFAULT_MIGRATE_ANNOUNCE_STEP),
>> -    DEFINE_PROP_STRING("tls-creds", MigrationState, parameters.tls_creds),
>> -    DEFINE_PROP_STRING("tls-hostname", MigrationState, parameters.tls_hostname),
>> -    DEFINE_PROP_STRING("tls-authz", MigrationState, parameters.tls_authz),
>> +    /*
>> +     * tls-creds, tls-hostname and tls-authz are of type StrOrNull,
>> +     * which can't be easily handled (if at all) by qdev. So these
>> +     * will not be exposed as global migration options (-global).
>> +     */
>
> This is a compatibility break.
>
> The orthodox way to break it is deprecate, let the grace period expire,
> break.  Record in docs/about/deprecated.rst at the beginning, move the
> record to docs/about/removed-features.rst at the end.
>
> An argument could be made that the interface in question is
> accidental[*], not actually used by anything, and therefore breaking it
> without a grace period is fine.  But even then we should record the
> breakage in docs/about/removed-features.rst.
>

Ok. Alternatively I could try a little harder to keep these
options. I'll see what I can do.

> Aside: the interface in question is a hack (making the migration object
> a device) piled onto a hack (the way compat properties work, and how
> they spill into -global).  Past sins catching up with us...
>

Hm, I've been experimenting with adding new objects (still TYPE_DEVICE)
to hold the compatibility options and parameters only. Not the entire
migration state. The MigrationState object would then be converted into
a regular struct.

MigrationState => internal type holding migration state (a better name
could be used)
MigrationOptionsState => -global migration-options.foo=on
MigrationCompatState => -global migration foo=on

But it's getting a little tricky due to s->parameters *also* containing
compat options, namely zero-page-detection.

>>      DEFINE_PROP_UINT64("x-vcpu-dirty-limit-period", MigrationState,
>>                         parameters.x_vcpu_dirty_limit_period,
>>                         DEFAULT_MIGRATE_VCPU_DIRTY_LIMIT_PERIOD),
>> @@ -379,13 +381,6 @@ bool migrate_rdma(void)
>>      return s->rdma_migration;
>>  }
>>  
>> -bool migrate_tls(void)
>> -{
>> -    MigrationState *s = migrate_get_current();
>> -
>> -    return s->parameters.tls_creds && *s->parameters.tls_creds;
>> -}
>> -
>>  typedef enum WriteTrackingSupport {
>>      WT_SUPPORT_UNKNOWN = 0,
>>      WT_SUPPORT_ABSENT,
>> @@ -834,21 +829,44 @@ const char *migrate_tls_authz(void)
>>  {
>>      MigrationState *s = migrate_get_current();
>>  
>> -    return s->parameters.tls_authz;
>> +    if (s->parameters.tls_authz &&
>> +        s->parameters.tls_authz->type == QTYPE_QSTRING &&
>> +        *s->parameters.tls_authz->u.s) {
>> +        return s->parameters.tls_authz->u.s;
>> +    }
>> +
>> +    return NULL;
>>  }
>>  
>>  const char *migrate_tls_creds(void)
>>  {
>>      MigrationState *s = migrate_get_current();
>>  
>> -    return s->parameters.tls_creds;
>> +    if (s->parameters.tls_creds &&
>> +        s->parameters.tls_creds->type == QTYPE_QSTRING &&
>> +        *s->parameters.tls_creds->u.s) {
>> +        return s->parameters.tls_creds->u.s;
>> +    }
>> +
>> +    return NULL;
>>  }
>>  
>>  const char *migrate_tls_hostname(void)
>>  {
>>      MigrationState *s = migrate_get_current();
>>  
>> -    return s->parameters.tls_hostname;
>> +    if (s->parameters.tls_hostname &&
>> +        s->parameters.tls_hostname->type == QTYPE_QSTRING &&
>> +        *s->parameters.tls_hostname->u.s) {
>> +        return s->parameters.tls_hostname->u.s;
>> +    }
>> +
>> +    return NULL;
>> +}
>
> Again, the code changes to cope with null.  Why is that necessary?
> Again, see below.
>

This is actually a bit roundabout indeed. In my new version I do:

- migrate-set-parameters: always write either NULL or a non-empty
  QTYPE_QSTRING to s->parameters.
    
- query-migrate-parameters: always return a (possibly empty)
  QTYPE_QSTRING.

With this, internal representation is: NULL for not present, non-empty
string for present.

>> +
>> +bool migrate_tls(void)
>> +{
>> +    return !!migrate_tls_creds();
>>  }
>>  
>>  uint64_t migrate_vcpu_dirty_limit_period(void)
>> @@ -888,6 +906,36 @@ AnnounceParameters *migrate_announce_params(void)
>>      return &ap;
>>  }
>>  
>> +void migrate_tls_opts_free(MigrationParameters *params)
>> +{
>> +    qapi_free_StrOrNull(params->tls_creds);
>> +    qapi_free_StrOrNull(params->tls_hostname);
>> +    qapi_free_StrOrNull(params->tls_authz);
>> +}
>> +
>> +/* needs BQL if dst is part of s->parameters */
>> +static void tls_option_set_str(StrOrNull **dstp, StrOrNull *src)
>> +{
>> +    StrOrNull *dst = *dstp;
>> +
>> +    assert(!dst);
>> +
>> +    dst = *dstp = g_new0(StrOrNull, 1);
>> +    dst->type = QTYPE_QSTRING;
>> +
>> +    if (!src) {
>> +        dst->u.s = g_strdup("");
>> +        return;
>> +    }
>> +
>> +    if (src->type == QTYPE_QSTRING) {
>> +        dst->u.s = g_strdup(src->u.s);
>> +    } else {
>> +        assert(src->type == QTYPE_QNULL);
>> +        dst->u.s = g_strdup("");
>> +    }
>> +}
>
> Postcondition: dstp points to a StrOrNull containing a str,
> i.e. QTYPE_QSTRING.  Makes sense.
>
> I'd prefer something like
>
>        StrOrNull *dst = g_new0(StrOrNull, 1);
>
>        ... fill in members ...
>
>        assert(!*dstp);
>        *dstp = dst;
>

This code is also reworked a bit on the next version, I'll see whether
the comment still applies.

>> +
>>  MigrationParameters *qmp_query_migrate_parameters(Error **errp)
>>  {
>>      MigrationParameters *params;
>> @@ -903,10 +951,11 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp)
>>      params->cpu_throttle_increment = s->parameters.cpu_throttle_increment;
>>      params->has_cpu_throttle_tailslow = true;
>>      params->cpu_throttle_tailslow = s->parameters.cpu_throttle_tailslow;
>> -    params->tls_creds = g_strdup(s->parameters.tls_creds);
>> -    params->tls_hostname = g_strdup(s->parameters.tls_hostname);
>> -    params->tls_authz = g_strdup(s->parameters.tls_authz ?
>> -                                 s->parameters.tls_authz : "");
>> +
>> +    tls_option_set_str(&params->tls_creds, s->parameters.tls_creds);
>> +    tls_option_set_str(&params->tls_hostname, s->parameters.tls_hostname);
>> +    tls_option_set_str(&params->tls_authz, s->parameters.tls_authz);
>> +
>>      params->has_max_bandwidth = true;
>>      params->max_bandwidth = s->parameters.max_bandwidth;
>>      params->has_avail_switchover_bandwidth = true;
>> @@ -963,9 +1012,6 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp)
>>  
>>  void migrate_params_init(MigrationParameters *params)
>>  {
>> -    params->tls_hostname = g_strdup("");
>> -    params->tls_creds = g_strdup("");
>> -
>
> Is this the reason why the code now needs to deal with null?
>
> I'm not objecting, just pointing out that the commit message didn't
> prepare me for such a change.
>

I'll document it.

>>      /* Set has_* up only for parameter checks */
>>      params->has_throttle_trigger_threshold = true;
>>      params->has_cpu_throttle_initial = true;
>
> I'm stopping here to ask: how exactly does the patch change quasi-global
> state, namely current_migration->parameters->tls_*?
>

It makes sure the current_migration->parameters->tls_* options are
always QTYPE_QSTRING and either a non-empty or an empty string.

The next version of the patch will instead use non-empty QTYPE_QSTRING
or NULL, which is cleaner from a C perspective.

Both versions ensure the query-* and set-* commands continue to expose
the same values. Query only shows non-empty or empty string and Set
accepts all values of a StrOrNull type.

> [...]
>
>
> [*] We have oh so many accidental external interfaces.


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

* Re: [PATCH 02/21] migration: Remove MigrateSetParameters
  2025-06-25 11:31   ` Markus Armbruster
@ 2025-06-25 17:21     ` Fabiano Rosas
  2025-06-26  9:40       ` Markus Armbruster
  0 siblings, 1 reply; 83+ messages in thread
From: Fabiano Rosas @ 2025-06-25 17:21 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel, Peter Xu, Daniel P . Berrangé

Markus Armbruster <armbru@redhat.com> writes:

> Fabiano Rosas <farosas@suse.de> writes:
>
>> Now that the TLS options have been made the same between
>> migrate-set-parameters and query-migrate-parameters, a single type can
>> be used. Remove MigrateSetParameters.
>>
>> The TLS options documentation from MigrationParameters were replaced
>> with the ones from MigrateSetParameters which was more complete.
>>
>> I'm choosing to somewhat ignore any ambiguity between "query" and
>> "set" because other options' docs are already ambiguous in that
>> regard.
>>
>> Signed-off-by: Fabiano Rosas <farosas@suse.de>
>> ---
>>  migration/migration-hmp-cmds.c |   4 +-
>>  migration/options.c            |   6 +-
>>  qapi/migration.json            | 221 +++------------------------------
>>  3 files changed, 20 insertions(+), 211 deletions(-)
>>
>> diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c
>> index bc8179c582..aacffdc532 100644
>> --- a/migration/migration-hmp-cmds.c
>> +++ b/migration/migration-hmp-cmds.c
>> @@ -490,7 +490,7 @@ void hmp_migrate_set_parameter(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);
>> -    MigrateSetParameters *p = g_new0(MigrateSetParameters, 1);
>> +    MigrationParameters *p = g_new0(MigrationParameters, 1);
>>      uint64_t valuebw = 0;
>>      uint64_t cache_size;
>>      Error *err = NULL;
>> @@ -656,7 +656,7 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict)
>>      qmp_migrate_set_parameters(p, &err);
>>  
>>   cleanup:
>> -    qapi_free_MigrateSetParameters(p);
>> +    qapi_free_MigrationParameters(p);
>>      visit_free(v);
>>      hmp_handle_error(mon, err);
>>  }
>> diff --git a/migration/options.c b/migration/options.c
>> index 45a95dc6da..e49d584a99 100644
>> --- a/migration/options.c
>> +++ b/migration/options.c
>> @@ -1227,7 +1227,7 @@ bool migrate_params_check(MigrationParameters *params, Error **errp)
>>      return true;
>>  }
>>  
>> -static void migrate_params_test_apply(MigrateSetParameters *params,
>> +static void migrate_params_test_apply(MigrationParameters *params,
>>                                        MigrationParameters *dest)
>>  {
>>      *dest = migrate_get_current()->parameters;
>> @@ -1350,7 +1350,7 @@ static void migrate_params_test_apply(MigrateSetParameters *params,
>>      }
>>  }
>>  
>> -static void migrate_params_apply(MigrateSetParameters *params, Error **errp)
>> +static void migrate_params_apply(MigrationParameters *params, Error **errp)
>>  {
>>      MigrationState *s = migrate_get_current();
>>  
>> @@ -1479,7 +1479,7 @@ static void migrate_params_apply(MigrateSetParameters *params, Error **errp)
>>      }
>>  }
>>  
>> -void qmp_migrate_set_parameters(MigrateSetParameters *params, Error **errp)
>> +void qmp_migrate_set_parameters(MigrationParameters *params, Error **errp)
>>  {
>>      MigrationParameters tmp;
>>  
>> diff --git a/qapi/migration.json b/qapi/migration.json
>> index fa42d94810..080968993a 100644
>> --- a/qapi/migration.json
>> +++ b/qapi/migration.json
>> @@ -914,202 +914,6 @@
>>             'zero-page-detection',
>>             'direct-io'] }
>>  
>> -##
>> -# @MigrateSetParameters:
>
> Only use is argument type of migrate-set-parameters.  You're replacing
> it by MigrationParameters there.  Let's compare the deleted docs to
> their replacement.  I'll quote replacement docs exactly where they
> differ.
>
>    # @MigrationParameters:
>    #
>    # Migration parameters. Optional members are optional when used with
>    # an input command, otherwise mandatory.
>
> Figuring out which commands are input commands is left to the reader.
> Why not simply "optional with migrate-set-parameters"?
>

Future patches include migrate and migrate-incoming. I can enumerate
them if that's better.

> However, it doesn't end there.  The paragraph creates a problem with
> John Snow's "inliner", which I hope to merge later this year.  Let me
> explain.
>
> Generated command documentation normally looks like this:
>
>     Command migrate-set-capabilities (Since: 1.2)
>
>        Enable/Disable the following migration capabilities (like xbzrle)
>
>        Arguments:
>           * **capabilities** ("[""MigrationCapabilityStatus""]") -- json
>             array of capability modifications to make
>
> Except when we happen to use a named type for the arguments.  This
> should be an implementation detail, and it is, except for generated
> documentation, which looks like
>
>     Command migrate-set-parameters (Since: 2.4)
>
>        Set various migration parameters.
>
>        Arguments:
>           * The members of "MigrationParameters".
>
> The arguments are hidden behind a link.  The "inliner" will show the
> them normally *always*, for better usability.  It will not, however,
> inline the introductory paragraph above.  I can explain why if
> necessary.
>
> To compensate for the loss of that paragraph, we'll have to add suitable
> text to migrate-set-parameters's doc comment.
>
> I think we could just as well do that *now*: scratch the paragraph here,
> add a suitable paragraph there.
>

Ok, no worries.

>> -#
>> -# @announce-initial: Initial delay (in milliseconds) before sending
>> -#     the first announce (Since 4.0)
>> -#
>> -# @announce-max: Maximum delay (in milliseconds) between packets in
>> -#     the announcement (Since 4.0)
>> -#
>> -# @announce-rounds: Number of self-announce packets sent after
>> -#     migration (Since 4.0)
>> -#
>> -# @announce-step: Increase in delay (in milliseconds) between
>> -#     subsequent packets in the announcement (Since 4.0)
>> -#
>> -# @throttle-trigger-threshold: The ratio of bytes_dirty_period and
>> -#     bytes_xfer_period to trigger throttling.  It is expressed as
>> -#     percentage.  The default value is 50.  (Since 5.0)
>> -#
>> -# @cpu-throttle-initial: Initial percentage of time guest cpus are
>> -#     throttled when migration auto-converge is activated.  The
>> -#     default value is 20.  (Since 2.7)
>> -#
>
>    # @cpu-throttle-initial: Initial percentage of time guest cpus are
>    #     throttled when migration auto-converge is activated.  (Since
>    #     2.7)
>
> We no longer document the default value.
>

Rebase blunder, I believe the defaults were added recently.

>> -# @cpu-throttle-increment: throttle percentage increase each time
>> -#     auto-converge detects that migration is not making progress.
>> -#     The default value is 10.  (Since 2.7)
>
>    # @cpu-throttle-increment: throttle percentage increase each time
>    #     auto-converge detects that migration is not making progress.
>    #     (Since 2.7)
>
> Likewise.
>

Here as well.

>> -#
>> -# @cpu-throttle-tailslow: Make CPU throttling slower at tail stage At
>> -#     the tail stage of throttling, the Guest is very sensitive to CPU
>> -#     percentage while the @cpu-throttle -increment is excessive
>> -#     usually at tail stage.  If this parameter is true, we will
>> -#     compute the ideal CPU percentage used by the Guest, which may
>> -#     exactly make the dirty rate match the dirty rate threshold.
>> -#     Then we will choose a smaller throttle increment between the one
>> -#     specified by @cpu-throttle-increment and the one generated by
>> -#     ideal CPU percentage.  Therefore, it is compatible to
>> -#     traditional throttling, meanwhile the throttle increment won't
>> -#     be excessive at tail stage.  The default value is false.  (Since
>> -#     5.1)
>> -#
>> -# @tls-creds: ID of the 'tls-creds' object that provides credentials
>> -#     for establishing a TLS connection over the migration data
>> -#     channel.  On the outgoing side of the migration, the credentials
>> -#     must be for a 'client' endpoint, while for the incoming side the
>> -#     credentials must be for a 'server' endpoint.  Setting this to a
>> -#     non-empty string enables TLS for all migrations.  An empty
>> -#     string means that QEMU will use plain text mode for migration,
>> -#     rather than TLS.  This is the default.  (Since 2.7)
>> -#
>> -# @tls-hostname: migration target's hostname for validating the
>> -#     server's x509 certificate identity.  If empty, QEMU will use the
>> -#     hostname from the migration URI, if any.  A non-empty value is
>> -#     required when using x509 based TLS credentials and the migration
>> -#     URI does not include a hostname, such as fd: or exec: based
>> -#     migration.  (Since 2.7)
>> -#
>> -#     Note: empty value works only since 2.9.
>> -#
>> -# @tls-authz: ID of the 'authz' object subclass that provides access
>> -#     control checking of the TLS x509 certificate distinguished name.
>> -#     This object is only resolved at time of use, so can be deleted
>> -#     and recreated on the fly while the migration server is active.
>> -#     If missing, it will default to denying access (Since 4.0)
>> -#
>> -# @max-bandwidth: maximum speed for migration, in bytes per second.
>> -#     (Since 2.8)
>> -#
>> -# @avail-switchover-bandwidth: to set the available bandwidth that
>> -#     migration can use during switchover phase.  NOTE!  This does not
>> -#     limit the bandwidth during switchover, but only for calculations
>> -#     when making decisions to switchover.  By default, this value is
>> -#     zero, which means QEMU will estimate the bandwidth
>> -#     automatically.  This can be set when the estimated value is not
>> -#     accurate, while the user is able to guarantee such bandwidth is
>> -#     available when switching over.  When specified correctly, this
>> -#     can make the switchover decision much more accurate.
>> -#     (Since 8.2)
>> -#
>> -# @downtime-limit: set maximum tolerated downtime for migration.
>> -#     maximum downtime in milliseconds (Since 2.8)
>> -#
>> -# @x-checkpoint-delay: The delay time (in ms) between two COLO
>> -#     checkpoints in periodic mode.  (Since 2.8)
>
>    # @x-checkpoint-delay: the delay time between two COLO checkpoints.
>    #     (Since 2.8)
>
> We no longer mention periodic mode.
>

I'll fix it.

>> -#
>> -# @multifd-channels: Number of channels used to migrate data in
>> -#     parallel.  This is the same number that the number of sockets
>> -#     used for migration.  The default value is 2 (since 4.0)
>> -#
>> -# @xbzrle-cache-size: cache size to be used by XBZRLE migration.  It
>> -#     needs to be a multiple of the target page size and a power of 2
>> -#     (Since 2.11)
>> -#
>> -# @max-postcopy-bandwidth: Background transfer bandwidth during
>> -#     postcopy.  Defaults to 0 (unlimited).  In bytes per second.
>> -#     (Since 3.0)
>> -#
>> -# @max-cpu-throttle: maximum cpu throttle percentage.  Defaults to 99.
>> -#     (Since 3.1)
>> -#
>> -# @multifd-compression: Which compression method to use.  Defaults to
>> -#     none.  (Since 5.0)
>> -#
>> -# @multifd-zlib-level: Set the compression level to be used in live
>> -#     migration, the compression level is an integer between 0 and 9,
>> -#     where 0 means no compression, 1 means the best compression
>> -#     speed, and 9 means best compression ratio which will consume
>> -#     more CPU.  Defaults to 1.  (Since 5.0)
>> -#
>> -# @multifd-qatzip-level: Set the compression level to be used in live
>> -#     migration. The level is an integer between 1 and 9, where 1 means
>> -#     the best compression speed, and 9 means the best compression
>> -#     ratio which will consume more CPU. Defaults to 1.  (Since 9.2)
>> -#
>> -# @multifd-zstd-level: Set the compression level to be used in live
>> -#     migration, the compression level is an integer between 0 and 20,
>> -#     where 0 means no compression, 1 means the best compression
>> -#     speed, and 20 means best compression ratio which will consume
>> -#     more CPU.  Defaults to 1.  (Since 5.0)
>> -#
>> -# @block-bitmap-mapping: Maps block nodes and bitmaps on them to
>> -#     aliases for the purpose of dirty bitmap migration.  Such aliases
>> -#     may for example be the corresponding names on the opposite site.
>> -#     The mapping must be one-to-one, but not necessarily complete: On
>> -#     the source, unmapped bitmaps and all bitmaps on unmapped nodes
>> -#     will be ignored.  On the destination, encountering an unmapped
>> -#     alias in the incoming migration stream will result in a report,
>> -#     and all further bitmap migration data will then be discarded.
>> -#     Note that the destination does not know about bitmaps it does
>> -#     not receive, so there is no limitation or requirement regarding
>> -#     the number of bitmaps received, or how they are named, or on
>> -#     which nodes they are placed.  By default (when this parameter
>> -#     has never been set), bitmap names are mapped to themselves.
>> -#     Nodes are mapped to their block device name if there is one, and
>> -#     to their node name otherwise.  (Since 5.2)
>> -#
>> -# @x-vcpu-dirty-limit-period: Periodic time (in milliseconds) of dirty
>> -#     limit during live migration.  Should be in the range 1 to
>> -#     1000ms.  Defaults to 1000ms.  (Since 8.1)
>> -#
>> -# @vcpu-dirty-limit: Dirtyrate limit (MB/s) during live migration.
>> -#     Defaults to 1.  (Since 8.1)
>> -#
>> -# @mode: Migration mode.  See description in @MigMode.  Default is
>> -#     'normal'.  (Since 8.2)
>> -#
>> -# @zero-page-detection: Whether and how to detect zero pages.
>> -#     See description in @ZeroPageDetection.  Default is 'multifd'.
>> -#     (since 9.0)
>> -#
>> -# @direct-io: Open migration files with O_DIRECT when possible.  This
>> -#     only has effect if the @mapped-ram capability is enabled.
>> -#     (Since 9.1)
>> -#
>> -# Features:
>> -#
>> -# @unstable: Members @x-checkpoint-delay and
>> -#     @x-vcpu-dirty-limit-period are experimental.
>> -#
>> -# TODO: either fuse back into MigrationParameters, or make
>> -#     MigrationParameters members mandatory
>
> The TODO is gone.  Makes sense.
>
>> -#
>> -# Since: 2.4
>> -##
>> -{ 'struct': 'MigrateSetParameters',
>> -  'data': { '*announce-initial': 'size',
>> -            '*announce-max': 'size',
>> -            '*announce-rounds': 'size',
>> -            '*announce-step': 'size',
>> -            '*throttle-trigger-threshold': 'uint8',
>> -            '*cpu-throttle-initial': 'uint8',
>> -            '*cpu-throttle-increment': 'uint8',
>> -            '*cpu-throttle-tailslow': 'bool',
>> -            '*tls-creds': 'StrOrNull',
>> -            '*tls-hostname': 'StrOrNull',
>> -            '*tls-authz': 'StrOrNull',
>> -            '*max-bandwidth': 'size',
>> -            '*avail-switchover-bandwidth': 'size',
>> -            '*downtime-limit': 'uint64',
>> -            '*x-checkpoint-delay': { 'type': 'uint32',
>> -                                     'features': [ 'unstable' ] },
>> -            '*multifd-channels': 'uint8',
>> -            '*xbzrle-cache-size': 'size',
>> -            '*max-postcopy-bandwidth': 'size',
>> -            '*max-cpu-throttle': 'uint8',
>> -            '*multifd-compression': 'MultiFDCompression',
>> -            '*multifd-zlib-level': 'uint8',
>> -            '*multifd-qatzip-level': 'uint8',
>> -            '*multifd-zstd-level': 'uint8',
>> -            '*block-bitmap-mapping': [ 'BitmapMigrationNodeAlias' ],
>> -            '*x-vcpu-dirty-limit-period': { 'type': 'uint64',
>> -                                            'features': [ 'unstable' ] },
>> -            '*vcpu-dirty-limit': 'uint64',
>> -            '*mode': 'MigMode',
>> -            '*zero-page-detection': 'ZeroPageDetection',
>> -            '*direct-io': 'bool' } }
>> -
>>  ##
>>  # @migrate-set-parameters:
>>  #
>> @@ -1124,12 +928,13 @@
>>  #     <- { "return": {} }
>>  ##
>>  { 'command': 'migrate-set-parameters', 'boxed': true,
>> -  'data': 'MigrateSetParameters' }
>> +  'data': 'MigrationParameters' }
>>  
>>  ##
>>  # @MigrationParameters:
>>  #
>> -# The optional members aren't actually optional.
>> +# Migration parameters. Optional members are optional when used with
>> +# an input command, otherwise mandatory.
>>  #
>>  # @announce-initial: Initial delay (in milliseconds) before sending
>>  #     the first announce (Since 4.0)
>> @@ -1172,21 +977,25 @@
>>  #     for establishing a TLS connection over the migration data
>>  #     channel.  On the outgoing side of the migration, the credentials
>>  #     must be for a 'client' endpoint, while for the incoming side the
>> -#     credentials must be for a 'server' endpoint.  An empty string
>> -#     means that QEMU will use plain text mode for migration, rather
>> -#     than TLS.  (Since 2.7)
>> -#
>> -#     Note: 2.8 omits empty @tls-creds instead.
>> +#     credentials must be for a 'server' endpoint.  Setting this to a
>> +#     non-empty string enables TLS for all migrations.  An empty
>> +#     string means that QEMU will use plain text mode for migration,
>> +#     rather than TLS.  This is the default.  (Since 2.7)
>>  #
>>  # @tls-hostname: migration target's hostname for validating the
>>  #     server's x509 certificate identity.  If empty, QEMU will use the
>> -#     hostname from the migration URI, if any.  (Since 2.7)
>> +#     hostname from the migration URI, if any.  A non-empty value is
>> +#     required when using x509 based TLS credentials and the migration
>> +#     URI does not include a hostname, such as fd: or exec: based
>> +#     migration.  (Since 2.7)
>>  #
>> -#     Note: 2.8 omits empty @tls-hostname instead.
>> +#     Note: empty value works only since 2.9.
>>  #
>>  # @tls-authz: ID of the 'authz' object subclass that provides access
>>  #     control checking of the TLS x509 certificate distinguished name.
>> -#     (Since 4.0)
>> +#     This object is only resolved at time of use, so can be deleted
>> +#     and recreated on the fly while the migration server is active.
>> +#     If missing, it will default to denying access (Since 4.0)
>>  #
>>  # @max-bandwidth: maximum speed for migration, in bytes per second.
>>  #     (Since 2.8)


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

* Re: [PATCH 03/21] qapi/migration: Don't document MigrationParameter
  2025-06-25 12:22     ` Markus Armbruster
@ 2025-06-25 17:29       ` Fabiano Rosas
  0 siblings, 0 replies; 83+ messages in thread
From: Fabiano Rosas @ 2025-06-25 17:29 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel, Peter Xu, Daniel P . Berrangé

Markus Armbruster <armbru@redhat.com> writes:

> Markus Armbruster <armbru@redhat.com> writes:
>
>> Fabiano Rosas <farosas@suse.de> writes:
>>
>>> The MigrationParameter (singular) enumeration is not part of the
>>> migration QMP API, it's only used for nicely converting HMP strings
>>> into MigrationParameters (plural) members and for providing readline
>>> completion.
>>
>>
>>
>>> Documenting this enum only serves to duplicate documentation between
>>> MigrationParameter and MigrationParameters.
>>>
>>> Add an exception to QAPIs pragma.json and stop documenting it.
>>>
>>> Signed-off-by: Fabiano Rosas <farosas@suse.de>
>>> ---
>>>  qapi/migration.json | 152 +-------------------------------------------
>>>  qapi/pragma.json    |   3 +-
>>>  2 files changed, 3 insertions(+), 152 deletions(-)
>>>
>>> diff --git a/qapi/migration.json b/qapi/migration.json
>>> index 080968993a..452e6dedaa 100644
>>> --- a/qapi/migration.json
>>> +++ b/qapi/migration.json
>>> @@ -734,157 +734,7 @@
>>>  ##
>>>  # @MigrationParameter:
>>>  #
>>> -# Migration parameters enumeration
>>> -#
>>> -# @announce-initial: Initial delay (in milliseconds) before sending
>>> -#     the first announce (Since 4.0)
>>
>> [...]
>>
>>> -# @direct-io: Open migration files with O_DIRECT when possible.  This
>>> -#     only has effect if the @mapped-ram capability is enabled.
>>> -#     (Since 9.1)
>>> +# Migration parameters enumeration. See @MigrationParameters for more info.
>>
>> Suggest something like
>>
>>    # The enumeration values mirror the members of MigrationParameters,
>>    # which see.
>>
>> I could compare the deleted docs with MigrationParameters docs, but I
>> doubt it's worthwhile: the type is only ever used internally.  That it
>> appears in the QEMU QMP Reference Manual anyway is a defect.  There are
>> quite a few more like it (list appended).
>>
>> If I remember correctly, John Snow's doc generator work will fix this
>> defect.
>
> Until then, this patch has a drawback: the manual now shows all the
> members as "Not documented" .  Ugly and a bit embarrassing.  Maybe even
> confusing.
>

I expect the part about checking @MigrationParameters to be clear enough
in explaining why the members are "Not documented".

I could live with the enum being documented still, but there's always
churn when people touch migration.json and get something wrong due to
the duplication. I'd rather not have it.


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

* Re: [PATCH 01/21] migration: Normalize tls arguments
  2025-06-25 17:17     ` Fabiano Rosas
@ 2025-06-26  9:38       ` Markus Armbruster
  2025-06-26 14:51         ` Fabiano Rosas
  0 siblings, 1 reply; 83+ messages in thread
From: Markus Armbruster @ 2025-06-26  9:38 UTC (permalink / raw)
  To: Fabiano Rosas
  Cc: Markus Armbruster, qemu-devel, Peter Xu, Daniel P . Berrangé

Fabiano Rosas <farosas@suse.de> writes:

> Markus Armbruster <armbru@redhat.com> writes:
>
>> Fabiano Rosas <farosas@suse.de> writes:
>>
>>> The migration parameters tls_creds, tls_authz and tls_hostname
>>> currently have a non-uniform handling. When used as arguments to
>>> migrate-set-parameters, their type is StrOrNull and when used as
>>> return value from query-migrate-parameters, their type is a plain
>>> string.
>>>
>>> Not only having to convert between the types is cumbersome, but it
>>> also creates the issue of requiring two different QAPI types to be
>>> used, one for each command. MigrateSetParameters is used for
>>> migrate-set-parameters with the TLS arguments as StrOrNull while
>>> MigrationParameters is used for query-migrate-parameters with the TLS
>>> arguments as str.
>>>
>>> Since StrOrNull could be considered a superset of str, change the type
>>> of the TLS arguments in MigrationParameters to StrOrNull and add a
>>> helper to ensure they're never actually used as QTYPE_QNULL.
>>
>> The type of @tls_creds, @tls-hostname, @tls-authz changes from str to
>> StrOrNull in introspection query-migrate-parameters.  Loss of precision.
>> Introspection is already imprecise: it shows the members optional even
>> though they aren't.  We accept the loss of precision to enable
>> de-duplication.
>>
>> This should be worked into the commit message.
>>
>
> Ack.
>
>>> This will allow the type duplication to be removed in the next
>>> patches.
>>>
>>> Signed-off-by: Fabiano Rosas <farosas@suse.de>
>>> ---
>>>  migration/migration-hmp-cmds.c |   8 +-
>>>  migration/migration.c          |   2 +
>>>  migration/options.c            | 149 ++++++++++++++++++++-------------
>>>  migration/options.h            |   1 +
>>>  migration/tls.c                |   2 +-
>>>  qapi/migration.json            |   6 +-
>>>  6 files changed, 99 insertions(+), 69 deletions(-)
>>>
>>> diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c
>>> index e8a563c7d8..bc8179c582 100644
>>> --- a/migration/migration-hmp-cmds.c
>>> +++ b/migration/migration-hmp-cmds.c
>>> @@ -276,14 +276,12 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict)
>>>          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);
>>> -        assert(params->tls_hostname);
>>> +                       params->tls_creds ? params->tls_creds->u.s : "");
>>>          monitor_printf(mon, "%s: '%s'\n",
>>>              MigrationParameter_str(MIGRATION_PARAMETER_TLS_HOSTNAME),
>>> -            params->tls_hostname);
>>> +                       params->tls_hostname ? params->tls_hostname->u.s : "");
>>>          assert(params->has_max_bandwidth);
>>>          monitor_printf(mon, "%s: %" PRIu64 " bytes/second\n",
>>>              MigrationParameter_str(MIGRATION_PARAMETER_MAX_BANDWIDTH),
>>> @@ -319,7 +317,7 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict)
>>>              params->max_postcopy_bandwidth);
>>>          monitor_printf(mon, "%s: '%s'\n",
>>>              MigrationParameter_str(MIGRATION_PARAMETER_TLS_AUTHZ),
>>> -            params->tls_authz);
>>> +                       params->tls_authz ? params->tls_authz->u.s : "");
>>>  
>>>          if (params->has_block_bitmap_mapping) {
>>>              const BitmapMigrationNodeAliasList *bmnal;
>>
>> Before, the code assumes ->tls_creds, ->tls_hostname, and ->tls_authz
>> are non-null.  It assert its assumption for the first two.
>>
>> Afterwards, it maps null to "".  Why is that necessary?  Hmm, see below.
>>
>
> Maps NULL to "" because the intermediate type, MigrationParameters, has
> been changed from str to StrOrNull. For the purposes of info
> migrate_parameters and query-migrate-parameters the only valid values
> are a non-empty string or an empty string.

But is NULL possible?  If you just change the type from str to StrOrNull
and no more, it isn't.

>>> diff --git a/migration/migration.c b/migration/migration.c
>>> index 4697732bef..f65cb81b6d 100644
>>> --- a/migration/migration.c
>>> +++ b/migration/migration.c
>>> @@ -4053,6 +4053,8 @@ static void migration_instance_finalize(Object *obj)
>>>  {
>>>      MigrationState *ms = MIGRATION_OBJ(obj);
>>>  
>>> +    migrate_tls_opts_free(&ms->parameters);
>>
>> Is this a bug fix?
>>
>
> My new version is a little different, but I can make it a separate patch
> if that happens to be the case.

Yes, please.

>> As far as I can tell, the object gets destroyed only on QEMU shutdown.
>> Freeing resources then is unnecessary, except it may help leak detection
>> tools.
>>
>
> From a maintenance perspective I consider any leak as a bug because I
> don't have control over what kinds of leak detection tools are ran on
> the code. To avoid spending time looking at such bug reports I have ASAN
> automated in my testing and I fix early any leaks that it founds.
>
>>> +
>>>      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 162c72cda4..45a95dc6da 100644
>>> --- a/migration/options.c
>>> +++ b/migration/options.c
>>> @@ -162,9 +162,11 @@ const Property migration_properties[] = {
>>>      DEFINE_PROP_SIZE("announce-step", MigrationState,
>>>                        parameters.announce_step,
>>>                        DEFAULT_MIGRATE_ANNOUNCE_STEP),
>>> -    DEFINE_PROP_STRING("tls-creds", MigrationState, parameters.tls_creds),
>>> -    DEFINE_PROP_STRING("tls-hostname", MigrationState, parameters.tls_hostname),
>>> -    DEFINE_PROP_STRING("tls-authz", MigrationState, parameters.tls_authz),
>>> +    /*
>>> +     * tls-creds, tls-hostname and tls-authz are of type StrOrNull,
>>> +     * which can't be easily handled (if at all) by qdev. So these
>>> +     * will not be exposed as global migration options (-global).
>>> +     */
>>
>> This is a compatibility break.
>>
>> The orthodox way to break it is deprecate, let the grace period expire,
>> break.  Record in docs/about/deprecated.rst at the beginning, move the
>> record to docs/about/removed-features.rst at the end.
>>
>> An argument could be made that the interface in question is
>> accidental[*], not actually used by anything, and therefore breaking it
>> without a grace period is fine.  But even then we should record the
>> breakage in docs/about/removed-features.rst.
>>
>
> Ok. Alternatively I could try a little harder to keep these
> options. I'll see what I can do.

What do we think about this external interface?

If we think it's accidental and unused, then putting in more work to
keep it makes no sense.

If we think it's deliberate and/or used, we should either keep it, or
replace it the orthodox way.

Trouble is a common answer for use of accidental interfaces is "beats
me".  I wish we'd stop creating them.

>> Aside: the interface in question is a hack (making the migration object
>> a device) piled onto a hack (the way compat properties work, and how
>> they spill into -global).  Past sins catching up with us...
>>
>
> Hm, I've been experimenting with adding new objects (still TYPE_DEVICE)
> to hold the compatibility options and parameters only. Not the entire
> migration state. The MigrationState object would then be converted into
> a regular struct.
>
> MigrationState => internal type holding migration state (a better name
> could be used)
> MigrationOptionsState => -global migration-options.foo=on
> MigrationCompatState => -global migration foo=on
>
> But it's getting a little tricky due to s->parameters *also* containing
> compat options, namely zero-page-detection.

-global is a trap.  Heck, global configuration state is a trap.  See
discussion in review of RFC v3.

We can't always fill in the our spiked pits.  Sometimes the best we can
do is to plank them and put up some "here be danger" signs.

>>>      DEFINE_PROP_UINT64("x-vcpu-dirty-limit-period", MigrationState,
>>>                         parameters.x_vcpu_dirty_limit_period,
>>>                         DEFAULT_MIGRATE_VCPU_DIRTY_LIMIT_PERIOD),
>>> @@ -379,13 +381,6 @@ bool migrate_rdma(void)
>>>      return s->rdma_migration;
>>>  }
>>>  
>>> -bool migrate_tls(void)
>>> -{
>>> -    MigrationState *s = migrate_get_current();
>>> -
>>> -    return s->parameters.tls_creds && *s->parameters.tls_creds;
>>> -}
>>> -
>>>  typedef enum WriteTrackingSupport {
>>>      WT_SUPPORT_UNKNOWN = 0,
>>>      WT_SUPPORT_ABSENT,
>>> @@ -834,21 +829,44 @@ const char *migrate_tls_authz(void)
>>>  {
>>>      MigrationState *s = migrate_get_current();
>>>  
>>> -    return s->parameters.tls_authz;
>>> +    if (s->parameters.tls_authz &&
>>> +        s->parameters.tls_authz->type == QTYPE_QSTRING &&
>>> +        *s->parameters.tls_authz->u.s) {
>>> +        return s->parameters.tls_authz->u.s;
>>> +    }
>>> +
>>> +    return NULL;
>>>  }
>>>  
>>>  const char *migrate_tls_creds(void)
>>>  {
>>>      MigrationState *s = migrate_get_current();
>>>  
>>> -    return s->parameters.tls_creds;
>>> +    if (s->parameters.tls_creds &&
>>> +        s->parameters.tls_creds->type == QTYPE_QSTRING &&
>>> +        *s->parameters.tls_creds->u.s) {
>>> +        return s->parameters.tls_creds->u.s;
>>> +    }
>>> +
>>> +    return NULL;
>>>  }
>>>  
>>>  const char *migrate_tls_hostname(void)
>>>  {
>>>      MigrationState *s = migrate_get_current();
>>>  
>>> -    return s->parameters.tls_hostname;
>>> +    if (s->parameters.tls_hostname &&
>>> +        s->parameters.tls_hostname->type == QTYPE_QSTRING &&
>>> +        *s->parameters.tls_hostname->u.s) {
>>> +        return s->parameters.tls_hostname->u.s;
>>> +    }
>>> +
>>> +    return NULL;
>>> +}
>>
>> Again, the code changes to cope with null.  Why is that necessary?
>> Again, see below.
>>
>
> This is actually a bit roundabout indeed. In my new version I do:
>
> - migrate-set-parameters: always write either NULL or a non-empty
>   QTYPE_QSTRING to s->parameters.
>     
> - query-migrate-parameters: always return a (possibly empty)
>   QTYPE_QSTRING.
>
> With this, internal representation is: NULL for not present, non-empty
> string for present.
>
>>> +
>>> +bool migrate_tls(void)
>>> +{
>>> +    return !!migrate_tls_creds();
>>>  }
>>>  
>>>  uint64_t migrate_vcpu_dirty_limit_period(void)
>>> @@ -888,6 +906,36 @@ AnnounceParameters *migrate_announce_params(void)
>>>      return &ap;
>>>  }
>>>  
>>> +void migrate_tls_opts_free(MigrationParameters *params)
>>> +{
>>> +    qapi_free_StrOrNull(params->tls_creds);
>>> +    qapi_free_StrOrNull(params->tls_hostname);
>>> +    qapi_free_StrOrNull(params->tls_authz);
>>> +}
>>> +
>>> +/* needs BQL if dst is part of s->parameters */
>>> +static void tls_option_set_str(StrOrNull **dstp, StrOrNull *src)
>>> +{
>>> +    StrOrNull *dst = *dstp;
>>> +
>>> +    assert(!dst);
>>> +
>>> +    dst = *dstp = g_new0(StrOrNull, 1);
>>> +    dst->type = QTYPE_QSTRING;
>>> +
>>> +    if (!src) {
>>> +        dst->u.s = g_strdup("");
>>> +        return;
>>> +    }
>>> +
>>> +    if (src->type == QTYPE_QSTRING) {
>>> +        dst->u.s = g_strdup(src->u.s);
>>> +    } else {
>>> +        assert(src->type == QTYPE_QNULL);
>>> +        dst->u.s = g_strdup("");
>>> +    }
>>> +}
>>
>> Postcondition: dstp points to a StrOrNull containing a str,
>> i.e. QTYPE_QSTRING.  Makes sense.
>>
>> I'd prefer something like
>>
>>        StrOrNull *dst = g_new0(StrOrNull, 1);
>>
>>        ... fill in members ...
>>
>>        assert(!*dstp);
>>        *dstp = dst;
>>
>
> This code is also reworked a bit on the next version, I'll see whether
> the comment still applies.
>
>>> +
>>>  MigrationParameters *qmp_query_migrate_parameters(Error **errp)
>>>  {
>>>      MigrationParameters *params;
>>> @@ -903,10 +951,11 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp)
>>>      params->cpu_throttle_increment = s->parameters.cpu_throttle_increment;
>>>      params->has_cpu_throttle_tailslow = true;
>>>      params->cpu_throttle_tailslow = s->parameters.cpu_throttle_tailslow;
>>> -    params->tls_creds = g_strdup(s->parameters.tls_creds);
>>> -    params->tls_hostname = g_strdup(s->parameters.tls_hostname);
>>> -    params->tls_authz = g_strdup(s->parameters.tls_authz ?
>>> -                                 s->parameters.tls_authz : "");
>>> +
>>> +    tls_option_set_str(&params->tls_creds, s->parameters.tls_creds);
>>> +    tls_option_set_str(&params->tls_hostname, s->parameters.tls_hostname);
>>> +    tls_option_set_str(&params->tls_authz, s->parameters.tls_authz);
>>> +
>>>      params->has_max_bandwidth = true;
>>>      params->max_bandwidth = s->parameters.max_bandwidth;
>>>      params->has_avail_switchover_bandwidth = true;
>>> @@ -963,9 +1012,6 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp)
>>>  
>>>  void migrate_params_init(MigrationParameters *params)
>>>  {
>>> -    params->tls_hostname = g_strdup("");
>>> -    params->tls_creds = g_strdup("");
>>> -
>>
>> Is this the reason why the code now needs to deal with null?
>>
>> I'm not objecting, just pointing out that the commit message didn't
>> prepare me for such a change.
>>
>
> I'll document it.
>
>>>      /* Set has_* up only for parameter checks */
>>>      params->has_throttle_trigger_threshold = true;
>>>      params->has_cpu_throttle_initial = true;
>>
>> I'm stopping here to ask: how exactly does the patch change quasi-global
>> state, namely current_migration->parameters->tls_*?
>>
>
> It makes sure the current_migration->parameters->tls_* options are
> always QTYPE_QSTRING and either a non-empty or an empty string.
>
> The next version of the patch will instead use non-empty QTYPE_QSTRING
> or NULL, which is cleaner from a C perspective.

Agree.

The struct members will have type StrOrNull, but only certain values can
occur: non-empty string, and NULL.  Worth a comment, I think.  Also
consider assertions.

> Both versions ensure the query-* and set-* commands continue to expose
> the same values. Query only shows non-empty or empty string and Set
> accepts all values of a StrOrNull type.
>
>> [...]
>>
>>
>> [*] We have oh so many accidental external interfaces.



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

* Re: [PATCH 02/21] migration: Remove MigrateSetParameters
  2025-06-25 17:21     ` Fabiano Rosas
@ 2025-06-26  9:40       ` Markus Armbruster
  0 siblings, 0 replies; 83+ messages in thread
From: Markus Armbruster @ 2025-06-26  9:40 UTC (permalink / raw)
  To: Fabiano Rosas
  Cc: Markus Armbruster, qemu-devel, Peter Xu, Daniel P . Berrangé

Fabiano Rosas <farosas@suse.de> writes:

> Markus Armbruster <armbru@redhat.com> writes:
>
>> Fabiano Rosas <farosas@suse.de> writes:
>>
>>> Now that the TLS options have been made the same between
>>> migrate-set-parameters and query-migrate-parameters, a single type can
>>> be used. Remove MigrateSetParameters.
>>>
>>> The TLS options documentation from MigrationParameters were replaced
>>> with the ones from MigrateSetParameters which was more complete.
>>>
>>> I'm choosing to somewhat ignore any ambiguity between "query" and
>>> "set" because other options' docs are already ambiguous in that
>>> regard.
>>>
>>> Signed-off-by: Fabiano Rosas <farosas@suse.de>
>>> ---
>>>  migration/migration-hmp-cmds.c |   4 +-
>>>  migration/options.c            |   6 +-
>>>  qapi/migration.json            | 221 +++------------------------------
>>>  3 files changed, 20 insertions(+), 211 deletions(-)
>>>
>>> diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c
>>> index bc8179c582..aacffdc532 100644
>>> --- a/migration/migration-hmp-cmds.c
>>> +++ b/migration/migration-hmp-cmds.c
>>> @@ -490,7 +490,7 @@ void hmp_migrate_set_parameter(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);
>>> -    MigrateSetParameters *p = g_new0(MigrateSetParameters, 1);
>>> +    MigrationParameters *p = g_new0(MigrationParameters, 1);
>>>      uint64_t valuebw = 0;
>>>      uint64_t cache_size;
>>>      Error *err = NULL;
>>> @@ -656,7 +656,7 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict)
>>>      qmp_migrate_set_parameters(p, &err);
>>>  
>>>   cleanup:
>>> -    qapi_free_MigrateSetParameters(p);
>>> +    qapi_free_MigrationParameters(p);
>>>      visit_free(v);
>>>      hmp_handle_error(mon, err);
>>>  }
>>> diff --git a/migration/options.c b/migration/options.c
>>> index 45a95dc6da..e49d584a99 100644
>>> --- a/migration/options.c
>>> +++ b/migration/options.c
>>> @@ -1227,7 +1227,7 @@ bool migrate_params_check(MigrationParameters *params, Error **errp)
>>>      return true;
>>>  }
>>>  
>>> -static void migrate_params_test_apply(MigrateSetParameters *params,
>>> +static void migrate_params_test_apply(MigrationParameters *params,
>>>                                        MigrationParameters *dest)
>>>  {
>>>      *dest = migrate_get_current()->parameters;
>>> @@ -1350,7 +1350,7 @@ static void migrate_params_test_apply(MigrateSetParameters *params,
>>>      }
>>>  }
>>>  
>>> -static void migrate_params_apply(MigrateSetParameters *params, Error **errp)
>>> +static void migrate_params_apply(MigrationParameters *params, Error **errp)
>>>  {
>>>      MigrationState *s = migrate_get_current();
>>>  
>>> @@ -1479,7 +1479,7 @@ static void migrate_params_apply(MigrateSetParameters *params, Error **errp)
>>>      }
>>>  }
>>>  
>>> -void qmp_migrate_set_parameters(MigrateSetParameters *params, Error **errp)
>>> +void qmp_migrate_set_parameters(MigrationParameters *params, Error **errp)
>>>  {
>>>      MigrationParameters tmp;
>>>  
>>> diff --git a/qapi/migration.json b/qapi/migration.json
>>> index fa42d94810..080968993a 100644
>>> --- a/qapi/migration.json
>>> +++ b/qapi/migration.json
>>> @@ -914,202 +914,6 @@
>>>             'zero-page-detection',
>>>             'direct-io'] }
>>>  
>>> -##
>>> -# @MigrateSetParameters:
>>
>> Only use is argument type of migrate-set-parameters.  You're replacing
>> it by MigrationParameters there.  Let's compare the deleted docs to
>> their replacement.  I'll quote replacement docs exactly where they
>> differ.
>>
>>    # @MigrationParameters:
>>    #
>>    # Migration parameters. Optional members are optional when used with
>>    # an input command, otherwise mandatory.
>>
>> Figuring out which commands are input commands is left to the reader.
>> Why not simply "optional with migrate-set-parameters"?
>>
>
> Future patches include migrate and migrate-incoming. I can enumerate
> them if that's better.

Not necessary if you move the note to commands as discussed below.

>> However, it doesn't end there.  The paragraph creates a problem with
>> John Snow's "inliner", which I hope to merge later this year.  Let me
>> explain.
>>
>> Generated command documentation normally looks like this:
>>
>>     Command migrate-set-capabilities (Since: 1.2)
>>
>>        Enable/Disable the following migration capabilities (like xbzrle)
>>
>>        Arguments:
>>           * **capabilities** ("[""MigrationCapabilityStatus""]") -- json
>>             array of capability modifications to make
>>
>> Except when we happen to use a named type for the arguments.  This
>> should be an implementation detail, and it is, except for generated
>> documentation, which looks like
>>
>>     Command migrate-set-parameters (Since: 2.4)
>>
>>        Set various migration parameters.
>>
>>        Arguments:
>>           * The members of "MigrationParameters".
>>
>> The arguments are hidden behind a link.  The "inliner" will show the
>> them normally *always*, for better usability.  It will not, however,
>> inline the introductory paragraph above.  I can explain why if
>> necessary.
>>
>> To compensate for the loss of that paragraph, we'll have to add suitable
>> text to migrate-set-parameters's doc comment.
>>
>> I think we could just as well do that *now*: scratch the paragraph here,
>> add a suitable paragraph there.
>>
>
> Ok, no worries.

[...]



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

* Re: [PATCH 01/21] migration: Normalize tls arguments
  2025-06-26  9:38       ` Markus Armbruster
@ 2025-06-26 14:51         ` Fabiano Rosas
  2025-06-27  7:10           ` Markus Armbruster
  0 siblings, 1 reply; 83+ messages in thread
From: Fabiano Rosas @ 2025-06-26 14:51 UTC (permalink / raw)
  To: Markus Armbruster
  Cc: Markus Armbruster, qemu-devel, Peter Xu, Daniel P . Berrangé

Markus Armbruster <armbru@redhat.com> writes:

> Fabiano Rosas <farosas@suse.de> writes:
>
>> Markus Armbruster <armbru@redhat.com> writes:
>>
>>> Fabiano Rosas <farosas@suse.de> writes:
>>>
>>>> The migration parameters tls_creds, tls_authz and tls_hostname
>>>> currently have a non-uniform handling. When used as arguments to
>>>> migrate-set-parameters, their type is StrOrNull and when used as
>>>> return value from query-migrate-parameters, their type is a plain
>>>> string.
>>>>
>>>> Not only having to convert between the types is cumbersome, but it
>>>> also creates the issue of requiring two different QAPI types to be
>>>> used, one for each command. MigrateSetParameters is used for
>>>> migrate-set-parameters with the TLS arguments as StrOrNull while
>>>> MigrationParameters is used for query-migrate-parameters with the TLS
>>>> arguments as str.
>>>>
>>>> Since StrOrNull could be considered a superset of str, change the type
>>>> of the TLS arguments in MigrationParameters to StrOrNull and add a
>>>> helper to ensure they're never actually used as QTYPE_QNULL.
>>>
>>> The type of @tls_creds, @tls-hostname, @tls-authz changes from str to
>>> StrOrNull in introspection query-migrate-parameters.  Loss of precision.
>>> Introspection is already imprecise: it shows the members optional even
>>> though they aren't.  We accept the loss of precision to enable
>>> de-duplication.
>>>
>>> This should be worked into the commit message.
>>>
>>
>> Ack.
>>
>>>> This will allow the type duplication to be removed in the next
>>>> patches.
>>>>
>>>> Signed-off-by: Fabiano Rosas <farosas@suse.de>
>>>> ---
>>>>  migration/migration-hmp-cmds.c |   8 +-
>>>>  migration/migration.c          |   2 +
>>>>  migration/options.c            | 149 ++++++++++++++++++++-------------
>>>>  migration/options.h            |   1 +
>>>>  migration/tls.c                |   2 +-
>>>>  qapi/migration.json            |   6 +-
>>>>  6 files changed, 99 insertions(+), 69 deletions(-)
>>>>
>>>> diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c
>>>> index e8a563c7d8..bc8179c582 100644
>>>> --- a/migration/migration-hmp-cmds.c
>>>> +++ b/migration/migration-hmp-cmds.c
>>>> @@ -276,14 +276,12 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict)
>>>>          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);
>>>> -        assert(params->tls_hostname);
>>>> +                       params->tls_creds ? params->tls_creds->u.s : "");
>>>>          monitor_printf(mon, "%s: '%s'\n",
>>>>              MigrationParameter_str(MIGRATION_PARAMETER_TLS_HOSTNAME),
>>>> -            params->tls_hostname);
>>>> +                       params->tls_hostname ? params->tls_hostname->u.s : "");
>>>>          assert(params->has_max_bandwidth);
>>>>          monitor_printf(mon, "%s: %" PRIu64 " bytes/second\n",
>>>>              MigrationParameter_str(MIGRATION_PARAMETER_MAX_BANDWIDTH),
>>>> @@ -319,7 +317,7 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict)
>>>>              params->max_postcopy_bandwidth);
>>>>          monitor_printf(mon, "%s: '%s'\n",
>>>>              MigrationParameter_str(MIGRATION_PARAMETER_TLS_AUTHZ),
>>>> -            params->tls_authz);
>>>> +                       params->tls_authz ? params->tls_authz->u.s : "");
>>>>  
>>>>          if (params->has_block_bitmap_mapping) {
>>>>              const BitmapMigrationNodeAliasList *bmnal;
>>>
>>> Before, the code assumes ->tls_creds, ->tls_hostname, and ->tls_authz
>>> are non-null.  It assert its assumption for the first two.
>>>
>>> Afterwards, it maps null to "".  Why is that necessary?  Hmm, see below.
>>>
>>
>> Maps NULL to "" because the intermediate type, MigrationParameters, has
>> been changed from str to StrOrNull. For the purposes of info
>> migrate_parameters and query-migrate-parameters the only valid values
>> are a non-empty string or an empty string.
>
> But is NULL possible?  If you just change the type from str to StrOrNull
> and no more, it isn't.
>

Since the TLS options don't have a qdev property anymore, they also
don't get set a default value. So s->parameters can indeed have the NULL
value in it.

I could initialize them in migrate_params_init. It's all about choosing
where to move the complexity. I'm leaning towards keeping it in the
interface: query-migrate converts them to whatever it needs to output
and set-migrate writes a normalized version into s->parameters.

>>>> diff --git a/migration/migration.c b/migration/migration.c
>>>> index 4697732bef..f65cb81b6d 100644
>>>> --- a/migration/migration.c
>>>> +++ b/migration/migration.c
>>>> @@ -4053,6 +4053,8 @@ static void migration_instance_finalize(Object *obj)
>>>>  {
>>>>      MigrationState *ms = MIGRATION_OBJ(obj);
>>>>  
>>>> +    migrate_tls_opts_free(&ms->parameters);
>>>
>>> Is this a bug fix?
>>>
>>
>> My new version is a little different, but I can make it a separate patch
>> if that happens to be the case.
>
> Yes, please.
>
>>> As far as I can tell, the object gets destroyed only on QEMU shutdown.
>>> Freeing resources then is unnecessary, except it may help leak detection
>>> tools.
>>>
>>
>> From a maintenance perspective I consider any leak as a bug because I
>> don't have control over what kinds of leak detection tools are ran on
>> the code. To avoid spending time looking at such bug reports I have ASAN
>> automated in my testing and I fix early any leaks that it founds.
>>
>>>> +
>>>>      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 162c72cda4..45a95dc6da 100644
>>>> --- a/migration/options.c
>>>> +++ b/migration/options.c
>>>> @@ -162,9 +162,11 @@ const Property migration_properties[] = {
>>>>      DEFINE_PROP_SIZE("announce-step", MigrationState,
>>>>                        parameters.announce_step,
>>>>                        DEFAULT_MIGRATE_ANNOUNCE_STEP),
>>>> -    DEFINE_PROP_STRING("tls-creds", MigrationState, parameters.tls_creds),
>>>> -    DEFINE_PROP_STRING("tls-hostname", MigrationState, parameters.tls_hostname),
>>>> -    DEFINE_PROP_STRING("tls-authz", MigrationState, parameters.tls_authz),
>>>> +    /*
>>>> +     * tls-creds, tls-hostname and tls-authz are of type StrOrNull,
>>>> +     * which can't be easily handled (if at all) by qdev. So these
>>>> +     * will not be exposed as global migration options (-global).
>>>> +     */
>>>
>>> This is a compatibility break.
>>>
>>> The orthodox way to break it is deprecate, let the grace period expire,
>>> break.  Record in docs/about/deprecated.rst at the beginning, move the
>>> record to docs/about/removed-features.rst at the end.
>>>
>>> An argument could be made that the interface in question is
>>> accidental[*], not actually used by anything, and therefore breaking it
>>> without a grace period is fine.  But even then we should record the
>>> breakage in docs/about/removed-features.rst.
>>>
>>
>> Ok. Alternatively I could try a little harder to keep these
>> options. I'll see what I can do.
>
> What do we think about this external interface?
>
> If we think it's accidental and unused, then putting in more work to
> keep it makes no sense.
>
> If we think it's deliberate and/or used, we should either keep it, or
> replace it the orthodox way.
>

There are two external interfaces actually.

-global migration.some_compat_option=on (stored in MigrationState):

seems intentional and I believe we'd lose the ability to get out of some
tricky situations if we ditched it.

-global migation.some_random_option=on (stored in MigrationParameters):

has become a debugging *feature*, which I personally don't use, but
others do. And worse: we don't know if anyone uses it in production.

We also arbitrarily put x- in front of options for some reason. There is
an argument to drop those because x- is scary and no one should be using
them.

I think it would be good to at least separate the responsibilities so
when the time comes we can deprecate/remove/replace the offending
interfaces. But I won't go into that now, there's already too much
change going on for this release.



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

* Re: [PATCH 01/21] migration: Normalize tls arguments
  2025-06-26 14:51         ` Fabiano Rosas
@ 2025-06-27  7:10           ` Markus Armbruster
  2025-06-27 20:28             ` Fabiano Rosas
  0 siblings, 1 reply; 83+ messages in thread
From: Markus Armbruster @ 2025-06-27  7:10 UTC (permalink / raw)
  To: Fabiano Rosas; +Cc: qemu-devel, Peter Xu, Daniel P . Berrangé

Fabiano Rosas <farosas@suse.de> writes:

> Markus Armbruster <armbru@redhat.com> writes:
>
>> Fabiano Rosas <farosas@suse.de> writes:
>>
>>> Markus Armbruster <armbru@redhat.com> writes:
>>>
>>>> Fabiano Rosas <farosas@suse.de> writes:
>>>>
>>>>> The migration parameters tls_creds, tls_authz and tls_hostname
>>>>> currently have a non-uniform handling. When used as arguments to
>>>>> migrate-set-parameters, their type is StrOrNull and when used as
>>>>> return value from query-migrate-parameters, their type is a plain
>>>>> string.
>>>>>
>>>>> Not only having to convert between the types is cumbersome, but it
>>>>> also creates the issue of requiring two different QAPI types to be
>>>>> used, one for each command. MigrateSetParameters is used for
>>>>> migrate-set-parameters with the TLS arguments as StrOrNull while
>>>>> MigrationParameters is used for query-migrate-parameters with the TLS
>>>>> arguments as str.
>>>>>
>>>>> Since StrOrNull could be considered a superset of str, change the type
>>>>> of the TLS arguments in MigrationParameters to StrOrNull and add a
>>>>> helper to ensure they're never actually used as QTYPE_QNULL.
>>>>
>>>> The type of @tls_creds, @tls-hostname, @tls-authz changes from str to
>>>> StrOrNull in introspection query-migrate-parameters.  Loss of precision.
>>>> Introspection is already imprecise: it shows the members optional even
>>>> though they aren't.  We accept the loss of precision to enable
>>>> de-duplication.
>>>>
>>>> This should be worked into the commit message.
>>>>
>>>
>>> Ack.
>>>
>>>>> This will allow the type duplication to be removed in the next
>>>>> patches.
>>>>>
>>>>> Signed-off-by: Fabiano Rosas <farosas@suse.de>
>>>>> ---
>>>>>  migration/migration-hmp-cmds.c |   8 +-
>>>>>  migration/migration.c          |   2 +
>>>>>  migration/options.c            | 149 ++++++++++++++++++++-------------
>>>>>  migration/options.h            |   1 +
>>>>>  migration/tls.c                |   2 +-
>>>>>  qapi/migration.json            |   6 +-
>>>>>  6 files changed, 99 insertions(+), 69 deletions(-)
>>>>>
>>>>> diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c
>>>>> index e8a563c7d8..bc8179c582 100644
>>>>> --- a/migration/migration-hmp-cmds.c
>>>>> +++ b/migration/migration-hmp-cmds.c
>>>>> @@ -276,14 +276,12 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict)
>>>>>          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);
>>>>> -        assert(params->tls_hostname);
>>>>> +                       params->tls_creds ? params->tls_creds->u.s : "");
>>>>>          monitor_printf(mon, "%s: '%s'\n",
>>>>>              MigrationParameter_str(MIGRATION_PARAMETER_TLS_HOSTNAME),
>>>>> -            params->tls_hostname);
>>>>> +                       params->tls_hostname ? params->tls_hostname->u.s : "");
>>>>>          assert(params->has_max_bandwidth);
>>>>>          monitor_printf(mon, "%s: %" PRIu64 " bytes/second\n",
>>>>>              MigrationParameter_str(MIGRATION_PARAMETER_MAX_BANDWIDTH),
>>>>> @@ -319,7 +317,7 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict)
>>>>>              params->max_postcopy_bandwidth);
>>>>>          monitor_printf(mon, "%s: '%s'\n",
>>>>>              MigrationParameter_str(MIGRATION_PARAMETER_TLS_AUTHZ),
>>>>> -            params->tls_authz);
>>>>> +                       params->tls_authz ? params->tls_authz->u.s : "");
>>>>>  
>>>>>          if (params->has_block_bitmap_mapping) {
>>>>>              const BitmapMigrationNodeAliasList *bmnal;
>>>>
>>>> Before, the code assumes ->tls_creds, ->tls_hostname, and ->tls_authz
>>>> are non-null.  It assert its assumption for the first two.
>>>>
>>>> Afterwards, it maps null to "".  Why is that necessary?  Hmm, see below.
>>>>
>>>
>>> Maps NULL to "" because the intermediate type, MigrationParameters, has
>>> been changed from str to StrOrNull. For the purposes of info
>>> migrate_parameters and query-migrate-parameters the only valid values
>>> are a non-empty string or an empty string.
>>
>> But is NULL possible?  If you just change the type from str to StrOrNull
>> and no more, it isn't.
>>
>
> Since the TLS options don't have a qdev property anymore, they also
> don't get set a default value. So s->parameters can indeed have the NULL
> value in it.
>
> I could initialize them in migrate_params_init. It's all about choosing
> where to move the complexity.

True!

>                               I'm leaning towards keeping it in the
> interface: query-migrate converts them to whatever it needs to output
> and set-migrate writes a normalized version into s->parameters.

There more than one way to skin this cat.  I like to keep state
normalized.

State is an optional StrOrNull.  Possible values:

* NULL

* QNull, i.e. non-NULL, ->type is QTYPE_QNULL

* Empty string, i.e. non-NULL, ->type is QTYPE_QSTRING, ->u.s is ""

* Non-empty string, i.e. non-NULL, -> type is QTYPE_QSTRING, ->u.s is
  not "" (and cannot be NULL)

As far as I understand, we have just two cases semantically:

* Set, value is a non-empty string (empty makes no sense)

* Unset

I'd normalize the state to "either NULL, or (non-empty) string".

When writing state, we need to normalize.

When reading state, we can rely on it being normalized.  Asserting it is
seems prudent, and should help readers.

[...]

>>>>> diff --git a/migration/options.c b/migration/options.c
>>>>> index 162c72cda4..45a95dc6da 100644
>>>>> --- a/migration/options.c
>>>>> +++ b/migration/options.c
>>>>> @@ -162,9 +162,11 @@ const Property migration_properties[] = {
>>>>>      DEFINE_PROP_SIZE("announce-step", MigrationState,
>>>>>                        parameters.announce_step,
>>>>>                        DEFAULT_MIGRATE_ANNOUNCE_STEP),
>>>>> -    DEFINE_PROP_STRING("tls-creds", MigrationState, parameters.tls_creds),
>>>>> -    DEFINE_PROP_STRING("tls-hostname", MigrationState, parameters.tls_hostname),
>>>>> -    DEFINE_PROP_STRING("tls-authz", MigrationState, parameters.tls_authz),
>>>>> +    /*
>>>>> +     * tls-creds, tls-hostname and tls-authz are of type StrOrNull,
>>>>> +     * which can't be easily handled (if at all) by qdev. So these
>>>>> +     * will not be exposed as global migration options (-global).
>>>>> +     */
>>>>
>>>> This is a compatibility break.
>>>>
>>>> The orthodox way to break it is deprecate, let the grace period expire,
>>>> break.  Record in docs/about/deprecated.rst at the beginning, move the
>>>> record to docs/about/removed-features.rst at the end.
>>>>
>>>> An argument could be made that the interface in question is
>>>> accidental[*], not actually used by anything, and therefore breaking it
>>>> without a grace period is fine.  But even then we should record the
>>>> breakage in docs/about/removed-features.rst.
>>>>
>>>
>>> Ok. Alternatively I could try a little harder to keep these
>>> options. I'll see what I can do.
>>
>> What do we think about this external interface?
>>
>> If we think it's accidental and unused, then putting in more work to
>> keep it makes no sense.
>>
>> If we think it's deliberate and/or used, we should either keep it, or
>> replace it the orthodox way.
>>
>
> There are two external interfaces actually.
>
> -global migration.some_compat_option=on (stored in MigrationState):
>
> seems intentional and I believe we'd lose the ability to get out of some
> tricky situations if we ditched it.
>
> -global migation.some_random_option=on (stored in MigrationParameters):
>
> has become a debugging *feature*, which I personally don't use, but
> others do. And worse: we don't know if anyone uses it in production.

Accidental external interface.

> We also arbitrarily put x- in front of options for some reason. There is
> an argument to drop those because x- is scary and no one should be using
> them.

We pretty much ditched the x- convention in the QAPI schema.
docs/devel/qapi-code-gen.rst:

    Names beginning with ``x-`` used to signify "experimental".  This
    convention has been replaced by special feature "unstable".

Goes back to

commit a3c45b3e62962f99338716b1347cfb0d427cea44
Author: Markus Armbruster <armbru@redhat.com>
Date:   Thu Oct 28 12:25:12 2021 +0200

    qapi: New special feature flag "unstable"
    
    By convention, names starting with "x-" are experimental.  The parts
    of external interfaces so named may be withdrawn or changed
    incompatibly in future releases.
    
    The naming convention makes unstable interfaces easy to recognize.
    Promoting something from experimental to stable involves a name
    change.  Client code needs to be updated.  Occasionally bothersome.
    
    Worse, the convention is not universally observed:
    
    * QOM type "input-barrier" has properties "x-origin", "y-origin".
      Looks accidental, but it's ABI since 4.2.
    
    * QOM types "memory-backend-file", "memory-backend-memfd",
      "memory-backend-ram", and "memory-backend-epc" have a property
      "x-use-canonical-path-for-ramblock-id" that is documented to be
      stable despite its name.
    
    We could document these exceptions, but documentation helps only
    humans.  We want to recognize "unstable" in code, like "deprecated".
    
    So support recognizing it the same way: introduce new special feature
    flag "unstable".  It will be treated specially by the QAPI generator,
    like the existing feature flag "deprecated", and unlike regular
    feature flags.
    
    This commit updates documentation and prepares tests.  The next commit
    updates the QAPI schema.  The remaining patches update the QAPI
    generator and wire up -compat policy checking.
    
    Management applications can then use query-qmp-schema and -compat to
    manage or guard against use of unstable interfaces the same way as for
    deprecated interfaces.
    
    docs/devel/qapi-code-gen.txt no longer mandates the naming convention.
    Using it anyway might help writers of programs that aren't
    full-fledged management applications.  Not using it can save us
    bothersome renames.  We'll see how that shakes out.
    
    Signed-off-by: Markus Armbruster <armbru@redhat.com>
    Reviewed-by: Juan Quintela <quintela@redhat.com>
    Reviewed-by: John Snow <jsnow@redhat.com>
    Message-Id: <20211028102520.747396-2-armbru@redhat.com>

The x- convention lives on in external interfaces that bypass QAPI, in
particular QOM/qdev properties.

> I think it would be good to at least separate the responsibilities so
> when the time comes we can deprecate/remove/replace the offending
> interfaces. But I won't go into that now, there's already too much
> change going on for this release.

Makes sense.



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

* Re: [PATCH 01/21] migration: Normalize tls arguments
  2025-06-27  7:10           ` Markus Armbruster
@ 2025-06-27 20:28             ` Fabiano Rosas
  2025-07-01  7:08               ` Markus Armbruster
  0 siblings, 1 reply; 83+ messages in thread
From: Fabiano Rosas @ 2025-06-27 20:28 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel, Peter Xu, Daniel P . Berrangé

Markus Armbruster <armbru@redhat.com> writes:

> Fabiano Rosas <farosas@suse.de> writes:
>
>> Markus Armbruster <armbru@redhat.com> writes:
>>
>>> Fabiano Rosas <farosas@suse.de> writes:
>>>
>>>> Markus Armbruster <armbru@redhat.com> writes:
>>>>
>>>>> Fabiano Rosas <farosas@suse.de> writes:
>>>>>
>>>>>> The migration parameters tls_creds, tls_authz and tls_hostname
>>>>>> currently have a non-uniform handling. When used as arguments to
>>>>>> migrate-set-parameters, their type is StrOrNull and when used as
>>>>>> return value from query-migrate-parameters, their type is a plain
>>>>>> string.
>>>>>>
>>>>>> Not only having to convert between the types is cumbersome, but it
>>>>>> also creates the issue of requiring two different QAPI types to be
>>>>>> used, one for each command. MigrateSetParameters is used for
>>>>>> migrate-set-parameters with the TLS arguments as StrOrNull while
>>>>>> MigrationParameters is used for query-migrate-parameters with the TLS
>>>>>> arguments as str.
>>>>>>
>>>>>> Since StrOrNull could be considered a superset of str, change the type
>>>>>> of the TLS arguments in MigrationParameters to StrOrNull and add a
>>>>>> helper to ensure they're never actually used as QTYPE_QNULL.
>>>>>
>>>>> The type of @tls_creds, @tls-hostname, @tls-authz changes from str to
>>>>> StrOrNull in introspection query-migrate-parameters.  Loss of precision.
>>>>> Introspection is already imprecise: it shows the members optional even
>>>>> though they aren't.  We accept the loss of precision to enable
>>>>> de-duplication.
>>>>>
>>>>> This should be worked into the commit message.
>>>>>
>>>>
>>>> Ack.
>>>>
>>>>>> This will allow the type duplication to be removed in the next
>>>>>> patches.
>>>>>>
>>>>>> Signed-off-by: Fabiano Rosas <farosas@suse.de>
>>>>>> ---
>>>>>>  migration/migration-hmp-cmds.c |   8 +-
>>>>>>  migration/migration.c          |   2 +
>>>>>>  migration/options.c            | 149 ++++++++++++++++++++-------------
>>>>>>  migration/options.h            |   1 +
>>>>>>  migration/tls.c                |   2 +-
>>>>>>  qapi/migration.json            |   6 +-
>>>>>>  6 files changed, 99 insertions(+), 69 deletions(-)
>>>>>>
>>>>>> diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c
>>>>>> index e8a563c7d8..bc8179c582 100644
>>>>>> --- a/migration/migration-hmp-cmds.c
>>>>>> +++ b/migration/migration-hmp-cmds.c
>>>>>> @@ -276,14 +276,12 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict)
>>>>>>          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);
>>>>>> -        assert(params->tls_hostname);
>>>>>> +                       params->tls_creds ? params->tls_creds->u.s : "");
>>>>>>          monitor_printf(mon, "%s: '%s'\n",
>>>>>>              MigrationParameter_str(MIGRATION_PARAMETER_TLS_HOSTNAME),
>>>>>> -            params->tls_hostname);
>>>>>> +                       params->tls_hostname ? params->tls_hostname->u.s : "");
>>>>>>          assert(params->has_max_bandwidth);
>>>>>>          monitor_printf(mon, "%s: %" PRIu64 " bytes/second\n",
>>>>>>              MigrationParameter_str(MIGRATION_PARAMETER_MAX_BANDWIDTH),
>>>>>> @@ -319,7 +317,7 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict)
>>>>>>              params->max_postcopy_bandwidth);
>>>>>>          monitor_printf(mon, "%s: '%s'\n",
>>>>>>              MigrationParameter_str(MIGRATION_PARAMETER_TLS_AUTHZ),
>>>>>> -            params->tls_authz);
>>>>>> +                       params->tls_authz ? params->tls_authz->u.s : "");
>>>>>>  
>>>>>>          if (params->has_block_bitmap_mapping) {
>>>>>>              const BitmapMigrationNodeAliasList *bmnal;
>>>>>
>>>>> Before, the code assumes ->tls_creds, ->tls_hostname, and ->tls_authz
>>>>> are non-null.  It assert its assumption for the first two.
>>>>>
>>>>> Afterwards, it maps null to "".  Why is that necessary?  Hmm, see below.
>>>>>
>>>>
>>>> Maps NULL to "" because the intermediate type, MigrationParameters, has
>>>> been changed from str to StrOrNull. For the purposes of info
>>>> migrate_parameters and query-migrate-parameters the only valid values
>>>> are a non-empty string or an empty string.
>>>
>>> But is NULL possible?  If you just change the type from str to StrOrNull
>>> and no more, it isn't.
>>>
>>
>> Since the TLS options don't have a qdev property anymore, they also
>> don't get set a default value. So s->parameters can indeed have the NULL
>> value in it.
>>
>> I could initialize them in migrate_params_init. It's all about choosing
>> where to move the complexity.
>
> True!
>
>>                               I'm leaning towards keeping it in the
>> interface: query-migrate converts them to whatever it needs to output
>> and set-migrate writes a normalized version into s->parameters.
>
> There more than one way to skin this cat.  I like to keep state
> normalized.
>
> State is an optional StrOrNull.  Possible values:
>
> * NULL
>
> * QNull, i.e. non-NULL, ->type is QTYPE_QNULL
>
> * Empty string, i.e. non-NULL, ->type is QTYPE_QSTRING, ->u.s is ""
>
> * Non-empty string, i.e. non-NULL, -> type is QTYPE_QSTRING, ->u.s is
>   not "" (and cannot be NULL)
>
> As far as I understand, we have just two cases semantically:
>
> * Set, value is a non-empty string (empty makes no sense)
>
> * Unset
>
> I'd normalize the state to "either NULL, or (non-empty) string".
>

This is what I wanted to do (in the next version), but it results in
more complex and less readable code:

A) "abc"|NULL

hmp_info_migrate_parameters:
    monitor_printf(..., params->tls_creds ? params->tls_creds->u.s : "");

migrate_tls_creds:
    if (s->parameters.tls_creds) {
        return s->parameters.tls_creds->u.s;
    }
    return NULL;

migrate_params_check:
    if (migrate_zero_copy_send() && params->tls_creds ...

qmp_migrate_set_parameters:
    migrate_params_test_apply(params);
    if (migrate_params_check(tmp)) {
        migrate_params_apply(params);
        tls_normalize_qnull_empty_str_to_null(&s->parameters);  <-- [1]
    }

qmp_query_migrate_parameters:
    params = QAPI_CLONE(MigrationParameters, &s->parameters);
    tls_normalize_null_back_to_empty_str();   <-- [2]

Issues here are that in [1] we can't normalize earlier because we need
QTYPE_QNULL to know whether the option was ever set and in [2], since
s->parameters may contain NULL, we need another conversion step to be
able to return "". Overall, this makes the normalization step not very
useful because we need to keep track of which parts of the code are
normalized or not.

If we instead normalize to "either non-empty string or empty string"
then:

B) "abc"|""

hmp_info_migrate_parameters:
    monitor_printf(..., params->tls_creds);

migrate_tls_creds:
    if (*s->parameters.tls_creds->u.s) {
        return s->parameters.tls_creds->u.s;
    }
    return NULL;   <-- [1]

migrate_params_check:
    if (migrate_zero_copy_send() && *params->tls_creds->u.s ...

qmp_migrate_set_parameters:
    tls_normalize_qnull_null_to_str(&s->parameters);  <-- [2]
    migrate_params_test_apply(params);
    if (migrate_params_check(tmp)) {
        migrate_params_apply(params);
    }

qmp_query_migrate_parameters:
    params = QAPI_CLONE(MigrationParameters, &s->parameters);

The query methods get simpler because s->parameters already contains
data in the format they expect, we can normalize earlier in [2], which
means data is always in the same format throughout
qmp_migrate_set_parameters() and lastly, we already have the getter
methods [1] which can expose "abc"|NULL to the rest of the code anyway.

> When writing state, we need to normalize.
>
> When reading state, we can rely on it being normalized.  Asserting it is
> seems prudent, and should help readers.
>

My main concern is that reading can rely on it being normalized, but the
query methods cannot, so they need to do an "extra conversion", which
from the reader's POV, will look nonsensical. It's not as simple as
using a ternary because the StrOrNull object needs to be allocated.

> [...]
>
>>>>>> diff --git a/migration/options.c b/migration/options.c
>>>>>> index 162c72cda4..45a95dc6da 100644
>>>>>> --- a/migration/options.c
>>>>>> +++ b/migration/options.c
>>>>>> @@ -162,9 +162,11 @@ const Property migration_properties[] = {
>>>>>>      DEFINE_PROP_SIZE("announce-step", MigrationState,
>>>>>>                        parameters.announce_step,
>>>>>>                        DEFAULT_MIGRATE_ANNOUNCE_STEP),
>>>>>> -    DEFINE_PROP_STRING("tls-creds", MigrationState, parameters.tls_creds),
>>>>>> -    DEFINE_PROP_STRING("tls-hostname", MigrationState, parameters.tls_hostname),
>>>>>> -    DEFINE_PROP_STRING("tls-authz", MigrationState, parameters.tls_authz),
>>>>>> +    /*
>>>>>> +     * tls-creds, tls-hostname and tls-authz are of type StrOrNull,
>>>>>> +     * which can't be easily handled (if at all) by qdev. So these
>>>>>> +     * will not be exposed as global migration options (-global).
>>>>>> +     */
>>>>>
>>>>> This is a compatibility break.

Coming back to this, I implemented a DEFINE_PROP_STR_OR_NULL. It's way
better than removing the options, because this allows a default to be
set, which means query-migrate-parameters() doesn't need to deal with an
initial NULL value anymore (if query is called before set).

>>>>>
>>>>> The orthodox way to break it is deprecate, let the grace period expire,
>>>>> break.  Record in docs/about/deprecated.rst at the beginning, move the
>>>>> record to docs/about/removed-features.rst at the end.
>>>>>
>>>>> An argument could be made that the interface in question is
>>>>> accidental[*], not actually used by anything, and therefore breaking it
>>>>> without a grace period is fine.  But even then we should record the
>>>>> breakage in docs/about/removed-features.rst.
>>>>>
>>>>
>>>> Ok. Alternatively I could try a little harder to keep these
>>>> options. I'll see what I can do.
>>>
>>> What do we think about this external interface?
>>>
>>> If we think it's accidental and unused, then putting in more work to
>>> keep it makes no sense.
>>>
>>> If we think it's deliberate and/or used, we should either keep it, or
>>> replace it the orthodox way.
>>>
>>
>> There are two external interfaces actually.
>>
>> -global migration.some_compat_option=on (stored in MigrationState):
>>
>> seems intentional and I believe we'd lose the ability to get out of some
>> tricky situations if we ditched it.
>>
>> -global migation.some_random_option=on (stored in MigrationParameters):
>>
>> has become a debugging *feature*, which I personally don't use, but
>> others do. And worse: we don't know if anyone uses it in production.
>
> Accidental external interface.
>
>> We also arbitrarily put x- in front of options for some reason. There is
>> an argument to drop those because x- is scary and no one should be using
>> them.
>
> We pretty much ditched the x- convention in the QAPI schema.
> docs/devel/qapi-code-gen.rst:
>
>     Names beginning with ``x-`` used to signify "experimental".  This
>     convention has been replaced by special feature "unstable".
>
> Goes back to
>
> commit a3c45b3e62962f99338716b1347cfb0d427cea44
> Author: Markus Armbruster <armbru@redhat.com>
> Date:   Thu Oct 28 12:25:12 2021 +0200
>
>     qapi: New special feature flag "unstable"
>     
>     By convention, names starting with "x-" are experimental.  The parts
>     of external interfaces so named may be withdrawn or changed
>     incompatibly in future releases.

This allows dropping about half of the parameters we expose. Deprecate
the other half, move the remaining legitimate compat options into
MigrationParameters, (which can be set by migrate-set-parameters) and
maybe we can remove the TYPE_DEVICE from MigrationState anytime this
decade.

Moving all qdev properties to their own TYPE_DEVICE object and putting
it under --enable-debug is also an idea.

I'm willing to do the work if we ever reach a consensus about this.

>     
>     The naming convention makes unstable interfaces easy to recognize.
>     Promoting something from experimental to stable involves a name
>     change.  Client code needs to be updated.  Occasionally bothersome.
>     
>     Worse, the convention is not universally observed:
>     
>     * QOM type "input-barrier" has properties "x-origin", "y-origin".
>       Looks accidental, but it's ABI since 4.2.
>     
>     * QOM types "memory-backend-file", "memory-backend-memfd",
>       "memory-backend-ram", and "memory-backend-epc" have a property
>       "x-use-canonical-path-for-ramblock-id" that is documented to be
>       stable despite its name.
>     
>     We could document these exceptions, but documentation helps only
>     humans.  We want to recognize "unstable" in code, like "deprecated".
>     
>     So support recognizing it the same way: introduce new special feature
>     flag "unstable".  It will be treated specially by the QAPI generator,
>     like the existing feature flag "deprecated", and unlike regular
>     feature flags.
>     
>     This commit updates documentation and prepares tests.  The next commit
>     updates the QAPI schema.  The remaining patches update the QAPI
>     generator and wire up -compat policy checking.
>     
>     Management applications can then use query-qmp-schema and -compat to
>     manage or guard against use of unstable interfaces the same way as for
>     deprecated interfaces.
>     
>     docs/devel/qapi-code-gen.txt no longer mandates the naming convention.
>     Using it anyway might help writers of programs that aren't
>     full-fledged management applications.  Not using it can save us
>     bothersome renames.  We'll see how that shakes out.
>     
>     Signed-off-by: Markus Armbruster <armbru@redhat.com>
>     Reviewed-by: Juan Quintela <quintela@redhat.com>
>     Reviewed-by: John Snow <jsnow@redhat.com>
>     Message-Id: <20211028102520.747396-2-armbru@redhat.com>
>
> The x- convention lives on in external interfaces that bypass QAPI, in
> particular QOM/qdev properties.
>
>> I think it would be good to at least separate the responsibilities so
>> when the time comes we can deprecate/remove/replace the offending
>> interfaces. But I won't go into that now, there's already too much
>> change going on for this release.
>
> Makes sense.


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

* Re: [PATCH 01/21] migration: Normalize tls arguments
  2025-06-27 20:28             ` Fabiano Rosas
@ 2025-07-01  7:08               ` Markus Armbruster
  2025-07-01  9:05                 ` Daniel P. Berrangé
  0 siblings, 1 reply; 83+ messages in thread
From: Markus Armbruster @ 2025-07-01  7:08 UTC (permalink / raw)
  To: Fabiano Rosas; +Cc: qemu-devel, Peter Xu, Daniel P . Berrangé

Fabiano Rosas <farosas@suse.de> writes:

> Markus Armbruster <armbru@redhat.com> writes:
>
>> Fabiano Rosas <farosas@suse.de> writes:
>>
>>> Markus Armbruster <armbru@redhat.com> writes:
>>>
>>>> Fabiano Rosas <farosas@suse.de> writes:
>>>>
>>>>> Markus Armbruster <armbru@redhat.com> writes:
>>>>>
>>>>>> Fabiano Rosas <farosas@suse.de> writes:

[...]

>> There more than one way to skin this cat.  I like to keep state
>> normalized.
>>
>> State is an optional StrOrNull.  Possible values:
>>
>> * NULL
>>
>> * QNull, i.e. non-NULL, ->type is QTYPE_QNULL
>>
>> * Empty string, i.e. non-NULL, ->type is QTYPE_QSTRING, ->u.s is ""
>>
>> * Non-empty string, i.e. non-NULL, -> type is QTYPE_QSTRING, ->u.s is
>>   not "" (and cannot be NULL)
>>
>> As far as I understand, we have just two cases semantically:
>>
>> * Set, value is a non-empty string (empty makes no sense)
>>
>> * Unset
>>
>> I'd normalize the state to "either NULL, or (non-empty) string".
>>
>
> This is what I wanted to do (in the next version), but it results in
> more complex and less readable code:

[...]

> If we instead normalize to "either non-empty string or empty string"
> then:

[...]

> The query methods get simpler because s->parameters already contains
> data in the format they expect, we can normalize earlier in [2], which
> means data is always in the same format throughout
> qmp_migrate_set_parameters() and lastly, we already have the getter
> methods [1] which can expose "abc"|NULL to the rest of the code anyway.

I'd like the possible states to be clearly visible, and suggest to guard
them with assertions.  Details, such as how exactly the states are
encoded, are up to you.  You're in a better position to judge them than
I am.

>> When writing state, we need to normalize.
>>
>> When reading state, we can rely on it being normalized.  Asserting it is
>> seems prudent, and should help readers.
>>
>
> My main concern is that reading can rely on it being normalized, but the
> query methods cannot, so they need to do an "extra conversion", which
> from the reader's POV, will look nonsensical. It's not as simple as
> using a ternary because the StrOrNull object needs to be allocated.

[...]

>>> There are two external interfaces actually.
>>>
>>> -global migration.some_compat_option=on (stored in MigrationState):
>>>
>>> seems intentional and I believe we'd lose the ability to get out of some
>>> tricky situations if we ditched it.
>>>
>>> -global migation.some_random_option=on (stored in MigrationParameters):
>>>
>>> has become a debugging *feature*, which I personally don't use, but
>>> others do. And worse: we don't know if anyone uses it in production.
>>
>> Accidental external interface.
>>
>>> We also arbitrarily put x- in front of options for some reason. There is
>>> an argument to drop those because x- is scary and no one should be using
>>> them.
>>
>> We pretty much ditched the x- convention in the QAPI schema.
>> docs/devel/qapi-code-gen.rst:
>>
>>     Names beginning with ``x-`` used to signify "experimental".  This
>>     convention has been replaced by special feature "unstable".
>>
>> Goes back to
>>
>> commit a3c45b3e62962f99338716b1347cfb0d427cea44
>> Author: Markus Armbruster <armbru@redhat.com>
>> Date:   Thu Oct 28 12:25:12 2021 +0200
>>
>>     qapi: New special feature flag "unstable"
>>     
>>     By convention, names starting with "x-" are experimental.  The parts
>>     of external interfaces so named may be withdrawn or changed
>>     incompatibly in future releases.
>
> This allows dropping about half of the parameters we expose. Deprecate
> the other half, move the remaining legitimate compat options into
> MigrationParameters, (which can be set by migrate-set-parameters) and
> maybe we can remove the TYPE_DEVICE from MigrationState anytime this
> decade.

I'd love to get rid of the pseudo-device.

> Moving all qdev properties to their own TYPE_DEVICE object and putting
> it under --enable-debug is also an idea.
>
> I'm willing to do the work if we ever reach a consensus about this.

I'd like migration to work more like other long-running tasks: pass the
entire configuration with the command starting it, provide commands and
events to manage the task while it runs.

This is advice, not a demand.  I'm not going to block change the
migration maintainers want.  I may ask you to do the QAPI schema part in
certain ways, but that's detail.

[...]



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

* Re: [PATCH 01/21] migration: Normalize tls arguments
  2025-07-01  7:08               ` Markus Armbruster
@ 2025-07-01  9:05                 ` Daniel P. Berrangé
  0 siblings, 0 replies; 83+ messages in thread
From: Daniel P. Berrangé @ 2025-07-01  9:05 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: Fabiano Rosas, qemu-devel, Peter Xu

On Tue, Jul 01, 2025 at 09:08:13AM +0200, Markus Armbruster wrote:
> Fabiano Rosas <farosas@suse.de> writes:
> 
> > Markus Armbruster <armbru@redhat.com> writes:
> >
> >> Fabiano Rosas <farosas@suse.de> writes:
> >>
> >>> Markus Armbruster <armbru@redhat.com> writes:
> >>>
> >>>> Fabiano Rosas <farosas@suse.de> writes:
> >>>>
> >>>>> Markus Armbruster <armbru@redhat.com> writes:
> >>>>>
> >>>>>> Fabiano Rosas <farosas@suse.de> writes:
> 
> [...]
> 
> >> There more than one way to skin this cat.  I like to keep state
> >> normalized.
> >>
> >> State is an optional StrOrNull.  Possible values:
> >>
> >> * NULL
> >>
> >> * QNull, i.e. non-NULL, ->type is QTYPE_QNULL
> >>
> >> * Empty string, i.e. non-NULL, ->type is QTYPE_QSTRING, ->u.s is ""
> >>
> >> * Non-empty string, i.e. non-NULL, -> type is QTYPE_QSTRING, ->u.s is
> >>   not "" (and cannot be NULL)
> >>
> >> As far as I understand, we have just two cases semantically:
> >>
> >> * Set, value is a non-empty string (empty makes no sense)
> >>
> >> * Unset
> >>
> >> I'd normalize the state to "either NULL, or (non-empty) string".
> >>
> >
> > This is what I wanted to do (in the next version), but it results in
> > more complex and less readable code:
> 
> [...]
> 
> > If we instead normalize to "either non-empty string or empty string"
> > then:
> 
> [...]
> 
> > The query methods get simpler because s->parameters already contains
> > data in the format they expect, we can normalize earlier in [2], which
> > means data is always in the same format throughout
> > qmp_migrate_set_parameters() and lastly, we already have the getter
> > methods [1] which can expose "abc"|NULL to the rest of the code anyway.
> 
> I'd like the possible states to be clearly visible, and suggest to guard
> them with assertions.  Details, such as how exactly the states are
> encoded, are up to you.  You're in a better position to judge them than
> I am.
> 
> >> When writing state, we need to normalize.
> >>
> >> When reading state, we can rely on it being normalized.  Asserting it is
> >> seems prudent, and should help readers.
> >>
> >
> > My main concern is that reading can rely on it being normalized, but the
> > query methods cannot, so they need to do an "extra conversion", which
> > from the reader's POV, will look nonsensical. It's not as simple as
> > using a ternary because the StrOrNull object needs to be allocated.
> 
> [...]
> 
> >>> There are two external interfaces actually.
> >>>
> >>> -global migration.some_compat_option=on (stored in MigrationState):
> >>>
> >>> seems intentional and I believe we'd lose the ability to get out of some
> >>> tricky situations if we ditched it.
> >>>
> >>> -global migation.some_random_option=on (stored in MigrationParameters):
> >>>
> >>> has become a debugging *feature*, which I personally don't use, but
> >>> others do. And worse: we don't know if anyone uses it in production.
> >>
> >> Accidental external interface.
> >>
> >>> We also arbitrarily put x- in front of options for some reason. There is
> >>> an argument to drop those because x- is scary and no one should be using
> >>> them.
> >>
> >> We pretty much ditched the x- convention in the QAPI schema.
> >> docs/devel/qapi-code-gen.rst:
> >>
> >>     Names beginning with ``x-`` used to signify "experimental".  This
> >>     convention has been replaced by special feature "unstable".
> >>
> >> Goes back to
> >>
> >> commit a3c45b3e62962f99338716b1347cfb0d427cea44
> >> Author: Markus Armbruster <armbru@redhat.com>
> >> Date:   Thu Oct 28 12:25:12 2021 +0200
> >>
> >>     qapi: New special feature flag "unstable"
> >>     
> >>     By convention, names starting with "x-" are experimental.  The parts
> >>     of external interfaces so named may be withdrawn or changed
> >>     incompatibly in future releases.
> >
> > This allows dropping about half of the parameters we expose. Deprecate
> > the other half, move the remaining legitimate compat options into
> > MigrationParameters, (which can be set by migrate-set-parameters) and
> > maybe we can remove the TYPE_DEVICE from MigrationState anytime this
> > decade.
> 
> I'd love to get rid of the pseudo-device.
> 
> > Moving all qdev properties to their own TYPE_DEVICE object and putting
> > it under --enable-debug is also an idea.
> >
> > I'm willing to do the work if we ever reach a consensus about this.
> 
> I'd like migration to work more like other long-running tasks: pass the
> entire configuration with the command starting it, provide commands and
> events to manage the task while it runs.
> 
> This is advice, not a demand.  I'm not going to block change the
> migration maintainers want.  I may ask you to do the QAPI schema part in
> certain ways, but that's detail.

This should be doable as IIUC at the end of this series, the QMP
'migrate-incoming' command parameters can express all the required
data for accepting an incoming migration. As such, that parameter
struct could be exposed on the CLI with the CLI json syntax to
accept complex args in a way that wasn't possible historically
with -incoming.

With regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|



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

end of thread, other threads:[~2025-07-01  9:13 UTC | newest]

Thread overview: 83+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-06-03  1:37 [PATCH 00/21] migration: Unify capabilities and parameters Fabiano Rosas
2025-06-03  1:37 ` [PATCH 01/21] migration: Normalize tls arguments Fabiano Rosas
2025-06-05 20:51   ` Peter Xu
2025-06-06 13:48     ` Fabiano Rosas
2025-06-25  9:41   ` Markus Armbruster
2025-06-25 17:17     ` Fabiano Rosas
2025-06-26  9:38       ` Markus Armbruster
2025-06-26 14:51         ` Fabiano Rosas
2025-06-27  7:10           ` Markus Armbruster
2025-06-27 20:28             ` Fabiano Rosas
2025-07-01  7:08               ` Markus Armbruster
2025-07-01  9:05                 ` Daniel P. Berrangé
2025-06-03  1:37 ` [PATCH 02/21] migration: Remove MigrateSetParameters Fabiano Rosas
2025-06-05 20:58   ` Peter Xu
2025-06-25 11:31   ` Markus Armbruster
2025-06-25 17:21     ` Fabiano Rosas
2025-06-26  9:40       ` Markus Armbruster
2025-06-03  1:37 ` [PATCH 03/21] qapi/migration: Don't document MigrationParameter Fabiano Rosas
2025-06-05 21:00   ` Peter Xu
2025-06-25 12:04   ` Markus Armbruster
2025-06-25 12:22     ` Markus Armbruster
2025-06-25 17:29       ` Fabiano Rosas
2025-06-03  1:37 ` [PATCH 04/21] migration: Run a post update routine after setting parameters Fabiano Rosas
2025-06-03  1:37 ` [PATCH 05/21] migration: Add a flag to track block-bitmap-mapping input Fabiano Rosas
2025-06-06 15:03   ` Peter Xu
2025-06-06 15:43     ` Fabiano Rosas
2025-06-06 17:44       ` Peter Xu
2025-06-06 18:38         ` Fabiano Rosas
2025-06-03  1:37 ` [PATCH 06/21] migration: Remove checks for s->parameters has_* fields Fabiano Rosas
2025-06-06 15:13   ` Peter Xu
2025-06-03  1:37 ` [PATCH 07/21] migration: Set block_bitmap_mapping unconditionally in query-migrate-parameters Fabiano Rosas
2025-06-03  1:37 ` [PATCH 08/21] migration: Do away with usage of QERR_INVALID_PARAMETER_VALUE Fabiano Rosas
2025-06-06 15:17   ` Peter Xu
2025-06-03  1:37 ` [PATCH 09/21] migration: Extract code to mark all parameters as present Fabiano Rosas
2025-06-06 15:26   ` Peter Xu
2025-06-06 15:51     ` Fabiano Rosas
2025-06-06 17:48       ` Peter Xu
2025-06-03  1:37 ` [PATCH 10/21] migration: Use QAPI_CLONE_MEMBERS in query_migrate_parameters Fabiano Rosas
2025-06-06 15:29   ` Peter Xu
2025-06-06 15:53     ` Fabiano Rosas
2025-06-12 20:58       ` Fabiano Rosas
2025-06-12 21:27         ` Peter Xu
2025-06-13 12:30           ` Fabiano Rosas
2025-06-03  1:38 ` [PATCH 11/21] migration: Use QAPI_CLONE_MEMBERS in migrate_params_test_apply Fabiano Rosas
2025-06-03  1:38 ` [PATCH 12/21] migration: Use QAPI_CLONE_MEMBERS in migrate_params_apply Fabiano Rosas
2025-06-03  1:38 ` [PATCH 13/21] migration: Use visitors in migrate_params_test_apply Fabiano Rosas
2025-06-03  1:38 ` [PATCH 14/21] migration: Cleanup hmp_info_migrate_parameters Fabiano Rosas
2025-06-06 18:52   ` Peter Xu
2025-06-03  1:38 ` [PATCH 15/21] migration: Add capabilities into MigrationParameters Fabiano Rosas
2025-06-06 19:01   ` Peter Xu
2025-06-03  1:38 ` [PATCH 16/21] qapi/migration: Mark that query/set-migrate-parameters support capabilities Fabiano Rosas
2025-06-03  9:01   ` Daniel P. Berrangé
2025-06-06 13:53     ` Fabiano Rosas
2025-06-03  1:38 ` [PATCH 17/21] migration: Remove s->capabilities Fabiano Rosas
2025-06-06 19:16   ` Peter Xu
2025-06-03  1:38 ` [PATCH 18/21] qapi/migration: Deprecate capabilities commands Fabiano Rosas
2025-06-06 19:16   ` Peter Xu
2025-06-03  1:38 ` [PATCH 19/21] migration: Allow migrate commands to provide the migration config Fabiano Rosas
2025-06-03  9:03   ` Daniel P. Berrangé
2025-06-06 19:28   ` Peter Xu
2025-06-06 20:23     ` Fabiano Rosas
2025-06-06 20:50       ` Peter Xu
2025-06-09 14:37         ` Fabiano Rosas
2025-06-09 15:51           ` Peter Xu
2025-06-09 16:13             ` Daniel P. Berrangé
2025-06-09 16:49               ` Peter Xu
2025-06-09 18:17                 ` Fabiano Rosas
2025-06-09 18:02             ` Fabiano Rosas
2025-06-09 19:05               ` Peter Xu
2025-06-09 19:41                 ` Fabiano Rosas
2025-06-09 20:35                   ` Peter Xu
2025-06-10 20:55                     ` Fabiano Rosas
2025-06-10 21:27                       ` Peter Xu
2025-06-09 15:03         ` Daniel P. Berrangé
2025-06-09 15:33           ` Peter Xu
2025-06-09 15:43             ` Daniel P. Berrangé
2025-06-09 15:53               ` Peter Xu
2025-06-09 15:58                 ` Peter Xu
2025-06-09 16:15                 ` Daniel P. Berrangé
2025-06-09 16:41                   ` Peter Xu
2025-06-03  1:38 ` [PATCH 20/21] libqtest: Add a function to check whether a QMP command supports a feature Fabiano Rosas
2025-06-03  1:38 ` [PATCH 21/21] tests/qtest/migration: Add a test for config passing Fabiano Rosas
2025-06-12  6:41 ` [PATCH 00/21] migration: Unify capabilities and parameters Mario Casquero

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).