aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/devel/migration.rst4
-rw-r--r--hmp-commands.hx3
-rw-r--r--migration/migration.c47
-rw-r--r--migration/migration.h10
-rw-r--r--migration/postcopy-ram.c60
-rw-r--r--migration/postcopy-ram.h2
-rw-r--r--migration/ram.c33
-rw-r--r--migration/savevm.c48
-rw-r--r--qapi/migration.json10
-rw-r--r--tests/migration-test.c117
-rwxr-xr-xtests/migration/rebuild-x86-bootblock.sh33
-rw-r--r--tests/migration/x86-a-b-bootblock.h51
-rw-r--r--tests/migration/x86-a-b-bootblock.s92
13 files changed, 388 insertions, 122 deletions
diff --git a/docs/devel/migration.rst b/docs/devel/migration.rst
index bf97080dac..9d1b7657f0 100644
--- a/docs/devel/migration.rst
+++ b/docs/devel/migration.rst
@@ -387,8 +387,8 @@ doesn't finish in a given time the switch is made to postcopy.
Enabling postcopy
-----------------
-To enable postcopy, issue this command on the monitor prior to the
-start of migration:
+To enable postcopy, issue this command on the monitor (both source and
+destination) prior to the start of migration:
``migrate_set_capability postcopy-ram on``
diff --git a/hmp-commands.hx b/hmp-commands.hx
index 15620c94d3..d26eb4119b 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -1041,7 +1041,8 @@ ETEXI
.params = "",
.help = "Followup to a migration command to switch the migration"
" to postcopy mode. The postcopy-ram capability must "
- "be set before the original migration command.",
+ "be set on both source and destination before the "
+ "original migration command .",
.cmd = hmp_migrate_start_postcopy,
},
diff --git a/migration/migration.c b/migration/migration.c
index 86d69120a6..0aa596f867 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -205,17 +205,35 @@ static void deferred_incoming_migration(Error **errp)
* Send a message on the return channel back to the source
* of the migration.
*/
-static void migrate_send_rp_message(MigrationIncomingState *mis,
- enum mig_rp_message_type message_type,
- uint16_t len, void *data)
+static int migrate_send_rp_message(MigrationIncomingState *mis,
+ enum mig_rp_message_type message_type,
+ uint16_t len, void *data)
{
+ int ret = 0;
+
trace_migrate_send_rp_message((int)message_type, len);
qemu_mutex_lock(&mis->rp_mutex);
+
+ /*
+ * It's possible that the file handle got lost due to network
+ * failures.
+ */
+ if (!mis->to_src_file) {
+ ret = -EIO;
+ goto error;
+ }
+
qemu_put_be16(mis->to_src_file, (unsigned int)message_type);
qemu_put_be16(mis->to_src_file, len);
qemu_put_buffer(mis->to_src_file, data, len);
qemu_fflush(mis->to_src_file);
+
+ /* It's possible that qemu file got error during sending */
+ ret = qemu_file_get_error(mis->to_src_file);
+
+error:
qemu_mutex_unlock(&mis->rp_mutex);
+ return ret;
}
/* Request a range of pages from the source VM at the given
@@ -225,11 +243,12 @@ static void migrate_send_rp_message(MigrationIncomingState *mis,
* Start: Address offset within the RB
* Len: Length in bytes required - must be a multiple of pagesize
*/
-void migrate_send_rp_req_pages(MigrationIncomingState *mis, const char *rbname,
- ram_addr_t start, size_t len)
+int migrate_send_rp_req_pages(MigrationIncomingState *mis, const char *rbname,
+ ram_addr_t start, size_t len)
{
uint8_t bufc[12 + 1 + 255]; /* start (8), len (4), rbname up to 256 */
size_t msglen = 12; /* start + len */
+ enum mig_rp_message_type msg_type;
*(uint64_t *)bufc = cpu_to_be64((uint64_t)start);
*(uint32_t *)(bufc + 8) = cpu_to_be32((uint32_t)len);
@@ -241,10 +260,12 @@ void migrate_send_rp_req_pages(MigrationIncomingState *mis, const char *rbname,
bufc[msglen++] = rbname_len;
memcpy(bufc + msglen, rbname, rbname_len);
msglen += rbname_len;
- migrate_send_rp_message(mis, MIG_RP_MSG_REQ_PAGES_ID, msglen, bufc);
+ msg_type = MIG_RP_MSG_REQ_PAGES_ID;
} else {
- migrate_send_rp_message(mis, MIG_RP_MSG_REQ_PAGES, msglen, bufc);
+ msg_type = MIG_RP_MSG_REQ_PAGES;
}
+
+ return migrate_send_rp_message(mis, msg_type, msglen, bufc);
}
void qemu_start_incoming_migration(const char *uri, Error **errp)
@@ -1237,10 +1258,8 @@ bool migration_is_idle(void)
return false;
}
-MigrationState *migrate_init(void)
+void migrate_init(MigrationState *s)
{
- MigrationState *s = migrate_get_current();
-
/*
* Reinitialise all migration state, except
* parameters/capabilities that the user set, and
@@ -1270,7 +1289,6 @@ MigrationState *migrate_init(void)
s->vm_was_running = false;
s->iteration_initial_bytes = 0;
s->threshold_size = 0;
- return s;
}
static GSList *migration_blockers;
@@ -1378,7 +1396,7 @@ void qmp_migrate(const char *uri, bool has_blk, bool blk,
migrate_set_block_incremental(s, true);
}
- s = migrate_init();
+ migrate_init(s);
if (strstart(uri, "tcp:", &p)) {
tcp_start_outgoing_migration(s, p, &local_err);
@@ -1709,6 +1727,11 @@ static void *source_return_path_thread(void *opaque)
header_type = qemu_get_be16(rp);
header_len = qemu_get_be16(rp);
+ if (qemu_file_get_error(rp)) {
+ mark_source_rp_bad(ms);
+ goto out;
+ }
+
if (header_type >= MIG_RP_MSG_MAX ||
header_type == MIG_RP_MSG_INVALID) {
error_report("RP: Received invalid message 0x%04x length 0x%04x",
diff --git a/migration/migration.h b/migration/migration.h
index 848f638a20..82cf926b17 100644
--- a/migration/migration.h
+++ b/migration/migration.h
@@ -35,6 +35,8 @@ struct MigrationIncomingState {
bool have_fault_thread;
QemuThread fault_thread;
QemuSemaphore fault_thread_sem;
+ /* Set this when we want the fault thread to quit */
+ bool fault_thread_quit;
bool have_listen_thread;
QemuThread listen_thread;
@@ -42,8 +44,8 @@ struct MigrationIncomingState {
/* For the kernel to send us notifications */
int userfault_fd;
- /* To tell the fault_thread to quit */
- int userfault_quit_fd;
+ /* To notify the fault_thread to wake, e.g., when need to quit */
+ int userfault_event_fd;
QEMUFile *to_src_file;
QemuMutex rp_mutex; /* We send replies from multiple threads */
void *postcopy_tmp_page;
@@ -191,7 +193,7 @@ void migrate_fd_error(MigrationState *s, const Error *error);
void migrate_fd_connect(MigrationState *s, Error *error_in);
-MigrationState *migrate_init(void);
+void migrate_init(MigrationState *s);
bool migration_is_blocked(Error **errp);
/* True if outgoing migration has entered postcopy phase */
bool migration_in_postcopy(void);
@@ -228,7 +230,7 @@ void migrate_send_rp_shut(MigrationIncomingState *mis,
uint32_t value);
void migrate_send_rp_pong(MigrationIncomingState *mis,
uint32_t value);
-void migrate_send_rp_req_pages(MigrationIncomingState *mis, const char* rbname,
+int migrate_send_rp_req_pages(MigrationIncomingState *mis, const char* rbname,
ram_addr_t start, size_t len);
#endif
diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c
index bec6c2c66b..032abfbf1a 100644
--- a/migration/postcopy-ram.c
+++ b/migration/postcopy-ram.c
@@ -377,27 +377,18 @@ int postcopy_ram_incoming_cleanup(MigrationIncomingState *mis)
trace_postcopy_ram_incoming_cleanup_entry();
if (mis->have_fault_thread) {
- uint64_t tmp64;
-
if (qemu_ram_foreach_block(cleanup_range, mis)) {
return -1;
}
- /*
- * Tell the fault_thread to exit, it's an eventfd that should
- * currently be at 0, we're going to increment it to 1
- */
- tmp64 = 1;
- if (write(mis->userfault_quit_fd, &tmp64, 8) == 8) {
- trace_postcopy_ram_incoming_cleanup_join();
- qemu_thread_join(&mis->fault_thread);
- } else {
- /* Not much we can do here, but may as well report it */
- error_report("%s: incrementing userfault_quit_fd: %s", __func__,
- strerror(errno));
- }
+ /* Let the fault thread quit */
+ atomic_set(&mis->fault_thread_quit, 1);
+ postcopy_fault_thread_notify(mis);
+ trace_postcopy_ram_incoming_cleanup_join();
+ qemu_thread_join(&mis->fault_thread);
+
trace_postcopy_ram_incoming_cleanup_closeuf();
close(mis->userfault_fd);
- close(mis->userfault_quit_fd);
+ close(mis->userfault_event_fd);
mis->have_fault_thread = false;
}
@@ -520,7 +511,7 @@ static void *postcopy_ram_fault_thread(void *opaque)
pfd[0].fd = mis->userfault_fd;
pfd[0].events = POLLIN;
pfd[0].revents = 0;
- pfd[1].fd = mis->userfault_quit_fd;
+ pfd[1].fd = mis->userfault_event_fd;
pfd[1].events = POLLIN; /* Waiting for eventfd to go positive */
pfd[1].revents = 0;
@@ -530,8 +521,18 @@ static void *postcopy_ram_fault_thread(void *opaque)
}
if (pfd[1].revents) {
- trace_postcopy_ram_fault_thread_quit();
- break;
+ uint64_t tmp64 = 0;
+
+ /* Consume the signal */
+ if (read(mis->userfault_event_fd, &tmp64, 8) != 8) {
+ /* Nothing obviously nicer than posting this error. */
+ error_report("%s: read() failed", __func__);
+ }
+
+ if (atomic_read(&mis->fault_thread_quit)) {
+ trace_postcopy_ram_fault_thread_quit();
+ break;
+ }
}
ret = read(mis->userfault_fd, &msg, sizeof(msg));
@@ -610,9 +611,9 @@ int postcopy_ram_enable_notify(MigrationIncomingState *mis)
}
/* Now an eventfd we use to tell the fault-thread to quit */
- mis->userfault_quit_fd = eventfd(0, EFD_CLOEXEC);
- if (mis->userfault_quit_fd == -1) {
- error_report("%s: Opening userfault_quit_fd: %s", __func__,
+ mis->userfault_event_fd = eventfd(0, EFD_CLOEXEC);
+ if (mis->userfault_event_fd == -1) {
+ error_report("%s: Opening userfault_event_fd: %s", __func__,
strerror(errno));
close(mis->userfault_fd);
return -1;
@@ -813,6 +814,21 @@ void *postcopy_get_tmp_page(MigrationIncomingState *mis)
/* ------------------------------------------------------------------------- */
+void postcopy_fault_thread_notify(MigrationIncomingState *mis)
+{
+ uint64_t tmp64 = 1;
+
+ /*
+ * Wakeup the fault_thread. It's an eventfd that should currently
+ * be at 0, we're going to increment it to 1
+ */
+ if (write(mis->userfault_event_fd, &tmp64, 8) != 8) {
+ /* Not much we can do here, but may as well report it */
+ error_report("%s: incrementing failed: %s", __func__,
+ strerror(errno));
+ }
+}
+
/**
* postcopy_discard_send_init: Called at the start of each RAMBlock before
* asking to discard individual ranges.
diff --git a/migration/postcopy-ram.h b/migration/postcopy-ram.h
index 77ea0fd264..14f6cadcbd 100644
--- a/migration/postcopy-ram.h
+++ b/migration/postcopy-ram.h
@@ -114,4 +114,6 @@ PostcopyState postcopy_state_get(void);
/* Set the state and return the old state */
PostcopyState postcopy_state_set(PostcopyState new_state);
+void postcopy_fault_thread_notify(MigrationIncomingState *mis);
+
#endif
diff --git a/migration/ram.c b/migration/ram.c
index 8333d8e35e..5e33e5cc79 100644
--- a/migration/ram.c
+++ b/migration/ram.c
@@ -1602,11 +1602,13 @@ static void xbzrle_load_cleanup(void)
static void ram_state_cleanup(RAMState **rsp)
{
- migration_page_queue_free(*rsp);
- qemu_mutex_destroy(&(*rsp)->bitmap_mutex);
- qemu_mutex_destroy(&(*rsp)->src_page_req_mutex);
- g_free(*rsp);
- *rsp = NULL;
+ if (*rsp) {
+ migration_page_queue_free(*rsp);
+ qemu_mutex_destroy(&(*rsp)->bitmap_mutex);
+ qemu_mutex_destroy(&(*rsp)->src_page_req_mutex);
+ g_free(*rsp);
+ *rsp = NULL;
+ }
}
static void xbzrle_cleanup(void)
@@ -2698,6 +2700,16 @@ static int ram_load_postcopy(QEMUFile *f)
uint8_t ch;
addr = qemu_get_be64(f);
+
+ /*
+ * If qemu file error, we should stop here, and then "addr"
+ * may be invalid
+ */
+ ret = qemu_file_get_error(f);
+ if (ret) {
+ break;
+ }
+
flags = addr & ~TARGET_PAGE_MASK;
addr &= TARGET_PAGE_MASK;
@@ -2778,9 +2790,15 @@ static int ram_load_postcopy(QEMUFile *f)
error_report("Unknown combination of migration flags: %#x"
" (postcopy mode)", flags);
ret = -EINVAL;
+ break;
+ }
+
+ /* Detect for any possible file errors */
+ if (!ret && qemu_file_get_error(f)) {
+ ret = qemu_file_get_error(f);
}
- if (place_needed) {
+ if (!ret && place_needed) {
/* This gets called at the last target page in the host page */
void *place_dest = host + TARGET_PAGE_SIZE - block->page_size;
@@ -2792,9 +2810,6 @@ static int ram_load_postcopy(QEMUFile *f)
place_source, block);
}
}
- if (!ret) {
- ret = qemu_file_get_error(f);
- }
}
return ret;
diff --git a/migration/savevm.c b/migration/savevm.c
index 3f611c02e8..8e6d872452 100644
--- a/migration/savevm.c
+++ b/migration/savevm.c
@@ -1257,8 +1257,11 @@ void qemu_savevm_state_cleanup(void)
static int qemu_savevm_state(QEMUFile *f, Error **errp)
{
int ret;
- MigrationState *ms = migrate_init();
+ MigrationState *ms = migrate_get_current();
MigrationStatus status;
+
+ migrate_init(ms);
+
ms->to_dst_file = f;
if (migration_is_blocked(errp)) {
@@ -1781,6 +1784,11 @@ static int loadvm_process_command(QEMUFile *f)
cmd = qemu_get_be16(f);
len = qemu_get_be16(f);
+ /* Check validity before continue processing of cmds */
+ if (qemu_file_get_error(f)) {
+ return qemu_file_get_error(f);
+ }
+
trace_loadvm_process_command(cmd, len);
if (cmd >= MIG_CMD_MAX || cmd == MIG_CMD_INVALID) {
error_report("MIG_CMD 0x%x unknown (len 0x%x)", cmd, len);
@@ -1846,6 +1854,7 @@ static int loadvm_process_command(QEMUFile *f)
*/
static bool check_section_footer(QEMUFile *f, SaveStateEntry *se)
{
+ int ret;
uint8_t read_mark;
uint32_t read_section_id;
@@ -1856,6 +1865,13 @@ static bool check_section_footer(QEMUFile *f, SaveStateEntry *se)
read_mark = qemu_get_byte(f);
+ ret = qemu_file_get_error(f);
+ if (ret) {
+ error_report("%s: Read section footer failed: %d",
+ __func__, ret);
+ return false;
+ }
+
if (read_mark != QEMU_VM_SECTION_FOOTER) {
error_report("Missing section footer for %s", se->idstr);
return false;
@@ -1891,6 +1907,13 @@ qemu_loadvm_section_start_full(QEMUFile *f, MigrationIncomingState *mis)
instance_id = qemu_get_be32(f);
version_id = qemu_get_be32(f);
+ ret = qemu_file_get_error(f);
+ if (ret) {
+ error_report("%s: Failed to read instance/version ID: %d",
+ __func__, ret);
+ return ret;
+ }
+
trace_qemu_loadvm_state_section_startfull(section_id, idstr,
instance_id, version_id);
/* Find savevm section */
@@ -1938,6 +1961,13 @@ qemu_loadvm_section_part_end(QEMUFile *f, MigrationIncomingState *mis)
section_id = qemu_get_be32(f);
+ ret = qemu_file_get_error(f);
+ if (ret) {
+ error_report("%s: Failed to read section ID: %d",
+ __func__, ret);
+ return ret;
+ }
+
trace_qemu_loadvm_state_section_partend(section_id);
QTAILQ_FOREACH(se, &savevm_state.handlers, entry) {
if (se->load_section_id == section_id) {
@@ -2005,8 +2035,14 @@ static int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis)
uint8_t section_type;
int ret = 0;
- while ((section_type = qemu_get_byte(f)) != QEMU_VM_EOF) {
- ret = 0;
+ while (true) {
+ section_type = qemu_get_byte(f);
+
+ if (qemu_file_get_error(f)) {
+ ret = qemu_file_get_error(f);
+ break;
+ }
+
trace_qemu_loadvm_state_section(section_type);
switch (section_type) {
case QEMU_VM_SECTION_START:
@@ -2030,6 +2066,9 @@ static int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis)
goto out;
}
break;
+ case QEMU_VM_EOF:
+ /* This is the end of migration */
+ goto out;
default:
error_report("Unknown savevm section type %d", section_type);
ret = -EINVAL;
@@ -2284,8 +2323,7 @@ void qmp_xen_save_devices_state(const char *filename, bool has_live, bool live,
f = qemu_fopen_channel_output(QIO_CHANNEL(ioc));
object_unref(OBJECT(ioc));
ret = qemu_save_device_state(f);
- qemu_fclose(f);
- if (ret < 0) {
+ if (ret < 0 || qemu_fclose(f) < 0) {
error_setg(errp, QERR_IO_ERROR);
} else {
/* libxl calls the QMP command "stop" before calling
diff --git a/qapi/migration.json b/qapi/migration.json
index 4cd3d13158..7f465a1902 100644
--- a/qapi/migration.json
+++ b/qapi/migration.json
@@ -327,8 +327,10 @@
# to speed up convergence of RAM migration. (since 1.6)
#
# @postcopy-ram: Start executing on the migration target before all of RAM has
-# been migrated, pulling the remaining pages along as needed. NOTE: If
-# the migration fails during postcopy the VM will fail. (since 2.6)
+# been migrated, pulling the remaining pages along as needed. The
+# capacity must have the same setting on both source and target
+# or migration will not even start. NOTE: If the migration fails during
+# postcopy the VM will fail. (since 2.6)
#
# @x-colo: If enabled, migration will never end, and the state of the VM on the
# primary side will be migrated continuously to the VM on secondary
@@ -742,8 +744,8 @@
# @migrate-start-postcopy:
#
# Followup to a migration command to switch the migration to postcopy mode.
-# The postcopy-ram capability must be set before the original migration
-# command.
+# The postcopy-ram capability must be set on both source and destination
+# before the original migration command.
#
# Since: 2.5
#
diff --git a/tests/migration-test.c b/tests/migration-test.c
index d0abad40f5..74f9361bdd 100644
--- a/tests/migration-test.c
+++ b/tests/migration-test.c
@@ -1,7 +1,7 @@
/*
* QTest testcase for migration
*
- * Copyright (c) 2016 Red Hat, Inc. and/or its affiliates
+ * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates
* based on the vhost-user-test.c that is:
* Copyright (c) 2014 Virtual Open Systems Sarl.
*
@@ -78,59 +78,15 @@ static bool ufd_version_check(void)
static const char *tmpfs;
/* A simple PC boot sector that modifies memory (1-100MB) quickly
- * outputing a 'B' every so often if it's still running.
+ * outputting a 'B' every so often if it's still running.
*/
-unsigned char bootsect[] = {
- 0xfa, 0x0f, 0x01, 0x16, 0x74, 0x7c, 0x66, 0xb8, 0x01, 0x00, 0x00, 0x00,
- 0x0f, 0x22, 0xc0, 0x66, 0xea, 0x20, 0x7c, 0x00, 0x00, 0x08, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe4, 0x92, 0x0c, 0x02,
- 0xe6, 0x92, 0xb8, 0x10, 0x00, 0x00, 0x00, 0x8e, 0xd8, 0x66, 0xb8, 0x41,
- 0x00, 0x66, 0xba, 0xf8, 0x03, 0xee, 0xb3, 0x00, 0xb8, 0x00, 0x00, 0x10,
- 0x00, 0xfe, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x40,
- 0x06, 0x7c, 0xf2, 0xfe, 0xc3, 0x75, 0xe9, 0x66, 0xb8, 0x42, 0x00, 0x66,
- 0xba, 0xf8, 0x03, 0xee, 0xeb, 0xde, 0x66, 0x90, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x9a, 0xcf, 0x00,
- 0xff, 0xff, 0x00, 0x00, 0x00, 0x92, 0xcf, 0x00, 0x27, 0x00, 0x5c, 0x7c,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xaa
-};
+#include "tests/migration/x86-a-b-bootblock.h"
static void init_bootfile_x86(const char *bootpath)
{
FILE *bootfile = fopen(bootpath, "wb");
- g_assert_cmpint(fwrite(bootsect, 512, 1, bootfile), ==, 1);
+ g_assert_cmpint(fwrite(x86_bootsect, 512, 1, bootfile), ==, 1);
fclose(bootfile);
}
@@ -478,28 +434,31 @@ static void test_migrate_start(QTestState **from, QTestState **to,
g_free(cmd_dst);
}
-static void test_migrate_end(QTestState *from, QTestState *to)
+static void test_migrate_end(QTestState *from, QTestState *to, bool test_dest)
{
unsigned char dest_byte_a, dest_byte_b, dest_byte_c, dest_byte_d;
qtest_quit(from);
- qtest_memread(to, start_address, &dest_byte_a, 1);
+ if (test_dest) {
+ qtest_memread(to, start_address, &dest_byte_a, 1);
- /* Destination still running, wait for a byte to change */
- do {
- qtest_memread(to, start_address, &dest_byte_b, 1);
- usleep(1000 * 10);
- } while (dest_byte_a == dest_byte_b);
+ /* Destination still running, wait for a byte to change */
+ do {
+ qtest_memread(to, start_address, &dest_byte_b, 1);
+ usleep(1000 * 10);
+ } while (dest_byte_a == dest_byte_b);
+
+ qtest_qmp_discard_response(to, "{ 'execute' : 'stop'}");
- qtest_qmp_discard_response(to, "{ 'execute' : 'stop'}");
- /* With it stopped, check nothing changes */
- qtest_memread(to, start_address, &dest_byte_c, 1);
- usleep(1000 * 200);
- qtest_memread(to, start_address, &dest_byte_d, 1);
- g_assert_cmpint(dest_byte_c, ==, dest_byte_d);
+ /* With it stopped, check nothing changes */
+ qtest_memread(to, start_address, &dest_byte_c, 1);
+ usleep(1000 * 200);
+ qtest_memread(to, start_address, &dest_byte_d, 1);
+ g_assert_cmpint(dest_byte_c, ==, dest_byte_d);
- check_guests_ram(to);
+ check_guests_ram(to);
+ }
qtest_quit(to);
@@ -591,7 +550,38 @@ static void test_migrate(void)
g_free(uri);
- test_migrate_end(from, to);
+ test_migrate_end(from, to, true);
+}
+
+static void test_baddest(void)
+{
+ QTestState *from, *to;
+ QDict *rsp, *rsp_return;
+ const char *status;
+ bool failed;
+
+ test_migrate_start(&from, &to, "tcp:0:0");
+ migrate(from, "tcp:0:0");
+ do {
+ rsp = wait_command(from, "{ 'execute': 'query-migrate' }");
+ rsp_return = qdict_get_qdict(rsp, "return");
+
+ status = qdict_get_str(rsp_return, "status");
+
+ g_assert(!strcmp(status, "setup") || !(strcmp(status, "failed")));
+ failed = !strcmp(status, "failed");
+ QDECREF(rsp);
+ } while (!failed);
+
+ /* Is the machine currently running? */
+ rsp = wait_command(from, "{ 'execute': 'query-status' }");
+ g_assert(qdict_haskey(rsp, "return"));
+ rsp_return = qdict_get_qdict(rsp, "return");
+ g_assert(qdict_haskey(rsp_return, "running"));
+ g_assert(qdict_get_bool(rsp_return, "running"));
+ QDECREF(rsp);
+
+ test_migrate_end(from, to, false);
}
int main(int argc, char **argv)
@@ -615,6 +605,7 @@ int main(int argc, char **argv)
qtest_add_func("/migration/postcopy/unix", test_migrate);
qtest_add_func("/migration/deprecated", test_deprecated);
+ qtest_add_func("/migration/bad_dest", test_baddest);
ret = g_test_run();
diff --git a/tests/migration/rebuild-x86-bootblock.sh b/tests/migration/rebuild-x86-bootblock.sh
new file mode 100755
index 0000000000..86cec5d284
--- /dev/null
+++ b/tests/migration/rebuild-x86-bootblock.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+# Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates
+# This work is licensed under the terms of the GNU GPL, version 2 or later.
+# See the COPYING file in the top-level directory.
+#
+# Author: dgilbert@redhat.com
+
+ASMFILE=$PWD/tests/migration/x86-a-b-bootblock.s
+HEADER=$PWD/tests/migration/x86-a-b-bootblock.h
+
+if [ ! -e "$ASMFILE" ]
+then
+ echo "Couldn't find $ASMFILE" >&2
+ exit 1
+fi
+
+ASM_WORK_DIR=$(mktemp -d --tmpdir X86BB.XXXXXX)
+cd "$ASM_WORK_DIR" &&
+as --32 -march=i486 "$ASMFILE" -o x86.o &&
+objcopy -O binary x86.o x86.boot &&
+dd if=x86.boot of=x86.bootsect bs=256 count=2 skip=124 &&
+xxd -i x86.bootsect |
+sed -e 's/.*int.*//' > x86.hex &&
+cat - x86.hex <<HERE > "$HEADER"
+/* This file is automatically generated from
+ * tests/migration/x86-a-b-bootblock.s, edit that and then run
+ * tests/migration/rebuild-x86-bootblock.sh to update,
+ * and then remember to send both in your patch submission.
+ */
+HERE
+
+rm x86.hex x86.bootsect x86.boot x86.o
+cd .. && rmdir "$ASM_WORK_DIR"
diff --git a/tests/migration/x86-a-b-bootblock.h b/tests/migration/x86-a-b-bootblock.h
new file mode 100644
index 0000000000..78a151fe2a
--- /dev/null
+++ b/tests/migration/x86-a-b-bootblock.h
@@ -0,0 +1,51 @@
+/* This file is automatically generated from
+ * tests/migration/x86-a-b-bootblock.s, edit that and then run
+ * tests/migration/rebuild-x86-bootblock.sh to update,
+ * and then remember to send both in your patch submission.
+ */
+unsigned char x86_bootsect[] = {
+ 0xfa, 0x0f, 0x01, 0x16, 0x74, 0x7c, 0x66, 0xb8, 0x01, 0x00, 0x00, 0x00,
+ 0x0f, 0x22, 0xc0, 0x66, 0xea, 0x20, 0x7c, 0x00, 0x00, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe4, 0x92, 0x0c, 0x02,
+ 0xe6, 0x92, 0xb8, 0x10, 0x00, 0x00, 0x00, 0x8e, 0xd8, 0x66, 0xb8, 0x41,
+ 0x00, 0x66, 0xba, 0xf8, 0x03, 0xee, 0xb3, 0x00, 0xb8, 0x00, 0x00, 0x10,
+ 0x00, 0xfe, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x40,
+ 0x06, 0x7c, 0xf2, 0xfe, 0xc3, 0x75, 0xe9, 0x66, 0xb8, 0x42, 0x00, 0x66,
+ 0xba, 0xf8, 0x03, 0xee, 0xeb, 0xde, 0x66, 0x90, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x9a, 0xcf, 0x00,
+ 0xff, 0xff, 0x00, 0x00, 0x00, 0x92, 0xcf, 0x00, 0x27, 0x00, 0x5c, 0x7c,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xaa
+};
+
diff --git a/tests/migration/x86-a-b-bootblock.s b/tests/migration/x86-a-b-bootblock.s
new file mode 100644
index 0000000000..b1642641a7
--- /dev/null
+++ b/tests/migration/x86-a-b-bootblock.s
@@ -0,0 +1,92 @@
+# x86 bootblock used in migration test
+# repeatedly increments the first byte of each page in a 100MB
+# range.
+# Outputs an initial 'A' on serial followed by repeated 'B's
+#
+# run tests/migration/rebuild-x86-bootblock.sh
+# to regenerate the hex, and remember to include both the .h and .s
+# in any patches.
+#
+# Copyright (c) 2016 Red Hat, Inc. and/or its affiliates
+# This work is licensed under the terms of the GNU GPL, version 2 or later.
+# See the COPYING file in the top-level directory.
+#
+# Author: dgilbert@redhat.com
+
+
+.code16
+.org 0x7c00
+ .file "fill.s"
+ .text
+ .globl start
+ .type start, @function
+start: # at 0x7c00 ?
+ cli
+ lgdt gdtdesc
+ mov $1,%eax
+ mov %eax,%cr0 # Protected mode enable
+ data32 ljmp $8,$0x7c20
+
+.org 0x7c20
+.code32
+ # A20 enable - not sure I actually need this
+ inb $0x92,%al
+ or $2,%al
+ outb %al, $0x92
+
+ # set up DS for the whole of RAM (needed on KVM)
+ mov $16,%eax
+ mov %eax,%ds
+
+ mov $65,%ax
+ mov $0x3f8,%dx
+ outb %al,%dx
+
+ # bl keeps a counter so we limit the output speed
+ mov $0, %bl
+mainloop:
+ # Start from 1MB
+ mov $(1024*1024),%eax
+innerloop:
+ incb (%eax)
+ add $4096,%eax
+ cmp $(100*1024*1024),%eax
+ jl innerloop
+
+ inc %bl
+ jnz mainloop
+
+ mov $66,%ax
+ mov $0x3f8,%dx
+ outb %al,%dx
+
+ jmp mainloop
+
+ # GDT magic from old (GPLv2) Grub startup.S
+ .p2align 2 /* force 4-byte alignment */
+gdt:
+ .word 0, 0
+ .byte 0, 0, 0, 0
+
+ /* -- code segment --
+ * base = 0x00000000, limit = 0xFFFFF (4 KiB Granularity), present
+ * type = 32bit code execute/read, DPL = 0
+ */
+ .word 0xFFFF, 0
+ .byte 0, 0x9A, 0xCF, 0
+
+ /* -- data segment --
+ * base = 0x00000000, limit 0xFFFFF (4 KiB Granularity), present
+ * type = 32 bit data read/write, DPL = 0
+ */
+ .word 0xFFFF, 0
+ .byte 0, 0x92, 0xCF, 0
+
+gdtdesc:
+ .word 0x27 /* limit */
+ .long gdt /* addr */
+
+/* I'm a bootable disk */
+.org 0x7dfe
+ .byte 0x55
+ .byte 0xAA