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.
 -----BEGIN PGP SIGNATURE-----
 
 iQIcBAABAgAGBQJahFawAAoJEAUWMx68W/3nwE4P/ih+c4uIG6rmwmQzfrKz7usM
 6yI9doyejn8uslIQAsU+me8LTGzGI8EeevAOONGO7atySIc2BzujVBxwZIRr70rj
 zJpv3dbU/kLPJC40sZixJ8lvaGQ0iGe9WAGxoOf523ezBzr0ir/Ha1W270ZxgGgw
 /1MFY45krye/wc+D/TdH3qjh3fkdcbbOMEcTaVJLaM2eKHfSnpQdrx7iE3Z9KZ/G
 /hgXiIzz8mZsFJTRtwjV1701fj0wKVDUAqgpsY8lmOxRMmFYuZmFKk1D+ppFXaov
 3dcrnP5gaX+42lQPKN5uVLH9pix3Q6AqcFRgSHRECugqFBlhckG2ZevatQIynCDB
 t9kV3fG5oAbfWUASayOhH2u4RUNGo9Hp1HPx7SPRSAhhpH1/p4vUfJKOn84e+jc2
 5r4Vyuit9BLwvHtt1awnVJZYVIJ/S/178/bSw//zHDQqKA1YakSmGkAWy5IT6LgF
 0eGGHmzxpFE4jMI7biM8OiSd0/SjU/ijV5sSAUyJM36d+ZuzsYf7myI17JQUoPmr
 /K1ld8y48MAY4K4M0wVBr8pcDY4eUw6XwWTX4FMPwYwdzdGbPNet+aR+VwCieoJU
 85e2gP8celL1xQz1bb9q43vV2gqkoAiw8tyUkuXvpPwRSa5foMOrteTr5V7MP660
 10Qg5Vp69SvnU7ggQCX+
 =drNV
 -----END PGP SIGNATURE-----

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>
This commit is contained in:
Peter Maydell 2018-02-16 12:51:40 +00:00
commit 648ba91596
13 changed files with 388 additions and 122 deletions

View file

@ -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``

View file

@ -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,
},

View file

@ -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",

View file

@ -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

View file

@ -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.

View file

@ -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

View file

@ -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;
}
if (place_needed) {
/* Detect for any possible file errors */
if (!ret && qemu_file_get_error(f)) {
ret = qemu_file_get_error(f);
}
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;

View file

@ -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

View file

@ -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
#

View file

@ -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'}");
/* 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);
qtest_qmp_discard_response(to, "{ 'execute' : 'stop'}");
check_guests_ram(to);
/* 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);
}
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();

View file

@ -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"

View file

@ -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
};

View file

@ -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