[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH v4 19/33] migration: Add save_live_complete_precopy_thread handle
From: |
Maciej S. Szmigiero |
Subject: |
[PATCH v4 19/33] migration: Add save_live_complete_precopy_thread handler |
Date: |
Thu, 30 Jan 2025 11:08:40 +0100 |
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>
---
include/migration/misc.h | 8 +++
include/migration/register.h | 21 ++++++++
include/qemu/typedefs.h | 4 ++
migration/multifd-device-state.c | 83 ++++++++++++++++++++++++++++++++
migration/savevm.c | 37 +++++++++++++-
5 files changed, 152 insertions(+), 1 deletion(-)
diff --git a/include/migration/misc.h b/include/migration/misc.h
index cc987e6e97af..008d22df8e72 100644
--- a/include/migration/misc.h
+++ b/include/migration/misc.h
@@ -116,4 +116,12 @@ 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);
+
+void multifd_abort_device_state_save_threads(void);
+int multifd_join_device_state_save_threads(void);
+
#endif
diff --git a/include/migration/register.h b/include/migration/register.h
index 58891aa54b76..f63b3ca3fd44 100644
--- a/include/migration/register.h
+++ b/include/migration/register.h
@@ -105,6 +105,27 @@ 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.
+ *
+ * @idstr: this device section idstr
+ * @instance_id: this device section instance_id
+ * @abort_flag: flag indicating that the migration core wants to abort
+ * the transmission and so the handler should exit ASAP. To be read by
+ * qatomic_read() or similar.
+ * @opaque: data pointer passed to register_savevm_live()
+ *
+ * Returns zero to indicate success and negative for error
+ */
+ 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 fd23ff7771b1..76578dee8fd3 100644
--- a/include/qemu/typedefs.h
+++ b/include/qemu/typedefs.h
@@ -133,5 +133,9 @@ 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 int (*SaveLiveCompletePrecopyThreadHandler)(char *idstr,
+ uint32_t instance_id,
+ bool *abort_flag,
+ void *opaque);
#endif /* QEMU_TYPEDEFS_H */
diff --git a/migration/multifd-device-state.c b/migration/multifd-device-state.c
index cee3c44bcf2a..fb09e6a48df9 100644
--- a/migration/multifd-device-state.c
+++ b/migration/multifd-device-state.c
@@ -9,12 +9,17 @@
#include "qemu/osdep.h"
#include "qemu/lockable.h"
+#include "block/thread-pool.h"
#include "migration/misc.h"
#include "multifd.h"
#include "options.h"
static QemuMutex queue_job_mutex;
+static ThreadPool *send_threads;
+static int send_threads_ret;
+static bool send_threads_abort;
+
static MultiFDSendData *device_state_send;
void multifd_device_state_send_setup(void)
@@ -23,10 +28,16 @@ void multifd_device_state_send_setup(void)
assert(!device_state_send);
device_state_send = multifd_send_data_alloc();
+
+ assert(!send_threads);
+ send_threads = thread_pool_new();
+ send_threads_ret = 0;
+ send_threads_abort = false;
}
void multifd_device_state_send_cleanup(void)
{
+ g_clear_pointer(&send_threads, thread_pool_free);
g_clear_pointer(&device_state_send, multifd_send_data_free);
qemu_mutex_destroy(&queue_job_mutex);
@@ -107,3 +118,75 @@ bool multifd_device_state_supported(void)
return migrate_multifd() && !migrate_mapped_ram() &&
migrate_multifd_compression() == MULTIFD_COMPRESSION_NONE;
}
+
+struct MultiFDDSSaveThreadData {
+ SaveLiveCompletePrecopyThreadHandler hdlr;
+ char *idstr;
+ uint32_t instance_id;
+ void *handler_opaque;
+};
+
+static void multifd_device_state_save_thread_data_free(void *opaque)
+{
+ struct MultiFDDSSaveThreadData *data = opaque;
+
+ g_clear_pointer(&data->idstr, g_free);
+ g_free(data);
+}
+
+static int multifd_device_state_save_thread(void *opaque)
+{
+ struct MultiFDDSSaveThreadData *data = opaque;
+ int ret;
+
+ ret = data->hdlr(data->idstr, data->instance_id, &send_threads_abort,
+ data->handler_opaque);
+ if (ret && !qatomic_read(&send_threads_ret)) {
+ /*
+ * Racy with the above read but that's okay - which thread error
+ * return we report is purely arbitrary anyway.
+ */
+ qatomic_set(&send_threads_ret, ret);
+ }
+
+ return 0;
+}
+
+void
+multifd_spawn_device_state_save_thread(SaveLiveCompletePrecopyThreadHandler
hdlr,
+ char *idstr, uint32_t instance_id,
+ void *opaque)
+{
+ struct MultiFDDSSaveThreadData *data;
+
+ assert(multifd_device_state_supported());
+
+ assert(!qatomic_read(&send_threads_abort));
+
+ data = g_new(struct MultiFDDSSaveThreadData, 1);
+ data->hdlr = hdlr;
+ data->idstr = g_strdup(idstr);
+ data->instance_id = instance_id;
+ data->handler_opaque = opaque;
+
+ thread_pool_submit_immediate(send_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(&send_threads_abort, true);
+}
+
+int multifd_join_device_state_save_threads(void)
+{
+ assert(multifd_device_state_supported());
+
+ thread_pool_wait(send_threads);
+
+ return send_threads_ret;
+}
diff --git a/migration/savevm.c b/migration/savevm.c
index 74d1960de3c6..e47c6c92fe50 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"
@@ -1521,6 +1522,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 ||
@@ -1546,16 +1565,32 @@ 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) {
+ ret = multifd_join_device_state_save_threads();
+ if (ret) {
+ qemu_file_set_error(f, ret);
+ 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,
- [PATCH v4 09/33] migration: postcopy_ram_listen_thread() needs to take BQL for some calls, (continued)
- [PATCH v4 09/33] migration: postcopy_ram_listen_thread() needs to take BQL for some calls, Maciej S. Szmigiero, 2025/01/30
- [PATCH v4 10/33] error: define g_autoptr() cleanup function for the Error type, Maciej S. Szmigiero, 2025/01/30
- [PATCH v4 11/33] migration: Add thread pool of optional load threads, Maciej S. Szmigiero, 2025/01/30
- [PATCH v4 12/33] migration/multifd: Split packet into header and RAM data, Maciej S. Szmigiero, 2025/01/30
- [PATCH v4 13/33] migration/multifd: Device state transfer support - receive side, Maciej S. Szmigiero, 2025/01/30
- [PATCH v4 14/33] migration/multifd: Make multifd_send() thread safe, Maciej S. Szmigiero, 2025/01/30
- [PATCH v4 15/33] migration/multifd: Add an explicit MultiFDSendData destructor, Maciej S. Szmigiero, 2025/01/30
- [PATCH v4 16/33] migration/multifd: Device state transfer support - send side, Maciej S. Szmigiero, 2025/01/30
- [PATCH v4 17/33] migration/multifd: Make MultiFDSendData a struct, Maciej S. Szmigiero, 2025/01/30
- [PATCH v4 18/33] migration/multifd: Add multifd_device_state_supported(), Maciej S. Szmigiero, 2025/01/30
- [PATCH v4 19/33] migration: Add save_live_complete_precopy_thread handler,
Maciej S. Szmigiero <=
- [PATCH v4 20/33] vfio/migration: Add x-migration-load-config-after-iter VFIO property, Maciej S. Szmigiero, 2025/01/30
- [PATCH v4 21/33] vfio/migration: Add load_device_config_state_start trace event, Maciej S. Szmigiero, 2025/01/30
- [PATCH v4 22/33] vfio/migration: Convert bytes_transferred counter to atomic, Maciej S. Szmigiero, 2025/01/30
- [PATCH v4 23/33] vfio/migration: Multifd device state transfer support - basic types, Maciej S. Szmigiero, 2025/01/30
- [PATCH v4 24/33] vfio/migration: Multifd device state transfer support - VFIOStateBuffer(s), Maciej S. Szmigiero, 2025/01/30
- [PATCH v4 25/33] vfio/migration: Multifd device state transfer - add support checking function, Maciej S. Szmigiero, 2025/01/30
- [PATCH v4 26/33] vfio/migration: Multifd device state transfer support - receive init/cleanup, Maciej S. Szmigiero, 2025/01/30
- [PATCH v4 27/33] vfio/migration: Multifd device state transfer support - received buffers queuing, Maciej S. Szmigiero, 2025/01/30