|
@@ -10,7 +10,10 @@
|
|
*/
|
|
*/
|
|
|
|
|
|
#include "qemu/osdep.h"
|
|
#include "qemu/osdep.h"
|
|
|
|
+#include "qapi/error.h"
|
|
#include "qemu/lockable.h"
|
|
#include "qemu/lockable.h"
|
|
|
|
+#include "block/thread-pool.h"
|
|
|
|
+#include "migration.h"
|
|
#include "migration/misc.h"
|
|
#include "migration/misc.h"
|
|
#include "multifd.h"
|
|
#include "multifd.h"
|
|
#include "options.h"
|
|
#include "options.h"
|
|
@@ -19,6 +22,9 @@ static struct {
|
|
QemuMutex queue_job_mutex;
|
|
QemuMutex queue_job_mutex;
|
|
|
|
|
|
MultiFDSendData *send_data;
|
|
MultiFDSendData *send_data;
|
|
|
|
+
|
|
|
|
+ ThreadPool *threads;
|
|
|
|
+ bool threads_abort;
|
|
} *multifd_send_device_state;
|
|
} *multifd_send_device_state;
|
|
|
|
|
|
void multifd_device_state_send_setup(void)
|
|
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);
|
|
qemu_mutex_init(&multifd_send_device_state->queue_job_mutex);
|
|
|
|
|
|
multifd_send_device_state->send_data = multifd_send_data_alloc();
|
|
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)
|
|
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,
|
|
g_clear_pointer(&multifd_send_device_state->send_data,
|
|
multifd_send_data_free);
|
|
multifd_send_data_free);
|
|
|
|
|
|
@@ -118,3 +128,85 @@ bool multifd_device_state_supported(void)
|
|
return migrate_multifd() && !migrate_mapped_ram() &&
|
|
return migrate_multifd() && !migrate_mapped_ram() &&
|
|
migrate_multifd_compression() == MULTIFD_COMPRESSION_NONE;
|
|
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);
|
|
|
|
+}
|