qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [PATCH V5 00/19] Live update: cpr-exec
@ 2025-10-01 15:33 Steve Sistare
  2025-10-01 15:33 ` [PATCH V5 01/19] migration: multi-mode notifier Steve Sistare
                   ` (19 more replies)
  0 siblings, 20 replies; 34+ messages in thread
From: Steve Sistare @ 2025-10-01 15:33 UTC (permalink / raw)
  To: qemu-devel
  Cc: Fabiano Rosas, Peter Xu, Markus Armbruster, Paolo Bonzini,
	Eric Blake, Dr. David Alan Gilbert, Cedric Le Goater,
	Alex Williamson, Steve Sistare

This patch series adds the live migration cpr-exec mode.

The new user-visible interfaces are:
  * cpr-exec (MigMode migration parameter)
  * cpr-exec-command (migration parameter)

cpr-exec mode is similar in most respects to cpr-transfer mode, with the
primary difference being that old QEMU directly exec's new QEMU.  The user
specifies the command to exec new QEMU in the migration parameter
cpr-exec-command.

Why?

In a containerized QEMU environment, cpr-exec reuses an existing QEMU
container and its assigned resources.  By contrast, cpr-transfer mode
requires a new container to be created on the same host as the target of
the CPR operation.  Resources must be reserved for the new container, while
the old container still reserves resources until the operation completes.
Avoiding over commitment requires extra work in the management layer.
This is one reason why a cloud provider may prefer cpr-exec.  A second reason
is that the container may include agents with their own connections to the
outside world, and such connections remain intact if the container is reused.

How?

cpr-exec preserves descriptors across exec by clearing the CLOEXEC flag,
and by sending the unique name and value of each descriptor to new QEMU
via CPR state.

CPR state cannot be sent over the normal migration channel, because devices
and backends are created prior to reading the channel, so this mode sends
CPR state over a second migration channel that is not visible to the user.
New QEMU reads the second channel prior to creating devices or backends.

The exec itself is trivial.  After writing to the migration channels, the
migration code calls a new main-loop hook to perform the exec.

Example:

In this example, we simply restart the same version of QEMU, but in
a real scenario one would use a new QEMU binary path in cpr-exec-command.

  # qemu-kvm -monitor stdio
  -object memory-backend-memfd,id=ram0,size=1G
  -machine memory-backend=ram0 -machine aux-ram-share=on ...

  QEMU 10.1.50 monitor - type 'help' for more information
  (qemu) info status
  VM status: running
  (qemu) migrate_set_parameter mode cpr-exec
  (qemu) migrate_set_parameter cpr-exec-command qemu-kvm ... -incoming file:vm.state
  (qemu) migrate -d file:vm.state

  (qemu) QEMU 10.1.50 monitor - type 'help' for more information
  (qemu) info status
  VM status: running

Changes in V5:
  * included error-report.h, and used qemu_memfd_create to fix windows build
  * documented cpr-exec environment variable in CPR.rst
  * fixed err leak
  * defined cpr_exec_unpreserve_fds helper
  * misc cosmetic changes in cpr-exec.c
  * added patches from the series "migration-test: test cpr-exec", with
    comments addressed:
    - renamed qts parameter
    - fixed migrate_args bug
    - added code comments
    - re-privatized QEMU_ENV_DST
    - added RB's

Steve Sistare (19):
  migration: multi-mode notifier
  migration: add cpr_walk_fd
  oslib: qemu_clear_cloexec
  migration: cpr-exec-command parameter
  migration: cpr-exec save and load
  migration: cpr-exec mode
  migration: cpr-exec docs
  vfio: cpr-exec mode
  tests/qtest: export qtest_qemu_binary
  tests/qtest: qtest_qemu_args
  tests/qtest: qtest_create_test_state
  tests/qtest: qtest_qemu_spawn_func
  tests/qtest: qtest_init_after_exec
  migration-test: only_source option
  migration-test: shm path accessor
  migration-test: misc exports
  migration-test: migrate_args
  migration-test: strv parameter
  migration-test: test cpr-exec

 docs/devel/migration/CPR.rst          | 112 +++++++++++++++++++-
 qapi/migration.json                   |  46 ++++++++-
 include/migration/cpr.h               |  10 ++
 include/migration/misc.h              |  12 +++
 include/qemu/osdep.h                  |   9 ++
 tests/qtest/libqtest.h                |  25 +++++
 tests/qtest/migration/bootfile.h      |   1 +
 tests/qtest/migration/framework.h     |   5 +
 tests/qtest/migration/migration-qmp.h |   2 +
 hw/vfio/container.c                   |   3 +-
 hw/vfio/cpr-iommufd.c                 |   3 +-
 hw/vfio/cpr-legacy.c                  |   9 +-
 hw/vfio/cpr.c                         |  13 +--
 migration/cpr-exec.c                  | 188 ++++++++++++++++++++++++++++++++++
 migration/cpr.c                       |  36 ++++++-
 migration/migration-hmp-cmds.c        |  30 ++++++
 migration/migration.c                 |  70 ++++++++++---
 migration/options.c                   |  14 +++
 migration/ram.c                       |   1 +
 migration/vmstate-types.c             |   8 ++
 system/vl.c                           |   4 +-
 tests/qtest/libqtest.c                | 108 +++++++++++++------
 tests/qtest/migration/bootfile.c      |   5 +
 tests/qtest/migration/cpr-tests.c     | 133 ++++++++++++++++++++++++
 tests/qtest/migration/framework.c     | 101 ++++++++++++------
 tests/qtest/migration/migration-qmp.c |  16 +++
 util/oslib-posix.c                    |   9 ++
 util/oslib-win32.c                    |   4 +
 hmp-commands.hx                       |   2 +-
 migration/meson.build                 |   1 +
 migration/trace-events                |   1 +
 31 files changed, 879 insertions(+), 102 deletions(-)
 create mode 100644 migration/cpr-exec.c

-- 
1.8.3.1



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

* [PATCH V5 01/19] migration: multi-mode notifier
  2025-10-01 15:33 [PATCH V5 00/19] Live update: cpr-exec Steve Sistare
@ 2025-10-01 15:33 ` Steve Sistare
  2025-10-01 15:33 ` [PATCH V5 02/19] migration: add cpr_walk_fd Steve Sistare
                   ` (18 subsequent siblings)
  19 siblings, 0 replies; 34+ messages in thread
From: Steve Sistare @ 2025-10-01 15:33 UTC (permalink / raw)
  To: qemu-devel
  Cc: Fabiano Rosas, Peter Xu, Markus Armbruster, Paolo Bonzini,
	Eric Blake, Dr. David Alan Gilbert, Cedric Le Goater,
	Alex Williamson, Steve Sistare

Allow a notifier to be added for multiple migration modes.
To allow a notifier to appear on multiple per-node lists, use
a generic list type.  We can no longer use NotifierWithReturnList,
because it shoe horns the notifier onto a single list.

Signed-off-by: Steve Sistare <steven.sistare@oracle.com>
Reviewed-by: Fabiano Rosas <farosas@suse.de>
---
 include/migration/misc.h | 12 ++++++++++
 migration/migration.c    | 60 +++++++++++++++++++++++++++++++++++++-----------
 2 files changed, 59 insertions(+), 13 deletions(-)

diff --git a/include/migration/misc.h b/include/migration/misc.h
index a261f99..592b930 100644
--- a/include/migration/misc.h
+++ b/include/migration/misc.h
@@ -95,7 +95,19 @@ void migration_add_notifier(NotifierWithReturn *notify,
 void migration_add_notifier_mode(NotifierWithReturn *notify,
                                  MigrationNotifyFunc func, MigMode mode);
 
+/*
+ * Same as migration_add_notifier, but applies to all @mode in the argument
+ * list.  The list is terminated by -1 or MIG_MODE_ALL.  For the latter,
+ * the notifier is added for all modes.
+ */
+void migration_add_notifier_modes(NotifierWithReturn *notify,
+                                  MigrationNotifyFunc func, MigMode mode, ...);
+
+/*
+ * Remove a notifier from all modes.
+ */
 void migration_remove_notifier(NotifierWithReturn *notify);
+
 void migration_file_set_error(int ret, Error *err);
 
 /* True if incoming migration entered POSTCOPY_INCOMING_DISCARD */
diff --git a/migration/migration.c b/migration/migration.c
index 10c216d..08a98f7 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -74,11 +74,7 @@
 
 #define INMIGRATE_DEFAULT_EXIT_ON_ERROR true
 
-static NotifierWithReturnList migration_state_notifiers[] = {
-    NOTIFIER_ELEM_INIT(migration_state_notifiers, MIG_MODE_NORMAL),
-    NOTIFIER_ELEM_INIT(migration_state_notifiers, MIG_MODE_CPR_REBOOT),
-    NOTIFIER_ELEM_INIT(migration_state_notifiers, MIG_MODE_CPR_TRANSFER),
-};
+static GSList *migration_state_notifiers[MIG_MODE__MAX];
 
 /* Messages sent on the return path from destination to source */
 enum mig_rp_message_type {
@@ -1665,23 +1661,51 @@ void migration_cancel(void)
     }
 }
 
+static int get_modes(MigMode mode, va_list ap);
+
+static void add_notifiers(NotifierWithReturn *notify, int modes)
+{
+    for (MigMode mode = 0; mode < MIG_MODE__MAX; mode++) {
+        if (modes & BIT(mode)) {
+            migration_state_notifiers[mode] =
+                g_slist_prepend(migration_state_notifiers[mode], notify);
+        }
+    }
+}
+
+void migration_add_notifier_modes(NotifierWithReturn *notify,
+                                  MigrationNotifyFunc func, MigMode mode, ...)
+{
+    int modes;
+    va_list ap;
+
+    va_start(ap, mode);
+    modes = get_modes(mode, ap);
+    va_end(ap);
+
+    notify->notify = (NotifierWithReturnFunc)func;
+    add_notifiers(notify, modes);
+}
+
 void migration_add_notifier_mode(NotifierWithReturn *notify,
                                  MigrationNotifyFunc func, MigMode mode)
 {
-    notify->notify = (NotifierWithReturnFunc)func;
-    notifier_with_return_list_add(&migration_state_notifiers[mode], notify);
+    migration_add_notifier_modes(notify, func, mode, -1);
 }
 
 void migration_add_notifier(NotifierWithReturn *notify,
                             MigrationNotifyFunc func)
 {
-    migration_add_notifier_mode(notify, func, MIG_MODE_NORMAL);
+    migration_add_notifier_modes(notify, func, MIG_MODE_NORMAL, -1);
 }
 
 void migration_remove_notifier(NotifierWithReturn *notify)
 {
     if (notify->notify) {
-        notifier_with_return_remove(notify);
+        for (MigMode mode = 0; mode < MIG_MODE__MAX; mode++) {
+            migration_blockers[mode] =
+                g_slist_remove(migration_state_notifiers[mode], notify);
+        }
         notify->notify = NULL;
     }
 }
@@ -1691,13 +1715,23 @@ int migration_call_notifiers(MigrationState *s, MigrationEventType type,
 {
     MigMode mode = s->parameters.mode;
     MigrationEvent e;
+    NotifierWithReturn *notifier;
+    GSList *elem, *next;
     int ret;
 
     e.type = type;
-    ret = notifier_with_return_list_notify(&migration_state_notifiers[mode],
-                                           &e, errp);
-    assert(!ret || type == MIG_EVENT_PRECOPY_SETUP);
-    return ret;
+
+    for (elem = migration_state_notifiers[mode]; elem; elem = next) {
+        next = elem->next;
+        notifier = (NotifierWithReturn *)elem->data;
+        ret = notifier->notify(notifier, &e, errp);
+        if (ret) {
+            assert(type == MIG_EVENT_PRECOPY_SETUP);
+            return ret;
+        }
+    }
+
+    return 0;
 }
 
 bool migration_has_failed(MigrationState *s)
-- 
1.8.3.1



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

* [PATCH V5 02/19] migration: add cpr_walk_fd
  2025-10-01 15:33 [PATCH V5 00/19] Live update: cpr-exec Steve Sistare
  2025-10-01 15:33 ` [PATCH V5 01/19] migration: multi-mode notifier Steve Sistare
@ 2025-10-01 15:33 ` Steve Sistare
  2025-10-01 15:33 ` [PATCH V5 03/19] oslib: qemu_clear_cloexec Steve Sistare
                   ` (17 subsequent siblings)
  19 siblings, 0 replies; 34+ messages in thread
From: Steve Sistare @ 2025-10-01 15:33 UTC (permalink / raw)
  To: qemu-devel
  Cc: Fabiano Rosas, Peter Xu, Markus Armbruster, Paolo Bonzini,
	Eric Blake, Dr. David Alan Gilbert, Cedric Le Goater,
	Alex Williamson, Steve Sistare

Add a helper to walk all CPR fd's and run a callback for each.

Signed-off-by: Steve Sistare <steven.sistare@oracle.com>
Reviewed-by: Peter Xu <peterx@redhat.com>
---
 include/migration/cpr.h |  3 +++
 migration/cpr.c         | 13 +++++++++++++
 2 files changed, 16 insertions(+)

diff --git a/include/migration/cpr.h b/include/migration/cpr.h
index 3fc19a7..2b074d7 100644
--- a/include/migration/cpr.h
+++ b/include/migration/cpr.h
@@ -34,6 +34,9 @@ void cpr_resave_fd(const char *name, int id, int fd);
 int cpr_open_fd(const char *path, int flags, const char *name, int id,
                 Error **errp);
 
+typedef bool (*cpr_walk_fd_cb)(int fd);
+bool cpr_walk_fd(cpr_walk_fd_cb cb);
+
 MigMode cpr_get_incoming_mode(void);
 void cpr_set_incoming_mode(MigMode mode);
 bool cpr_is_incoming(void);
diff --git a/migration/cpr.c b/migration/cpr.c
index 42ad0b0..d3e370e 100644
--- a/migration/cpr.c
+++ b/migration/cpr.c
@@ -121,6 +121,19 @@ int cpr_open_fd(const char *path, int flags, const char *name, int id,
     return fd;
 }
 
+bool cpr_walk_fd(cpr_walk_fd_cb cb)
+{
+    CprFd *elem;
+
+    QLIST_FOREACH(elem, &cpr_state.fds, next) {
+        g_assert(elem->fd >= 0);
+        if (!cb(elem->fd)) {
+            return false;
+        }
+    }
+    return true;
+}
+
 /*************************************************************************/
 static const VMStateDescription vmstate_cpr_state = {
     .name = CPR_STATE,
-- 
1.8.3.1



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

* [PATCH V5 03/19] oslib: qemu_clear_cloexec
  2025-10-01 15:33 [PATCH V5 00/19] Live update: cpr-exec Steve Sistare
  2025-10-01 15:33 ` [PATCH V5 01/19] migration: multi-mode notifier Steve Sistare
  2025-10-01 15:33 ` [PATCH V5 02/19] migration: add cpr_walk_fd Steve Sistare
@ 2025-10-01 15:33 ` Steve Sistare
  2025-10-01 15:33 ` [PATCH V5 04/19] migration: cpr-exec-command parameter Steve Sistare
                   ` (16 subsequent siblings)
  19 siblings, 0 replies; 34+ messages in thread
From: Steve Sistare @ 2025-10-01 15:33 UTC (permalink / raw)
  To: qemu-devel
  Cc: Fabiano Rosas, Peter Xu, Markus Armbruster, Paolo Bonzini,
	Eric Blake, Dr. David Alan Gilbert, Cedric Le Goater,
	Alex Williamson, Steve Sistare

Define qemu_clear_cloexec, analogous to qemu_set_cloexec.

Signed-off-by: Steve Sistare <steven.sistare@oracle.com>
Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Fabiano Rosas <farosas@suse.de>
---
 include/qemu/osdep.h | 9 +++++++++
 util/oslib-posix.c   | 9 +++++++++
 util/oslib-win32.c   | 4 ++++
 3 files changed, 22 insertions(+)

diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h
index be3460b..8dac4ed 100644
--- a/include/qemu/osdep.h
+++ b/include/qemu/osdep.h
@@ -688,6 +688,15 @@ ssize_t qemu_write_full(int fd, const void *buf, size_t count)
 
 void qemu_set_cloexec(int fd);
 
