aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2016-10-17 10:31:10 +0100
committerPeter Maydell <peter.maydell@linaro.org>2016-10-17 10:31:10 +0100
commit4378caf59edf6df796b9ad3174e5703fd25a781c (patch)
treeea5627e0532e0cbfffd103be09cff3796f5122d0
parent6aa5a3679449cdf0b6fe5a6829b22e642ded57fd (diff)
parent7c2b0f65cc2b6b14ad797549b8bde13aa97f6ba2 (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.txt13
-rw-r--r--docs/xbzrle.txt4
-rw-r--r--exec.c19
-rw-r--r--hmp.c77
-rw-r--r--include/exec/cpu-common.h1
-rw-r--r--include/exec/ram_addr.h1
-rw-r--r--include/migration/migration.h1
-rw-r--r--migration/migration.c157
-rw-r--r--migration/postcopy-ram.c24
-rw-r--r--migration/ram.c4
-rw-r--r--migration/rdma.c9
-rw-r--r--migration/savevm.c19
-rw-r--r--migration/socket.c16
-rw-r--r--migration/vmstate.c10
-rw-r--r--qapi-schema.json108
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
diff --git a/exec.c b/exec.c
index 374c364dff..e63c5a1bd4 100644
--- a/exec.c
+++ b/exec.c
@@ -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;
diff --git a/hmp.c b/hmp.c
index 42bef84ed5..80f7f1fefb 100644
--- a/hmp.c
+++ b/hmp.c
@@ -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
##