[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH v6 06/28] migration: allow dst vm pause on postcopy
From: |
Peter Xu |
Subject: |
[Qemu-devel] [PATCH v6 06/28] migration: allow dst vm pause on postcopy |
Date: |
Thu, 8 Feb 2018 18:31:10 +0800 |
When there is IO error on the incoming channel (e.g., network down),
instead of bailing out immediately, we allow the dst vm to switch to the
new POSTCOPY_PAUSE state. Currently it is still simple - it waits the
new semaphore, until someone poke it for another attempt.
One note is that here on ram loading thread we cannot detect the
POSTCOPY_ACTIVE state, but we need to detect the more specific
POSTCOPY_INCOMING_RUNNING state, to make sure we have already loaded all
the device states.
Reviewed-by: Dr. David Alan Gilbert <address@hidden>
Signed-off-by: Peter Xu <address@hidden>
---
migration/migration.c | 1 +
migration/migration.h | 3 +++
migration/savevm.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++--
migration/trace-events | 2 ++
4 files changed, 67 insertions(+), 2 deletions(-)
diff --git a/migration/migration.c b/migration/migration.c
index de5ee9c1d4..fc0d0e0e31 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -155,6 +155,7 @@ MigrationIncomingState *migration_incoming_get_current(void)
memset(&mis_current, 0, sizeof(MigrationIncomingState));
qemu_mutex_init(&mis_current.rp_mutex);
qemu_event_init(&mis_current.main_thread_load_event, false);
+ qemu_sem_init(&mis_current.postcopy_pause_sem_dst, 0);
once = true;
}
return &mis_current;
diff --git a/migration/migration.h b/migration/migration.h
index 3e4af0a831..27ba256c40 100644
--- a/migration/migration.h
+++ b/migration/migration.h
@@ -61,6 +61,9 @@ struct MigrationIncomingState {
/* The coroutine we should enter (back) after failover */
Coroutine *migration_incoming_co;
QemuSemaphore colo_incoming_sem;
+
+ /* notify PAUSED postcopy incoming migrations to try to continue */
+ QemuSemaphore postcopy_pause_sem_dst;
};
MigrationIncomingState *migration_incoming_get_current(void);
diff --git a/migration/savevm.c b/migration/savevm.c
index 76fcc2b1c3..d83c1e846a 100644
--- a/migration/savevm.c
+++ b/migration/savevm.c
@@ -1544,8 +1544,8 @@ static int
loadvm_postcopy_ram_handle_discard(MigrationIncomingState *mis,
*/
static void *postcopy_ram_listen_thread(void *opaque)
{
- QEMUFile *f = opaque;
MigrationIncomingState *mis = migration_incoming_get_current();
+ QEMUFile *f = mis->from_src_file;
int load_res;
migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE,
@@ -1559,6 +1559,14 @@ static void *postcopy_ram_listen_thread(void *opaque)
*/
qemu_file_set_blocking(f, true);
load_res = qemu_loadvm_state_main(f, mis);
+
+ /*
+ * This is tricky, but, mis->from_src_file can change after it
+ * returns, when postcopy recovery happened. In the future, we may
+ * want a wrapper for the QEMUFile handle.
+ */
+ f = mis->from_src_file;
+
/* And non-blocking again so we don't block in any cleanup */
qemu_file_set_blocking(f, false);
@@ -1641,7 +1649,7 @@ static int
loadvm_postcopy_handle_listen(MigrationIncomingState *mis)
/* Start up the listening thread and wait for it to signal ready */
qemu_sem_init(&mis->listen_thread_sem, 0);
qemu_thread_create(&mis->listen_thread, "postcopy/listen",
- postcopy_ram_listen_thread, mis->from_src_file,
+ postcopy_ram_listen_thread, NULL,
QEMU_THREAD_DETACHED);
qemu_sem_wait(&mis->listen_thread_sem);
qemu_sem_destroy(&mis->listen_thread_sem);
@@ -2026,11 +2034,44 @@ void qemu_loadvm_state_cleanup(void)
}
}
+/* Return true if we should continue the migration, or false. */
+static bool postcopy_pause_incoming(MigrationIncomingState *mis)
+{
+ trace_postcopy_pause_incoming();
+
+ migrate_set_state(&mis->state, MIGRATION_STATUS_POSTCOPY_ACTIVE,
+ MIGRATION_STATUS_POSTCOPY_PAUSED);
+
+ assert(mis->from_src_file);
+ qemu_file_shutdown(mis->from_src_file);
+ qemu_fclose(mis->from_src_file);
+ mis->from_src_file = NULL;
+
+ assert(mis->to_src_file);
+ qemu_file_shutdown(mis->to_src_file);
+ qemu_mutex_lock(&mis->rp_mutex);
+ qemu_fclose(mis->to_src_file);
+ mis->to_src_file = NULL;
+ qemu_mutex_unlock(&mis->rp_mutex);
+
+ error_report("Detected IO failure for postcopy. "
+ "Migration paused.");
+
+ while (mis->state == MIGRATION_STATUS_POSTCOPY_PAUSED) {
+ qemu_sem_wait(&mis->postcopy_pause_sem_dst);
+ }
+
+ trace_postcopy_pause_incoming_continued();
+
+ return true;
+}
+
static int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis)
{
uint8_t section_type;
int ret = 0;
+retry:
while (true) {
section_type = qemu_get_byte(f);
@@ -2075,6 +2116,24 @@ static int qemu_loadvm_state_main(QEMUFile *f,
MigrationIncomingState *mis)
out:
if (ret < 0) {
qemu_file_set_error(f, ret);
+
+ /*
+ * Detect whether it is:
+ *
+ * 1. postcopy running (after receiving all device data, which
+ * must be in POSTCOPY_INCOMING_RUNNING state. Note that
+ * POSTCOPY_INCOMING_LISTENING is still not enough, it's
+ * still receiving device states).
+ * 2. network failure (-EIO)
+ *
+ * If so, we try to wait for a recovery.
+ */
+ if (postcopy_state_get() == POSTCOPY_INCOMING_RUNNING &&
+ ret == -EIO && postcopy_pause_incoming(mis)) {
+ /* Reset f to point to the newly created channel */
+ f = mis->from_src_file;
+ goto retry;
+ }
}
return ret;
}
diff --git a/migration/trace-events b/migration/trace-events
index 7afbb3e791..8685a62c98 100644
--- a/migration/trace-events
+++ b/migration/trace-events
@@ -100,6 +100,8 @@ open_return_path_on_source(void) ""
open_return_path_on_source_continue(void) ""
postcopy_start(void) ""
postcopy_pause_continued(void) ""
+postcopy_pause_incoming(void) ""
+postcopy_pause_incoming_continued(void) ""
postcopy_start_set_run(void) ""
source_return_path_thread_bad_end(void) ""
source_return_path_thread_end(void) ""
--
2.14.3
- [Qemu-devel] [PATCH v6 00/28] Migration: postcopy failure recovery, Peter Xu, 2018/02/08
- [Qemu-devel] [PATCH v6 01/28] migration: better error handling with QEMUFile, Peter Xu, 2018/02/08
- [Qemu-devel] [PATCH v6 03/28] migration: provide postcopy_fault_thread_notify(), Peter Xu, 2018/02/08
- [Qemu-devel] [PATCH v6 02/28] migration: reuse mis->userfault_quit_fd, Peter Xu, 2018/02/08
- [Qemu-devel] [PATCH v6 05/28] migration: implement "postcopy-pause" src logic, Peter Xu, 2018/02/08
- [Qemu-devel] [PATCH v6 04/28] migration: new postcopy-pause state, Peter Xu, 2018/02/08
- [Qemu-devel] [PATCH v6 06/28] migration: allow dst vm pause on postcopy,
Peter Xu <=
- [Qemu-devel] [PATCH v6 07/28] migration: allow src return path to pause, Peter Xu, 2018/02/08
- [Qemu-devel] [PATCH v6 08/28] migration: allow send_rq to fail, Peter Xu, 2018/02/08
- [Qemu-devel] [PATCH v6 09/28] migration: allow fault thread to pause, Peter Xu, 2018/02/08
- [Qemu-devel] [PATCH v6 10/28] qmp: hmp: add migrate "resume" option, Peter Xu, 2018/02/08
- [Qemu-devel] [PATCH v6 11/28] migration: pass MigrationState to migrate_init(), Peter Xu, 2018/02/08
- [Qemu-devel] [PATCH v6 12/28] migration: rebuild channel on source, Peter Xu, 2018/02/08
- [Qemu-devel] [PATCH v6 13/28] migration: new state "postcopy-recover", Peter Xu, 2018/02/08
- [Qemu-devel] [PATCH v6 14/28] migration: wakeup dst ram-load-thread for recover, Peter Xu, 2018/02/08
- [Qemu-devel] [PATCH v6 15/28] migration: new cmd MIG_CMD_RECV_BITMAP, Peter Xu, 2018/02/08
- [Qemu-devel] [PATCH v6 16/28] migration: new message MIG_RP_MSG_RECV_BITMAP, Peter Xu, 2018/02/08