+/*
+ * Clear FD_CLOEXEC for a descriptor.
+ *
+ * The caller must guarantee that no other fork+exec's occur before the
+ * exec that is intended to inherit this descriptor, eg by suspending CPUs
+ * and blocking monitor commands.
+ */
+void qemu_clear_cloexec(int fd);
+
 /* Return a dynamically allocated directory path that is appropriate for storing
  * local state.
  *
diff --git a/util/oslib-posix.c b/util/oslib-posix.c
index 4ff577e..4c04658 100644
--- a/util/oslib-posix.c
+++ b/util/oslib-posix.c
@@ -307,6 +307,15 @@ int qemu_socketpair(int domain, int type, int protocol, int sv[2])
     return ret;
 }
 
+void qemu_clear_cloexec(int fd)
+{
+    int f;
+    f = fcntl(fd, F_GETFD);
+    assert(f != -1);
+    f = fcntl(fd, F_SETFD, f & ~FD_CLOEXEC);
+    assert(f != -1);
+}
+
 char *
 qemu_get_local_state_dir(void)
 {
diff --git a/util/oslib-win32.c b/util/oslib-win32.c
index b735163..843a901 100644
--- a/util/oslib-win32.c
+++ b/util/oslib-win32.c
@@ -222,6 +222,10 @@ void qemu_set_cloexec(int fd)
 {
 }
 
+void qemu_clear_cloexec(int fd)
+{
+}
+
 int qemu_get_thread_id(void)
 {
     return GetCurrentThreadId();
-- 
1.8.3.1



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

* [PATCH V5 04/19] migration: cpr-exec-command parameter
  2025-10-01 15:33 [PATCH V5 00/19] Live update: cpr-exec Steve Sistare
                   ` (2 preceding siblings ...)
  2025-10-01 15:33 ` [PATCH V5 03/19] oslib: qemu_clear_cloexec Steve Sistare
@ 2025-10-01 15:33 ` Steve Sistare
  2025-10-01 15:33 ` [PATCH V5 05/19] migration: cpr-exec save and load Steve Sistare
                   ` (15 subsequent siblings)
  19 siblings, 0 replies; 34+ messages in thread
From: Steve Sistare @ 2025-10-01 15:33 UTC (permalink / raw)
  To: qemu-devel
  Cc: Fabiano Rosas, Peter Xu, Markus Armbruster, Paolo Bonzini,
	Eric Blake, Dr. David Alan Gilbert, Cedric Le Goater,
	Alex Williamson, Steve Sistare

Create the cpr-exec-command migration parameter, defined as a list of
strings.  It will be used for cpr-exec migration mode in a subsequent
patch, and contains forward references to cpr-exec mode in the qapi
doc.

No functional change, except that cpr-exec-command is shown by the
'info migrate' command.

Signed-off-by: Steve Sistare <steven.sistare@oracle.com>
Acked-by: Markus Armbruster <armbru@redhat.com>
---
 qapi/migration.json            | 21 ++++++++++++++++++---
 migration/migration-hmp-cmds.c | 30 ++++++++++++++++++++++++++++++
 migration/options.c            | 14 ++++++++++++++
 hmp-commands.hx                |  2 +-
 4 files changed, 63 insertions(+), 4 deletions(-)

diff --git a/qapi/migration.json b/qapi/migration.json
index 2387c21..2be8fa1 100644
--- a/qapi/migration.json
+++ b/qapi/migration.json
@@ -924,6 +924,10 @@
 #     only has effect if the @mapped-ram capability is enabled.
 #     (Since 9.1)
 #
+# @cpr-exec-command: Command to start the new QEMU process when @mode
+#     is @cpr-exec.  The first list element is the program's filename,
+#     the remainder its arguments.  (Since 10.2)
+#
 # Features:
 #
 # @unstable: Members @x-checkpoint-delay and
@@ -950,7 +954,8 @@
            'vcpu-dirty-limit',
            'mode',
            'zero-page-detection',
-           'direct-io'] }
+           'direct-io',
+           'cpr-exec-command'] }
 
 ##
 # @MigrateSetParameters:
@@ -1105,6 +1110,10 @@
 #     only has effect if the @mapped-ram capability is enabled.
 #     (Since 9.1)
 #
+# @cpr-exec-command: Command to start the new QEMU process when @mode
+#     is @cpr-exec.  The first list element is the program's filename,
+#     the remainder its arguments.  (Since 10.2)
+#
 # Features:
 #
 # @unstable: Members @x-checkpoint-delay and
@@ -1146,7 +1155,8 @@
             '*vcpu-dirty-limit': 'uint64',
             '*mode': 'MigMode',
             '*zero-page-detection': 'ZeroPageDetection',
-            '*direct-io': 'bool' } }
+            '*direct-io': 'bool',
+            '*cpr-exec-command': [ 'str' ]} }
 
 ##
 # @migrate-set-parameters:
@@ -1315,6 +1325,10 @@
 #     only has effect if the @mapped-ram capability is enabled.
 #     (Since 9.1)
 #
+# @cpr-exec-command: Command to start the new QEMU process when @mode
+#     is @cpr-exec.  The first list element is the program's filename,
+#     the remainder its arguments.  (Since 10.2)
+#
 # Features:
 #
 # @unstable: Members @x-checkpoint-delay and
@@ -1353,7 +1367,8 @@
             '*vcpu-dirty-limit': 'uint64',
             '*mode': 'MigMode',
             '*zero-page-detection': 'ZeroPageDetection',
-            '*direct-io': 'bool' } }
+            '*direct-io': 'bool',
+            '*cpr-exec-command': [ 'str' ]} }
 
 ##
 # @query-migrate-parameters:
diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c
index 0fc21f0..54df615 100644
--- a/migration/migration-hmp-cmds.c
+++ b/migration/migration-hmp-cmds.c
@@ -306,6 +306,18 @@ void hmp_info_migrate_capabilities(Monitor *mon, const QDict *qdict)
     qapi_free_MigrationCapabilityStatusList(caps);
 }
 
+static void monitor_print_cpr_exec_command(Monitor *mon, strList *args)
+{
+    monitor_printf(mon, "%s:",
+        MigrationParameter_str(MIGRATION_PARAMETER_CPR_EXEC_COMMAND));
+
+    while (args) {
+        monitor_printf(mon, " %s", args->value);
+        args = args->next;
+    }
+    monitor_printf(mon, "\n");
+}
+
 void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict)
 {
     MigrationParameters *params;
@@ -435,6 +447,9 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict)
                                MIGRATION_PARAMETER_DIRECT_IO),
                            params->direct_io ? "on" : "off");
         }
+
+        assert(params->has_cpr_exec_command);
+        monitor_print_cpr_exec_command(mon, params->cpr_exec_command);
     }
 
     qapi_free_MigrationParameters(params);
@@ -716,6 +731,21 @@ 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_CPR_EXEC_COMMAND: {
+        g_autofree char **strv = NULL;
+        g_autoptr(GError) gerr = NULL;
+        strList **tail = &p->cpr_exec_command;
+
+        if (!g_shell_parse_argv(valuestr, NULL, &strv, &gerr)) {
+            error_setg(&err, "%s", gerr->message);
+            break;
+        }
+        for (int i = 0; strv[i]; i++) {
+            QAPI_LIST_APPEND(tail, strv[i]);
+        }
+        p->has_cpr_exec_command = true;
+        break;
+    }
     default:
         g_assert_not_reached();
     }
diff --git a/migration/options.c b/migration/options.c
index 4e923a2..5183112 100644
--- a/migration/options.c
+++ b/migration/options.c
@@ -959,6 +959,9 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp)
     params->zero_page_detection = s->parameters.zero_page_detection;
     params->has_direct_io = true;
     params->direct_io = s->parameters.direct_io;
+    params->has_cpr_exec_command = true;
+    params->cpr_exec_command = QAPI_CLONE(strList,
+                                          s->parameters.cpr_exec_command);
 
     return params;
 }
@@ -993,6 +996,7 @@ void migrate_params_init(MigrationParameters *params)
     params->has_mode = true;
     params->has_zero_page_detection = true;
     params->has_direct_io = true;
+    params->has_cpr_exec_command = true;
 }
 
 /*
@@ -1297,6 +1301,10 @@ static void migrate_params_test_apply(MigrateSetParameters *params,
     if (params->has_direct_io) {
         dest->direct_io = params->direct_io;
     }
+
+    if (params->has_cpr_exec_command) {
+        dest->cpr_exec_command = params->cpr_exec_command;
+    }
 }
 
 static void migrate_params_apply(MigrateSetParameters *params, Error **errp)
@@ -1429,6 +1437,12 @@ static void migrate_params_apply(MigrateSetParameters *params, Error **errp)
     if (params->has_direct_io) {
         s->parameters.direct_io = params->direct_io;
     }
+
+    if (params->has_cpr_exec_command) {
+        qapi_free_strList(s->parameters.cpr_exec_command);
+        s->parameters.cpr_exec_command =
+            QAPI_CLONE(strList, params->cpr_exec_command);
+    }
 }
 
 void qmp_migrate_set_parameters(MigrateSetParameters *params, Error **errp)
diff --git a/hmp-commands.hx b/hmp-commands.hx
index d0e4f35..3cace8f 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -1009,7 +1009,7 @@ ERST
 
     {
         .name       = "migrate_set_parameter",
-        .args_type  = "parameter:s,value:s",
+        .args_type  = "parameter:s,value:S",
         .params     = "parameter value",
         .help       = "Set the parameter for migration",
         .cmd        = hmp_migrate_set_parameter,
-- 
1.8.3.1



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

* [PATCH V5 05/19] migration: cpr-exec save and load
  2025-10-01 15:33 [PATCH V5 00/19] Live update: cpr-exec Steve Sistare
                   ` (3 preceding siblings ...)
  2025-10-01 15:33 ` [PATCH V5 04/19] migration: cpr-exec-command parameter Steve Sistare
@ 2025-10-01 15:33 ` Steve Sistare
  2025-10-01 16:36   ` Peter Xu
  2025-10-01 15:33 ` [PATCH V5 06/19] migration: cpr-exec mode Steve Sistare
                   ` (14 subsequent siblings)
  19 siblings, 1 reply; 34+ messages in thread
From: Steve Sistare @ 2025-10-01 15:33 UTC (permalink / raw)
  To: qemu-devel
  Cc: Fabiano Rosas, Peter Xu, Markus Armbruster, Paolo Bonzini,
	Eric Blake, Dr. David Alan Gilbert, Cedric Le Goater,
	Alex Williamson, Steve Sistare

To preserve CPR state across exec, create a QEMUFile based on a memfd, and
keep the memfd open across exec.  Save the value of the memfd in an
environment variable so post-exec QEMU can find it.

These new functions are called in a subsequent patch.

Signed-off-by: Steve Sistare <steven.sistare@oracle.com>
---
 include/migration/cpr.h |  5 +++
 migration/cpr-exec.c    | 93 +++++++++++++++++++++++++++++++++++++++++++++++++
 migration/meson.build   |  1 +
 3 files changed, 99 insertions(+)
 create mode 100644 migration/cpr-exec.c

diff --git a/include/migration/cpr.h b/include/migration/cpr.h
index 2b074d7..b84389f 100644
--- a/include/migration/cpr.h
+++ b/include/migration/cpr.h
@@ -53,4 +53,9 @@ int cpr_get_fd_param(const char *name, const char *fdname, int index,
 QEMUFile *cpr_transfer_output(MigrationChannel *channel, Error **errp);
 QEMUFile *cpr_transfer_input(MigrationChannel *channel, Error **errp);
 
+QEMUFile *cpr_exec_output(Error **errp);
+QEMUFile *cpr_exec_input(Error **errp);
+void cpr_exec_persist_state(QEMUFile *f);
+bool cpr_exec_has_state(void);
+void cpr_exec_unpersist_state(void);
 #endif
diff --git a/migration/cpr-exec.c b/migration/cpr-exec.c
new file mode 100644
index 0000000..9b54eab
--- /dev/null
+++ b/migration/cpr-exec.c
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2021-2025 Oracle and/or its affiliates.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/cutils.h"
+#include "qemu/memfd.h"
+#include "qapi/error.h"
+#include "io/channel-file.h"
+#include "io/channel-socket.h"
+#include "migration/cpr.h"
+#include "migration/qemu-file.h"
+#include "migration/misc.h"
+#include "migration/vmstate.h"
+#include "system/runstate.h"
+
+#define CPR_EXEC_STATE_NAME "QEMU_CPR_EXEC_STATE"
+
+static QEMUFile *qemu_file_new_fd_input(int fd, const char *name)
+{
+    g_autoptr(QIOChannelFile) fioc = qio_channel_file_new_fd(fd);
+    QIOChannel *ioc = QIO_CHANNEL(fioc);
+    qio_channel_set_name(ioc, name);
+    return qemu_file_new_input(ioc);
+}
+
+static QEMUFile *qemu_file_new_fd_output(int fd, const char *name)
+{
+    g_autoptr(QIOChannelFile) fioc = qio_channel_file_new_fd(fd);
+    QIOChannel *ioc = QIO_CHANNEL(fioc);
+    qio_channel_set_name(ioc, name);
+    return qemu_file_new_output(ioc);
+}
+
+void cpr_exec_persist_state(QEMUFile *f)
+{
+    QIOChannelFile *fioc = QIO_CHANNEL_FILE(qemu_file_get_ioc(f));
+    int mfd = dup(fioc->fd);
+    char val[16];
+
+    /* Remember mfd in environment for post-exec load */
+    qemu_clear_cloexec(mfd);
+    snprintf(val, sizeof(val), "%d", mfd);
+    g_setenv(CPR_EXEC_STATE_NAME, val, 1);
+}
+
+static int cpr_exec_find_state(void)
+{
+    const char *val = g_getenv(CPR_EXEC_STATE_NAME);
+    int mfd;
+
+    assert(val);
+    g_unsetenv(CPR_EXEC_STATE_NAME);
+    assert(!qemu_strtoi(val, NULL, 10, &mfd));
+    return mfd;
+}
+
+bool cpr_exec_has_state(void)
+{
+    return g_getenv(CPR_EXEC_STATE_NAME) != NULL;
+}
+
+void cpr_exec_unpersist_state(void)
+{
+    int mfd;
+    const char *val = g_getenv(CPR_EXEC_STATE_NAME);
+
+    g_unsetenv(CPR_EXEC_STATE_NAME);
+    assert(val);
+    assert(!qemu_strtoi(val, NULL, 10, &mfd));
+    close(mfd);
+}
+
+QEMUFile *cpr_exec_output(Error **errp)
+{
+    int mfd = qemu_memfd_create(CPR_EXEC_STATE_NAME, 0, false, 0, 0, errp);
+
+    if (mfd < 0) {
+        return NULL;
+    }
+
+    return qemu_file_new_fd_output(mfd, CPR_EXEC_STATE_NAME);
+}
+
+QEMUFile *cpr_exec_input(Error **errp)
+{
+    int mfd = cpr_exec_find_state();
+
+    lseek(mfd, 0, SEEK_SET);
+    return qemu_file_new_fd_input(mfd, CPR_EXEC_STATE_NAME);
+}
diff --git a/migration/meson.build b/migration/meson.build
index 0f71544..16909d5 100644
--- a/migration/meson.build
+++ b/migration/meson.build
@@ -16,6 +16,7 @@ system_ss.add(files(
   'channel-block.c',
   'cpr.c',
   'cpr-transfer.c',
+  'cpr-exec.c',
   'cpu-throttle.c',
   'dirtyrate.c',
   'exec.c',
-- 
1.8.3.1



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

* [PATCH V5 06/19] migration: cpr-exec mode
  2025-10-01 15:33 [PATCH V5 00/19] Live update: cpr-exec Steve Sistare
                   ` (4 preceding siblings ...)
  2025-10-01 15:33 ` [PATCH V5 05/19] migration: cpr-exec save and load Steve Sistare
@ 2025-10-01 15:33 ` Steve Sistare
  2025-10-01 15:33 ` [PATCH V5 07/19] migration: cpr-exec docs Steve Sistare
                   ` (13 subsequent siblings)
  19 siblings, 0 replies; 34+ messages in thread
From: Steve Sistare @ 2025-10-01 15:33 UTC (permalink / raw)
  To: qemu-devel
  Cc: Fabiano Rosas, Peter Xu, Markus Armbruster, Paolo Bonzini,
	Eric Blake, Dr. David Alan Gilbert, Cedric Le Goater,
	Alex Williamson, Steve Sistare

Add the cpr-exec migration mode.  Usage:
  qemu-system-$arch -machine aux-ram-share=on ...
  migrate_set_parameter mode cpr-exec
  migrate_set_parameter cpr-exec-command \
    <arg1> <arg2> ... -incoming <uri-1> \
  migrate -d <uri-1>

The migrate command stops the VM, saves state to uri-1,
directly exec's a new version of QEMU on the same host,
replacing the original process while retaining its PID, and
loads state from uri-1.  Guest RAM is preserved in place,
albeit with new virtual addresses.

The new QEMU process is started by exec'ing the command
specified by the @cpr-exec-command parameter.  The first word of
the command is the binary, and the remaining words are its
arguments.  The command may be a direct invocation of new QEMU,
or may be a non-QEMU command that exec's the new QEMU binary.

This mode creates a second migration channel that is not visible
to the user.  At the start of migration, old QEMU saves CPR state
to the second channel, and at the end of migration, it tells the
main loop to call cpr_exec.  New QEMU loads CPR state early, before
objects are created.

Because old QEMU terminates when new QEMU starts, one cannot
stream data between the two, so uri-1 must be a type,
such as a file, that accepts all data before old QEMU exits.
Otherwise, old QEMU may quietly block writing to the channel.

Memory-backend objects must have the share=on attribute, but
memory-backend-epc is not supported.  The VM must be started with
the '-machine aux-ram-share=on' option, which allows anonymous
memory to be transferred in place to the new process.  The memfds
are kept open across exec by clearing the close-on-exec flag, their
values are saved in CPR state, and they are mmap'd in new QEMU.

Signed-off-by: Steve Sistare <steven.sistare@oracle.com>
Acked-by: Markus Armbruster <armbru@redhat.com>
---
 qapi/migration.json       | 25 ++++++++++++-
 include/migration/cpr.h   |  2 +
 migration/cpr-exec.c      | 95 +++++++++++++++++++++++++++++++++++++++++++++++
 migration/cpr.c           | 23 +++++++++++-
 migration/migration.c     | 10 ++++-
 migration/ram.c           |  1 +
 migration/vmstate-types.c |  8 ++++
 system/vl.c               |  4 +-
 migration/trace-events    |  1 +
 9 files changed, 164 insertions(+), 5 deletions(-)

diff --git a/qapi/migration.json b/qapi/migration.json
index 2be8fa1..be0f3fc 100644
--- a/qapi/migration.json
+++ b/qapi/migration.json
@@ -694,9 +694,32 @@
 #     until you issue the `migrate-incoming` command.
 #
 #     (since 10.0)
+#
+# @cpr-exec: The migrate command stops the VM, saves state to the
+#     migration channel, directly exec's a new version of QEMU on the
+#     same host, replacing the original process while retaining its
+#     PID, and loads state from the channel.  Guest RAM is preserved
+#     in place.  Devices and their pinned pages are also preserved for
+#     VFIO and IOMMUFD.
+#
+#     Old QEMU starts new QEMU by exec'ing the command specified by
+#     the @cpr-exec-command parameter.  The command may be a direct
+#     invocation of new QEMU, or may be a wrapper that exec's the new
+#     QEMU binary.
+#
+#     Because old QEMU terminates when new QEMU starts, one cannot
+#     stream data between the two, so the channel must be a type,
+#     such as a file, that accepts all data before old QEMU exits.
+#     Otherwise, old QEMU may quietly block writing to the channel.
+#
+#     Memory-backend objects must have the share=on attribute, but
+#     memory-backend-epc is not supported.  The VM must be started
+#     with the '-machine aux-ram-share=on' option.
+#
+#     (since 10.2)
 ##
 { 'enum': 'MigMode',
-  'data': [ 'normal', 'cpr-reboot', 'cpr-transfer' ] }
+  'data': [ 'normal', 'cpr-reboot', 'cpr-transfer', 'cpr-exec' ] }
 
 ##
 # @ZeroPageDetection:
diff --git a/include/migration/cpr.h b/include/migration/cpr.h
index b84389f..a412d66 100644
--- a/include/migration/cpr.h
+++ b/include/migration/cpr.h
@@ -53,9 +53,11 @@ int cpr_get_fd_param(const char *name, const char *fdname, int index,
 QEMUFile *cpr_transfer_output(MigrationChannel *channel, Error **errp);
 QEMUFile *cpr_transfer_input(MigrationChannel *channel, Error **errp);
 
+void cpr_exec_init(void);
 QEMUFile *cpr_exec_output(Error **errp);
 QEMUFile *cpr_exec_input(Error **errp);
 void cpr_exec_persist_state(QEMUFile *f);
 bool cpr_exec_has_state(void);
 void cpr_exec_unpersist_state(void);
+void cpr_exec_unpreserve_fds(void);
 #endif
diff --git a/migration/cpr-exec.c b/migration/cpr-exec.c
index 9b54eab..8b12ba9 100644
--- a/migration/cpr-exec.c
+++ b/migration/cpr-exec.c
@@ -6,15 +6,21 @@
 
 #include "qemu/osdep.h"
 #include "qemu/cutils.h"
+#include "qemu/error-report.h"
 #include "qemu/memfd.h"
 #include "qapi/error.h"
+#include "qapi/type-helpers.h"
 #include "io/channel-file.h"
 #include "io/channel-socket.h"
+#include "block/block-global-state.h"
+#include "qemu/main-loop.h"
 #include "migration/cpr.h"
 #include "migration/qemu-file.h"
+#include "migration/migration.h"
 #include "migration/misc.h"
 #include "migration/vmstate.h"
 #include "system/runstate.h"
+#include "trace.h"
 
 #define CPR_EXEC_STATE_NAME "QEMU_CPR_EXEC_STATE"
 
@@ -91,3 +97,92 @@ QEMUFile *cpr_exec_input(Error **errp)
     lseek(mfd, 0, SEEK_SET);
     return qemu_file_new_fd_input(mfd, CPR_EXEC_STATE_NAME);
 }
+
+static bool preserve_fd(int fd)
+{
+    qemu_clear_cloexec(fd);
+    return true;
+}
+
+static bool unpreserve_fd(int fd)
+{
+    qemu_set_cloexec(fd);
+    return true;
+}
+
+static void cpr_exec_preserve_fds(void)
+{
+    cpr_walk_fd(preserve_fd);
+}
+
+void cpr_exec_unpreserve_fds(void)
+{
+    cpr_walk_fd(unpreserve_fd);
+}
+
+static void cpr_exec_cb(void *opaque)
+{
+    MigrationState *s = migrate_get_current();
+    char **argv = strv_from_str_list(s->parameters.cpr_exec_command);
+    Error *err = NULL;
+
+    /*
+     * Clear the close-on-exec flag for all preserved fd's.  We cannot do so
+     * earlier because they should not persist across miscellaneous fork and
+     * exec calls that are performed during normal operation.
+     */
+    cpr_exec_preserve_fds();
+
+    trace_cpr_exec();
+    execvp(argv[0], argv);
+
+    /*
+     * exec should only fail if argv[0] is bogus, or has a permissions problem,
+     * or the system is very short on resources.
+     */
+    g_strfreev(argv);
+    cpr_exec_unpreserve_fds();
+
+    error_setg_errno(&err, errno, "execvp %s failed", argv[0]);
+    error_report_err(error_copy(err));
+    migrate_set_state(&s->state, s->state, MIGRATION_STATUS_FAILED);
+    migrate_set_error(s, err);
+
+    /* Note, we can go from state COMPLETED to FAILED */
+    migration_call_notifiers(s, MIG_EVENT_PRECOPY_FAILED, NULL);
+
+    err = NULL;
+    if (!migration_block_activate(&err)) {
+        /* error was already reported */
+        error_free(err);
+        return;
+    }
+
+    if (runstate_is_live(s->vm_old_state)) {
+        vm_start();
+    }
+}
+
+static int cpr_exec_notifier(NotifierWithReturn *notifier, MigrationEvent *e,
+                             Error **errp)
+{
+    MigrationState *s = migrate_get_current();
+
+    if (e->type == MIG_EVENT_PRECOPY_DONE) {
+        QEMUBH *cpr_exec_bh = qemu_bh_new(cpr_exec_cb, NULL);
+        assert(s->state == MIGRATION_STATUS_COMPLETED);
+        qemu_bh_schedule(cpr_exec_bh);
+        qemu_notify_event();
+    } else if (e->type == MIG_EVENT_PRECOPY_FAILED) {
+        cpr_exec_unpersist_state();
+    }
+    return 0;
+}
+
+void cpr_exec_init(void)
+{
+    static NotifierWithReturn exec_notifier;
+
+    migration_add_notifier_mode(&exec_notifier, cpr_exec_notifier,
+                                MIG_MODE_CPR_EXEC);
+}
diff --git a/migration/cpr.c b/migration/cpr.c
index d3e370e..afadf69 100644
--- a/migration/cpr.c
+++ b/migration/cpr.c
@@ -6,6 +6,7 @@
  */
 
 #include "qemu/osdep.h"
+#include "qemu/error-report.h"
 #include "qapi/error.h"
 #include "hw/vfio/vfio-device.h"
 #include "migration/cpr.h"
@@ -185,6 +186,8 @@ int cpr_state_save(MigrationChannel *channel, Error **errp)
     if (mode == MIG_MODE_CPR_TRANSFER) {
         g_assert(channel);
         f = cpr_transfer_output(channel, errp);
+    } else if (mode == MIG_MODE_CPR_EXEC) {
+        f = cpr_exec_output(errp);
     } else {
         return 0;
     }
@@ -202,6 +205,10 @@ int cpr_state_save(MigrationChannel *channel, Error **errp)
         return ret;
     }
 
+    if (migrate_mode() == MIG_MODE_CPR_EXEC) {
+        cpr_exec_persist_state(f);
+    }
+
     /*
      * Close the socket only partially so we can later detect when the other
      * end closes by getting a HUP event.
@@ -220,7 +227,13 @@ int cpr_state_load(MigrationChannel *channel, Error **errp)
     QEMUFile *f;
     MigMode mode = 0;
 
-    if (channel) {
+    if (cpr_exec_has_state()) {
+        mode = MIG_MODE_CPR_EXEC;
+        f = cpr_exec_input(errp);
+        if (channel) {
+            warn_report("ignoring cpr channel for migration mode cpr-exec");
+        }
+    } else if (channel) {
         mode = MIG_MODE_CPR_TRANSFER;
         cpr_set_incoming_mode(mode);
         f = cpr_transfer_input(channel, errp);
@@ -232,6 +245,7 @@ int cpr_state_load(MigrationChannel *channel, Error **errp)
     }
 
     trace_cpr_state_load(MigMode_str(mode));
+    cpr_set_incoming_mode(mode);
 
     v = qemu_get_be32(f);
     if (v != QEMU_CPR_FILE_MAGIC) {
@@ -253,6 +267,11 @@ int cpr_state_load(MigrationChannel *channel, Error **errp)
         return ret;
     }
 
+    if (migrate_mode() == MIG_MODE_CPR_EXEC) {
+        /* Set cloexec to prevent fd leaks from fork until the next cpr-exec */
+        cpr_exec_unpreserve_fds();
+    }
+
     /*
      * Let the caller decide when to close the socket (and generate a HUP event
      * for the sending side).
@@ -273,7 +292,7 @@ void cpr_state_close(void)
 bool cpr_incoming_needed(void *opaque)
 {
     MigMode mode = migrate_mode();
-    return mode == MIG_MODE_CPR_TRANSFER;
+    return mode == MIG_MODE_CPR_TRANSFER || mode == MIG_MODE_CPR_EXEC;
 }
 
 /*
diff --git a/migration/migration.c b/migration/migration.c
index 08a98f7..2515bec 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -333,6 +333,7 @@ void migration_object_init(void)
 
     ram_mig_init();
     dirty_bitmap_mig_init();
+    cpr_exec_init();
 
     /* Initialize cpu throttle timers */
     cpu_throttle_init();
@@ -1796,7 +1797,8 @@ bool migrate_mode_is_cpr(MigrationState *s)
 {
     MigMode mode = s->parameters.mode;
     return mode == MIG_MODE_CPR_REBOOT ||
-           mode == MIG_MODE_CPR_TRANSFER;
+           mode == MIG_MODE_CPR_TRANSFER ||
+           mode == MIG_MODE_CPR_EXEC;
 }
 
 int migrate_init(MigrationState *s, Error **errp)
@@ -2145,6 +2147,12 @@ static bool migrate_prepare(MigrationState *s, bool resume, Error **errp)
         return false;
     }
 
+    if (migrate_mode() == MIG_MODE_CPR_EXEC &&
+        !s->parameters.has_cpr_exec_command) {
+        error_setg(errp, "cpr-exec mode requires setting cpr-exec-command");
+        return false;
+    }
+
     if (migration_is_blocked(errp)) {
         return false;
     }
diff --git a/migration/ram.c b/migration/ram.c
index 7208bc1..6730a41 100644
--- a/migration/ram.c
+++ b/migration/ram.c
@@ -228,6 +228,7 @@ bool migrate_ram_is_ignored(RAMBlock *block)
     MigMode mode = migrate_mode();
     return !qemu_ram_is_migratable(block) ||
            mode == MIG_MODE_CPR_TRANSFER ||
+           mode == MIG_MODE_CPR_EXEC ||
            (migrate_ignore_shared() && qemu_ram_is_shared(block)
                                     && qemu_ram_is_named_file(block));
 }
diff --git a/migration/vmstate-types.c b/migration/vmstate-types.c
index 741a588..1aa0573 100644
--- a/migration/vmstate-types.c
+++ b/migration/vmstate-types.c
@@ -321,6 +321,10 @@ static int get_fd(QEMUFile *f, void *pv, size_t size,
                   const VMStateField *field)
 {
     int32_t *v = pv;
+    if (migrate_mode() == MIG_MODE_CPR_EXEC) {
+        qemu_get_sbe32s(f, v);
+        return 0;
+    }
     *v = qemu_file_get_fd(f);
     return 0;
 }
@@ -329,6 +333,10 @@ static int put_fd(QEMUFile *f, void *pv, size_t size,
                   const VMStateField *field, JSONWriter *vmdesc)
 {
     int32_t *v = pv;
+    if (migrate_mode() == MIG_MODE_CPR_EXEC) {
+        qemu_put_sbe32s(f, v);
+        return 0;
+    }
     return qemu_file_put_fd(f, *v);
 }
 
diff --git a/system/vl.c b/system/vl.c
index 4c24073..f395d04 100644
--- a/system/vl.c
+++ b/system/vl.c
@@ -3867,6 +3867,8 @@ void qemu_init(int argc, char **argv)
     }
     qemu_init_displays();
     accel_setup_post(current_machine);
-    os_setup_post();
+    if (migrate_mode() != MIG_MODE_CPR_EXEC) {
+        os_setup_post();
+    }
     resume_mux_open();
 }
diff --git a/migration/trace-events b/migration/trace-events
index 706db97..e8edd1f 100644
--- a/migration/trace-events
+++ b/migration/trace-events
@@ -354,6 +354,7 @@ cpr_state_save(const char *mode) "%s mode"
 cpr_state_load(const char *mode) "%s mode"
 cpr_transfer_input(const char *path) "%s"
 cpr_transfer_output(const char *path) "%s"
+cpr_exec(void) ""
 
 # block-dirty-bitmap.c
 send_bitmap_header_enter(void) ""
-- 
1.8.3.1



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

* [PATCH V5 07/19] migration: cpr-exec docs
  2025-10-01 15:33 [PATCH V5 00/19] Live update: cpr-exec Steve Sistare
                   ` (5 preceding siblings ...)
  2025-10-01 15:33 ` [PATCH V5 06/19] migration: cpr-exec mode Steve Sistare
@ 2025-10-01 15:33 ` Steve Sistare
  2025-10-01 15:34 ` [PATCH V5 08/19] vfio: cpr-exec mode Steve Sistare
                   ` (12 subsequent siblings)
  19 siblings, 0 replies; 34+ messages in thread
From: Steve Sistare @ 2025-10-01 15:33 UTC (permalink / raw)
  To: qemu-devel
  Cc: Fabiano Rosas, Peter Xu, Markus Armbruster, Paolo Bonzini,
	Eric Blake, Dr. David Alan Gilbert, Cedric Le Goater,
	Alex Williamson, Steve Sistare

Update developer documentation for cpr-exec mode.

Signed-off-by: Steve Sistare <steven.sistare@oracle.com>
Reviewed-by: Fabiano Rosas <farosas@suse.de>
---
 docs/devel/migration/CPR.rst | 112 ++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 111 insertions(+), 1 deletion(-)

diff --git a/docs/devel/migration/CPR.rst b/docs/devel/migration/CPR.rst
index 0a0fd4f..b617856 100644
--- a/docs/devel/migration/CPR.rst
+++ b/docs/devel/migration/CPR.rst
@@ -5,7 +5,7 @@ CPR is the umbrella name for a set of migration modes in which the
 VM is migrated to a new QEMU instance on the same host.  It is
 intended for use when the goal is to update host software components
 that run the VM, such as QEMU or even the host kernel.  At this time,
-the cpr-reboot and cpr-transfer modes are available.
+the cpr-reboot, cpr-transfer, and cpr-exec modes are available.
 
 Because QEMU is restarted on the same host, with access to the same
 local devices, CPR is allowed in certain cases where normal migration
@@ -324,3 +324,113 @@ descriptors from old to new QEMU.  In the future, descriptors for
 vhost, and char devices could be transferred,
 preserving those devices and their kernel state without interruption,
 even if they do not explicitly support live migration.
+
+cpr-exec mode
+-------------
+
+In this mode, QEMU stops the VM, writes VM state to the migration
+URI, and directly exec's a new version of QEMU on the same host,
+replacing the original process while retaining its PID.  Guest RAM is
+preserved in place, albeit with new virtual addresses.  The user
+completes the migration by specifying the ``-incoming`` option, and
+by issuing the ``migrate-incoming`` command if necessary; see details
+below.
+
+This mode supports VFIO/IOMMUFD devices by preserving device
+descriptors and hence kernel state across the exec, even for devices
+that do not support live migration.
+
+Because the old and new QEMU instances are not active concurrently,
+the URI cannot be a type that streams data from one instance to the
+other.
+
+This mode does not require a channel of type ``cpr``.  The information
+that is passed over that channel for cpr-transfer mode is instead
+serialized to a memfd, the number of the fd is saved in the
+QEMU_CPR_EXEC_STATE environment variable during the exec of new QEMU.
+and new QEMU mmaps the memfd.
+
+Usage
+^^^^^
+
+Arguments for the new QEMU process are taken from the
+@cpr-exec-command parameter.  The first argument should be the
+path of a new QEMU binary, or a prefix command that exec's the
+new QEMU binary, and the arguments should include the ''-incoming''
+option.
+
+Memory backend objects must have the ``share=on`` attribute.
+The VM must be started with the ``-machine aux-ram-share=on`` option.
+
+Outgoing:
+  * Set the migration mode parameter to ``cpr-exec``.
+  * Set the ``cpr-exec-command`` parameter.
+  * Issue the ``migrate`` command.  It is recommended that the URI be
+    a ``file`` type, but one can use other types such as ``exec``,
+    provided the command captures all the data from the outgoing side,
+    and provides all the data to the incoming side.
+
+Incoming:
+  * You do not need to explicitly start new QEMU.  It is started as
+    a side effect of the migrate command above.
+  * If the VM was running when the outgoing ``migrate`` command was
+    issued, then QEMU automatically resumes VM execution.
+
+Example 1: incoming URI
+^^^^^^^^^^^^^^^^^^^^^^^
+
+In these examples, we simply restart the same version of QEMU, but in
+a real scenario one would set a new QEMU binary path in
+cpr-exec-command.
+
+::
+
+  # qemu-kvm -monitor stdio
+  -object memory-backend-memfd,id=ram0,size=4G
+  -machine memory-backend=ram0
+  -machine aux-ram-share=on
+  ...
+
+  QEMU 10.2.50 monitor - type 'help' for more information
+  (qemu) info status
+  VM status: running
+  (qemu) migrate_set_parameter mode cpr-exec
+  (qemu) migrate_set_parameter cpr-exec-command qemu-kvm ... -incoming file:vm.state
+  (qemu) migrate -d file:vm.state
+  (qemu) QEMU 10.2.50 monitor - type 'help' for more information
+  (qemu) info status
+  VM status: running
+
+Example 2: incoming defer
+^^^^^^^^^^^^^^^^^^^^^^^^^
+::
+
+  # qemu-kvm -monitor stdio
+  -object memory-backend-memfd,id=ram0,size=4G
+  -machine memory-backend=ram0
+  -machine aux-ram-share=on
+  ...
+
+  QEMU 10.2.50 monitor - type 'help' for more information
+  (qemu) info status
+  VM status: running
+  (qemu) migrate_set_parameter mode cpr-exec
+  (qemu) migrate_set_parameter cpr-exec-command qemu-kvm ... -incoming defer
+  (qemu) migrate -d file:vm.state
+  (qemu) QEMU 10.2.50 monitor - type 'help' for more information
+  (qemu) info status
+  status: paused (inmigrate)
+  (qemu) migrate_incoming file:vm.state
+  (qemu) info status
+  VM status: running
+
+Caveats
+^^^^^^^
+
+cpr-exec mode may not be used with postcopy, background-snapshot,
+or COLO.
+
+cpr-exec mode requires permission to use the exec system call, which
+is denied by certain sandbox options, such as spawn.
+
+The guest pause time increases for large guest RAM backed by small pages.
-- 
1.8.3.1



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

* [PATCH V5 08/19] vfio: cpr-exec mode
  2025-10-01 15:33 [PATCH V5 00/19] Live update: cpr-exec Steve Sistare
                   ` (6 preceding siblings ...)
  2025-10-01 15:33 ` [PATCH V5 07/19] migration: cpr-exec docs Steve Sistare
@ 2025-10-01 15:34 ` Steve Sistare
  2025-10-01 15:34 ` [PATCH V5 09/19] tests/qtest: export qtest_qemu_binary Steve Sistare
                   ` (11 subsequent siblings)
  19 siblings, 0 replies; 34+ messages in thread
From: Steve Sistare @ 2025-10-01 15:34 UTC (permalink / raw)
  To: qemu-devel
  Cc: Fabiano Rosas, Peter Xu, Markus Armbruster, Paolo Bonzini,
	Eric Blake, Dr. David Alan Gilbert, Cedric Le Goater,
	Alex Williamson, Steve Sistare

All blockers and notifiers for cpr-transfer mode also apply to cpr-exec.

Signed-off-by: Steve Sistare <steven.sistare@oracle.com>
Acked-by: Cédric Le Goater <clg@redhat.com>
---
 hw/vfio/container.c   |  3 ++-
 hw/vfio/cpr-iommufd.c |  3 ++-
 hw/vfio/cpr-legacy.c  |  9 +++++----
 hw/vfio/cpr.c         | 13 +++++++------
 4 files changed, 16 insertions(+), 12 deletions(-)

diff --git a/hw/vfio/container.c b/hw/vfio/container.c
index 030c6d3..935f14d 100644
--- a/hw/vfio/container.c
+++ b/hw/vfio/container.c
@@ -988,7 +988,8 @@ static bool vfio_legacy_attach_device(const char *name, VFIODevice *vbasedev,
         error_setg(&vbasedev->cpr.mdev_blocker,
                    "CPR does not support vfio mdev %s", vbasedev->name);
         if (migrate_add_blocker_modes(&vbasedev->cpr.mdev_blocker, errp,
-                                      MIG_MODE_CPR_TRANSFER, -1) < 0) {
+                                      MIG_MODE_CPR_TRANSFER, MIG_MODE_CPR_EXEC,
+                                      -1) < 0) {
             goto hiod_unref_exit;
         }
     }
diff --git a/hw/vfio/cpr-iommufd.c b/hw/vfio/cpr-iommufd.c
index 148a06d..e1f1854 100644
--- a/hw/vfio/cpr-iommufd.c
+++ b/hw/vfio/cpr-iommufd.c
@@ -159,7 +159,8 @@ bool vfio_iommufd_cpr_register_iommufd(IOMMUFDBackend *be, Error **errp)
 
     if (!vfio_cpr_supported(be, cpr_blocker)) {
         return migrate_add_blocker_modes(cpr_blocker, errp,
-                                         MIG_MODE_CPR_TRANSFER, -1) == 0;
+                                         MIG_MODE_CPR_TRANSFER,
+                                         MIG_MODE_CPR_EXEC, -1) == 0;
     }
 
     vmstate_register(NULL, -1, &iommufd_cpr_vmstate, be);
diff --git a/hw/vfio/cpr-legacy.c b/hw/vfio/cpr-legacy.c
index 8f43719..eebb3bf 100644
--- a/hw/vfio/cpr-legacy.c
+++ b/hw/vfio/cpr-legacy.c
@@ -176,16 +176,17 @@ bool vfio_legacy_cpr_register_container(VFIOContainer *container, Error **errp)
 
     if (!vfio_cpr_supported(container, cpr_blocker)) {
         return migrate_add_blocker_modes(cpr_blocker, errp,
-                                         MIG_MODE_CPR_TRANSFER, -1) == 0;
+                                         MIG_MODE_CPR_TRANSFER,
+                                         MIG_MODE_CPR_EXEC, -1) == 0;
     }
 
     vfio_cpr_add_kvm_notifier();
 
     vmstate_register(NULL, -1, &vfio_container_vmstate, container);
 
-    migration_add_notifier_mode(&container->cpr.transfer_notifier,
-                                vfio_cpr_fail_notifier,
-                                MIG_MODE_CPR_TRANSFER);
+    migration_add_notifier_modes(&container->cpr.transfer_notifier,
+                                 vfio_cpr_fail_notifier,
+                                 MIG_MODE_CPR_TRANSFER, MIG_MODE_CPR_EXEC, -1);
     return true;
 }
 
diff --git a/hw/vfio/cpr.c b/hw/vfio/cpr.c
index 2c71fc1..db462aa 100644
--- a/hw/vfio/cpr.c
+++ b/hw/vfio/cpr.c
@@ -195,9 +195,10 @@ static int vfio_cpr_kvm_close_notifier(NotifierWithReturn *notifier,
 void vfio_cpr_add_kvm_notifier(void)
 {
     if (!kvm_close_notifier.notify) {
-        migration_add_notifier_mode(&kvm_close_notifier,
-                                    vfio_cpr_kvm_close_notifier,
-                                    MIG_MODE_CPR_TRANSFER);
+        migration_add_notifier_modes(&kvm_close_notifier,
+                                     vfio_cpr_kvm_close_notifier,
+                                     MIG_MODE_CPR_TRANSFER, MIG_MODE_CPR_EXEC,
+                                     -1);
     }
 }
 
@@ -282,9 +283,9 @@ static int vfio_cpr_pci_notifier(NotifierWithReturn *notifier,
 
 void vfio_cpr_pci_register_device(VFIOPCIDevice *vdev)
 {
-    migration_add_notifier_mode(&vdev->cpr.transfer_notifier,
-                                vfio_cpr_pci_notifier,
-                                MIG_MODE_CPR_TRANSFER);
+    migration_add_notifier_modes(&vdev->cpr.transfer_notifier,
+                                 vfio_cpr_pci_notifier,
+                                 MIG_MODE_CPR_TRANSFER, MIG_MODE_CPR_EXEC, -1);
 }
 
 void vfio_cpr_pci_unregister_device(VFIOPCIDevice *vdev)
-- 
1.8.3.1



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

* [PATCH V5 09/19] tests/qtest: export qtest_qemu_binary
  2025-10-01 15:33 [PATCH V5 00/19] Live update: cpr-exec Steve Sistare
                   ` (7 preceding siblings ...)
  2025-10-01 15:34 ` [PATCH V5 08/19] vfio: cpr-exec mode Steve Sistare
@ 2025-10-01 15:34 ` Steve Sistare
  2025-10-01 15:34 ` [PATCH V5 10/19] tests/qtest: qtest_qemu_args Steve Sistare
                   ` (10 subsequent siblings)
  19 siblings, 0 replies; 34+ messages in thread
From: Steve Sistare @ 2025-10-01 15:34 UTC (permalink / raw)
  To: qemu-devel
  Cc: Fabiano Rosas, Peter Xu, Markus Armbruster, Paolo Bonzini,
	Eric Blake, Dr. David Alan Gilbert, Cedric Le Goater,
	Alex Williamson, Steve Sistare

Signed-off-by: Steve Sistare <steven.sistare@oracle.com>
Reviewed-by: Fabiano Rosas <farosas@suse.de>
---
 tests/qtest/libqtest.h | 9 +++++++++
 tests/qtest/libqtest.c | 2 +-
 2 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/tests/qtest/libqtest.h b/tests/qtest/libqtest.h
index fd27521..dc2cdd0 100644
--- a/tests/qtest/libqtest.h
+++ b/tests/qtest/libqtest.h
@@ -48,6 +48,15 @@ QTestState *qtest_initf(const char *fmt, ...) G_GNUC_PRINTF(1, 2);
 QTestState *qtest_vinitf(const char *fmt, va_list ap) G_GNUC_PRINTF(1, 0);
 
 /**
+ * qtest_qemu_binary:
+ * @var: environment variable name
+ *
+ * Look up @var and return its value as the qemu binary path.
+ * If @var is NULL, look up  the default var name.
+ */
+const char *qtest_qemu_binary(const char *var);
+
+/**
  * qtest_init:
  * @extra_args: other arguments to pass to QEMU.  CAUTION: these
  * arguments are subject to word splitting and shell evaluation.
diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c
index 94526b7..43fd997 100644
--- a/tests/qtest/libqtest.c
+++ b/tests/qtest/libqtest.c
@@ -357,7 +357,7 @@ void qtest_remove_abrt_handler(void *data)
     }
 }
 
-static const char *qtest_qemu_binary(const char *var)
+const char *qtest_qemu_binary(const char *var)
 {
     const char *qemu_bin;
 
-- 
1.8.3.1



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

* [PATCH V5 10/19] tests/qtest: qtest_qemu_args
  2025-10-01 15:33 [PATCH V5 00/19] Live update: cpr-exec Steve Sistare
                   ` (8 preceding siblings ...)
  2025-10-01 15:34 ` [PATCH V5 09/19] tests/qtest: export qtest_qemu_binary Steve Sistare
@ 2025-10-01 15:34 ` Steve Sistare
  2025-10-01 15:34 ` [PATCH V5 11/19] tests/qtest: qtest_create_test_state Steve Sistare
                   ` (9 subsequent siblings)
  19 siblings, 0 replies; 34+ messages in thread
From: Steve Sistare @ 2025-10-01 15:34 UTC (permalink / raw)
  To: qemu-devel
  Cc: Fabiano Rosas, Peter Xu, Markus Armbruster, Paolo Bonzini,
	Eric Blake, Dr. David Alan Gilbert, Cedric Le Goater,
	Alex Williamson, Steve Sistare

Define an accessor that returns all the arguments used to exec QEMU.
Collect the arguments that were passed to qtest_spawn_qemu, plus the trace
arguments that were composed inside qtest_spawn_qemu, and move them to a
new function qtest_qemu_args.

This will be needed to test the cpr-exec migration mode.

Signed-off-by: Steve Sistare <steven.sistare@oracle.com>
Reviewed-by: Fabiano Rosas <farosas@suse.de>
---
 tests/qtest/libqtest.h |  8 ++++++++
 tests/qtest/libqtest.c | 54 +++++++++++++++++++++++++++++---------------------
 2 files changed, 39 insertions(+), 23 deletions(-)

diff --git a/tests/qtest/libqtest.h b/tests/qtest/libqtest.h
index dc2cdd0..7f8dd0a 100644
--- a/tests/qtest/libqtest.h
+++ b/tests/qtest/libqtest.h
@@ -57,6 +57,14 @@ QTestState *qtest_vinitf(const char *fmt, va_list ap) G_GNUC_PRINTF(1, 0);
 const char *qtest_qemu_binary(const char *var);
 
 /**
+ * qtest_qemu_args:
+ * @extra_args: Other arguments to pass to QEMU.
+ *
+ * Return the command line used to start QEMU, sans binary.
+ */
+gchar *qtest_qemu_args(const char *extra_args);
+
+/**
  * qtest_init:
  * @extra_args: other arguments to pass to QEMU.  CAUTION: these
  * arguments are subject to word splitting and shell evaluation.
diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c
index 43fd997..b3b3fc2 100644
--- a/tests/qtest/libqtest.c
+++ b/tests/qtest/libqtest.c
@@ -409,20 +409,12 @@ static pid_t qtest_create_process(char *cmd)
 }
 #endif /* _WIN32 */
 
-static QTestState *G_GNUC_PRINTF(2, 3) qtest_spawn_qemu(const char *qemu_bin,
-                                                        const char *fmt, ...)
+static QTestState *qtest_spawn_qemu(const char *qemu_bin, const char *args)
 {
-    va_list ap;
     QTestState *s = g_new0(QTestState, 1);
-    const char *trace = g_getenv("QTEST_TRACE");
-    g_autofree char *tracearg = trace ?
-        g_strdup_printf("-trace %s ", trace) : g_strdup("");
     g_autoptr(GString) command = g_string_new("");
 
-    va_start(ap, fmt);
-    g_string_append_printf(command, CMD_EXEC "%s %s", qemu_bin, tracearg);
-    g_string_append_vprintf(command, fmt, ap);
-    va_end(ap);
+    g_string_printf(command, CMD_EXEC "%s %s", qemu_bin, args);
 
     qtest_add_abrt_handler(kill_qemu_hook_func, s);
 
@@ -466,6 +458,33 @@ static char *qtest_socket_path(const char *suffix)
     return g_strdup_printf("%s/qtest-%d.%s", g_get_tmp_dir(), getpid(), suffix);
 }
 
+gchar *qtest_qemu_args(const char *extra_args)
+{
+    g_autofree gchar *socket_path = qtest_socket_path("sock");
+    g_autofree gchar *qmp_socket_path = qtest_socket_path("qmp");
+    const char *trace = g_getenv("QTEST_TRACE");
+    g_autofree char *tracearg = trace ? g_strdup_printf("-trace %s ", trace) :
+                                        g_strdup("");
+    gchar *args = g_strdup_printf(
+                      "%s"
+                      "-qtest unix:%s "
+                      "-qtest-log %s "
+                      "-chardev socket,path=%s,id=char0 "
+                      "-mon chardev=char0,mode=control "
+                      "-display none "
+                      "-audio none "
+                      "%s"
+                      " -accel qtest",
+
+                      tracearg,
+                      socket_path,
+                      getenv("QTEST_LOG") ? DEV_STDERR : DEV_NULL,
+                      qmp_socket_path,
+                      extra_args ?: "");
+
+    return args;
+}
+
 static QTestState *qtest_init_internal(const char *qemu_bin,
                                        const char *extra_args,
                                        bool do_connect)
@@ -474,6 +493,7 @@ static QTestState *qtest_init_internal(const char *qemu_bin,
     int sock, qmpsock, i;
     g_autofree gchar *socket_path = qtest_socket_path("sock");
     g_autofree gchar *qmp_socket_path = qtest_socket_path("qmp");
+    g_autofree gchar *args = qtest_qemu_args(extra_args);
 
     /*
      * It's possible that if an earlier test run crashed it might
@@ -488,19 +508,7 @@ static QTestState *qtest_init_internal(const char *qemu_bin,
     sock = init_socket(socket_path);
     qmpsock = init_socket(qmp_socket_path);
 
-    s = qtest_spawn_qemu(qemu_bin,
-                         "-qtest unix:%s "
-                         "-qtest-log %s "
-                         "-chardev socket,path=%s,id=char0 "
-                         "-mon chardev=char0,mode=control "
-                         "-display none "
-                         "-audio none "
-                         "%s"
-                         " -accel qtest",
-                         socket_path,
-                         getenv("QTEST_LOG") ? DEV_STDERR : DEV_NULL,
-                         qmp_socket_path,
-                         extra_args ?: "");
+    s = qtest_spawn_qemu(qemu_bin, args);
 
     qtest_client_set_rx_handler(s, qtest_client_socket_recv_line);
     qtest_client_set_tx_handler(s, qtest_client_socket_send);
-- 
1.8.3.1



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

* [PATCH V5 11/19] tests/qtest: qtest_create_test_state
  2025-10-01 15:33 [PATCH V5 00/19] Live update: cpr-exec Steve Sistare
                   ` (9 preceding siblings ...)
  2025-10-01 15:34 ` [PATCH V5 10/19] tests/qtest: qtest_qemu_args Steve Sistare
@ 2025-10-01 15:34 ` Steve Sistare
  2025-10-01 15:34 ` [PATCH V5 12/19] tests/qtest: qtest_qemu_spawn_func Steve Sistare
                   ` (8 subsequent siblings)
  19 siblings, 0 replies; 34+ messages in thread
From: Steve Sistare @ 2025-10-01 15:34 UTC (permalink / raw)
  To: qemu-devel
  Cc: Fabiano Rosas, Peter Xu, Markus Armbruster, Paolo Bonzini,
	Eric Blake, Dr. David Alan Gilbert, Cedric Le Goater,
	Alex Williamson, Steve Sistare

Refactor qtest_spawn_qemu and create a subroutine to create a QTestState
object, to be used in a subsequent patch.

Signed-off-by: Steve Sistare <steven.sistare@oracle.com>
Reviewed-by: Fabiano Rosas <farosas@suse.de>
---
 tests/qtest/libqtest.c | 21 ++++++++++++++-------
 1 file changed, 14 insertions(+), 7 deletions(-)

diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c
index b3b3fc2..e1ecd38 100644
--- a/tests/qtest/libqtest.c
+++ b/tests/qtest/libqtest.c
@@ -409,22 +409,29 @@ static pid_t qtest_create_process(char *cmd)
 }
 #endif /* _WIN32 */
 
-static QTestState *qtest_spawn_qemu(const char *qemu_bin, const char *args)
+static QTestState *qtest_create_test_state(int pid)
 {
     QTestState *s = g_new0(QTestState, 1);
+
+    s->qemu_pid = pid;
+    qtest_add_abrt_handler(kill_qemu_hook_func, s);
+    return s;
+}
+
+static QTestState *qtest_spawn_qemu(const char *qemu_bin, const char *args)
+{
+    int pid;
     g_autoptr(GString) command = g_string_new("");
 
     g_string_printf(command, CMD_EXEC "%s %s", qemu_bin, args);
 
-    qtest_add_abrt_handler(kill_qemu_hook_func, s);
-
     if (!silence_spawn_log) {
         g_test_message("starting QEMU: %s", command->str);
     }
 
 #ifndef _WIN32
-    s->qemu_pid = fork();
-    if (s->qemu_pid == 0) {
+    pid = fork();
+    if (pid == 0) {
 #ifdef __linux__
         /*
          * Although we register a ABRT handler to kill off QEMU
@@ -447,10 +454,10 @@ static QTestState *qtest_spawn_qemu(const char *qemu_bin, const char *args)
         exit(1);
     }
 #else
-    s->qemu_pid = qtest_create_process(command->str);
+    pid = qtest_create_process(command->str);
 #endif /* _WIN32 */
 
-    return s;
+    return qtest_create_test_state(pid);
 }
 
 static char *qtest_socket_path(const char *suffix)
-- 
1.8.3.1



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

* [PATCH V5 12/19] tests/qtest: qtest_qemu_spawn_func
  2025-10-01 15:33 [PATCH V5 00/19] Live update: cpr-exec Steve Sistare
                   ` (10 preceding siblings ...)
  2025-10-01 15:34 ` [PATCH V5 11/19] tests/qtest: qtest_create_test_state Steve Sistare
@ 2025-10-01 15:34 ` Steve Sistare
  2025-10-01 15:34 ` [PATCH V5 13/19] tests/qtest: qtest_init_after_exec Steve Sistare
                   ` (7 subsequent siblings)
  19 siblings, 0 replies; 34+ messages in thread
From: Steve Sistare @ 2025-10-01 15:34 UTC (permalink / raw)
  To: qemu-devel
  Cc: Fabiano Rosas, Peter Xu, Markus Armbruster, Paolo Bonzini,
	Eric Blake, Dr. David Alan Gilbert, Cedric Le Goater,
	Alex Williamson, Steve Sistare

Allow the qtest_qemu_spawn caller to pass the function to be called
to perform the spawn.  The opaque argument is needed by a new spawn
function in a subsequent patch.

Signed-off-by: Steve Sistare <steven.sistare@oracle.com>
Reviewed-by: Fabiano Rosas <farosas@suse.de>
---
 tests/qtest/libqtest.c | 18 +++++++++++++-----
 1 file changed, 13 insertions(+), 5 deletions(-)

diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c
index e1ecd38..644c634 100644
--- a/tests/qtest/libqtest.c
+++ b/tests/qtest/libqtest.c
@@ -418,7 +418,8 @@ static QTestState *qtest_create_test_state(int pid)
     return s;
 }
 
-static QTestState *qtest_spawn_qemu(const char *qemu_bin, const char *args)
+static QTestState *qtest_spawn_qemu(const char *qemu_bin, const char *args,
+                                    void *opaque)
 {
     int pid;
     g_autoptr(GString) command = g_string_new("");
@@ -492,9 +493,15 @@ gchar *qtest_qemu_args(const char *extra_args)
     return args;
 }
 
+typedef QTestState *(*qtest_qemu_spawn_func)(const char *qemu_bin,
+                                             const char *extra_args,
+                                             void *opaque);
+
 static QTestState *qtest_init_internal(const char *qemu_bin,
                                        const char *extra_args,
-                                       bool do_connect)
+                                       bool do_connect,
+                                       qtest_qemu_spawn_func spawn,
+                                       void *opaque)
 {
     QTestState *s;
     int sock, qmpsock, i;
@@ -515,7 +522,7 @@ static QTestState *qtest_init_internal(const char *qemu_bin,
     sock = init_socket(socket_path);
     qmpsock = init_socket(qmp_socket_path);
 
-    s = qtest_spawn_qemu(qemu_bin, args);
+    s = spawn(qemu_bin, args, opaque);
 
     qtest_client_set_rx_handler(s, qtest_client_socket_recv_line);
     qtest_client_set_tx_handler(s, qtest_client_socket_send);
@@ -570,7 +577,8 @@ void qtest_connect(QTestState *s)
 
 QTestState *qtest_init_without_qmp_handshake(const char *extra_args)
 {
-    return qtest_init_internal(qtest_qemu_binary(NULL), extra_args, true);
+    return qtest_init_internal(qtest_qemu_binary(NULL), extra_args, true,
+                               qtest_spawn_qemu, NULL);
 }
 
 void qtest_qmp_handshake(QTestState *s, QList *capabilities)
@@ -593,7 +601,7 @@ QTestState *qtest_init_ext(const char *var, const char *extra_args,
                            QList *capabilities, bool do_connect)
 {
     QTestState *s = qtest_init_internal(qtest_qemu_binary(var), extra_args,
-                                        do_connect);
+                                        do_connect, qtest_spawn_qemu, NULL);
 
     if (do_connect) {
         qtest_qmp_handshake(s, capabilities);
-- 
1.8.3.1



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

* [PATCH V5 13/19] tests/qtest: qtest_init_after_exec
  2025-10-01 15:33 [PATCH V5 00/19] Live update: cpr-exec Steve Sistare
                   ` (11 preceding siblings ...)
  2025-10-01 15:34 ` [PATCH V5 12/19] tests/qtest: qtest_qemu_spawn_func Steve Sistare
@ 2025-10-01 15:34 ` Steve Sistare
  2025-10-01 15:34 ` [PATCH V5 14/19] migration-test: only_source option Steve Sistare
                   ` (6 subsequent siblings)
  19 siblings, 0 replies; 34+ messages in thread
From: Steve Sistare @ 2025-10-01 15:34 UTC (permalink / raw)
  To: qemu-devel
  Cc: Fabiano Rosas, Peter Xu, Markus Armbruster, Paolo Bonzini,
	Eric Blake, Dr. David Alan Gilbert, Cedric Le Goater,
	Alex Williamson, Steve Sistare

Define a function to create a QTestState object representing the state
of QEMU after old QEMU exec's new QEMU.  This is needed for testing
the cpr-exec migration mode.

Signed-off-by: Steve Sistare <steven.sistare@oracle.com>
Reviewed-by: Fabiano Rosas <farosas@suse.de>
---
 tests/qtest/libqtest.h |  8 ++++++++
 tests/qtest/libqtest.c | 19 +++++++++++++++++++
 2 files changed, 27 insertions(+)

diff --git a/tests/qtest/libqtest.h b/tests/qtest/libqtest.h
index 7f8dd0a..9c118c8 100644
--- a/tests/qtest/libqtest.h
+++ b/tests/qtest/libqtest.h
@@ -57,6 +57,14 @@ QTestState *qtest_vinitf(const char *fmt, va_list ap) G_GNUC_PRINTF(1, 0);
 const char *qtest_qemu_binary(const char *var);
 
 /**
+ * qtest_init_after_exec:
+ * @qts: the previous QEMU state
+ *
+ * Return a test state representing new QEMU after @qts exec's it.
+ */
+QTestState *qtest_init_after_exec(QTestState *qts);
+
+/**
  * qtest_qemu_args:
  * @extra_args: Other arguments to pass to QEMU.
  *
diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c
index 644c634..ea39c22 100644
--- a/tests/qtest/libqtest.c
+++ b/tests/qtest/libqtest.c
@@ -615,6 +615,25 @@ QTestState *qtest_init_ext(const char *var, const char *extra_args,
     return s;
 }
 
+static QTestState *qtest_attach_qemu(const char *qemu_bin,
+                                     const char *extra_args,
+                                     void *opaque)
+{
+    int pid = *(int *)opaque;
+    return qtest_create_test_state(pid);
+}
+
+QTestState *qtest_init_after_exec(QTestState *qts)
+{
+    void *opaque = (void *)&qts->qemu_pid;
+    QTestState *s;
+
+    s = qtest_init_internal(NULL, NULL, true, qtest_attach_qemu, opaque);
+    qts->qemu_pid = -1;
+    qtest_qmp_handshake(s, NULL);
+    return s;
+}
+
 QTestState *qtest_init(const char *extra_args)
 {
     return qtest_init_ext(NULL, extra_args, NULL, true);
-- 
1.8.3.1



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

* [PATCH V5 14/19] migration-test: only_source option
  2025-10-01 15:33 [PATCH V5 00/19] Live update: cpr-exec Steve Sistare
                   ` (12 preceding siblings ...)
  2025-10-01 15:34 ` [PATCH V5 13/19] tests/qtest: qtest_init_after_exec Steve Sistare
@ 2025-10-01 15:34 ` Steve Sistare
  2025-10-01 15:34 ` [PATCH V5 15/19] migration-test: shm path accessor Steve Sistare
                   ` (5 subsequent siblings)
  19 siblings, 0 replies; 34+ messages in thread
From: Steve Sistare @ 2025-10-01 15:34 UTC (permalink / raw)
  To: qemu-devel
  Cc: Fabiano Rosas, Peter Xu, Markus Armbruster, Paolo Bonzini,
	Eric Blake, Dr. David Alan Gilbert, Cedric Le Goater,
	Alex Williamson, Steve Sistare

Add the only_source option, analogous to only_target.

Signed-off-by: Steve Sistare <steven.sistare@oracle.com>
Reviewed-by: Fabiano Rosas <farosas@suse.de>
---
 tests/qtest/migration/framework.h |  2 ++
 tests/qtest/migration/framework.c | 24 +++++++++++++++---------
 2 files changed, 17 insertions(+), 9 deletions(-)

diff --git a/tests/qtest/migration/framework.h b/tests/qtest/migration/framework.h
index 01e425e..f1bb9d4 100644
--- a/tests/qtest/migration/framework.h
+++ b/tests/qtest/migration/framework.h
@@ -103,6 +103,8 @@ typedef struct {
      */
     bool hide_stderr;
     bool use_shmem;
+    /* only launch the source process */
+    bool only_source;
     /* only launch the target process */
     bool only_target;
     /* Use dirty ring if true; dirty logging otherwise */
diff --git a/tests/qtest/migration/framework.c b/tests/qtest/migration/framework.c
index 407c902..9564293 100644
--- a/tests/qtest/migration/framework.c
+++ b/tests/qtest/migration/framework.c
@@ -234,7 +234,7 @@ static void migrate_start_set_capabilities(QTestState *from, QTestState *to,
      * to mimic as closer as that.
      */
     migrate_set_capability(from, "events", true);
-    if (!args->defer_target_connect) {
+    if (!args->defer_target_connect && to) {
         migrate_set_capability(to, "events", true);
     }
 
@@ -246,8 +246,10 @@ static void migrate_start_set_capabilities(QTestState *from, QTestState *to,
     if (args->caps[MIGRATION_CAPABILITY_MULTIFD]) {
         migrate_set_parameter_int(from, "multifd-channels",
                                   MULTIFD_TEST_CHANNELS);
-        migrate_set_parameter_int(to, "multifd-channels",
-                                  MULTIFD_TEST_CHANNELS);
+        if (to) {
+            migrate_set_parameter_int(to, "multifd-channels",
+                                      MULTIFD_TEST_CHANNELS);
+        }
     }
 
     return;
@@ -410,11 +412,13 @@ int migrate_start(QTestState **from, QTestState **to, const char *uri,
                                  shmem_opts ? shmem_opts : "",
                                  args->opts_target ? args->opts_target : "",
                                  ignore_stderr);
-    *to = qtest_init_ext(QEMU_ENV_DST, cmd_target, capabilities,
-                         !args->defer_target_connect);
-    qtest_qmp_set_event_callback(*to,
-                                 migrate_watch_for_events,
-                                 &dst_state);
+    if (!args->only_source) {
+        *to = qtest_init_ext(QEMU_ENV_DST, cmd_target, capabilities,
+                             !args->defer_target_connect);
+        qtest_qmp_set_event_callback(*to,
+                                     migrate_watch_for_events,
+                                     &dst_state);
+    }
 
     /*
      * Remove shmem file immediately to avoid memory leak in test failed case.
@@ -424,7 +428,9 @@ int migrate_start(QTestState **from, QTestState **to, const char *uri,
         unlink(shmem_path);
     }
 
-    migrate_start_set_capabilities(*from, *to, args);
+    migrate_start_set_capabilities(*from,
+                                   args->only_source ? NULL : *to,
+                                   args);
 
     return 0;
 }
-- 
1.8.3.1



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

* [PATCH V5 15/19] migration-test: shm path accessor
  2025-10-01 15:33 [PATCH V5 00/19] Live update: cpr-exec Steve Sistare
                   ` (13 preceding siblings ...)
  2025-10-01 15:34 ` [PATCH V5 14/19] migration-test: only_source option Steve Sistare
@ 2025-10-01 15:34 ` Steve Sistare
  2025-10-01 15:34 ` [PATCH V5 16/19] migration-test: misc exports Steve Sistare
                   ` (4 subsequent siblings)
  19 siblings, 0 replies; 34+ messages in thread
From: Steve Sistare @ 2025-10-01 15:34 UTC (permalink / raw)
  To: qemu-devel
  Cc: Fabiano Rosas, Peter Xu, Markus Armbruster, Paolo Bonzini,
	Eric Blake, Dr. David Alan Gilbert, Cedric Le Goater,
	Alex Williamson, Steve Sistare

Define an accessor for the shm path.  It will be referenced from
multiple sites in a subsequent patch.

Signed-off-by: Steve Sistare <steven.sistare@oracle.com>
Reviewed-by: Fabiano Rosas <farosas@suse.de>
---
 tests/qtest/migration/framework.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/tests/qtest/migration/framework.c b/tests/qtest/migration/framework.c
index 9564293..9d04f36 100644
--- a/tests/qtest/migration/framework.c
+++ b/tests/qtest/migration/framework.c
@@ -255,6 +255,11 @@ static void migrate_start_set_capabilities(QTestState *from, QTestState *to,
     return;
 }
 
+static char *test_shmem_path(void)
+{
+    return g_strdup_printf("/dev/shm/qemu-%d", getpid());
+}
+
 int migrate_start(QTestState **from, QTestState **to, const char *uri,
                   MigrateStart *args)
 {
@@ -342,7 +347,7 @@ int migrate_start(QTestState **from, QTestState **to, const char *uri,
     }
 
     if (args->use_shmem) {
-        shmem_path = g_strdup_printf("/dev/shm/qemu-%d", getpid());
+        shmem_path = test_shmem_path();
         shmem_opts = g_strdup_printf(
             "-object memory-backend-file,id=mem0,size=%s"
             ",mem-path=%s,share=on -numa node,memdev=mem0",
-- 
1.8.3.1



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

* [PATCH V5 16/19] migration-test: misc exports
  2025-10-01 15:33 [PATCH V5 00/19] Live update: cpr-exec Steve Sistare
                   ` (14 preceding siblings ...)
  2025-10-01 15:34 ` [PATCH V5 15/19] migration-test: shm path accessor Steve Sistare
@ 2025-10-01 15:34 ` Steve Sistare
  2025-10-01 15:34 ` [PATCH V5 17/19] migration-test: migrate_args Steve Sistare
                   ` (3 subsequent siblings)
  19 siblings, 0 replies; 34+ messages in thread
From: Steve Sistare @ 2025-10-01 15:34 UTC (permalink / raw)
  To: qemu-devel
  Cc: Fabiano Rosas, Peter Xu, Markus Armbruster, Paolo Bonzini,
	Eric Blake, Dr. David Alan Gilbert, Cedric Le Goater,
	Alex Williamson, Steve Sistare

Export misc definitions needed by the cpr-exec test.

Signed-off-by: Steve Sistare <steven.sistare@oracle.com>
Reviewed-by: Fabiano Rosas <farosas@suse.de>
---
 tests/qtest/migration/bootfile.h  | 1 +
 tests/qtest/migration/framework.h | 1 +
 tests/qtest/migration/bootfile.c  | 5 +++++
 tests/qtest/migration/framework.c | 5 +++++
 4 files changed, 12 insertions(+)

diff --git a/tests/qtest/migration/bootfile.h b/tests/qtest/migration/bootfile.h
index 6d6a673..96e784b 100644
--- a/tests/qtest/migration/bootfile.h
+++ b/tests/qtest/migration/bootfile.h
@@ -35,5 +35,6 @@
 
 void bootfile_delete(void);
 char *bootfile_create(const char *arch, const char *dir, bool suspend_me);
+char *bootfile_get(void);
 
 #endif /* BOOTFILE_H */
diff --git a/tests/qtest/migration/framework.h b/tests/qtest/migration/framework.h
index f1bb9d4..ba7145a 100644
--- a/tests/qtest/migration/framework.h
+++ b/tests/qtest/migration/framework.h
@@ -237,6 +237,7 @@ void *migrate_hook_start_precopy_tcp_multifd_common(QTestState *from,
 
 typedef struct QTestMigrationState QTestMigrationState;
 QTestMigrationState *get_src(void);
+QTestMigrationState *get_dst(void);
 
 #ifdef CONFIG_GNUTLS
 void migration_test_add_tls(MigrationTestEnv *env);
diff --git a/tests/qtest/migration/bootfile.c b/tests/qtest/migration/bootfile.c
index fac059d..479c432 100644
--- a/tests/qtest/migration/bootfile.c
+++ b/tests/qtest/migration/bootfile.c
@@ -68,3 +68,8 @@ char *bootfile_create(const char *arch, const char *dir, bool suspend_me)
 
     return bootpath;
 }
+
+char *bootfile_get(void)
+{
+    return bootpath;
+}
diff --git a/tests/qtest/migration/framework.c b/tests/qtest/migration/framework.c
index 9d04f36..ca13477 100644
--- a/tests/qtest/migration/framework.c
+++ b/tests/qtest/migration/framework.c
@@ -1005,6 +1005,11 @@ QTestMigrationState *get_src(void)
     return &src_state;
 }
 
+QTestMigrationState *get_dst(void)
+{
+    return &dst_state;
+}
+
 MigrationTestEnv *migration_get_env(void)
 {
     static MigrationTestEnv *env;
-- 
1.8.3.1



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

* [PATCH V5 17/19] migration-test: migrate_args
  2025-10-01 15:33 [PATCH V5 00/19] Live update: cpr-exec Steve Sistare
                   ` (15 preceding siblings ...)
  2025-10-01 15:34 ` [PATCH V5 16/19] migration-test: misc exports Steve Sistare
@ 2025-10-01 15:34 ` Steve Sistare
  2025-10-01 15:34 ` [PATCH V5 18/19] migration-test: strv parameter Steve Sistare
                   ` (2 subsequent siblings)
  19 siblings, 0 replies; 34+ messages in thread
From: Steve Sistare @ 2025-10-01 15:34 UTC (permalink / raw)
  To: qemu-devel
  Cc: Fabiano Rosas, Peter Xu, Markus Armbruster, Paolo Bonzini,
	Eric Blake, Dr. David Alan Gilbert, Cedric Le Goater,
	Alex Williamson, Steve Sistare

Define the subroutine migrate_args to return the arguments that are
used to exec the source or target qemu process.

Signed-off-by: Steve Sistare <steven.sistare@oracle.com>
Reviewed-by: Fabiano Rosas <farosas@suse.de>
---
 tests/qtest/migration/framework.h |  2 ++
 tests/qtest/migration/framework.c | 65 ++++++++++++++++++++++++---------------
 2 files changed, 43 insertions(+), 24 deletions(-)

diff --git a/tests/qtest/migration/framework.h b/tests/qtest/migration/framework.h
index ba7145a..02f9681 100644
--- a/tests/qtest/migration/framework.h
+++ b/tests/qtest/migration/framework.h
@@ -223,6 +223,8 @@ typedef struct {
 void wait_for_serial(const char *side);
 void migrate_prepare_for_dirty_mem(QTestState *from);
 void migrate_wait_for_dirty_mem(QTestState *from, QTestState *to);
+
+int migrate_args(char **from, char **to, const char *uri, MigrateStart *args);
 int migrate_start(QTestState **from, QTestState **to, const char *uri,
                   MigrateStart *args);
 void migrate_end(QTestState *from, QTestState *to, bool test_dest);
diff --git a/tests/qtest/migration/framework.c b/tests/qtest/migration/framework.c
index ca13477..c0b299f 100644
--- a/tests/qtest/migration/framework.c
+++ b/tests/qtest/migration/framework.c
@@ -260,13 +260,12 @@ static char *test_shmem_path(void)
     return g_strdup_printf("/dev/shm/qemu-%d", getpid());
 }
 
-int migrate_start(QTestState **from, QTestState **to, const char *uri,
-                  MigrateStart *args)
+int migrate_args(char **from, char **to, const char *uri, MigrateStart *args)
 {
     /* options for source and target */
     g_autofree gchar *arch_opts = NULL;
-    g_autofree gchar *cmd_source = NULL;
-    g_autofree gchar *cmd_target = NULL;
+    gchar *cmd_source = NULL;
+    gchar *cmd_target = NULL;
     const gchar *ignore_stderr;
     g_autofree char *shmem_opts = NULL;
     g_autofree char *shmem_path = NULL;
@@ -275,23 +274,10 @@ int migrate_start(QTestState **from, QTestState **to, const char *uri,
     const char *memory_size;
     const char *machine_alias, *machine_opts = "";
     g_autofree char *machine = NULL;
-    const char *bootpath;
-    g_autoptr(QList) capabilities = migrate_start_get_qmp_capabilities(args);
+    const char *bootpath = bootfile_get();
     g_autofree char *memory_backend = NULL;
     const char *events;
 
-    if (args->use_shmem) {
-        if (!g_file_test("/dev/shm", G_FILE_TEST_IS_DIR)) {
-            g_test_skip("/dev/shm is not supported");
-            return -1;
-        }
-    }
-
-    dst_state = (QTestMigrationState) { };
-    src_state = (QTestMigrationState) { };
-    bootpath = bootfile_create(arch, tmpfs, args->suspend_me);
-    src_state.suspend_me = args->suspend_me;
-
     if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
         memory_size = "150M";
 
@@ -388,12 +374,6 @@ int migrate_start(QTestState **from, QTestState **to, const char *uri,
                                  shmem_opts ? shmem_opts : "",
                                  args->opts_source ? args->opts_source : "",
                                  ignore_stderr);
-    if (!args->only_target) {
-        *from = qtest_init_ext(QEMU_ENV_SRC, cmd_source, capabilities, true);
-        qtest_qmp_set_event_callback(*from,
-                                     migrate_watch_for_events,
-                                     &src_state);
-    }
 
     /*
      * If the monitor connection is deferred, enable events on the command line
@@ -417,6 +397,42 @@ int migrate_start(QTestState **from, QTestState **to, const char *uri,
                                  shmem_opts ? shmem_opts : "",
                                  args->opts_target ? args->opts_target : "",
                                  ignore_stderr);
+
+    *from = cmd_source;
+    *to = cmd_target;
+    return 0;
+}
+
+int migrate_start(QTestState **from, QTestState **to, const char *uri,
+                  MigrateStart *args)
+{
+    g_autofree gchar *cmd_source = NULL;
+    g_autofree gchar *cmd_target = NULL;
+    g_autoptr(QList) capabilities = migrate_start_get_qmp_capabilities(args);
+
+    if (args->use_shmem) {
+        if (!g_file_test("/dev/shm", G_FILE_TEST_IS_DIR)) {
+            g_test_skip("/dev/shm is not supported");
+            return -1;
+        }
+    }
+
+    dst_state = (QTestMigrationState) { };
+    src_state = (QTestMigrationState) { };
+    bootfile_create(qtest_get_arch(), tmpfs, args->suspend_me);
+    src_state.suspend_me = args->suspend_me;
+
+    if (migrate_args(&cmd_source, &cmd_target, uri, args)) {
+        return -1;
+    }
+
+    if (!args->only_target) {
+        *from = qtest_init_ext(QEMU_ENV_SRC, cmd_source, capabilities, true);
+        qtest_qmp_set_event_callback(*from,
+                                     migrate_watch_for_events,
+                                     &src_state);
+    }
+
     if (!args->only_source) {
         *to = qtest_init_ext(QEMU_ENV_DST, cmd_target, capabilities,
                              !args->defer_target_connect);
@@ -430,6 +446,7 @@ int migrate_start(QTestState **from, QTestState **to, const char *uri,
      * It's valid because QEMU has already opened this file
      */
     if (args->use_shmem) {
+        g_autofree char *shmem_path = test_shmem_path();
         unlink(shmem_path);
     }
 
-- 
1.8.3.1



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

* [PATCH V5 18/19] migration-test: strv parameter
  2025-10-01 15:33 [PATCH V5 00/19] Live update: cpr-exec Steve Sistare
                   ` (16 preceding siblings ...)
  2025-10-01 15:34 ` [PATCH V5 17/19] migration-test: migrate_args Steve Sistare
@ 2025-10-01 15:34 ` Steve Sistare
  2025-10-01 15:34 ` [PATCH V5 19/19] migration-test: test cpr-exec Steve Sistare
  2025-10-01 18:56 ` [PATCH V5 00/19] Live update: cpr-exec Peter Xu
  19 siblings, 0 replies; 34+ messages in thread
From: Steve Sistare @ 2025-10-01 15:34 UTC (permalink / raw)
  To: qemu-devel
  Cc: Fabiano Rosas, Peter Xu, Markus Armbruster, Paolo Bonzini,
	Eric Blake, Dr. David Alan Gilbert, Cedric Le Goater,
	Alex Williamson, Steve Sistare

Define migrate_set_parameter_strv.

Signed-off-by: Steve Sistare <steven.sistare@oracle.com>
Reviewed-by: Fabiano Rosas <farosas@suse.de>
---
 tests/qtest/migration/migration-qmp.h |  2 ++
 tests/qtest/migration/migration-qmp.c | 16 ++++++++++++++++
 2 files changed, 18 insertions(+)

diff --git a/tests/qtest/migration/migration-qmp.h b/tests/qtest/migration/migration-qmp.h
index faa8181..44482d2 100644
--- a/tests/qtest/migration/migration-qmp.h
+++ b/tests/qtest/migration/migration-qmp.h
@@ -34,6 +34,8 @@ void read_blocktime(QTestState *who);
 void wait_for_migration_pass(QTestState *who, QTestMigrationState *src_state);
 void migrate_set_parameter_str(QTestState *who, const char *parameter,
                                const char *value);
+void migrate_set_parameter_strv(QTestState *who, const char *parameter,
+                                char **strv);
 void migrate_set_parameter_bool(QTestState *who, const char *parameter,
                                 int value);
 void migrate_ensure_non_converge(QTestState *who);
diff --git a/tests/qtest/migration/migration-qmp.c b/tests/qtest/migration/migration-qmp.c
index 66dd369..c803fce 100644
--- a/tests/qtest/migration/migration-qmp.c
+++ b/tests/qtest/migration/migration-qmp.c
@@ -442,6 +442,22 @@ void migrate_set_parameter_str(QTestState *who, const char *parameter,
     migrate_check_parameter_str(who, parameter, value);
 }
 
+void migrate_set_parameter_strv(QTestState *who, const char *parameter,
+                                char **strv)
+{
+    g_autofree char *args = g_strjoinv("\",\"", strv);
+    g_autoptr(GString) value = g_string_new("");
+    g_autofree char *command = NULL;
+
+    g_string_printf(value, "\"%s\"", args);
+
+    command = g_strdup_printf("{ 'execute': 'migrate-set-parameters',"
+                              "'arguments': { %%s: [ %s ]}}",
+                              value->str);
+
+    qtest_qmp_assert_success(who, command, parameter);
+}
+
 static long long migrate_get_parameter_bool(QTestState *who,
                                             const char *parameter)
 {
-- 
1.8.3.1



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

* [PATCH V5 19/19] migration-test: test cpr-exec
  2025-10-01 15:33 [PATCH V5 00/19] Live update: cpr-exec Steve Sistare
                   ` (17 preceding siblings ...)
  2025-10-01 15:34 ` [PATCH V5 18/19] migration-test: strv parameter Steve Sistare
@ 2025-10-01 15:34 ` Steve Sistare
  2025-10-01 18:56 ` [PATCH V5 00/19] Live update: cpr-exec Peter Xu
  19 siblings, 0 replies; 34+ messages in thread
From: Steve Sistare @ 2025-10-01 15:34 UTC (permalink / raw)
  To: qemu-devel
  Cc: Fabiano Rosas, Peter Xu, Markus Armbruster, Paolo Bonzini,
	Eric Blake, Dr. David Alan Gilbert, Cedric Le Goater,
	Alex Williamson, Steve Sistare

Add a test for the cpr-exec migration mode.

Signed-off-by: Steve Sistare <steven.sistare@oracle.com>
---
 tests/qtest/migration/cpr-tests.c | 133 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 133 insertions(+)

diff --git a/tests/qtest/migration/cpr-tests.c b/tests/qtest/migration/cpr-tests.c
index 5e764a6..e22bc3b 100644
--- a/tests/qtest/migration/cpr-tests.c
+++ b/tests/qtest/migration/cpr-tests.c
@@ -110,6 +110,138 @@ static void test_mode_transfer_defer(void)
     test_mode_transfer_common(true);
 }
 
+static void set_cpr_exec_args(QTestState *who, MigrateCommon *args)
+{
+    g_autofree char *qtest_from_args = NULL;
+    g_autofree char *from_args = NULL;
+    g_autofree char *to_args = NULL;
+    g_autofree char *exec_args = NULL;
+    g_auto(GStrv) argv = NULL;
+    char *from_str, *src, *dst;
+    int ret;
+
+    /*
+     * hide_stderr appends "2>/dev/null" to the command line, but cpr-exec
+     * passes the command-line words to execv, not to the shell, so suppress it
+     * here.  fd 2 was already bound in the source VM, and execv preserves it.
+     */
+    g_assert(args->start.hide_stderr == false);
+
+    ret = migrate_args(&from_args, &to_args, args->listen_uri, &args->start);
+    g_assert(!ret);
+    qtest_from_args = qtest_qemu_args(from_args);
+
+    /*
+     * The generated args may have been formatted using "%s %s" with empty
+     * strings, which can produce consecutive spaces, which g_strsplit would
+     * convert into empty strings.  Ditto for leading and trailing space.
+     * De-dup spaces to avoid that.
+     */
+
+    from_str = src = dst = g_strstrip(qtest_from_args);
+    do {
+        if (*src != ' ' || src[-1] != ' ') {
+            *dst++ = *src;
+        }
+    } while (*src++);
+
+    exec_args = g_strconcat(qtest_qemu_binary(migration_get_env()->qemu_dst),
+                            " -incoming defer ", from_str, NULL);
+    argv = g_strsplit(exec_args, " ", -1);
+    migrate_set_parameter_strv(who, "cpr-exec-command", argv);
+}
+
+static void wait_for_migration_event(QTestState *who, const char *waitfor)
+{
+    QDict *rsp, *data;
+    char *status;
+    bool done = false;
+
+    while (!done) {
+        rsp = qtest_qmp_eventwait_ref(who, "MIGRATION");
+        g_assert(qdict_haskey(rsp, "data"));
+        data = qdict_get_qdict(rsp, "data");
+        g_assert(qdict_haskey(data, "status"));
+        status = g_strdup(qdict_get_str(data, "status"));
+        g_assert(strcmp(status, "failed"));
+        done = !strcmp(status, waitfor);
+        qobject_unref(rsp);
+    }
+}
+
+static void test_cpr_exec(MigrateCommon *args)
+{
+    QTestState *from, *to;
+    void *data_hook = NULL;
+    g_autofree char *connect_uri = g_strdup(args->connect_uri);
+    g_autofree char *filename = g_strdup_printf("%s/%s", tmpfs,
+                                                FILE_TEST_FILENAME);
+
+    if (migrate_start(&from, NULL, args->listen_uri, &args->start)) {
+        return;
+    }
+
+    /* Source and dest never run concurrently */
+    g_assert_false(args->live);
+
+    if (args->start_hook) {
+        data_hook = args->start_hook(from, NULL);
+    }
+
+    wait_for_serial("src_serial");
+    set_cpr_exec_args(from, args);
+    migrate_set_capability(from, "events", true);
+    migrate_qmp(from, NULL, connect_uri, NULL, "{}");
+    wait_for_migration_event(from, "completed");
+
+    to = qtest_init_after_exec(from);
+
+    qtest_qmp_assert_success(to, "{ 'execute': 'migrate-incoming',"
+                             "  'arguments': { "
+                             "      'channels': [ { 'channel-type': 'main',"
+                             "      'addr': { 'transport': 'file',"
+                             "                'filename': %s,"
+                             "                'offset': 0  } } ] } }",
+                             filename);
+    wait_for_migration_complete(to);
+
+    wait_for_resume(to, get_dst());
+    /* Device on target is still named src_serial because args do not change */
+    wait_for_serial("src_serial");
+
+    if (args->end_hook) {
+        args->end_hook(from, to, data_hook);
+    }
+
+    migrate_end(from, to, args->result == MIG_TEST_SUCCEED);
+}
+
+static void *test_mode_exec_start(QTestState *from, QTestState *to)
+{
+    assert(!to);
+    migrate_set_parameter_str(from, "mode", "cpr-exec");
+    return NULL;
+}
+
+static void test_mode_exec(void)
+{
+    g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs,
+                                           FILE_TEST_FILENAME);
+    g_autofree char *listen_uri = g_strdup_printf("defer");
+
+    MigrateCommon args = {
+        .start.only_source = true,
+        .start.opts_source = "-machine aux-ram-share=on -nodefaults",
+        .start.memory_backend = "-object memory-backend-memfd,id=pc.ram,size=%s"
+                                " -machine memory-backend=pc.ram",
+        .connect_uri = uri,
+        .listen_uri = listen_uri,
+        .start_hook = test_mode_exec_start,
+    };
+
+    test_cpr_exec(&args);
+}
+
 void migration_test_add_cpr(MigrationTestEnv *env)
 {
     tmpfs = env->tmpfs;
@@ -132,5 +264,6 @@ void migration_test_add_cpr(MigrationTestEnv *env)
         migration_test_add("/migration/mode/transfer", test_mode_transfer);
         migration_test_add("/migration/mode/transfer/defer",
                            test_mode_transfer_defer);
+        migration_test_add("/migration/mode/exec", test_mode_exec);
     }
 }
-- 
1.8.3.1



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

* Re: [PATCH V5 05/19] migration: cpr-exec save and load
  2025-10-01 15:33 ` [PATCH V5 05/19] migration: cpr-exec save and load Steve Sistare
@ 2025-10-01 16:36   ` Peter Xu
  0 siblings, 0 replies; 34+ messages in thread
From: Peter Xu @ 2025-10-01 16:36 UTC (permalink / raw)
  To: Steve Sistare
  Cc: qemu-devel, Fabiano Rosas, Markus Armbruster, Paolo Bonzini,
	Eric Blake, Dr. David Alan Gilbert, Cedric Le Goater,
	Alex Williamson

On Wed, Oct 01, 2025 at 08:33:57AM -0700, Steve Sistare wrote:
> diff --git a/migration/meson.build b/migration/meson.build
> index 0f71544..16909d5 100644
> --- a/migration/meson.build
> +++ b/migration/meson.build
> @@ -16,6 +16,7 @@ system_ss.add(files(
>    'channel-block.c',
>    'cpr.c',
>    'cpr-transfer.c',
> +  'cpr-exec.c',
>    'cpu-throttle.c',
>    'dirtyrate.c',
>    'exec.c',

I'll give it a shot on cross builds soon, but I wonder if the whole cpr*
should be only compiled when "host_os == 'linux'".

I'm not even sure above qemu_memfd_create() is always defined.  But even if
it'll work right now, we still compile these codes that will not be used
outside linux..

-- 
Peter Xu



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

* Re: [PATCH V5 00/19] Live update: cpr-exec
  2025-10-01 15:33 [PATCH V5 00/19] Live update: cpr-exec Steve Sistare
                   ` (18 preceding siblings ...)
  2025-10-01 15:34 ` [PATCH V5 19/19] migration-test: test cpr-exec Steve Sistare
@ 2025-10-01 18:56 ` Peter Xu
  2025-10-01 19:07   ` Steven Sistare
  19 siblings, 1 reply; 34+ messages in thread
From: Peter Xu @ 2025-10-01 18:56 UTC (permalink / raw)
  To: Steve Sistare
  Cc: qemu-devel, Fabiano Rosas, Markus Armbruster, Paolo Bonzini,
	Eric Blake, Dr. David Alan Gilbert, Cedric Le Goater,
	Alex Williamson

On Wed, Oct 01, 2025 at 08:33:52AM -0700, Steve Sistare wrote:
> This patch series adds the live migration cpr-exec mode.
> 
> The new user-visible interfaces are:
>   * cpr-exec (MigMode migration parameter)
>   * cpr-exec-command (migration parameter)
> 
> cpr-exec mode is similar in most respects to cpr-transfer mode, with the
> primary difference being that old QEMU directly exec's new QEMU.  The user
> specifies the command to exec new QEMU in the migration parameter
> cpr-exec-command.

It turns out I was right where I replied to patch 5; this fails the Windows
build.

Smallest fix is to wrap qemu_memfd_create() with a CONFIG_LINUX ifdef,
returning -1 to mfd otherwise.

A better one is we only include cpr*.c in meson.build if it's linux.
Personally I'm OK if we go with the smaller one as of now, however then it
would definitely be nice to have a follow up series to reach the better
solution, if that makes sense.

Feel free to use "make docker-test-build@fedora-win64-cross" for verifying
the changes.  I hope it'll work for you, even if for me currently it didn't
work due to a gitlab.com dns resolution pulling dtc src, where I didn't dig
deeper yet..

The other thing is, this series doesn't apply on master branch.  I didn't
feel confident to do it myself on the vfio change, please have a look on
both issues.

Thanks,

-- 
Peter Xu



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

* Re: [PATCH V5 00/19] Live update: cpr-exec
  2025-10-01 18:56 ` [PATCH V5 00/19] Live update: cpr-exec Peter Xu
@ 2025-10-01 19:07   ` Steven Sistare
  2025-10-01 19:24     ` Steven Sistare
  2025-10-01 20:03     ` Peter Xu
  0 siblings, 2 replies; 34+ messages in thread
From: Steven Sistare @ 2025-10-01 19:07 UTC (permalink / raw)
  To: Peter Xu
  Cc: qemu-devel, Fabiano Rosas, Markus Armbruster, Paolo Bonzini,
	Eric Blake, Dr. David Alan Gilbert, Cedric Le Goater,
	Alex Williamson

On 10/1/2025 2:56 PM, Peter Xu wrote:
> On Wed, Oct 01, 2025 at 08:33:52AM -0700, Steve Sistare wrote:
>> This patch series adds the live migration cpr-exec mode.
>>
>> The new user-visible interfaces are:
>>    * cpr-exec (MigMode migration parameter)
>>    * cpr-exec-command (migration parameter)
>>
>> cpr-exec mode is similar in most respects to cpr-transfer mode, with the
>> primary difference being that old QEMU directly exec's new QEMU.  The user
>> specifies the command to exec new QEMU in the migration parameter
>> cpr-exec-command.
> 
> It turns out I was right where I replied to patch 5; this fails the Windows
> build.
> 
> Smallest fix is to wrap qemu_memfd_create() with a CONFIG_LINUX ifdef,
> returning -1 to mfd otherwise.

That does not make sense.  It already does that, which is why I used it:

util/memfd.c

int qemu_memfd_create(const char *name, size_t size, bool hugetlb,
                       uint64_t hugetlbsize, unsigned int seals, Error **errp)
{
     ...
#ifdef CONFIG_LINUX
     ...
     return mfd;
#else
     error_setg_errno(errp, ENOSYS, "failed to create memfd");
#endif
     return -1;
}

Did the windows build fail due to a different error?

> A better one is we only include cpr*.c in meson.build if it's linux.
> Personally I'm OK if we go with the smaller one as of now, however then it
> would definitely be nice to have a follow up series to reach the better
> solution, if that makes sense.
> 
> Feel free to use "make docker-test-build@fedora-win64-cross" for verifying
> the changes.  I hope it'll work for you, even if for me currently it didn't
> work due to a gitlab.com dns resolution pulling dtc src, where I didn't dig
> deeper yet..
> 
> The other thing is, this series doesn't apply on master branch.  I didn't
> feel confident to do it myself on the vfio change, please have a look on
> both issues.
Ugh, they renamed everything.  I will rebase and send V6.

- Steve



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

* Re: [PATCH V5 00/19] Live update: cpr-exec
  2025-10-01 19:07   ` Steven Sistare
@ 2025-10-01 19:24     ` Steven Sistare
  2025-10-01 20:05       ` Peter Xu
  2025-10-02  8:48       ` Cédric Le Goater
  2025-10-01 20:03     ` Peter Xu
  1 sibling, 2 replies; 34+ messages in thread
From: Steven Sistare @ 2025-10-01 19:24 UTC (permalink / raw)
  To: Peter Xu
  Cc: qemu-devel, Fabiano Rosas, Markus Armbruster, Paolo Bonzini,
	Eric Blake, Dr. David Alan Gilbert, Cedric Le Goater,
	Alex Williamson

On 10/1/2025 3:07 PM, Steven Sistare wrote:
> On 10/1/2025 2:56 PM, Peter Xu wrote:
>> On Wed, Oct 01, 2025 at 08:33:52AM -0700, Steve Sistare wrote:
>>> This patch series adds the live migration cpr-exec mode.
>>>
>>> The new user-visible interfaces are:
>>>    * cpr-exec (MigMode migration parameter)
>>>    * cpr-exec-command (migration parameter)
>>>
>>> cpr-exec mode is similar in most respects to cpr-transfer mode, with the
>>> primary difference being that old QEMU directly exec's new QEMU.  The user
>>> specifies the command to exec new QEMU in the migration parameter
>>> cpr-exec-command.
>>
>> It turns out I was right where I replied to patch 5; this fails the Windows
>> build.
>>
>> Smallest fix is to wrap qemu_memfd_create() with a CONFIG_LINUX ifdef,
>> returning -1 to mfd otherwise.
> 
> That does not make sense.  It already does that, which is why I used it:
> 
> util/memfd.c
> 
> int qemu_memfd_create(const char *name, size_t size, bool hugetlb,
>                        uint64_t hugetlbsize, unsigned int seals, Error **errp)
> {
>      ...
> #ifdef CONFIG_LINUX
>      ...
>      return mfd;
> #else
>      error_setg_errno(errp, ENOSYS, "failed to create memfd");
> #endif
>      return -1;
> }
> 
> Did the windows build fail due to a different error?
> 
>> A better one is we only include cpr*.c in meson.build if it's linux.
>> Personally I'm OK if we go with the smaller one as of now, however then it
>> would definitely be nice to have a follow up series to reach the better
>> solution, if that makes sense.
>>
>> Feel free to use "make docker-test-build@fedora-win64-cross" for verifying
>> the changes.  I hope it'll work for you, even if for me currently it didn't
>> work due to a gitlab.com dns resolution pulling dtc src, where I didn't dig
>> deeper yet..
>>
>> The other thing is, this series doesn't apply on master branch.  I didn't
>> feel confident to do it myself on the vfio change, please have a look on
>> both issues.
> Ugh, they renamed everything.  I will rebase and send V6.

Actually that was easy to resolve, not worth a V6?

In patch "vfio: cpr-exec mode", drop the change in container.c, and instead
add MIG_MODE_CPR_EXEC to container-legacy.c here:

         if (migrate_add_blocker_modes(&vbasedev->cpr.mdev_blocker, errp,
                                       MIG_MODE_CPR_TRANSFER, MIG_MODE_CPR_EXEC,
                                       -1) < 0) {

- Steve




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

* Re: [PATCH V5 00/19] Live update: cpr-exec
  2025-10-01 19:07   ` Steven Sistare
  2025-10-01 19:24     ` Steven Sistare
@ 2025-10-01 20:03     ` Peter Xu
  2025-10-02  8:50       ` Cédric Le Goater
  2025-10-02 13:44       ` Steven Sistare
  1 sibling, 2 replies; 34+ messages in thread
From: Peter Xu @ 2025-10-01 20:03 UTC (permalink / raw)
  To: Steven Sistare
  Cc: qemu-devel, Fabiano Rosas, Markus Armbruster, Paolo Bonzini,
	Eric Blake, Dr. David Alan Gilbert, Cedric Le Goater,
	Alex Williamson

On Wed, Oct 01, 2025 at 03:07:23PM -0400, Steven Sistare wrote:
> That does not make sense.  It already does that, which is why I used it:
> 
> util/memfd.c
> 
> int qemu_memfd_create(const char *name, size_t size, bool hugetlb,
>                       uint64_t hugetlbsize, unsigned int seals, Error **errp)
> {
>     ...
> #ifdef CONFIG_LINUX
>     ...
>     return mfd;
> #else
>     error_setg_errno(errp, ENOSYS, "failed to create memfd");
> #endif
>     return -1;
> }
> 
> Did the windows build fail due to a different error?

https://gitlab.com/peterx/qemu/-/jobs/11566477462

See util/meson.build:

if host_os != 'windows'
  ...
  util_ss.add(files('memfd.c'))
  ...

-- 
Peter Xu



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

* Re: [PATCH V5 00/19] Live update: cpr-exec
  2025-10-01 19:24     ` Steven Sistare
@ 2025-10-01 20:05       ` Peter Xu
  2025-10-02 12:44         ` Steven Sistare
  2025-10-02  8:48       ` Cédric Le Goater
  1 sibling, 1 reply; 34+ messages in thread
From: Peter Xu @ 2025-10-01 20:05 UTC (permalink / raw)
  To: Steven Sistare
  Cc: qemu-devel, Fabiano Rosas, Markus Armbruster, Paolo Bonzini,
	Eric Blake, Dr. David Alan Gilbert, Cedric Le Goater,
	Alex Williamson

On Wed, Oct 01, 2025 at 03:24:44PM -0400, Steven Sistare wrote:
> Actually that was easy to resolve, not worth a V6?
> 
> In patch "vfio: cpr-exec mode", drop the change in container.c, and instead
> add MIG_MODE_CPR_EXEC to container-legacy.c here:
> 
>         if (migrate_add_blocker_modes(&vbasedev->cpr.mdev_blocker, errp,
>                                       MIG_MODE_CPR_TRANSFER, MIG_MODE_CPR_EXEC,
>                                       -1) < 0) {

If that's the only conflict, could I request to send that new vfio patch
only, by replying it to this email or that vfio patch?

I can fix the win build.

-- 
Peter Xu



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

* Re: [PATCH V5 00/19] Live update: cpr-exec
  2025-10-01 19:24     ` Steven Sistare
  2025-10-01 20:05       ` Peter Xu
@ 2025-10-02  8:48       ` Cédric Le Goater
  1 sibling, 0 replies; 34+ messages in thread
From: Cédric Le Goater @ 2025-10-02  8:48 UTC (permalink / raw)
  To: Steven Sistare, Peter Xu
  Cc: qemu-devel, Fabiano Rosas, Markus Armbruster, Paolo Bonzini,
	Eric Blake, Dr. David Alan Gilbert, Alex Williamson

On 10/1/25 21:24, Steven Sistare wrote:
> On 10/1/2025 3:07 PM, Steven Sistare wrote:
>> On 10/1/2025 2:56 PM, Peter Xu wrote:
>>> On Wed, Oct 01, 2025 at 08:33:52AM -0700, Steve Sistare wrote:
>>>> This patch series adds the live migration cpr-exec mode.
>>>>
>>>> The new user-visible interfaces are:
>>>>    * cpr-exec (MigMode migration parameter)
>>>>    * cpr-exec-command (migration parameter)
>>>>
>>>> cpr-exec mode is similar in most respects to cpr-transfer mode, with the
>>>> primary difference being that old QEMU directly exec's new QEMU.  The user
>>>> specifies the command to exec new QEMU in the migration parameter
>>>> cpr-exec-command.
>>>
>>> It turns out I was right where I replied to patch 5; this fails the Windows
>>> build.
>>>
>>> Smallest fix is to wrap qemu_memfd_create() with a CONFIG_LINUX ifdef,
>>> returning -1 to mfd otherwise.
>>
>> That does not make sense.  It already does that, which is why I used it:
>>
>> util/memfd.c
>>
>> int qemu_memfd_create(const char *name, size_t size, bool hugetlb,
>>                        uint64_t hugetlbsize, unsigned int seals, Error **errp)
>> {
>>      ...
>> #ifdef CONFIG_LINUX
>>      ...
>>      return mfd;
>> #else
>>      error_setg_errno(errp, ENOSYS, "failed to create memfd");
>> #endif
>>      return -1;
>> }
>>
>> Did the windows build fail due to a different error?
>>
>>> A better one is we only include cpr*.c in meson.build if it's linux.
>>> Personally I'm OK if we go with the smaller one as of now, however then it
>>> would definitely be nice to have a follow up series to reach the better
>>> solution, if that makes sense.
>>>
>>> Feel free to use "make docker-test-build@fedora-win64-cross" for verifying
>>> the changes.  I hope it'll work for you, even if for me currently it didn't
>>> work due to a gitlab.com dns resolution pulling dtc src, where I didn't dig
>>> deeper yet..
>>>
>>> The other thing is, this series doesn't apply on master branch.  I didn't
>>> feel confident to do it myself on the vfio change, please have a look on
>>> both issues.
>> Ugh, they renamed everything.  I will rebase and send V6.
> 
> Actually that was easy to resolve, not worth a V6?
> 
> In patch "vfio: cpr-exec mode", drop the change in container.c, and instead
> add MIG_MODE_CPR_EXEC to container-legacy.c here:
> 
>          if (migrate_add_blocker_modes(&vbasedev->cpr.mdev_blocker, errp,
>                                        MIG_MODE_CPR_TRANSFER, MIG_MODE_CPR_EXEC,
>                                        -1) < 0) {
yes.

Thanks,

C.



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

* Re: [PATCH V5 00/19] Live update: cpr-exec
  2025-10-01 20:03     ` Peter Xu
@ 2025-10-02  8:50       ` Cédric Le Goater
  2025-10-02 13:45         ` Steven Sistare
  2025-10-02 13:44       ` Steven Sistare
  1 sibling, 1 reply; 34+ messages in thread
From: Cédric Le Goater @ 2025-10-02  8:50 UTC (permalink / raw)
  To: Peter Xu, Steven Sistare
  Cc: qemu-devel, Fabiano Rosas, Markus Armbruster, Paolo Bonzini,
	Eric Blake, Dr. David Alan Gilbert, Alex Williamson

On 10/1/25 22:03, Peter Xu wrote:
> On Wed, Oct 01, 2025 at 03:07:23PM -0400, Steven Sistare wrote:
>> That does not make sense.  It already does that, which is why I used it:
>>
>> util/memfd.c
>>
>> int qemu_memfd_create(const char *name, size_t size, bool hugetlb,
>>                        uint64_t hugetlbsize, unsigned int seals, Error **errp)
>> {
>>      ...
>> #ifdef CONFIG_LINUX
>>      ...
>>      return mfd;
>> #else
>>      error_setg_errno(errp, ENOSYS, "failed to create memfd");
>> #endif
>>      return -1;
>> }
>>
>> Did the windows build fail due to a different error?
> 
> https://gitlab.com/peterx/qemu/-/jobs/11566477462
> 
> See util/meson.build:
> 
> if host_os != 'windows'
>    ...
>    util_ss.add(files('memfd.c'))
>    ...
> 

Steve, you can test the windows build with :

--cross-prefix=x86_64-w64-mingw32- --disable-sdl --disable-docs

C.



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

* Re: [PATCH V5 00/19] Live update: cpr-exec
  2025-10-01 20:05       ` Peter Xu
@ 2025-10-02 12:44         ` Steven Sistare
  0 siblings, 0 replies; 34+ messages in thread
From: Steven Sistare @ 2025-10-02 12:44 UTC (permalink / raw)
  To: Peter Xu
  Cc: qemu-devel, Fabiano Rosas, Markus Armbruster, Paolo Bonzini,
	Eric Blake, Dr. David Alan Gilbert, Cedric Le Goater,
	Alex Williamson

[-- Attachment #1: Type: text/plain, Size: 712 bytes --]

On 10/1/2025 4:05 PM, Peter Xu wrote:
> On Wed, Oct 01, 2025 at 03:24:44PM -0400, Steven Sistare wrote:
>> Actually that was easy to resolve, not worth a V6?
>>
>> In patch "vfio: cpr-exec mode", drop the change in container.c, and instead
>> add MIG_MODE_CPR_EXEC to container-legacy.c here:
>>
>>          if (migrate_add_blocker_modes(&vbasedev->cpr.mdev_blocker, errp,
>>                                        MIG_MODE_CPR_TRANSFER, MIG_MODE_CPR_EXEC,
>>                                        -1) < 0) {
> 
> If that's the only conflict, could I request to send that new vfio patch
> only, by replying it to this email or that vfio patch?

see attached.

> I can fix the win build.

Cool, thanks!

- Steve

[-- Attachment #2: 0001-vfio-cpr-exec-mode.patch --]
[-- Type: text/plain, Size: 4449 bytes --]

From b35a588d6c6d0db31a5032f213a48243c970b588 Mon Sep 17 00:00:00 2001
From: Steve Sistare <steven.sistare@oracle.com>
Date: Mon, 30 Sep 2024 09:31:52 -0700
Subject: [PATCH] vfio: cpr-exec mode
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

All blockers and notifiers for cpr-transfer mode also apply to cpr-exec.

Signed-off-by: Steve Sistare <steven.sistare@oracle.com>
Acked-by: Cédric Le Goater <clg@redhat.com>
---
 hw/vfio/container-legacy.c |  3 ++-
 hw/vfio/cpr-iommufd.c      |  3 ++-
 hw/vfio/cpr-legacy.c       |  9 +++++----
 hw/vfio/cpr.c              | 13 +++++++------
 4 files changed, 16 insertions(+), 12 deletions(-)

diff --git a/hw/vfio/container-legacy.c b/hw/vfio/container-legacy.c
index c0f87f7..c0540f2 100644
--- a/hw/vfio/container-legacy.c
+++ b/hw/vfio/container-legacy.c
@@ -990,7 +990,8 @@ static bool vfio_legacy_attach_device(const char *name, VFIODevice *vbasedev,
         error_setg(&vbasedev->cpr.mdev_blocker,
                    "CPR does not support vfio mdev %s", vbasedev->name);
         if (migrate_add_blocker_modes(&vbasedev->cpr.mdev_blocker, errp,
-                                      MIG_MODE_CPR_TRANSFER, -1) < 0) {
+                                      MIG_MODE_CPR_TRANSFER, MIG_MODE_CPR_EXEC,
+                                      -1) < 0) {
             goto hiod_unref_exit;
         }
     }
diff --git a/hw/vfio/cpr-iommufd.c b/hw/vfio/cpr-iommufd.c
index 1d70c87..8a4d65d 100644
--- a/hw/vfio/cpr-iommufd.c
+++ b/hw/vfio/cpr-iommufd.c
@@ -159,7 +159,8 @@ bool vfio_iommufd_cpr_register_iommufd(IOMMUFDBackend *be, Error **errp)
 
     if (!vfio_cpr_supported(be, cpr_blocker)) {
         return migrate_add_blocker_modes(cpr_blocker, errp,
-                                         MIG_MODE_CPR_TRANSFER, -1) == 0;
+                                         MIG_MODE_CPR_TRANSFER,
+                                         MIG_MODE_CPR_EXEC, -1) == 0;
     }
 
     vmstate_register(NULL, -1, &iommufd_cpr_vmstate, be);
diff --git a/hw/vfio/cpr-legacy.c b/hw/vfio/cpr-legacy.c
index bbf7a0d..1a16cb1 100644
--- a/hw/vfio/cpr-legacy.c
+++ b/hw/vfio/cpr-legacy.c
@@ -179,16 +179,17 @@ bool vfio_legacy_cpr_register_container(VFIOLegacyContainer *container,
 
     if (!vfio_cpr_supported(container, cpr_blocker)) {
         return migrate_add_blocker_modes(cpr_blocker, errp,
-                                         MIG_MODE_CPR_TRANSFER, -1) == 0;
+                                         MIG_MODE_CPR_TRANSFER,
+                                         MIG_MODE_CPR_EXEC, -1) == 0;
     }
 
     vfio_cpr_add_kvm_notifier();
 
     vmstate_register(NULL, -1, &vfio_container_vmstate, container);
 
-    migration_add_notifier_mode(&container->cpr.transfer_notifier,
-                                vfio_cpr_fail_notifier,
-                                MIG_MODE_CPR_TRANSFER);
+    migration_add_notifier_modes(&container->cpr.transfer_notifier,
+                                 vfio_cpr_fail_notifier,
+                                 MIG_MODE_CPR_TRANSFER, MIG_MODE_CPR_EXEC, -1);
     return true;
 }
 
diff --git a/hw/vfio/cpr.c b/hw/vfio/cpr.c
index 2c71fc1..db462aa 100644
--- a/hw/vfio/cpr.c
+++ b/hw/vfio/cpr.c
@@ -195,9 +195,10 @@ static int vfio_cpr_kvm_close_notifier(NotifierWithReturn *notifier,
 void vfio_cpr_add_kvm_notifier(void)
 {
     if (!kvm_close_notifier.notify) {
-        migration_add_notifier_mode(&kvm_close_notifier,
-                                    vfio_cpr_kvm_close_notifier,
-                                    MIG_MODE_CPR_TRANSFER);
+        migration_add_notifier_modes(&kvm_close_notifier,
+                                     vfio_cpr_kvm_close_notifier,
+                                     MIG_MODE_CPR_TRANSFER, MIG_MODE_CPR_EXEC,
+                                     -1);
     }
 }
 
@@ -282,9 +283,9 @@ static int vfio_cpr_pci_notifier(NotifierWithReturn *notifier,
 
 void vfio_cpr_pci_register_device(VFIOPCIDevice *vdev)
 {
-    migration_add_notifier_mode(&vdev->cpr.transfer_notifier,
-                                vfio_cpr_pci_notifier,
-                                MIG_MODE_CPR_TRANSFER);
+    migration_add_notifier_modes(&vdev->cpr.transfer_notifier,
+                                 vfio_cpr_pci_notifier,
+                                 MIG_MODE_CPR_TRANSFER, MIG_MODE_CPR_EXEC, -1);
 }
 
 void vfio_cpr_pci_unregister_device(VFIOPCIDevice *vdev)
-- 
1.8.3.1


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

* Re: [PATCH V5 00/19] Live update: cpr-exec
  2025-10-01 20:03     ` Peter Xu
  2025-10-02  8:50       ` Cédric Le Goater
@ 2025-10-02 13:44       ` Steven Sistare
  2025-10-03 11:36         ` Steven Sistare
  1 sibling, 1 reply; 34+ messages in thread
From: Steven Sistare @ 2025-10-02 13:44 UTC (permalink / raw)
  To: Peter Xu
  Cc: qemu-devel, Fabiano Rosas, Markus Armbruster, Paolo Bonzini,
	Eric Blake, Dr. David Alan Gilbert, Cedric Le Goater,
	Alex Williamson

On 10/1/2025 4:03 PM, Peter Xu wrote:
> On Wed, Oct 01, 2025 at 03:07:23PM -0400, Steven Sistare wrote:
>> That does not make sense.  It already does that, which is why I used it:
>>
>> util/memfd.c
>>
>> int qemu_memfd_create(const char *name, size_t size, bool hugetlb,
>>                        uint64_t hugetlbsize, unsigned int seals, Error **errp)
>> {
>>      ...
>> #ifdef CONFIG_LINUX
>>      ...
>>      return mfd;
>> #else
>>      error_setg_errno(errp, ENOSYS, "failed to create memfd");
>> #endif
>>      return -1;
>> }
>>
>> Did the windows build fail due to a different error?
> 
> https://gitlab.com/peterx/qemu/-/jobs/11566477462
> 
> See util/meson.build:
> 
> if host_os != 'windows'
>    ...
>    util_ss.add(files('memfd.c'))
>    ...


I see.  So you were suggesting, as the minimal fix, to add ifdef CONFIG_LINUX at
the call site, not inside qemu_memfd_create, eg:

QEMUFile *cpr_exec_output(Error **errp)
{
#ifndef CONFIG_LINUX
     error_setg(errp, "qemu_memfd_create requires CONFIG_LINUX");
     return NULL;
#endif

     int mfd = qemu_memfd_create(CPR_EXEC_STATE_NAME, 0, false, 0, 0, errp);

     if (mfd < 0) {
         return NULL;
     }

     return qemu_file_new_fd_output(mfd, CPR_EXEC_STATE_NAME);
}


Do you want me to re-submit a patch, or can you add the above to your tree?
If the latter, feel free to tweak it.

- Steve


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

* Re: [PATCH V5 00/19] Live update: cpr-exec
  2025-10-02  8:50       ` Cédric Le Goater
@ 2025-10-02 13:45         ` Steven Sistare
  0 siblings, 0 replies; 34+ messages in thread
From: Steven Sistare @ 2025-10-02 13:45 UTC (permalink / raw)
  To: Cédric Le Goater, Peter Xu
  Cc: qemu-devel, Fabiano Rosas, Markus Armbruster, Paolo Bonzini,
	Eric Blake, Dr. David Alan Gilbert, Alex Williamson

On 10/2/2025 4:50 AM, Cédric Le Goater wrote:
> On 10/1/25 22:03, Peter Xu wrote:
>> On Wed, Oct 01, 2025 at 03:07:23PM -0400, Steven Sistare wrote:
>>> That does not make sense.  It already does that, which is why I used it:
>>>
>>> util/memfd.c
>>>
>>> int qemu_memfd_create(const char *name, size_t size, bool hugetlb,
>>>                        uint64_t hugetlbsize, unsigned int seals, Error **errp)
>>> {
>>>      ...
>>> #ifdef CONFIG_LINUX
>>>      ...
>>>      return mfd;
>>> #else
>>>      error_setg_errno(errp, ENOSYS, "failed to create memfd");
>>> #endif
>>>      return -1;
>>> }
>>>
>>> Did the windows build fail due to a different error?
>>
>> https://gitlab.com/peterx/qemu/-/jobs/11566477462
>>
>> See util/meson.build:
>>
>> if host_os != 'windows'
>>    ...
>>    util_ss.add(files('memfd.c'))
>>    ...
>>
> 
> Steve, you can test the windows build with :
> 
> --cross-prefix=x86_64-w64-mingw32- --disable-sdl --disable-docs

Thanks for the tip, I did not know this incantation - steve



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

* Re: [PATCH V5 00/19] Live update: cpr-exec
  2025-10-02 13:44       ` Steven Sistare
@ 2025-10-03 11:36         ` Steven Sistare
  2025-10-03 13:11           ` Peter Xu
  0 siblings, 1 reply; 34+ messages in thread
From: Steven Sistare @ 2025-10-03 11:36 UTC (permalink / raw)
  To: Peter Xu
  Cc: qemu-devel, Fabiano Rosas, Markus Armbruster, Paolo Bonzini,
	Eric Blake, Dr. David Alan Gilbert, Cedric Le Goater,
	Alex Williamson

On 10/2/2025 9:44 AM, Steven Sistare wrote:
> On 10/1/2025 4:03 PM, Peter Xu wrote:
>> On Wed, Oct 01, 2025 at 03:07:23PM -0400, Steven Sistare wrote:
>>> That does not make sense.  It already does that, which is why I used it:
>>>
>>> util/memfd.c
>>>
>>> int qemu_memfd_create(const char *name, size_t size, bool hugetlb,
>>>                        uint64_t hugetlbsize, unsigned int seals, Error **errp)
>>> {
>>>      ...
>>> #ifdef CONFIG_LINUX
>>>      ...
>>>      return mfd;
>>> #else
>>>      error_setg_errno(errp, ENOSYS, "failed to create memfd");
>>> #endif
>>>      return -1;
>>> }
>>>
>>> Did the windows build fail due to a different error?
>>
>> https://gitlab.com/peterx/qemu/-/jobs/11566477462
>>
>> See util/meson.build:
>>
>> if host_os != 'windows'
>>    ...
>>    util_ss.add(files('memfd.c'))
>>    ...
> 
> 
> I see.  So you were suggesting, as the minimal fix, to add ifdef CONFIG_LINUX at
> the call site, not inside qemu_memfd_create, eg:
> 
> QEMUFile *cpr_exec_output(Error **errp)
> {
> #ifndef CONFIG_LINUX
>      error_setg(errp, "qemu_memfd_create requires CONFIG_LINUX");
>      return NULL;
> #endif
> 
>      int mfd = qemu_memfd_create(CPR_EXEC_STATE_NAME, 0, false, 0, 0, errp);
> 
>      if (mfd < 0) {
>          return NULL;
>      }
> 
>      return qemu_file_new_fd_output(mfd, CPR_EXEC_STATE_NAME);
> }
> 
> Do you want me to re-submit a patch, or can you add the above to your tree?
> If the latter, feel free to tweak it.

Hi Peter, do you need anything more from me at this time?
AFAIK I have addressed all current issues, with the snippet above, and
with the new 0001-vfio-cpr-exec-mode.patch attached earlier in this thread.

- Steve



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

* Re: [PATCH V5 00/19] Live update: cpr-exec
  2025-10-03 11:36         ` Steven Sistare
@ 2025-10-03 13:11           ` Peter Xu
  2025-10-03 13:36             ` Steven Sistare
  0 siblings, 1 reply; 34+ messages in thread
From: Peter Xu @ 2025-10-03 13:11 UTC (permalink / raw)
  To: Steven Sistare
  Cc: qemu-devel, Fabiano Rosas, Markus Armbruster, Paolo Bonzini,
	Eric Blake, Dr. David Alan Gilbert, Cedric Le Goater,
	Alex Williamson

On Fri, Oct 03, 2025 at 07:36:50AM -0400, Steven Sistare wrote:
> Hi Peter, do you need anything more from me at this time?
> AFAIK I have addressed all current issues, with the snippet above, and
> with the new 0001-vfio-cpr-exec-mode.patch attached earlier in this thread.

I got everything. As there's some dependency to Fabiano's pull that already
sent, I'll wait that to land first to send the migration pull.  It's likely
easier for Richard.

It should be early next week.

Thanks,

-- 
Peter Xu



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

* Re: [PATCH V5 00/19] Live update: cpr-exec
  2025-10-03 13:11           ` Peter Xu
@ 2025-10-03 13:36             ` Steven Sistare
  0 siblings, 0 replies; 34+ messages in thread
From: Steven Sistare @ 2025-10-03 13:36 UTC (permalink / raw)
  To: Peter Xu
  Cc: qemu-devel, Fabiano Rosas, Markus Armbruster, Paolo Bonzini,
	Eric Blake, Dr. David Alan Gilbert, Cedric Le Goater,
	Alex Williamson

On 10/3/2025 9:11 AM, Peter Xu wrote:
> On Fri, Oct 03, 2025 at 07:36:50AM -0400, Steven Sistare wrote:
>> Hi Peter, do you need anything more from me at this time?
>> AFAIK I have addressed all current issues, with the snippet above, and
>> with the new 0001-vfio-cpr-exec-mode.patch attached earlier in this thread.
> 
> I got everything. As there's some dependency to Fabiano's pull that already
> sent, I'll wait that to land first to send the migration pull.  It's likely
> easier for Richard.
> 
> It should be early next week.

Thanks much.
And FYI, Fabiano's pull landed.  I just pulled and I see the test patches.

- Steve


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

end of thread, other threads:[~2025-10-03 13:38 UTC | newest]

Thread overview: 34+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-10-01 15:33 [PATCH V5 00/19] Live update: cpr-exec Steve Sistare
2025-10-01 15:33 ` [PATCH V5 01/19] migration: multi-mode notifier Steve Sistare
2025-10-01 15:33 ` [PATCH V5 02/19] migration: add cpr_walk_fd Steve Sistare
2025-10-01 15:33 ` [PATCH V5 03/19] oslib: qemu_clear_cloexec Steve Sistare
2025-10-01 15:33 ` [PATCH V5 04/19] migration: cpr-exec-command parameter Steve Sistare
2025-10-01 15:33 ` [PATCH V5 05/19] migration: cpr-exec save and load Steve Sistare
2025-10-01 16:36   ` Peter Xu
2025-10-01 15:33 ` [PATCH V5 06/19] migration: cpr-exec mode Steve Sistare
2025-10-01 15:33 ` [PATCH V5 07/19] migration: cpr-exec docs Steve Sistare
2025-10-01 15:34 ` [PATCH V5 08/19] vfio: cpr-exec mode Steve Sistare
2025-10-01 15:34 ` [PATCH V5 09/19] tests/qtest: export qtest_qemu_binary Steve Sistare
2025-10-01 15:34 ` [PATCH V5 10/19] tests/qtest: qtest_qemu_args Steve Sistare
2025-10-01 15:34 ` [PATCH V5 11/19] tests/qtest: qtest_create_test_state Steve Sistare
2025-10-01 15:34 ` [PATCH V5 12/19] tests/qtest: qtest_qemu_spawn_func Steve Sistare
2025-10-01 15:34 ` [PATCH V5 13/19] tests/qtest: qtest_init_after_exec Steve Sistare
2025-10-01 15:34 ` [PATCH V5 14/19] migration-test: only_source option Steve Sistare
2025-10-01 15:34 ` [PATCH V5 15/19] migration-test: shm path accessor Steve Sistare
2025-10-01 15:34 ` [PATCH V5 16/19] migration-test: misc exports Steve Sistare
2025-10-01 15:34 ` [PATCH V5 17/19] migration-test: migrate_args Steve Sistare
2025-10-01 15:34 ` [PATCH V5 18/19] migration-test: strv parameter Steve Sistare
2025-10-01 15:34 ` [PATCH V5 19/19] migration-test: test cpr-exec Steve Sistare
2025-10-01 18:56 ` [PATCH V5 00/19] Live update: cpr-exec Peter Xu
2025-10-01 19:07   ` Steven Sistare
2025-10-01 19:24     ` Steven Sistare
2025-10-01 20:05       ` Peter Xu
2025-10-02 12:44         ` Steven Sistare
2025-10-02  8:48       ` Cédric Le Goater
2025-10-01 20:03     ` Peter Xu
2025-10-02  8:50       ` Cédric Le Goater
2025-10-02 13:45         ` Steven Sistare
2025-10-02 13:44       ` Steven Sistare
2025-10-03 11:36         ` Steven Sistare
2025-10-03 13:11           ` Peter Xu
2025-10-03 13:36             ` Steven Sistare

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).