331 lines
12 KiB
Diff
331 lines
12 KiB
Diff
|
From 29eee1fbb84c0e2f0ece9e6d996afa7238ed2912 Mon Sep 17 00:00:00 2001
|
||
|
From: "manish.mishra" <manish.mishra@nutanix.com>
|
||
|
Date: Tue, 20 Dec 2022 18:44:18 +0000
|
||
|
Subject: [PATCH 7/8] migration: check magic value for deciding the mapping of
|
||
|
channels
|
||
|
MIME-Version: 1.0
|
||
|
Content-Type: text/plain; charset=UTF-8
|
||
|
Content-Transfer-Encoding: 8bit
|
||
|
|
||
|
RH-Author: Peter Xu <peterx@redhat.com>
|
||
|
RH-MergeRequest: 150: migration: Fix multifd crash on channel disorders
|
||
|
RH-Bugzilla: 2169732
|
||
|
RH-Acked-by: quintela1 <quintela@redhat.com>
|
||
|
RH-Acked-by: Leonardo Brás <leobras@redhat.com>
|
||
|
RH-Acked-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
|
||
|
RH-Commit: [2/2] 4fb9408478923415a91fe0527bf4b1a0f022f329 (peterx/qemu-kvm)
|
||
|
|
||
|
Current logic assumes that channel connections on the destination side are
|
||
|
always established in the same order as the source and the first one will
|
||
|
always be the main channel followed by the multifid or post-copy
|
||
|
preemption channel. This may not be always true, as even if a channel has a
|
||
|
connection established on the source side it can be in the pending state on
|
||
|
the destination side and a newer connection can be established first.
|
||
|
Basically causing out of order mapping of channels on the destination side.
|
||
|
Currently, all channels except post-copy preempt send a magic number, this
|
||
|
patch uses that magic number to decide the type of channel. This logic is
|
||
|
applicable only for precopy(multifd) live migration, as mentioned, the
|
||
|
post-copy preempt channel does not send any magic number. Also, tls live
|
||
|
migrations already does tls handshake before creating other channels, so
|
||
|
this issue is not possible with tls, hence this logic is avoided for tls
|
||
|
live migrations. This patch uses read peek to check the magic number of
|
||
|
channels so that current data/control stream management remains
|
||
|
un-effected.
|
||
|
|
||
|
Reviewed-by: Peter Xu <peterx@redhat.com>
|
||
|
Reviewed-by: Daniel P. Berrange <berrange@redhat.com>
|
||
|
Reviewed-by: Juan Quintela <quintela@redhat.com>
|
||
|
Suggested-by: Daniel P. Berrange <berrange@redhat.com>
|
||
|
Signed-off-by: manish.mishra <manish.mishra@nutanix.com>
|
||
|
Signed-off-by: Juan Quintela <quintela@redhat.com>
|
||
|
(cherry picked from commit 6720c2b32725e6ac404f22851a0ecd0a71d0cbe2)
|
||
|
Signed-off-by: Peter Xu <peterx@redhat.com>
|
||
|
---
|
||
|
migration/channel.c | 45 +++++++++++++++++++++++++++++++++
|
||
|
migration/channel.h | 5 ++++
|
||
|
migration/migration.c | 54 ++++++++++++++++++++++++++++------------
|
||
|
migration/multifd.c | 19 +++++++-------
|
||
|
migration/multifd.h | 2 +-
|
||
|
migration/postcopy-ram.c | 5 +---
|
||
|
migration/postcopy-ram.h | 2 +-
|
||
|
7 files changed, 101 insertions(+), 31 deletions(-)
|
||
|
|
||
|
diff --git a/migration/channel.c b/migration/channel.c
|
||
|
index 1b0815039f..ca3319a309 100644
|
||
|
--- a/migration/channel.c
|
||
|
+++ b/migration/channel.c
|
||
|
@@ -92,3 +92,48 @@ void migration_channel_connect(MigrationState *s,
|
||
|
migrate_fd_connect(s, error);
|
||
|
error_free(error);
|
||
|
}
|
||
|
+
|
||
|
+
|
||
|
+/**
|
||
|
+ * @migration_channel_read_peek - Peek at migration channel, without
|
||
|
+ * actually removing it from channel buffer.
|
||
|
+ *
|
||
|
+ * @ioc: the channel object
|
||
|
+ * @buf: the memory region to read data into
|
||
|
+ * @buflen: the number of bytes to read in @buf
|
||
|
+ * @errp: pointer to a NULL-initialized error object
|
||
|
+ *
|
||
|
+ * Returns 0 if successful, returns -1 and sets @errp if fails.
|
||
|
+ */
|
||
|
+int migration_channel_read_peek(QIOChannel *ioc,
|
||
|
+ const char *buf,
|
||
|
+ const size_t buflen,
|
||
|
+ Error **errp)
|
||
|
+{
|
||
|
+ ssize_t len = 0;
|
||
|
+ struct iovec iov = { .iov_base = (char *)buf, .iov_len = buflen };
|
||
|
+
|
||
|
+ while (true) {
|
||
|
+ len = qio_channel_readv_full(ioc, &iov, 1, NULL, NULL,
|
||
|
+ QIO_CHANNEL_READ_FLAG_MSG_PEEK, errp);
|
||
|
+
|
||
|
+ if (len <= 0 && len != QIO_CHANNEL_ERR_BLOCK) {
|
||
|
+ error_setg(errp,
|
||
|
+ "Failed to peek at channel");
|
||
|
+ return -1;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (len == buflen) {
|
||
|
+ break;
|
||
|
+ }
|
||
|
+
|
||
|
+ /* 1ms sleep. */
|
||
|
+ if (qemu_in_coroutine()) {
|
||
|
+ qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, 1000000);
|
||
|
+ } else {
|
||
|
+ g_usleep(1000);
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
diff --git a/migration/channel.h b/migration/channel.h
|
||
|
index 67a461c28a..5bdb8208a7 100644
|
||
|
--- a/migration/channel.h
|
||
|
+++ b/migration/channel.h
|
||
|
@@ -24,4 +24,9 @@ void migration_channel_connect(MigrationState *s,
|
||
|
QIOChannel *ioc,
|
||
|
const char *hostname,
|
||
|
Error *error_in);
|
||
|
+
|
||
|
+int migration_channel_read_peek(QIOChannel *ioc,
|
||
|
+ const char *buf,
|
||
|
+ const size_t buflen,
|
||
|
+ Error **errp);
|
||
|
#endif
|
||
|
diff --git a/migration/migration.c b/migration/migration.c
|
||
|
index f485eea5fb..593dbd25de 100644
|
||
|
--- a/migration/migration.c
|
||
|
+++ b/migration/migration.c
|
||
|
@@ -31,6 +31,7 @@
|
||
|
#include "migration.h"
|
||
|
#include "savevm.h"
|
||
|
#include "qemu-file.h"
|
||
|
+#include "channel.h"
|
||
|
#include "migration/vmstate.h"
|
||
|
#include "block/block.h"
|
||
|
#include "qapi/error.h"
|
||
|
@@ -663,10 +664,6 @@ static bool migration_incoming_setup(QEMUFile *f, Error **errp)
|
||
|
{
|
||
|
MigrationIncomingState *mis = migration_incoming_get_current();
|
||
|
|
||
|
- if (multifd_load_setup(errp) != 0) {
|
||
|
- return false;
|
||
|
- }
|
||
|
-
|
||
|
if (!mis->from_src_file) {
|
||
|
mis->from_src_file = f;
|
||
|
}
|
||
|
@@ -733,31 +730,56 @@ void migration_ioc_process_incoming(QIOChannel *ioc, Error **errp)
|
||
|
{
|
||
|
MigrationIncomingState *mis = migration_incoming_get_current();
|
||
|
Error *local_err = NULL;
|
||
|
- bool start_migration;
|
||
|
QEMUFile *f;
|
||
|
+ bool default_channel = true;
|
||
|
+ uint32_t channel_magic = 0;
|
||
|
+ int ret = 0;
|
||
|
|
||
|
- if (!mis->from_src_file) {
|
||
|
- /* The first connection (multifd may have multiple) */
|
||
|
+ if (migrate_use_multifd() && !migrate_postcopy_ram() &&
|
||
|
+ qio_channel_has_feature(ioc, QIO_CHANNEL_FEATURE_READ_MSG_PEEK)) {
|
||
|
+ /*
|
||
|
+ * With multiple channels, it is possible that we receive channels
|
||
|
+ * out of order on destination side, causing incorrect mapping of
|
||
|
+ * source channels on destination side. Check channel MAGIC to
|
||
|
+ * decide type of channel. Please note this is best effort, postcopy
|
||
|
+ * preempt channel does not send any magic number so avoid it for
|
||
|
+ * postcopy live migration. Also tls live migration already does
|
||
|
+ * tls handshake while initializing main channel so with tls this
|
||
|
+ * issue is not possible.
|
||
|
+ */
|
||
|
+ ret = migration_channel_read_peek(ioc, (void *)&channel_magic,
|
||
|
+ sizeof(channel_magic), &local_err);
|
||
|
+
|
||
|
+ if (ret != 0) {
|
||
|
+ error_propagate(errp, local_err);
|
||
|
+ return;
|
||
|
+ }
|
||
|
+
|
||
|
+ default_channel = (channel_magic == cpu_to_be32(QEMU_VM_FILE_MAGIC));
|
||
|
+ } else {
|
||
|
+ default_channel = !mis->from_src_file;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (multifd_load_setup(errp) != 0) {
|
||
|
+ error_setg(errp, "Failed to setup multifd channels");
|
||
|
+ return;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (default_channel) {
|
||
|
f = qemu_file_new_input(ioc);
|
||
|
|
||
|
if (!migration_incoming_setup(f, errp)) {
|
||
|
return;
|
||
|
}
|
||
|
-
|
||
|
- /*
|
||
|
- * Common migration only needs one channel, so we can start
|
||
|
- * right now. Some features need more than one channel, we wait.
|
||
|
- */
|
||
|
- start_migration = !migration_needs_multiple_sockets();
|
||
|
} else {
|
||
|
/* Multiple connections */
|
||
|
assert(migration_needs_multiple_sockets());
|
||
|
if (migrate_use_multifd()) {
|
||
|
- start_migration = multifd_recv_new_channel(ioc, &local_err);
|
||
|
+ multifd_recv_new_channel(ioc, &local_err);
|
||
|
} else {
|
||
|
assert(migrate_postcopy_preempt());
|
||
|
f = qemu_file_new_input(ioc);
|
||
|
- start_migration = postcopy_preempt_new_channel(mis, f);
|
||
|
+ postcopy_preempt_new_channel(mis, f);
|
||
|
}
|
||
|
if (local_err) {
|
||
|
error_propagate(errp, local_err);
|
||
|
@@ -765,7 +787,7 @@ void migration_ioc_process_incoming(QIOChannel *ioc, Error **errp)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
- if (start_migration) {
|
||
|
+ if (migration_has_all_channels()) {
|
||
|
/* If it's a recovery, we're done */
|
||
|
if (postcopy_try_recover()) {
|
||
|
return;
|
||
|
diff --git a/migration/multifd.c b/migration/multifd.c
|
||
|
index 509bbbe3bf..c3385529cf 100644
|
||
|
--- a/migration/multifd.c
|
||
|
+++ b/migration/multifd.c
|
||
|
@@ -1167,9 +1167,14 @@ int multifd_load_setup(Error **errp)
|
||
|
uint32_t page_count = MULTIFD_PACKET_SIZE / qemu_target_page_size();
|
||
|
uint8_t i;
|
||
|
|
||
|
- if (!migrate_use_multifd()) {
|
||
|
+ /*
|
||
|
+ * Return successfully if multiFD recv state is already initialised
|
||
|
+ * or multiFD is not enabled.
|
||
|
+ */
|
||
|
+ if (multifd_recv_state || !migrate_use_multifd()) {
|
||
|
return 0;
|
||
|
}
|
||
|
+
|
||
|
if (!migrate_multi_channels_is_allowed()) {
|
||
|
error_setg(errp, "multifd is not supported by current protocol");
|
||
|
return -1;
|
||
|
@@ -1228,11 +1233,9 @@ bool multifd_recv_all_channels_created(void)
|
||
|
|
||
|
/*
|
||
|
* Try to receive all multifd channels to get ready for the migration.
|
||
|
- * - Return true and do not set @errp when correctly receiving all channels;
|
||
|
- * - Return false and do not set @errp when correctly receiving the current one;
|
||
|
- * - Return false and set @errp when failing to receive the current channel.
|
||
|
+ * Sets @errp when failing to receive the current channel.
|
||
|
*/
|
||
|
-bool multifd_recv_new_channel(QIOChannel *ioc, Error **errp)
|
||
|
+void multifd_recv_new_channel(QIOChannel *ioc, Error **errp)
|
||
|
{
|
||
|
MultiFDRecvParams *p;
|
||
|
Error *local_err = NULL;
|
||
|
@@ -1245,7 +1248,7 @@ bool multifd_recv_new_channel(QIOChannel *ioc, Error **errp)
|
||
|
"failed to receive packet"
|
||
|
" via multifd channel %d: ",
|
||
|
qatomic_read(&multifd_recv_state->count));
|
||
|
- return false;
|
||
|
+ return;
|
||
|
}
|
||
|
trace_multifd_recv_new_channel(id);
|
||
|
|
||
|
@@ -1255,7 +1258,7 @@ bool multifd_recv_new_channel(QIOChannel *ioc, Error **errp)
|
||
|
id);
|
||
|
multifd_recv_terminate_threads(local_err);
|
||
|
error_propagate(errp, local_err);
|
||
|
- return false;
|
||
|
+ return;
|
||
|
}
|
||
|
p->c = ioc;
|
||
|
object_ref(OBJECT(ioc));
|
||
|
@@ -1266,6 +1269,4 @@ bool multifd_recv_new_channel(QIOChannel *ioc, Error **errp)
|
||
|
qemu_thread_create(&p->thread, p->name, multifd_recv_thread, p,
|
||
|
QEMU_THREAD_JOINABLE);
|
||
|
qatomic_inc(&multifd_recv_state->count);
|
||
|
- return qatomic_read(&multifd_recv_state->count) ==
|
||
|
- migrate_multifd_channels();
|
||
|
}
|
||
|
diff --git a/migration/multifd.h b/migration/multifd.h
|
||
|
index 519f498643..913e4ba274 100644
|
||
|
--- a/migration/multifd.h
|
||
|
+++ b/migration/multifd.h
|
||
|
@@ -18,7 +18,7 @@ void multifd_save_cleanup(void);
|
||
|
int multifd_load_setup(Error **errp);
|
||
|
int multifd_load_cleanup(Error **errp);
|
||
|
bool multifd_recv_all_channels_created(void);
|
||
|
-bool multifd_recv_new_channel(QIOChannel *ioc, Error **errp);
|
||
|
+void multifd_recv_new_channel(QIOChannel *ioc, Error **errp);
|
||
|
void multifd_recv_sync_main(void);
|
||
|
int multifd_send_sync_main(QEMUFile *f);
|
||
|
int multifd_queue_page(QEMUFile *f, RAMBlock *block, ram_addr_t offset);
|
||
|
diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c
|
||
|
index 0c55df0e52..b98e95dab0 100644
|
||
|
--- a/migration/postcopy-ram.c
|
||
|
+++ b/migration/postcopy-ram.c
|
||
|
@@ -1538,7 +1538,7 @@ void postcopy_unregister_shared_ufd(struct PostCopyFD *pcfd)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
-bool postcopy_preempt_new_channel(MigrationIncomingState *mis, QEMUFile *file)
|
||
|
+void postcopy_preempt_new_channel(MigrationIncomingState *mis, QEMUFile *file)
|
||
|
{
|
||
|
/*
|
||
|
* The new loading channel has its own threads, so it needs to be
|
||
|
@@ -1547,9 +1547,6 @@ bool postcopy_preempt_new_channel(MigrationIncomingState *mis, QEMUFile *file)
|
||
|
qemu_file_set_blocking(file, true);
|
||
|
mis->postcopy_qemufile_dst = file;
|
||
|
trace_postcopy_preempt_new_channel();
|
||
|
-
|
||
|
- /* Start the migration immediately */
|
||
|
- return true;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
diff --git a/migration/postcopy-ram.h b/migration/postcopy-ram.h
|
||
|
index 6147bf7d1d..25881c4127 100644
|
||
|
--- a/migration/postcopy-ram.h
|
||
|
+++ b/migration/postcopy-ram.h
|
||
|
@@ -190,7 +190,7 @@ enum PostcopyChannels {
|
||
|
RAM_CHANNEL_MAX,
|
||
|
};
|
||
|
|
||
|
-bool postcopy_preempt_new_channel(MigrationIncomingState *mis, QEMUFile *file);
|
||
|
+void postcopy_preempt_new_channel(MigrationIncomingState *mis, QEMUFile *file);
|
||
|
int postcopy_preempt_setup(MigrationState *s, Error **errp);
|
||
|
int postcopy_preempt_wait_channel(MigrationState *s);
|
||
|
|
||
|
--
|
||
|
2.31.1
|
||
|
|