diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2018-02-16 12:51:40 +0000 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2018-02-16 12:51:40 +0000 |
commit | 648ba915961664ecb4cff0ee847a929c65e4b4b4 (patch) | |
tree | 4e688224f2bf948000d03c34cff398c44e65bff2 | |
parent | 0402ca3c70356e09e694fece39256790ff7755f2 (diff) | |
parent | 3e0c8050ebba3f55dc2d92b3790a3cfb80786d07 (diff) |
Merge remote-tracking branch 'remotes/dgilbert/tags/pull-migration-20180214a' into staging
Migration pull 20180214
Note that the 'Add test for migration to bad destination' displays
a 'Connection refused' during running, but still gives the correct exit
code and OK (It's checking that the source doesn't fail when
it can't connect, so that's the right error).
If it's particularly disliked that patch can be skipped individually.
# gpg: Signature made Wed 14 Feb 2018 15:33:04 GMT
# gpg: using RSA key 0516331EBC5BFDE7
# gpg: Good signature from "Dr. David Alan Gilbert (RH2) <dgilbert@redhat.com>"
# Primary key fingerprint: 45F5 C71B 4A0C B7FB 977A 9FA9 0516 331E BC5B FDE7
* remotes/dgilbert/tags/pull-migration-20180214a:
migration: pass MigrationState to migrate_init()
migration: allow send_rq to fail
migration: provide postcopy_fault_thread_notify()
migration: reuse mis->userfault_quit_fd
migration: better error handling with QEMUFile
tests/migration: Add test for migration to bad destination
migration: Fix early failure cleanup
tests/migration: Add source to PC boot block
migration: improve documentation of postcopy-ram
migration/xen: Check return value of qemu_fclose
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
-rw-r--r-- | docs/devel/migration.rst | 4 | ||||
-rw-r--r-- | hmp-commands.hx | 3 | ||||
-rw-r--r-- | migration/migration.c | 47 | ||||
-rw-r--r-- | migration/migration.h | 10 | ||||
-rw-r--r-- | migration/postcopy-ram.c | 60 | ||||
-rw-r--r-- | migration/postcopy-ram.h | 2 | ||||
-rw-r--r-- | migration/ram.c | 33 | ||||
-rw-r--r-- | migration/savevm.c | 48 | ||||
-rw-r--r-- | qapi/migration.json | 10 | ||||
-rw-r--r-- | tests/migration-test.c | 117 | ||||
-rwxr-xr-x | tests/migration/rebuild-x86-bootblock.sh | 33 | ||||
-rw-r--r-- | tests/migration/x86-a-b-bootblock.h | 51 | ||||
-rw-r--r-- | tests/migration/x86-a-b-bootblock.s | 92 |
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 |