From: "Cédric Le Goater" <clg@redhat.com>
To: qemu-devel@nongnu.org
Cc: "Alex Williamson" <alex.williamson@redhat.com>,
"Maciej S. Szmigiero" <maciej.szmigiero@oracle.com>,
"Peter Xu" <peterx@redhat.com>,
"Cédric Le Goater" <clg@redhat.com>
Subject: [PULL 26/42] migration: Add save_live_complete_precopy_thread handler
Date: Thu, 6 Mar 2025 15:14:02 +0100 [thread overview]
Message-ID: <20250306141419.2015340-27-clg@redhat.com> (raw)
In-Reply-To: <20250306141419.2015340-1-clg@redhat.com>
From: "Maciej S. Szmigiero" <maciej.szmigiero@oracle.com>
This SaveVMHandler helps device provide its own asynchronous transmission
of the remaining data at the end of a precopy phase via multifd channels,
in parallel with the transfer done by save_live_complete_precopy handlers.
These threads are launched only when multifd device state transfer is
supported.
Management of these threads in done in the multifd migration code,
wrapping them in the generic thread pool.
Signed-off-by: Maciej S. Szmigiero <maciej.szmigiero@oracle.com>
Reviewed-by: Peter Xu <peterx@redhat.com>
Link: https://lore.kernel.org/qemu-devel/eac74a4ca7edd8968bbf72aa07b9041c76364a16.1741124640.git.maciej.szmigiero@oracle.com
Signed-off-by: Cédric Le Goater <clg@redhat.com>
---
include/migration/misc.h | 17 ++++++
include/migration/register.h | 19 +++++++
include/qemu/typedefs.h | 3 ++
migration/multifd-device-state.c | 92 ++++++++++++++++++++++++++++++++
migration/savevm.c | 40 +++++++++++++-
5 files changed, 170 insertions(+), 1 deletion(-)
diff --git a/include/migration/misc.h b/include/migration/misc.h
index 273ebfca6256265507cd6aa28a104a3db1ac106b..8fd36eba1da72004bbc01cf18043180b21e10e41 100644
--- a/include/migration/misc.h
+++ b/include/migration/misc.h
@@ -119,8 +119,25 @@ bool migrate_uri_parse(const char *uri, MigrationChannel **channel,
Error **errp);
/* migration/multifd-device-state.c */
+typedef struct SaveLiveCompletePrecopyThreadData {
+ SaveLiveCompletePrecopyThreadHandler hdlr;
+ char *idstr;
+ uint32_t instance_id;
+ void *handler_opaque;
+} SaveLiveCompletePrecopyThreadData;
+
bool multifd_queue_device_state(char *idstr, uint32_t instance_id,
char *data, size_t len);
bool multifd_device_state_supported(void);
+void
+multifd_spawn_device_state_save_thread(SaveLiveCompletePrecopyThreadHandler hdlr,
+ char *idstr, uint32_t instance_id,
+ void *opaque);
+
+bool multifd_device_state_save_thread_should_exit(void);
+
+void multifd_abort_device_state_save_threads(void);
+bool multifd_join_device_state_save_threads(void);
+
#endif
diff --git a/include/migration/register.h b/include/migration/register.h
index 58891aa54b76cccdea9e442be9bf528a41593e26..c041ce32f2fcf0e0a102ee5a5d69ec0a84c5feac 100644
--- a/include/migration/register.h
+++ b/include/migration/register.h
@@ -105,6 +105,25 @@ typedef struct SaveVMHandlers {
*/
int (*save_live_complete_precopy)(QEMUFile *f, void *opaque);
+ /**
+ * @save_live_complete_precopy_thread (invoked in a separate thread)
+ *
+ * Called at the end of a precopy phase from a separate worker thread
+ * in configurations where multifd device state transfer is supported
+ * in order to perform asynchronous transmission of the remaining data in
+ * parallel with @save_live_complete_precopy handlers.
+ * When postcopy is enabled, devices that support postcopy will skip this
+ * step.
+ *
+ * @d: a #SaveLiveCompletePrecopyThreadData containing parameters that the
+ * handler may need, including this device section idstr and instance_id,
+ * and opaque data pointer passed to register_savevm_live().
+ * @errp: pointer to Error*, to store an error if it happens.
+ *
+ * Returns true to indicate success and false for errors.
+ */
+ SaveLiveCompletePrecopyThreadHandler save_live_complete_precopy_thread;
+
/* This runs both outside and inside the BQL. */
/**
diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h
index fd23ff7771b1bd6cd51f48bbc5be86aa57982307..42ed4e6be150acc39c842e1158cb87f3a8f5662d 100644
--- a/include/qemu/typedefs.h
+++ b/include/qemu/typedefs.h
@@ -108,6 +108,7 @@ typedef struct QString QString;
typedef struct RAMBlock RAMBlock;
typedef struct Range Range;
typedef struct ReservedRegion ReservedRegion;
+typedef struct SaveLiveCompletePrecopyThreadData SaveLiveCompletePrecopyThreadData;
typedef struct SHPCDevice SHPCDevice;
typedef struct SSIBus SSIBus;
typedef struct TCGCPUOps TCGCPUOps;
@@ -133,5 +134,7 @@ typedef struct IRQState *qemu_irq;
typedef void (*qemu_irq_handler)(void *opaque, int n, int level);
typedef bool (*MigrationLoadThread)(void *opaque, bool *should_quit,
Error **errp);
+typedef bool (*SaveLiveCompletePrecopyThreadHandler)(SaveLiveCompletePrecopyThreadData *d,
+ Error **errp);
#endif /* QEMU_TYPEDEFS_H */
diff --git a/migration/multifd-device-state.c b/migration/multifd-device-state.c
index 3097ffa31025971bb9b893c53dad352d517148d9..94222d0eb0d8f4dd0de0f5e5d6d742e9b3b6edf5 100644
--- a/migration/multifd-device-state.c
+++ b/migration/multifd-device-state.c
@@ -10,7 +10,10 @@
*/
#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "qemu/lockable.h"
+#include "block/thread-pool.h"
+#include "migration.h"
#include "migration/misc.h"
#include "multifd.h"
#include "options.h"
@@ -19,6 +22,9 @@ static struct {
QemuMutex queue_job_mutex;
MultiFDSendData *send_data;
+
+ ThreadPool *threads;
+ bool threads_abort;
} *multifd_send_device_state;
void multifd_device_state_send_setup(void)
@@ -29,10 +35,14 @@ void multifd_device_state_send_setup(void)
qemu_mutex_init(&multifd_send_device_state->queue_job_mutex);
multifd_send_device_state->send_data = multifd_send_data_alloc();
+
+ multifd_send_device_state->threads = thread_pool_new();
+ multifd_send_device_state->threads_abort = false;
}
void multifd_device_state_send_cleanup(void)
{
+ g_clear_pointer(&multifd_send_device_state->threads, thread_pool_free);
g_clear_pointer(&multifd_send_device_state->send_data,
multifd_send_data_free);
@@ -118,3 +128,85 @@ bool multifd_device_state_supported(void)
return migrate_multifd() && !migrate_mapped_ram() &&
migrate_multifd_compression() == MULTIFD_COMPRESSION_NONE;
}
+
+static void multifd_device_state_save_thread_data_free(void *opaque)
+{
+ SaveLiveCompletePrecopyThreadData *data = opaque;
+
+ g_clear_pointer(&data->idstr, g_free);
+ g_free(data);
+}
+
+static int multifd_device_state_save_thread(void *opaque)
+{
+ SaveLiveCompletePrecopyThreadData *data = opaque;
+ g_autoptr(Error) local_err = NULL;
+
+ if (!data->hdlr(data, &local_err)) {
+ MigrationState *s = migrate_get_current();
+
+ /*
+ * Can't call abort_device_state_save_threads() here since new
+ * save threads could still be in process of being launched
+ * (if, for example, the very first save thread launched exited
+ * with an error very quickly).
+ */
+
+ assert(local_err);
+
+ /*
+ * In case of multiple save threads failing which thread error
+ * return we end setting is purely arbitrary.
+ */
+ migrate_set_error(s, local_err);
+ }
+
+ return 0;
+}
+
+bool multifd_device_state_save_thread_should_exit(void)
+{
+ return qatomic_read(&multifd_send_device_state->threads_abort);
+}
+
+void
+multifd_spawn_device_state_save_thread(SaveLiveCompletePrecopyThreadHandler hdlr,
+ char *idstr, uint32_t instance_id,
+ void *opaque)
+{
+ SaveLiveCompletePrecopyThreadData *data;
+
+ assert(multifd_device_state_supported());
+ assert(multifd_send_device_state);
+
+ assert(!qatomic_read(&multifd_send_device_state->threads_abort));
+
+ data = g_new(SaveLiveCompletePrecopyThreadData, 1);
+ data->hdlr = hdlr;
+ data->idstr = g_strdup(idstr);
+ data->instance_id = instance_id;
+ data->handler_opaque = opaque;
+
+ thread_pool_submit_immediate(multifd_send_device_state->threads,
+ multifd_device_state_save_thread,
+ data,
+ multifd_device_state_save_thread_data_free);
+}
+
+void multifd_abort_device_state_save_threads(void)
+{
+ assert(multifd_device_state_supported());
+
+ qatomic_set(&multifd_send_device_state->threads_abort, true);
+}
+
+bool multifd_join_device_state_save_threads(void)
+{
+ MigrationState *s = migrate_get_current();
+
+ assert(multifd_device_state_supported());
+
+ thread_pool_wait(multifd_send_device_state->threads);
+
+ return !migrate_has_error(s);
+}
diff --git a/migration/savevm.c b/migration/savevm.c
index 1abc365570e324dd85f8a95adeb1a95f57b73264..5c4fdfd95eebf8d52fa02f2343090e4bac6c2d73 100644
--- a/migration/savevm.c
+++ b/migration/savevm.c
@@ -37,6 +37,7 @@
#include "migration/register.h"
#include "migration/global_state.h"
#include "migration/channel-block.h"
+#include "multifd.h"
#include "ram.h"
#include "qemu-file.h"
#include "savevm.h"
@@ -1527,6 +1528,24 @@ int qemu_savevm_state_complete_precopy_iterable(QEMUFile *f, bool in_postcopy)
int64_t start_ts_each, end_ts_each;
SaveStateEntry *se;
int ret;
+ bool multifd_device_state = multifd_device_state_supported();
+
+ if (multifd_device_state) {
+ QTAILQ_FOREACH(se, &savevm_state.handlers, entry) {
+ SaveLiveCompletePrecopyThreadHandler hdlr;
+
+ if (!se->ops || (in_postcopy && se->ops->has_postcopy &&
+ se->ops->has_postcopy(se->opaque)) ||
+ !se->ops->save_live_complete_precopy_thread) {
+ continue;
+ }
+
+ hdlr = se->ops->save_live_complete_precopy_thread;
+ multifd_spawn_device_state_save_thread(hdlr,
+ se->idstr, se->instance_id,
+ se->opaque);
+ }
+ }
QTAILQ_FOREACH(se, &savevm_state.handlers, entry) {
if (!se->ops ||
@@ -1552,16 +1571,35 @@ int qemu_savevm_state_complete_precopy_iterable(QEMUFile *f, bool in_postcopy)
save_section_footer(f, se);
if (ret < 0) {
qemu_file_set_error(f, ret);
- return -1;
+ goto ret_fail_abort_threads;
}
end_ts_each = qemu_clock_get_us(QEMU_CLOCK_REALTIME);
trace_vmstate_downtime_save("iterable", se->idstr, se->instance_id,
end_ts_each - start_ts_each);
}
+ if (multifd_device_state) {
+ if (migrate_has_error(migrate_get_current())) {
+ multifd_abort_device_state_save_threads();
+ }
+
+ if (!multifd_join_device_state_save_threads()) {
+ qemu_file_set_error(f, -EINVAL);
+ return -1;
+ }
+ }
+
trace_vmstate_downtime_checkpoint("src-iterable-saved");
return 0;
+
+ret_fail_abort_threads:
+ if (multifd_device_state) {
+ multifd_abort_device_state_save_threads();
+ multifd_join_device_state_save_threads();
+ }
+
+ return -1;
}
int qemu_savevm_state_complete_precopy_non_iterable(QEMUFile *f,
--
2.48.1
next prev parent reply other threads:[~2025-03-06 14:26 UTC|newest]
Thread overview: 44+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-03-06 14:13 [PULL 00/42] vfio queue Cédric Le Goater
2025-03-06 14:13 ` [PULL 01/42] vfio: Add property documentation Cédric Le Goater
2025-03-06 14:13 ` [PULL 02/42] vfio/ccw: Replace warn_once_pfch() with warn_report_once() Cédric Le Goater
2025-03-06 14:13 ` [PULL 03/42] hw/pci: Basic support for PCI power management Cédric Le Goater
2025-03-06 14:13 ` [PULL 04/42] pci: Use PCI PM capability initializer Cédric Le Goater
2025-03-06 14:13 ` [PULL 05/42] vfio/pci: Delete local pm_cap Cédric Le Goater
2025-03-06 14:13 ` [PULL 06/42] pcie, virtio: Remove redundant pm_cap Cédric Le Goater
2025-03-06 14:13 ` [PULL 07/42] hw/vfio/pci: Re-order pre-reset Cédric Le Goater
2025-03-06 14:13 ` [PULL 08/42] MAINTAINERS: Add myself as vfio-igd maintainer Cédric Le Goater
2025-03-06 14:13 ` [PULL 09/42] vfio-platform: Deprecate all forms of vfio-platform devices Cédric Le Goater
2025-03-06 14:13 ` [PULL 10/42] migration: Clarify that {load, save}_cleanup handlers can run without setup Cédric Le Goater
2025-03-06 14:13 ` [PULL 11/42] thread-pool: Remove thread_pool_submit() function Cédric Le Goater
2025-03-06 14:13 ` [PULL 12/42] thread-pool: Rename AIO pool functions to *_aio() and data types to *Aio Cédric Le Goater
2025-03-06 14:13 ` [PULL 13/42] thread-pool: Implement generic (non-AIO) pool support Cédric Le Goater
2025-03-06 14:13 ` [PULL 14/42] migration: Add MIG_CMD_SWITCHOVER_START and its load handler Cédric Le Goater
2025-03-06 14:13 ` [PULL 15/42] migration: Add qemu_loadvm_load_state_buffer() and its handler Cédric Le Goater
2025-03-06 14:13 ` [PULL 16/42] migration: Always take BQL for migration_incoming_state_destroy() Cédric Le Goater
2025-03-06 14:13 ` [PULL 17/42] error: define g_autoptr() cleanup function for the Error type Cédric Le Goater
2025-03-06 14:13 ` [PULL 18/42] migration: Add thread pool of optional load threads Cédric Le Goater
2025-03-06 14:13 ` [PULL 19/42] migration/multifd: Split packet into header and RAM data Cédric Le Goater
2025-03-06 14:13 ` [PULL 20/42] migration/multifd: Device state transfer support - receive side Cédric Le Goater
2025-03-06 14:13 ` [PULL 21/42] migration/multifd: Make multifd_send() thread safe Cédric Le Goater
2025-03-06 14:13 ` [PULL 22/42] migration/multifd: Add an explicit MultiFDSendData destructor Cédric Le Goater
2025-03-06 14:13 ` [PULL 23/42] migration/multifd: Device state transfer support - send side Cédric Le Goater
2025-03-06 14:14 ` [PULL 24/42] migration/multifd: Make MultiFDSendData a struct Cédric Le Goater
2025-03-06 14:14 ` [PULL 25/42] migration/multifd: Add multifd_device_state_supported() Cédric Le Goater
2025-03-06 14:14 ` Cédric Le Goater [this message]
2025-03-06 14:14 ` [PULL 27/42] vfio/migration: Add load_device_config_state_start trace event Cédric Le Goater
2025-03-06 14:14 ` [PULL 28/42] vfio/migration: Convert bytes_transferred counter to atomic Cédric Le Goater
2025-03-06 14:14 ` [PULL 29/42] vfio/migration: Add vfio_add_bytes_transferred() Cédric Le Goater
2025-03-06 14:14 ` [PULL 30/42] vfio/migration: Move migration channel flags to vfio-common.h header file Cédric Le Goater
2025-03-06 14:14 ` [PULL 31/42] vfio/migration: Multifd device state transfer support - basic types Cédric Le Goater
2025-03-06 14:14 ` [PULL 32/42] vfio/migration: Multifd device state transfer - add support checking function Cédric Le Goater
2025-03-06 14:14 ` [PULL 33/42] vfio/migration: Multifd setup/cleanup functions and associated VFIOMultifd Cédric Le Goater
2025-03-06 14:14 ` [PULL 34/42] vfio/migration: Setup and cleanup multifd transfer in these general methods Cédric Le Goater
2025-03-06 14:14 ` [PULL 35/42] vfio/migration: Multifd device state transfer support - received buffers queuing Cédric Le Goater
2025-03-06 14:14 ` [PULL 36/42] vfio/migration: Multifd device state transfer support - load thread Cédric Le Goater
2025-03-06 14:14 ` [PULL 37/42] migration/qemu-file: Define g_autoptr() cleanup function for QEMUFile Cédric Le Goater
2025-03-06 14:14 ` [PULL 38/42] vfio/migration: Multifd device state transfer support - config loading support Cédric Le Goater
2025-03-06 14:14 ` [PULL 39/42] vfio/migration: Multifd device state transfer support - send side Cédric Le Goater
2025-03-06 14:14 ` [PULL 40/42] vfio/migration: Add x-migration-multifd-transfer VFIO property Cédric Le Goater
2025-03-06 14:14 ` [PULL 41/42] vfio/migration: Make x-migration-multifd-transfer VFIO property mutable Cédric Le Goater
2025-03-06 14:14 ` [PULL 42/42] hw/core/machine: Add compat for x-migration-multifd-transfer VFIO property Cédric Le Goater
2025-03-07 7:18 ` [PULL 00/42] vfio queue Stefan Hajnoczi
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20250306141419.2015340-27-clg@redhat.com \
--to=clg@redhat.com \
--cc=alex.williamson@redhat.com \
--cc=maciej.szmigiero@oracle.com \
--cc=peterx@redhat.com \
--cc=qemu-devel@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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).