diff --git a/migration/channel.c b/migration/channel.c index 70ec7ea3b7..c5eaf0fa0e 100644 --- a/migration/channel.c +++ b/migration/channel.c @@ -55,29 +55,29 @@ void migration_channel_process_incoming(QIOChannel *ioc) * @s: Current migration state * @ioc: Channel to which we are connecting * @hostname: Where we want to connect + * @error: Error indicating failure to connect, free'd here */ void migration_channel_connect(MigrationState *s, QIOChannel *ioc, - const char *hostname) + const char *hostname, + Error *error) { trace_migration_set_outgoing_channel( - ioc, object_get_typename(OBJECT(ioc)), hostname); + ioc, object_get_typename(OBJECT(ioc)), hostname, error); + + if (!error) { + if (s->parameters.tls_creds && + *s->parameters.tls_creds && + !object_dynamic_cast(OBJECT(ioc), + TYPE_QIO_CHANNEL_TLS)) { + migration_tls_channel_connect(s, ioc, hostname, &error); + } else { + QEMUFile *f = qemu_fopen_channel_output(ioc); + + s->to_dst_file = f; - if (s->parameters.tls_creds && - *s->parameters.tls_creds && - !object_dynamic_cast(OBJECT(ioc), - TYPE_QIO_CHANNEL_TLS)) { - Error *local_err = NULL; - migration_tls_channel_connect(s, ioc, hostname, &local_err); - if (local_err) { - migrate_fd_error(s, local_err); - error_free(local_err); } - } else { - QEMUFile *f = qemu_fopen_channel_output(ioc); - - s->to_dst_file = f; - - migrate_fd_connect(s); } + migrate_fd_connect(s, error); + error_free(error); } diff --git a/migration/channel.h b/migration/channel.h index e4b40579a1..67a461c28a 100644 --- a/migration/channel.h +++ b/migration/channel.h @@ -22,5 +22,6 @@ void migration_channel_process_incoming(QIOChannel *ioc); void migration_channel_connect(MigrationState *s, QIOChannel *ioc, - const char *hostname); + const char *hostname, + Error *error_in); #endif diff --git a/migration/exec.c b/migration/exec.c index f3be1baf2e..c9537974ad 100644 --- a/migration/exec.c +++ b/migration/exec.c @@ -39,7 +39,7 @@ void exec_start_outgoing_migration(MigrationState *s, const char *command, Error } qio_channel_set_name(ioc, "migration-exec-outgoing"); - migration_channel_connect(s, ioc, NULL); + migration_channel_connect(s, ioc, NULL, NULL); object_unref(OBJECT(ioc)); } diff --git a/migration/fd.c b/migration/fd.c index 30de4b9847..6284a97cba 100644 --- a/migration/fd.c +++ b/migration/fd.c @@ -39,7 +39,7 @@ void fd_start_outgoing_migration(MigrationState *s, const char *fdname, Error ** } qio_channel_set_name(QIO_CHANNEL(ioc), "migration-fd-outgoing"); - migration_channel_connect(s, ioc, NULL); + migration_channel_connect(s, ioc, NULL, NULL); object_unref(OBJECT(ioc)); } diff --git a/migration/migration.c b/migration/migration.c index c99a4e62d7..0fdb2e410d 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -2122,7 +2122,8 @@ fail_invalidate: /* If not doing postcopy, vm_start() will be called: let's regain * control on images. */ - if (s->state == MIGRATION_STATUS_ACTIVE) { + if (s->state == MIGRATION_STATUS_ACTIVE || + s->state == MIGRATION_STATUS_DEVICE) { Error *local_err = NULL; qemu_mutex_lock_iothread(); @@ -2169,7 +2170,6 @@ static void migration_update_counters(MigrationState *s, int64_t current_time) { uint64_t transferred, time_spent; - int64_t threshold_size; double bandwidth; if (current_time < s->iteration_start_time + BUFFER_DELAY) { @@ -2179,7 +2179,7 @@ static void migration_update_counters(MigrationState *s, transferred = qemu_ftell(s->to_dst_file) - s->iteration_initial_bytes; time_spent = current_time - s->iteration_start_time; bandwidth = (double)transferred / time_spent; - threshold_size = bandwidth * s->parameters.downtime_limit; + s->threshold_size = bandwidth * s->parameters.downtime_limit; s->mbps = (((double) transferred * 8.0) / ((double) time_spent / 1000.0)) / 1000.0 / 1000.0; @@ -2199,7 +2199,7 @@ static void migration_update_counters(MigrationState *s, s->iteration_initial_bytes = qemu_ftell(s->to_dst_file); trace_migrate_transferred(transferred, time_spent, - bandwidth, threshold_size); + bandwidth, s->threshold_size); } /* Migration thread iteration status */ @@ -2378,10 +2378,15 @@ static void *migration_thread(void *opaque) return NULL; } -void migrate_fd_connect(MigrationState *s) +void migrate_fd_connect(MigrationState *s, Error *error_in) { s->expected_downtime = s->parameters.downtime_limit; s->cleanup_bh = qemu_bh_new(migrate_fd_cleanup, s); + if (error_in) { + migrate_fd_error(s, error_in); + migrate_fd_cleanup(s); + return; + } qemu_file_set_blocking(s->to_dst_file, true); qemu_file_set_rate_limit(s->to_dst_file, diff --git a/migration/migration.h b/migration/migration.h index 786d971ce2..d3b214e5ba 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -190,7 +190,7 @@ uint64_t migrate_max_downtime(void); void migrate_set_error(MigrationState *s, const Error *error); void migrate_fd_error(MigrationState *s, const Error *error); -void migrate_fd_connect(MigrationState *s); +void migrate_fd_connect(MigrationState *s, Error *error_in); MigrationState *migrate_init(void); bool migration_is_blocked(Error **errp); diff --git a/migration/ram.c b/migration/ram.c index cb1950f3eb..5a109efeda 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -907,11 +907,10 @@ static void migration_bitmap_sync(RAMState *rs) * @rs: current RAM state * @block: block that contains the page we want to send * @offset: offset inside the block for the page - * @p: pointer to the page */ -static int save_zero_page(RAMState *rs, RAMBlock *block, ram_addr_t offset, - uint8_t *p) +static int save_zero_page(RAMState *rs, RAMBlock *block, ram_addr_t offset) { + uint8_t *p = block->host + offset; int pages = -1; if (is_zero_range(p, TARGET_PAGE_SIZE)) { @@ -984,7 +983,7 @@ static int ram_save_page(RAMState *rs, PageSearchStatus *pss, bool last_stage) } } } else { - pages = save_zero_page(rs, block, offset, p); + pages = save_zero_page(rs, block, offset); if (pages > 0) { /* Must let xbzrle know, otherwise a previous (now 0'd) cached * page would be stale @@ -1160,7 +1159,7 @@ static int ram_save_compressed_page(RAMState *rs, PageSearchStatus *pss, */ if (block != rs->last_sent_block) { flush_compressed_data(rs); - pages = save_zero_page(rs, block, offset, p); + pages = save_zero_page(rs, block, offset); if (pages == -1) { /* Make sure the first page is sent out before other pages */ bytes_xmit = save_page_header(rs, rs->f, block, offset | @@ -1180,7 +1179,7 @@ static int ram_save_compressed_page(RAMState *rs, PageSearchStatus *pss, ram_release_pages(block->idstr, offset, pages); } } else { - pages = save_zero_page(rs, block, offset, p); + pages = save_zero_page(rs, block, offset); if (pages == -1) { pages = compress_page_with_multi_thread(rs, block, offset); } else { diff --git a/migration/rdma.c b/migration/rdma.c index 9d5a424011..da474fc19f 100644 --- a/migration/rdma.c +++ b/migration/rdma.c @@ -3758,7 +3758,7 @@ void rdma_start_outgoing_migration(void *opaque, trace_rdma_start_outgoing_migration_after_rdma_connect(); s->to_dst_file = qemu_fopen_rdma(rdma, "wb"); - migrate_fd_connect(s); + migrate_fd_connect(s, NULL); return; err: g_free(rdma); diff --git a/migration/savevm.c b/migration/savevm.c index b7908f62be..f202c3de3a 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -81,7 +81,7 @@ enum qemu_vm_cmd { MIG_CMD_MAX }; -#define MAX_VM_CMD_PACKAGED_SIZE (1ul << 24) +#define MAX_VM_CMD_PACKAGED_SIZE UINT32_MAX static struct mig_cmd_args { ssize_t len; /* -1 = variable */ const char *name; @@ -1376,7 +1376,8 @@ static int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis); * *might* happen - it might be skipped if precopy transferred everything * quickly. */ -static int loadvm_postcopy_handle_advise(MigrationIncomingState *mis) +static int loadvm_postcopy_handle_advise(MigrationIncomingState *mis, + uint16_t len) { PostcopyState ps = postcopy_state_set(POSTCOPY_INCOMING_ADVISE); uint64_t remote_pagesize_summary, local_pagesize_summary, remote_tps; @@ -1387,8 +1388,22 @@ static int loadvm_postcopy_handle_advise(MigrationIncomingState *mis) return -1; } - if (!migrate_postcopy_ram()) { + switch (len) { + case 0: + if (migrate_postcopy_ram()) { + error_report("RAM postcopy is enabled but have 0 byte advise"); + return -EINVAL; + } return 0; + case 8 + 8: + if (!migrate_postcopy_ram()) { + error_report("RAM postcopy is disabled but have 16 byte advise"); + return -EINVAL; + } + break; + default: + error_report("CMD_POSTCOPY_ADVISE invalid length (%d)", len); + return -EINVAL; } if (!postcopy_ram_supported_by_host(mis)) { @@ -1807,7 +1822,7 @@ static int loadvm_process_command(QEMUFile *f) return loadvm_handle_cmd_packaged(mis); case MIG_CMD_POSTCOPY_ADVISE: - return loadvm_postcopy_handle_advise(mis); + return loadvm_postcopy_handle_advise(mis, len); case MIG_CMD_POSTCOPY_LISTEN: return loadvm_postcopy_handle_listen(mis); @@ -2266,6 +2281,7 @@ void qmp_xen_save_devices_state(const char *filename, bool has_live, bool live, } qio_channel_set_name(QIO_CHANNEL(ioc), "migration-xen-save-state"); f = qemu_fopen_channel_output(QIO_CHANNEL(ioc)); + object_unref(OBJECT(ioc)); ret = qemu_save_device_state(f); qemu_fclose(f); if (ret < 0) { @@ -2313,6 +2329,7 @@ void qmp_xen_load_devices_state(const char *filename, Error **errp) } qio_channel_set_name(QIO_CHANNEL(ioc), "migration-xen-load-state"); f = qemu_fopen_channel_input(QIO_CHANNEL(ioc)); + object_unref(OBJECT(ioc)); ret = qemu_loadvm_state(f); qemu_fclose(f); diff --git a/migration/socket.c b/migration/socket.c index 3a8232dd2d..e090097077 100644 --- a/migration/socket.c +++ b/migration/socket.c @@ -79,12 +79,10 @@ static void socket_outgoing_migration(QIOTask *task, if (qio_task_propagate_error(task, &err)) { trace_migration_socket_outgoing_error(error_get_pretty(err)); - migrate_fd_error(data->s, err); - error_free(err); } else { trace_migration_socket_outgoing_connected(data->hostname); - migration_channel_connect(data->s, sioc, data->hostname); } + migration_channel_connect(data->s, sioc, data->hostname, err); object_unref(OBJECT(sioc)); } diff --git a/migration/tls.c b/migration/tls.c index 026a008667..a29b35b33c 100644 --- a/migration/tls.c +++ b/migration/tls.c @@ -118,11 +118,10 @@ static void migration_tls_outgoing_handshake(QIOTask *task, if (qio_task_propagate_error(task, &err)) { trace_migration_tls_outgoing_handshake_error(error_get_pretty(err)); - migrate_fd_error(s, err); } else { trace_migration_tls_outgoing_handshake_complete(); - migration_channel_connect(s, ioc, NULL); } + migration_channel_connect(s, ioc, NULL, err); object_unref(OBJECT(ioc)); } diff --git a/migration/trace-events b/migration/trace-events index 6f29fcc686..93961dea16 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -114,7 +114,7 @@ migrate_transferred(uint64_t tranferred, uint64_t time_spent, double bandwidth, process_incoming_migration_co_end(int ret, int ps) "ret=%d postcopy-state=%d" process_incoming_migration_co_postcopy_end_main(void) "" migration_set_incoming_channel(void *ioc, const char *ioctype) "ioc=%p ioctype=%s" -migration_set_outgoing_channel(void *ioc, const char *ioctype, const char *hostname) "ioc=%p ioctype=%s hostname=%s" +migration_set_outgoing_channel(void *ioc, const char *ioctype, const char *hostname, void *err) "ioc=%p ioctype=%s hostname=%s err=%p" # migration/rdma.c qemu_rdma_accept_incoming_migration(void) "" diff --git a/tests/migration-test.c b/tests/migration-test.c index 799e24ebc6..9efad95749 100644 --- a/tests/migration-test.c +++ b/tests/migration-test.c @@ -268,10 +268,9 @@ static uint64_t get_migration_pass(QTestState *who) static void wait_for_migration_complete(QTestState *who) { - QDict *rsp, *rsp_return; - bool completed; - - do { + while (true) { + QDict *rsp, *rsp_return; + bool completed; const char *status; rsp = wait_command(who, "{ 'execute': 'query-migrate' }"); @@ -280,8 +279,11 @@ static void wait_for_migration_complete(QTestState *who) completed = strcmp(status, "completed") == 0; g_assert_cmpstr(status, !=, "failed"); QDECREF(rsp); - usleep(1000 * 100); - } while (!completed); + if (completed) { + return; + } + usleep(1000); + } } static void wait_for_migration_pass(QTestState *who) @@ -290,16 +292,13 @@ static void wait_for_migration_pass(QTestState *who) uint64_t pass; /* Wait for the 1st sync */ - do { + while (!got_stop && !initial_pass) { + usleep(1000); initial_pass = get_migration_pass(who); - if (got_stop || initial_pass) { - break; - } - usleep(1000 * 100); - } while (true); + } do { - usleep(1000 * 100); + usleep(1000); pass = get_migration_pass(who); } while (pass == initial_pass && !got_stop); } @@ -369,37 +368,20 @@ static void migrate_check_parameter(QTestState *who, const char *parameter, QDECREF(rsp); } -static void migrate_set_downtime(QTestState *who, const double value) -{ - QDict *rsp; - gchar *cmd; - char *expected; - int64_t result_int; - - cmd = g_strdup_printf("{ 'execute': 'migrate_set_downtime'," - "'arguments': { 'value': %g } }", value); - rsp = qtest_qmp(who, cmd); - g_free(cmd); - g_assert(qdict_haskey(rsp, "return")); - QDECREF(rsp); - result_int = value * 1000L; - expected = g_strdup_printf("%" PRId64, result_int); - migrate_check_parameter(who, "downtime-limit", expected); - g_free(expected); -} - -static void migrate_set_speed(QTestState *who, const char *value) +static void migrate_set_parameter(QTestState *who, const char *parameter, + const char *value) { QDict *rsp; gchar *cmd; - cmd = g_strdup_printf("{ 'execute': 'migrate_set_speed'," - "'arguments': { 'value': %s } }", value); + cmd = g_strdup_printf("{ 'execute': 'migrate-set-parameters'," + "'arguments': { '%s': %s } }", + parameter, value); rsp = qtest_qmp(who, cmd); g_free(cmd); g_assert(qdict_haskey(rsp, "return")); QDECREF(rsp); - migrate_check_parameter(who, "max-bandwidth", value); + migrate_check_parameter(who, parameter, value); } static void migrate_set_capability(QTestState *who, const char *capability, @@ -433,41 +415,52 @@ static void migrate(QTestState *who, const char *uri) QDECREF(rsp); } +static void migrate_start_postcopy(QTestState *who) +{ + QDict *rsp; + + rsp = wait_command(who, "{ 'execute': 'migrate-start-postcopy' }"); + g_assert(qdict_haskey(rsp, "return")); + QDECREF(rsp); +} + static void test_migrate_start(QTestState **from, QTestState **to, const char *uri) { gchar *cmd_src, *cmd_dst; char *bootpath = g_strdup_printf("%s/bootsect", tmpfs); const char *arch = qtest_get_arch(); + const char *accel = "kvm:tcg"; got_stop = false; if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { init_bootfile_x86(bootpath); - cmd_src = g_strdup_printf("-machine accel=kvm:tcg -m 150M" - " -name pcsource,debug-threads=on" + cmd_src = g_strdup_printf("-machine accel=%s -m 150M" + " -name source,debug-threads=on" " -serial file:%s/src_serial" " -drive file=%s,format=raw", - tmpfs, bootpath); - cmd_dst = g_strdup_printf("-machine accel=kvm:tcg -m 150M" - " -name pcdest,debug-threads=on" + accel, tmpfs, bootpath); + cmd_dst = g_strdup_printf("-machine accel=%s -m 150M" + " -name target,debug-threads=on" " -serial file:%s/dest_serial" " -drive file=%s,format=raw" " -incoming %s", - tmpfs, bootpath, uri); + accel, tmpfs, bootpath, uri); } else if (strcmp(arch, "ppc64") == 0) { - const char *accel; /* On ppc64, the test only works with kvm-hv, but not with kvm-pr */ - accel = access("/sys/module/kvm_hv", F_OK) ? "tcg" : "kvm:tcg"; + if (access("/sys/module/kvm_hv", F_OK)) { + accel = "tcg"; + } init_bootfile_ppc(bootpath); cmd_src = g_strdup_printf("-machine accel=%s -m 256M" - " -name pcsource,debug-threads=on" + " -name source,debug-threads=on" " -serial file:%s/src_serial" " -drive file=%s,if=pflash,format=raw", accel, tmpfs, bootpath); cmd_dst = g_strdup_printf("-machine accel=%s -m 256M" - " -name pcdest,debug-threads=on" + " -name target,debug-threads=on" " -serial file:%s/dest_serial" " -incoming %s", accel, tmpfs, uri); @@ -495,13 +488,13 @@ static void test_migrate_end(QTestState *from, QTestState *to) /* Destination still running, wait for a byte to change */ do { qtest_memread(to, start_address, &dest_byte_b, 1); - usleep(10 * 1000); + 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); - sleep(1); + usleep(1000 * 200); qtest_memread(to, start_address, &dest_byte_d, 1); g_assert_cmpint(dest_byte_c, ==, dest_byte_d); @@ -515,11 +508,55 @@ static void test_migrate_end(QTestState *from, QTestState *to) cleanup("dest_serial"); } +static void deprecated_set_downtime(QTestState *who, const double value) +{ + QDict *rsp; + gchar *cmd; + char *expected; + int64_t result_int; + + cmd = g_strdup_printf("{ 'execute': 'migrate_set_downtime'," + "'arguments': { 'value': %g } }", value); + rsp = qtest_qmp(who, cmd); + g_free(cmd); + g_assert(qdict_haskey(rsp, "return")); + QDECREF(rsp); + result_int = value * 1000L; + expected = g_strdup_printf("%" PRId64, result_int); + migrate_check_parameter(who, "downtime-limit", expected); + g_free(expected); +} + +static void deprecated_set_speed(QTestState *who, const char *value) +{ + QDict *rsp; + gchar *cmd; + + cmd = g_strdup_printf("{ 'execute': 'migrate_set_speed'," + "'arguments': { 'value': %s } }", value); + rsp = qtest_qmp(who, cmd); + g_free(cmd); + g_assert(qdict_haskey(rsp, "return")); + QDECREF(rsp); + migrate_check_parameter(who, "max-bandwidth", value); +} + +static void test_deprecated(void) +{ + QTestState *from; + + from = qtest_start(""); + + deprecated_set_downtime(from, 0.12345); + deprecated_set_speed(from, "12345"); + + qtest_quit(from); +} + static void test_migrate(void) { char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); QTestState *from, *to; - QDict *rsp; test_migrate_start(&from, &to, uri); @@ -530,8 +567,8 @@ static void test_migrate(void) * quickly, but that it doesn't complete precopy even on a slow * machine, so also set the downtime. */ - migrate_set_speed(from, "100000000"); - migrate_set_downtime(from, 0.001); + migrate_set_parameter(from, "max-bandwidth", "100000000"); + migrate_set_parameter(from, "downtime-limit", "1"); /* Wait for the first serial output from the source */ wait_for_serial("src_serial"); @@ -540,9 +577,7 @@ static void test_migrate(void) wait_for_migration_pass(from); - rsp = wait_command(from, "{ 'execute': 'migrate-start-postcopy' }"); - g_assert(qdict_haskey(rsp, "return")); - QDECREF(rsp); + migrate_start_postcopy(from); if (!got_stop) { qtest_qmp_eventwait(from, "STOP"); @@ -578,6 +613,7 @@ int main(int argc, char **argv) module_call_init(MODULE_INIT_QOM); qtest_add_func("/migration/postcopy/unix", test_migrate); + qtest_add_func("/migration/deprecated", test_deprecated); ret = g_test_run();