diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2016-10-17 10:31:10 +0100 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2016-10-17 10:31:10 +0100 |
commit | 4378caf59edf6df796b9ad3174e5703fd25a781c (patch) | |
tree | ea5627e0532e0cbfffd103be09cff3796f5122d0 | |
parent | 6aa5a3679449cdf0b6fe5a6829b22e642ded57fd (diff) | |
parent | 7c2b0f65cc2b6b14ad797549b8bde13aa97f6ba2 (diff) |
Merge remote-tracking branch 'remotes/juanquintela/tags/migration/20161014' into staging
migration/next for 20161014
# gpg: Signature made Fri 14 Oct 2016 16:24:13 BST
# gpg: using RSA key 0xF487EF185872D723
# gpg: Good signature from "Juan Quintela <quintela@redhat.com>"
# gpg: aka "Juan Quintela <quintela@trasno.org>"
# Primary key fingerprint: 1899 FF8E DEBF 58CC EE03 4B82 F487 EF18 5872 D723
* remotes/juanquintela/tags/migration/20161014:
docs/xbzrle: correction
migrate: move max-bandwidth and downtime-limit to migrate_set_parameter
migration: Fix seg with missing port
migration/postcopy: Explicitly disallow huge pages
RAMBlocks: Store page size
Postcopy vs xbzrle: Don't send xbzrle pages once in postcopy [for 2.8]
migrate: Fix bounds check for migration parameters in migration.c
migrate: Use boxed qapi for migrate-set-parameters
migrate: Share common MigrationParameters struct
migrate: Fix cpu-throttle-increment regression in HMP
migration/rdma: Don't flag an error when we've been told about one
migration: Make failed migration load set file error
migration/rdma: Pass qemu_file errors across link
migration: Report values for comparisons
migration: report an error giving the failed field
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
-rw-r--r-- | docs/qmp-commands.txt | 13 | ||||
-rw-r--r-- | docs/xbzrle.txt | 4 | ||||
-rw-r--r-- | exec.c | 19 | ||||
-rw-r--r-- | hmp.c | 77 | ||||
-rw-r--r-- | include/exec/cpu-common.h | 1 | ||||
-rw-r--r-- | include/exec/ram_addr.h | 1 | ||||
-rw-r--r-- | include/migration/migration.h | 1 | ||||
-rw-r--r-- | migration/migration.c | 157 | ||||
-rw-r--r-- | migration/postcopy-ram.c | 24 | ||||
-rw-r--r-- | migration/ram.c | 4 | ||||
-rw-r--r-- | migration/rdma.c | 9 | ||||
-rw-r--r-- | migration/savevm.c | 19 | ||||
-rw-r--r-- | migration/socket.c | 16 | ||||
-rw-r--r-- | migration/vmstate.c | 10 | ||||
-rw-r--r-- | qapi-schema.json | 108 |
15 files changed, 281 insertions, 182 deletions
diff --git a/docs/qmp-commands.txt b/docs/qmp-commands.txt index 7f652e01e5..3220fb1075 100644 --- a/docs/qmp-commands.txt +++ b/docs/qmp-commands.txt @@ -2910,7 +2910,9 @@ Set migration parameters throttled for auto-converge (json-int) - "cpu-throttle-increment": set throttle increasing percentage for auto-converge (json-int) - +- "max-bandwidth": set maximum speed for migrations (in bytes/sec) (json-int) +- "downtime-limit": set maximum tolerated downtime (in milliseconds) for + migrations (json-int) Arguments: Example: @@ -2931,7 +2933,10 @@ Query current migration parameters throttled (json-int) - "cpu-throttle-increment" : throttle increasing percentage for auto-converge (json-int) - + - "max-bandwidth" : maximium migration speed in bytes per second + (json-int) + - "downtime-limit" : maximum tolerated downtime of migration in + milliseconds (json-int) Arguments: Example: @@ -2943,7 +2948,9 @@ Example: "cpu-throttle-increment": 10, "compress-threads": 8, "compress-level": 1, - "cpu-throttle-initial": 20 + "cpu-throttle-initial": 20, + "max-bandwidth": 33554432, + "downtime-limit": 300 } } diff --git a/docs/xbzrle.txt b/docs/xbzrle.txt index 52c8511a4c..c0a7dfd44c 100644 --- a/docs/xbzrle.txt +++ b/docs/xbzrle.txt @@ -42,7 +42,7 @@ nzrun = length byte... length = uleb128 encoded integer On the sender side XBZRLE is used as a compact delta encoding of page updates, -retrieving the old page content from the cache (default size of 512 MB). The +retrieving the old page content from the cache (default size of 64MB). The receiving side uses the existing page's content and XBZRLE to decode the new page's content. @@ -73,7 +73,7 @@ e9 07 0f 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 03 01 67 01 01 69 Cache update strategy ===================== -Keeping the hot pages in the cache is effective for decreased cache +Keeping the hot pages in the cache is effective for decreasing cache misses. XBZRLE uses a counter as the age of each page. The counter will increase after each ram dirty bitmap sync. When a cache conflict is detected, XBZRLE will only evict pages in the cache that are older than @@ -1199,7 +1199,6 @@ static void *file_ram_alloc(RAMBlock *block, char *c; void *area = MAP_FAILED; int fd = -1; - int64_t page_size; if (kvm_enabled() && !kvm_has_sync_mmu()) { error_setg(errp, @@ -1254,17 +1253,17 @@ static void *file_ram_alloc(RAMBlock *block, */ } - page_size = qemu_fd_getpagesize(fd); - block->mr->align = MAX(page_size, QEMU_VMALLOC_ALIGN); + block->page_size = qemu_fd_getpagesize(fd); + block->mr->align = MAX(block->page_size, QEMU_VMALLOC_ALIGN); - if (memory < page_size) { + if (memory < block->page_size) { error_setg(errp, "memory size 0x" RAM_ADDR_FMT " must be equal to " - "or larger than page size 0x%" PRIx64, - memory, page_size); + "or larger than page size 0x%zx", + memory, block->page_size); goto error; } - memory = ROUND_UP(memory, page_size); + memory = ROUND_UP(memory, block->page_size); /* * ftruncate is not supported by hugetlbfs in older @@ -1419,6 +1418,11 @@ void qemu_ram_unset_idstr(RAMBlock *block) } } +size_t qemu_ram_pagesize(RAMBlock *rb) +{ + return rb->page_size; +} + static int memory_try_enable_merging(void *addr, size_t len) { if (!machine_mem_merge(current_machine)) { @@ -1658,6 +1662,7 @@ RAMBlock *qemu_ram_alloc_internal(ram_addr_t size, ram_addr_t max_size, new_block->max_length = max_size; assert(max_size >= size); new_block->fd = -1; + new_block->page_size = getpagesize(); new_block->host = host; if (host) { new_block->flags |= RAM_PREALLOC; @@ -284,27 +284,40 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict) if (params) { monitor_printf(mon, "parameters:"); + assert(params->has_compress_level); monitor_printf(mon, " %s: %" PRId64, MigrationParameter_lookup[MIGRATION_PARAMETER_COMPRESS_LEVEL], params->compress_level); + assert(params->has_compress_threads); monitor_printf(mon, " %s: %" PRId64, MigrationParameter_lookup[MIGRATION_PARAMETER_COMPRESS_THREADS], params->compress_threads); + assert(params->has_decompress_threads); monitor_printf(mon, " %s: %" PRId64, MigrationParameter_lookup[MIGRATION_PARAMETER_DECOMPRESS_THREADS], params->decompress_threads); + assert(params->has_cpu_throttle_initial); monitor_printf(mon, " %s: %" PRId64, MigrationParameter_lookup[MIGRATION_PARAMETER_CPU_THROTTLE_INITIAL], params->cpu_throttle_initial); + assert(params->has_cpu_throttle_increment); monitor_printf(mon, " %s: %" PRId64, MigrationParameter_lookup[MIGRATION_PARAMETER_CPU_THROTTLE_INCREMENT], params->cpu_throttle_increment); monitor_printf(mon, " %s: '%s'", MigrationParameter_lookup[MIGRATION_PARAMETER_TLS_CREDS], - params->tls_creds ? : ""); + params->has_tls_creds ? params->tls_creds : ""); monitor_printf(mon, " %s: '%s'", MigrationParameter_lookup[MIGRATION_PARAMETER_TLS_HOSTNAME], - params->tls_hostname ? : ""); + params->has_tls_hostname ? params->tls_hostname : ""); + assert(params->has_max_bandwidth); + monitor_printf(mon, " %s: %" PRId64 " bytes/second", + MigrationParameter_lookup[MIGRATION_PARAMETER_MAX_BANDWIDTH], + params->max_bandwidth); + assert(params->has_downtime_limit); + monitor_printf(mon, " %s: %" PRId64 " milliseconds", + MigrationParameter_lookup[MIGRATION_PARAMETER_DOWNTIME_LIMIT], + params->downtime_limit); monitor_printf(mon, "\n"); } @@ -1260,6 +1273,7 @@ void hmp_migrate_incoming(Monitor *mon, const QDict *qdict) hmp_handle_error(mon, &err); } +/* Kept for backwards compatibility */ void hmp_migrate_set_downtime(Monitor *mon, const QDict *qdict) { double value = qdict_get_double(qdict, "value"); @@ -1278,6 +1292,7 @@ void hmp_migrate_set_cache_size(Monitor *mon, const QDict *qdict) } } +/* Kept for backwards compatibility */ void hmp_migrate_set_speed(Monitor *mon, const QDict *qdict) { int64_t value = qdict_get_int(qdict, "value"); @@ -1318,45 +1333,58 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict) { const char *param = qdict_get_str(qdict, "parameter"); const char *valuestr = qdict_get_str(qdict, "value"); + int64_t valuebw = 0; long valueint = 0; + char *endp; Error *err = NULL; - bool has_compress_level = false; - bool has_compress_threads = false; - bool has_decompress_threads = false; - bool has_cpu_throttle_initial = false; - bool has_cpu_throttle_increment = false; - bool has_tls_creds = false; - bool has_tls_hostname = false; bool use_int_value = false; int i; for (i = 0; i < MIGRATION_PARAMETER__MAX; i++) { if (strcmp(param, MigrationParameter_lookup[i]) == 0) { + MigrationParameters p = { 0 }; switch (i) { case MIGRATION_PARAMETER_COMPRESS_LEVEL: - has_compress_level = true; + p.has_compress_level = true; use_int_value = true; break; case MIGRATION_PARAMETER_COMPRESS_THREADS: - has_compress_threads = true; + p.has_compress_threads = true; use_int_value = true; break; case MIGRATION_PARAMETER_DECOMPRESS_THREADS: - has_decompress_threads = true; + p.has_decompress_threads = true; use_int_value = true; break; case MIGRATION_PARAMETER_CPU_THROTTLE_INITIAL: - has_cpu_throttle_initial = true; + p.has_cpu_throttle_initial = true; use_int_value = true; break; case MIGRATION_PARAMETER_CPU_THROTTLE_INCREMENT: - has_cpu_throttle_increment = true; + p.has_cpu_throttle_increment = true; + use_int_value = true; break; case MIGRATION_PARAMETER_TLS_CREDS: - has_tls_creds = true; + p.has_tls_creds = true; + p.tls_creds = (char *) valuestr; break; case MIGRATION_PARAMETER_TLS_HOSTNAME: - has_tls_hostname = true; + p.has_tls_hostname = true; + p.tls_hostname = (char *) valuestr; + break; + case MIGRATION_PARAMETER_MAX_BANDWIDTH: + p.has_max_bandwidth = true; + valuebw = qemu_strtosz(valuestr, &endp); + if (valuebw < 0 || (size_t)valuebw != valuebw + || *endp != '\0') { + error_setg(&err, "Invalid size %s", valuestr); + goto cleanup; + } + p.max_bandwidth = valuebw; + break; + case MIGRATION_PARAMETER_DOWNTIME_LIMIT: + p.has_downtime_limit = true; + use_int_value = true; break; } @@ -1366,16 +1394,17 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict) valuestr); goto cleanup; } + /* Set all integers; only one has_FOO will be set, and + * the code ignores the remaining values */ + p.compress_level = valueint; + p.compress_threads = valueint; + p.decompress_threads = valueint; + p.cpu_throttle_initial = valueint; + p.cpu_throttle_increment = valueint; + p.downtime_limit = valueint; } - qmp_migrate_set_parameters(has_compress_level, valueint, - has_compress_threads, valueint, - has_decompress_threads, valueint, - has_cpu_throttle_initial, valueint, - has_cpu_throttle_increment, valueint, - has_tls_creds, valuestr, - has_tls_hostname, valuestr, - &err); + qmp_migrate_set_parameters(&p, &err); break; } } diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h index 869ba41b0c..cffdc130e6 100644 --- a/include/exec/cpu-common.h +++ b/include/exec/cpu-common.h @@ -63,6 +63,7 @@ RAMBlock *qemu_ram_block_from_host(void *ptr, bool round_offset, void qemu_ram_set_idstr(RAMBlock *block, const char *name, DeviceState *dev); void qemu_ram_unset_idstr(RAMBlock *block); const char *qemu_ram_get_idstr(RAMBlock *rb); +size_t qemu_ram_pagesize(RAMBlock *block); void cpu_physical_memory_rw(hwaddr addr, uint8_t *buf, int len, int is_write); diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h index 2a9465da11..54d7108a9e 100644 --- a/include/exec/ram_addr.h +++ b/include/exec/ram_addr.h @@ -36,6 +36,7 @@ struct RAMBlock { /* RCU-enabled, writes protected by the ramlist lock */ QLIST_ENTRY(RAMBlock) next; int fd; + size_t page_size; }; static inline bool offset_in_ramblock(RAMBlock *b, ram_addr_t offset) diff --git a/include/migration/migration.h b/include/migration/migration.h index d4acc72b85..2791b90c00 100644 --- a/include/migration/migration.h +++ b/include/migration/migration.h @@ -129,7 +129,6 @@ struct MigrationSrcPageRequest { struct MigrationState { - int64_t bandwidth_limit; size_t bytes_xfer; size_t xfer_limit; QemuThread thread; diff --git a/migration/migration.c b/migration/migration.c index 955d5ee38c..4d417b76cf 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -44,6 +44,10 @@ #define BUFFER_DELAY 100 #define XFER_LIMIT_RATIO (1000 / BUFFER_DELAY) +/* Time in milliseconds we are allowed to stop the source, + * for sending the last part */ +#define DEFAULT_MIGRATE_SET_DOWNTIME 300 + /* Default compression thread count */ #define DEFAULT_MIGRATE_COMPRESS_THREAD_COUNT 8 /* Default decompression thread count, usually decompression is at @@ -80,7 +84,6 @@ MigrationState *migrate_get_current(void) static bool once; static MigrationState current_migration = { .state = MIGRATION_STATUS_NONE, - .bandwidth_limit = MAX_THROTTLE, .xbzrle_cache_size = DEFAULT_MIGRATE_CACHE_SIZE, .mbps = -1, .parameters = { @@ -89,6 +92,8 @@ MigrationState *migrate_get_current(void) .decompress_threads = DEFAULT_MIGRATE_DECOMPRESS_THREAD_COUNT, .cpu_throttle_initial = DEFAULT_MIGRATE_CPU_THROTTLE_INITIAL, .cpu_throttle_increment = DEFAULT_MIGRATE_CPU_THROTTLE_INCREMENT, + .max_bandwidth = MAX_THROTTLE, + .downtime_limit = DEFAULT_MIGRATE_SET_DOWNTIME, }, }; @@ -517,17 +522,6 @@ void migrate_send_rp_pong(MigrationIncomingState *mis, migrate_send_rp_message(mis, MIG_RP_MSG_PONG, sizeof(buf), &buf); } -/* amount of nanoseconds we are willing to wait for migration to be down. - * the choice of nanoseconds is because it is the maximum resolution that - * get_clock() can achieve. It is an internal measure. All user-visible - * units must be in seconds */ -static uint64_t max_downtime = 300000000; - -uint64_t migrate_max_downtime(void) -{ - return max_downtime; -} - MigrationCapabilityStatusList *qmp_query_migrate_capabilities(Error **errp) { MigrationCapabilityStatusList *head = NULL; @@ -559,13 +553,24 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp) MigrationState *s = migrate_get_current(); params = g_malloc0(sizeof(*params)); + params->has_compress_level = true; params->compress_level = s->parameters.compress_level; + params->has_compress_threads = true; params->compress_threads = s->parameters.compress_threads; + params->has_decompress_threads = true; params->decompress_threads = s->parameters.decompress_threads; + params->has_cpu_throttle_initial = true; params->cpu_throttle_initial = s->parameters.cpu_throttle_initial; + params->has_cpu_throttle_increment = true; params->cpu_throttle_increment = s->parameters.cpu_throttle_increment; + params->has_tls_creds = !!s->parameters.tls_creds; params->tls_creds = g_strdup(s->parameters.tls_creds); + params->has_tls_hostname = !!s->parameters.tls_hostname; params->tls_hostname = g_strdup(s->parameters.tls_hostname); + params->has_max_bandwidth = true; + params->max_bandwidth = s->parameters.max_bandwidth; + params->has_downtime_limit = true; + params->downtime_limit = s->parameters.downtime_limit; return params; } @@ -759,78 +764,92 @@ void qmp_migrate_set_capabilities(MigrationCapabilityStatusList *params, } } -void qmp_migrate_set_parameters(bool has_compress_level, - int64_t compress_level, - bool has_compress_threads, - int64_t compress_threads, - bool has_decompress_threads, - int64_t decompress_threads, - bool has_cpu_throttle_initial, - int64_t cpu_throttle_initial, - bool has_cpu_throttle_increment, - int64_t cpu_throttle_increment, - bool has_tls_creds, - const char *tls_creds, - bool has_tls_hostname, - const char *tls_hostname, - Error **errp) +void qmp_migrate_set_parameters(MigrationParameters *params, Error **errp) { MigrationState *s = migrate_get_current(); - if (has_compress_level && (compress_level < 0 || compress_level > 9)) { + if (params->has_compress_level && + (params->compress_level < 0 || params->compress_level > 9)) { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "compress_level", "is invalid, it should be in the range of 0 to 9"); return; } - if (has_compress_threads && - (compress_threads < 1 || compress_threads > 255)) { + if (params->has_compress_threads && + (params->compress_threads < 1 || params->compress_threads > 255)) { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "compress_threads", "is invalid, it should be in the range of 1 to 255"); return; } - if (has_decompress_threads && - (decompress_threads < 1 || decompress_threads > 255)) { + if (params->has_decompress_threads && + (params->decompress_threads < 1 || params->decompress_threads > 255)) { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "decompress_threads", "is invalid, it should be in the range of 1 to 255"); return; } - if (has_cpu_throttle_initial && - (cpu_throttle_initial < 1 || cpu_throttle_initial > 99)) { + if (params->has_cpu_throttle_initial && + (params->cpu_throttle_initial < 1 || + params->cpu_throttle_initial > 99)) { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "cpu_throttle_initial", "an integer in the range of 1 to 99"); + return; } - if (has_cpu_throttle_increment && - (cpu_throttle_increment < 1 || cpu_throttle_increment > 99)) { + if (params->has_cpu_throttle_increment && + (params->cpu_throttle_increment < 1 || + params->cpu_throttle_increment > 99)) { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "cpu_throttle_increment", "an integer in the range of 1 to 99"); + return; + } + if (params->has_max_bandwidth && + (params->max_bandwidth < 0 || params->max_bandwidth > SIZE_MAX)) { + error_setg(errp, "Parameter 'max_bandwidth' expects an integer in the" + " range of 0 to %zu bytes/second", SIZE_MAX); + return; + } + if (params->has_downtime_limit && + (params->downtime_limit < 0 || params->downtime_limit > 2000000)) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, + "downtime_limit", + "an integer in the range of 0 to 2000000 milliseconds"); + return; } - if (has_compress_level) { - s->parameters.compress_level = compress_level; + if (params->has_compress_level) { + s->parameters.compress_level = params->compress_level; } - if (has_compress_threads) { - s->parameters.compress_threads = compress_threads; + if (params->has_compress_threads) { + s->parameters.compress_threads = params->compress_threads; } - if (has_decompress_threads) { - s->parameters.decompress_threads = decompress_threads; + if (params->has_decompress_threads) { + s->parameters.decompress_threads = params->decompress_threads; } - if (has_cpu_throttle_initial) { - s->parameters.cpu_throttle_initial = cpu_throttle_initial; + if (params->has_cpu_throttle_initial) { + s->parameters.cpu_throttle_initial = params->cpu_throttle_initial; } - if (has_cpu_throttle_increment) { - s->parameters.cpu_throttle_increment = cpu_throttle_increment; + if (params->has_cpu_throttle_increment) { + s->parameters.cpu_throttle_increment = params->cpu_throttle_increment; } - if (has_tls_creds) { + if (params->has_tls_creds) { g_free(s->parameters.tls_creds); - s->parameters.tls_creds = g_strdup(tls_creds); + s->parameters.tls_creds = g_strdup(params->tls_creds); } - if (has_tls_hostname) { + if (params->has_tls_hostname) { g_free(s->parameters.tls_hostname); - s->parameters.tls_hostname = g_strdup(tls_hostname); + s->parameters.tls_hostname = g_strdup(params->tls_hostname); + } + if (params->has_max_bandwidth) { + s->parameters.max_bandwidth = params->max_bandwidth; + if (s->to_dst_file) { + qemu_file_set_rate_limit(s->to_dst_file, + s->parameters.max_bandwidth / XFER_LIMIT_RATIO); + } + } + if (params->has_downtime_limit) { + s->parameters.downtime_limit = params->downtime_limit; } } @@ -1165,28 +1184,25 @@ int64_t qmp_query_migrate_cache_size(Error **errp) void qmp_migrate_set_speed(int64_t value, Error **errp) { - MigrationState *s; - - if (value < 0) { - value = 0; - } - if (value > SIZE_MAX) { - value = SIZE_MAX; - } + MigrationParameters p = { + .has_max_bandwidth = true, + .max_bandwidth = value, + }; - s = migrate_get_current(); - s->bandwidth_limit = value; - if (s->to_dst_file) { - qemu_file_set_rate_limit(s->to_dst_file, - s->bandwidth_limit / XFER_LIMIT_RATIO); - } + qmp_migrate_set_parameters(&p, errp); } void qmp_migrate_set_downtime(double value, Error **errp) { - value *= 1e9; - value = MAX(0, MIN(UINT64_MAX, value)); - max_downtime = (uint64_t)value; + value *= 1000; /* Convert to milliseconds */ + value = MAX(0, MIN(INT64_MAX, value)); + + MigrationParameters p = { + .has_downtime_limit = true, + .downtime_limit = value, + }; + + qmp_migrate_set_parameters(&p, errp); } bool migrate_postcopy_ram(void) @@ -1793,7 +1809,7 @@ static void *migration_thread(void *opaque) initial_bytes; uint64_t time_spent = current_time - initial_time; double bandwidth = (double)transferred_bytes / time_spent; - max_size = bandwidth * migrate_max_downtime() / 1000000; + max_size = bandwidth * s->parameters.downtime_limit; s->mbps = (((double) transferred_bytes * 8.0) / ((double) time_spent / 1000.0)) / 1000.0 / 1000.0; @@ -1852,13 +1868,12 @@ static void *migration_thread(void *opaque) void migrate_fd_connect(MigrationState *s) { - /* This is a best 1st approximation. ns to ms */ - s->expected_downtime = max_downtime/1000000; + s->expected_downtime = s->parameters.downtime_limit; s->cleanup_bh = qemu_bh_new(migrate_fd_cleanup, s); qemu_file_set_blocking(s->to_dst_file, true); qemu_file_set_rate_limit(s->to_dst_file, - s->bandwidth_limit / XFER_LIMIT_RATIO); + s->parameters.max_bandwidth / XFER_LIMIT_RATIO); /* Notify before starting migration thread */ notifier_list_notify(&migration_state_notifiers, s); diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index 9b0477835f..a40dddbaf6 100644 --- a/migration/postcopy-ram.c +++ b/migration/postcopy-ram.c @@ -85,6 +85,24 @@ static bool ufd_version_check(int ufd) } /* + * Check for things that postcopy won't support; returns 0 if the block + * is fine. + */ +static int check_range(const char *block_name, void *host_addr, + ram_addr_t offset, ram_addr_t length, void *opaque) +{ + RAMBlock *rb = qemu_ram_block_by_name(block_name); + + if (qemu_ram_pagesize(rb) > getpagesize()) { + error_report("Postcopy doesn't support large page sizes yet (%s)", + block_name); + return -E2BIG; + } + + return 0; +} + +/* * Note: This has the side effect of munlock'ing all of RAM, that's * normally fine since if the postcopy succeeds it gets turned back on at the * end. @@ -104,6 +122,12 @@ bool postcopy_ram_supported_by_host(void) goto out; } + /* Check for anything about the RAMBlocks we don't support */ + if (qemu_ram_foreach_block(check_range, NULL)) { + /* check_range will have printed its own error */ + goto out; + } + ufd = syscall(__NR_userfaultfd, O_CLOEXEC); if (ufd == -1) { error_report("%s: userfaultfd not available: %s", __func__, diff --git a/migration/ram.c b/migration/ram.c index c8ec9f268f..bc6154fe34 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -771,7 +771,9 @@ static int ram_save_page(QEMUFile *f, PageSearchStatus *pss, * page would be stale */ xbzrle_cache_zero_page(current_addr); - } else if (!ram_bulk_stage && migrate_use_xbzrle()) { + } else if (!ram_bulk_stage && + !migration_in_postcopy(migrate_get_current()) && + migrate_use_xbzrle()) { pages = save_xbzrle_page(f, &p, current_addr, block, offset, last_stage, bytes_transferred); if (!last_stage) { diff --git a/migration/rdma.c b/migration/rdma.c index 88bdb64457..674ccab12e 100644 --- a/migration/rdma.c +++ b/migration/rdma.c @@ -350,6 +350,7 @@ typedef struct RDMAContext { */ int error_state; int error_reported; + int received_error; /* * Description of ram blocks used throughout the code. @@ -1676,6 +1677,9 @@ static int qemu_rdma_exchange_get_response(RDMAContext *rdma, ", but got: %s (%d), length: %d", control_desc[expecting], expecting, control_desc[head->type], head->type, head->len); + if (head->type == RDMA_CONTROL_ERROR) { + rdma->received_error = true; + } return -EIO; } if (head->len > RDMA_CONTROL_MAX_BUFFER - sizeof(*head)) { @@ -2202,7 +2206,7 @@ static void qemu_rdma_cleanup(RDMAContext *rdma) int ret, idx; if (rdma->cm_id && rdma->connected) { - if (rdma->error_state) { + if (rdma->error_state && !rdma->received_error) { RDMAControlHeader head = { .len = 0, .type = RDMA_CONTROL_ERROR, .repeat = 1, @@ -2804,6 +2808,9 @@ static int qio_channel_rdma_close(QIOChannel *ioc, QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(ioc); trace_qemu_rdma_close(); if (rioc->rdma) { + if (!rioc->rdma->error_state) { + rioc->rdma->error_state = qemu_file_get_error(rioc->file); + } qemu_rdma_cleanup(rioc->rdma); g_free(rioc->rdma); rioc->rdma = NULL; diff --git a/migration/savevm.c b/migration/savevm.c index 33a2911ec2..a831ec2d67 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -1828,40 +1828,45 @@ qemu_loadvm_section_part_end(QEMUFile *f, MigrationIncomingState *mis) static int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis) { uint8_t section_type; - int ret; + int ret = 0; while ((section_type = qemu_get_byte(f)) != QEMU_VM_EOF) { - + ret = 0; trace_qemu_loadvm_state_section(section_type); switch (section_type) { case QEMU_VM_SECTION_START: case QEMU_VM_SECTION_FULL: ret = qemu_loadvm_section_start_full(f, mis); if (ret < 0) { - return ret; + goto out; } break; case QEMU_VM_SECTION_PART: case QEMU_VM_SECTION_END: ret = qemu_loadvm_section_part_end(f, mis); if (ret < 0) { - return ret; + goto out; } break; case QEMU_VM_COMMAND: ret = loadvm_process_command(f); trace_qemu_loadvm_state_section_command(ret); if ((ret < 0) || (ret & LOADVM_QUIT)) { - return ret; + goto out; } break; default: error_report("Unknown savevm section type %d", section_type); - return -EINVAL; + ret = -EINVAL; + goto out; } } - return 0; +out: + if (ret < 0) { + qemu_file_set_error(f, ret); + } + return ret; } int qemu_loadvm_state(QEMUFile *f) diff --git a/migration/socket.c b/migration/socket.c index 00de1fe127..a21c0c5c35 100644 --- a/migration/socket.c +++ b/migration/socket.c @@ -112,8 +112,12 @@ void tcp_start_outgoing_migration(MigrationState *s, const char *host_port, Error **errp) { - SocketAddress *saddr = tcp_build_address(host_port, errp); - socket_start_outgoing_migration(s, saddr, errp); + Error *err = NULL; + SocketAddress *saddr = tcp_build_address(host_port, &err); + if (!err) { + socket_start_outgoing_migration(s, saddr, &err); + } + error_propagate(errp, err); } void unix_start_outgoing_migration(MigrationState *s, @@ -174,8 +178,12 @@ static void socket_start_incoming_migration(SocketAddress *saddr, void tcp_start_incoming_migration(const char *host_port, Error **errp) { - SocketAddress *saddr = tcp_build_address(host_port, errp); - socket_start_incoming_migration(saddr, errp); + Error *err = NULL; + SocketAddress *saddr = tcp_build_address(host_port, &err); + if (!err) { + socket_start_incoming_migration(saddr, &err); + } + error_propagate(errp, err); } void unix_start_incoming_migration(const char *path, Error **errp) diff --git a/migration/vmstate.c b/migration/vmstate.c index fc29acf74d..0bc9f35ef8 100644 --- a/migration/vmstate.c +++ b/migration/vmstate.c @@ -130,6 +130,8 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, } if (ret < 0) { qemu_file_set_error(f, ret); + error_report("Failed to load %s:%s", vmsd->name, + field->name); trace_vmstate_load_field_error(field->name, ret); return ret; } @@ -555,6 +557,7 @@ static int get_int32_equal(QEMUFile *f, void *pv, size_t size) if (*v == v2) { return 0; } + error_report("%" PRIx32 " != %" PRIx32, *v, v2); return -EINVAL; } @@ -578,6 +581,9 @@ static int get_int32_le(QEMUFile *f, void *pv, size_t size) *cur = loaded; return 0; } + error_report("Invalid value %" PRId32 + " expecting positive value <= %" PRId32, + loaded, *cur); return -EINVAL; } @@ -683,6 +689,7 @@ static int get_uint32_equal(QEMUFile *f, void *pv, size_t size) if (*v == v2) { return 0; } + error_report("%" PRIx32 " != %" PRIx32, *v, v2); return -EINVAL; } @@ -725,6 +732,7 @@ static int get_uint64_equal(QEMUFile *f, void *pv, size_t size) if (*v == v2) { return 0; } + error_report("%" PRIx64 " != %" PRIx64, *v, v2); return -EINVAL; } @@ -746,6 +754,7 @@ static int get_uint8_equal(QEMUFile *f, void *pv, size_t size) if (*v == v2) { return 0; } + error_report("%x != %x", *v, v2); return -EINVAL; } @@ -767,6 +776,7 @@ static int get_uint16_equal(QEMUFile *f, void *pv, size_t size) if (*v == v2) { return 0; } + error_report("%x != %x", *v, v2); return -EINVAL; } diff --git a/qapi-schema.json b/qapi-schema.json index 9e47b47cc7..ded1179f06 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -658,101 +658,85 @@ # hostname must be provided so that the server's x509 # certificate identity can be validated. (Since 2.7) # +# @max-bandwidth: to set maximum speed for migration. maximum speed in +# bytes per second. (Since 2.8) +# +# @downtime-limit: set maximum tolerated downtime for migration. maximum +# downtime in milliseconds (Since 2.8) +# # Since: 2.4 ## { 'enum': 'MigrationParameter', 'data': ['compress-level', 'compress-threads', 'decompress-threads', 'cpu-throttle-initial', 'cpu-throttle-increment', - 'tls-creds', 'tls-hostname'] } + 'tls-creds', 'tls-hostname', 'max-bandwidth', + 'downtime-limit'] } # # @migrate-set-parameters # -# Set the following migration parameters -# -# @compress-level: compression level -# -# @compress-threads: compression thread count -# -# @decompress-threads: decompression thread count -# -# @cpu-throttle-initial: Initial percentage of time guest cpus are throttled -# when migration auto-converge is activated. The -# default value is 20. (Since 2.7) -# -# @cpu-throttle-increment: throttle percentage increase each time -# auto-converge detects that migration is not making -# progress. The default value is 10. (Since 2.7) -# -# @tls-creds: ID of the 'tls-creds' object that provides credentials for -# establishing a TLS connection over the migration data channel. -# On the outgoing side of the migration, the credentials must -# be for a 'client' endpoint, while for the incoming side the -# credentials must be for a 'server' endpoint. Setting this -# will enable TLS for all migrations. The default is unset, -# resulting in unsecured migration at the QEMU level. (Since 2.7) -# -# @tls-hostname: hostname of the target host for the migration. This is -# required when using x509 based TLS credentials and the -# migration URI does not already include a hostname. For -# example if using fd: or exec: based migration, the -# hostname must be provided so that the server's x509 -# certificate identity can be validated. (Since 2.7) +# Set various migration parameters. See MigrationParameters for details. # # Since: 2.4 ## -{ 'command': 'migrate-set-parameters', - 'data': { '*compress-level': 'int', - '*compress-threads': 'int', - '*decompress-threads': 'int', - '*cpu-throttle-initial': 'int', - '*cpu-throttle-increment': 'int', - '*tls-creds': 'str', - '*tls-hostname': 'str'} } +{ 'command': 'migrate-set-parameters', 'boxed': true, + 'data': 'MigrationParameters' } # # @MigrationParameters # -# @compress-level: compression level +# Optional members can be omitted on input ('migrate-set-parameters') +# but most members will always be present on output +# ('query-migrate-parameters'), with the exception of tls-creds and +# tls-hostname. # -# @compress-threads: compression thread count +# @compress-level: #optional compression level # -# @decompress-threads: decompression thread count +# @compress-threads: #optional compression thread count # -# @cpu-throttle-initial: Initial percentage of time guest cpus are throttled -# when migration auto-converge is activated. The -# default value is 20. (Since 2.7) +# @decompress-threads: #optional decompression thread count # -# @cpu-throttle-increment: throttle percentage increase each time +# @cpu-throttle-initial: #optional Initial percentage of time guest cpus are +# throttledwhen migration auto-converge is activated. +# The default value is 20. (Since 2.7) +# +# @cpu-throttle-increment: #optional throttle percentage increase each time # auto-converge detects that migration is not making # progress. The default value is 10. (Since 2.7) # -# @tls-creds: ID of the 'tls-creds' object that provides credentials for -# establishing a TLS connection over the migration data channel. -# On the outgoing side of the migration, the credentials must -# be for a 'client' endpoint, while for the incoming side the +# @tls-creds: #optional ID of the 'tls-creds' object that provides credentials +# for establishing a TLS connection over the migration data +# channel. On the outgoing side of the migration, the credentials +# must be for a 'client' endpoint, while for the incoming side the # credentials must be for a 'server' endpoint. Setting this # will enable TLS for all migrations. The default is unset, # resulting in unsecured migration at the QEMU level. (Since 2.7) # -# @tls-hostname: hostname of the target host for the migration. This is -# required when using x509 based TLS credentials and the +# @tls-hostname: #optional hostname of the target host for the migration. This +# is required when using x509 based TLS credentials and the # migration URI does not already include a hostname. For # example if using fd: or exec: based migration, the # hostname must be provided so that the server's x509 # certificate identity can be validated. (Since 2.7) # +# @max-bandwidth: to set maximum speed for migration. maximum speed in +# bytes per second. (Since 2.8) +# +# @downtime-limit: set maximum tolerated downtime for migration. maximum +# downtime in milliseconds (Since 2.8) +# # Since: 2.4 ## { 'struct': 'MigrationParameters', - 'data': { 'compress-level': 'int', - 'compress-threads': 'int', - 'decompress-threads': 'int', - 'cpu-throttle-initial': 'int', - 'cpu-throttle-increment': 'int', - 'tls-creds': 'str', - 'tls-hostname': 'str'} } - + 'data': { '*compress-level': 'int', + '*compress-threads': 'int', + '*decompress-threads': 'int', + '*cpu-throttle-initial': 'int', + '*cpu-throttle-increment': 'int', + '*tls-creds': 'str', + '*tls-hostname': 'str', + '*max-bandwidth': 'int', + '*downtime-limit': 'int'} } ## # @query-migrate-parameters # @@ -1834,6 +1818,8 @@ # # Returns: nothing on success # +# Notes: This command is deprecated in favor of 'migrate-set-parameters' +# # Since: 0.14.0 ## { 'command': 'migrate_set_downtime', 'data': {'value': 'number'} } @@ -1847,7 +1833,7 @@ # # Returns: nothing on success # -# Notes: A value lesser than zero will be automatically round up to zero. +# Notes: This command is deprecated in favor of 'migrate-set-parameters' # # Since: 0.14.0 ## |