migration/next for 20170718

-----BEGIN PGP SIGNATURE-----
 
 iQIcBAABCAAGBQJZbiu1AAoJEPSH7xhYctcjIzgP/RvewJbZbofe7kEiQDrueWp2
 Jod3YFPhzoc+UgwzkKM0GFbCeyuyoC+b/mXiJrUcVcnrnKpF+hN/uPK7MjvCBfmb
 wkY1BX6dhYch48YzBWZHXL6y/PkS0rW2Nt7blDl6h0zr0xdIe85MPr1Z9vIkDYbI
 dTf5Mrw9axseoAi3ydZNh5pSkLmbVjNbh1S3y89XS0eZwExliBILKBOqt/liuQxd
 y1FDtliB7GWFq1Ykg2+lZgPcdiPZv1zRkH6n8jbpRLTXGYRx5OyA+FZOQiPV52wf
 leWB38NqyBkFJ9+mdcrA0x6FpVSLJLqKuZcnLVvN7fvvNF6EiB0ozRPVQpa3rSvs
 h339Ouk4GsdmRobhMrptpsPSxM+6c9bhvT2fCBZ6slwbxEdqEYk3+Xm5VGQkiOQ/
 l4a1fHLdwuIKXMGI8zTao+UW/3JZaU/BaOONyO0BJY+I/tde559eJWVHi1wMDJWV
 DOg22PF5Ux1tnr4+MbjXABCr9lUafQxGacVfNFLh0z2/GLe3Vvx7dBgvPZFIMS3W
 YPc/AqOvM2Rlwvi8jlsVM29lTGl9/YjB9EH99M1l30M0RNifcSLxJJoDBhKtB/d2
 CctmTS0b+/jDSwf2BiniqLNpLt33QdY7so9LFnsQWTq4whYxPKLjcivTPHX+mQjy
 Uys+Hyhhps81I2zbBHtx
 =75E9
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/juanquintela/tags/migration/20170718' into staging

migration/next for 20170718

# gpg: Signature made Tue 18 Jul 2017 16:39:33 BST
# gpg:                using RSA key 0xF487EF185872D723
# gpg: Good signature from "Juan Quintela <quintela@redhat.com>"
# gpg:                 aka "Juan Quintela <quintela@trasno.org>"
# gpg: WARNING: This key is not certified with a trusted signature!
# gpg:          There is no indication that the signature belongs to the owner.
# Primary key fingerprint: 1899 FF8E DEBF 58CC EE03  4B82 F487 EF18 5872 D723

* remotes/juanquintela/tags/migration/20170718:
  migration: check global caps for validity
  migration: provide migrate_cap_add()
  migration: provide migrate_caps_check()
  migration: remove check against colo support
  migration: check global params for validity
  migration: provide migrate_params_apply()
  migration: introduce migrate_params_check()
  migration: export capabilities to props
  migration: export parameters to props
  qdev: provide DEFINE_PROP_INT64()
  migration/rdma: Send error during cancelling
  migration/rdma: Safely convert control types
  migration/rdma: Allow cancelling while waiting for wrid
  migration/rdma: fix qemu_rdma_block_for_wrid error paths
  migration: Close file on failed migration load
  migration/rdma: Fix race on source

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2017-07-19 12:30:41 +01:00
commit 988879b66e
6 changed files with 346 additions and 106 deletions

View File

@ -404,6 +404,31 @@ static void set_uint64(Object *obj, Visitor *v, const char *name,
visit_type_uint64(v, name, ptr, errp); visit_type_uint64(v, name, ptr, errp);
} }
static void get_int64(Object *obj, Visitor *v, const char *name,
void *opaque, Error **errp)
{
DeviceState *dev = DEVICE(obj);
Property *prop = opaque;
int64_t *ptr = qdev_get_prop_ptr(dev, prop);
visit_type_int64(v, name, ptr, errp);
}
static void set_int64(Object *obj, Visitor *v, const char *name,
void *opaque, Error **errp)
{
DeviceState *dev = DEVICE(obj);
Property *prop = opaque;
int64_t *ptr = qdev_get_prop_ptr(dev, prop);
if (dev->realized) {
qdev_prop_set_after_realize(dev, name, errp);
return;
}
visit_type_int64(v, name, ptr, errp);
}
const PropertyInfo qdev_prop_uint64 = { const PropertyInfo qdev_prop_uint64 = {
.name = "uint64", .name = "uint64",
.get = get_uint64, .get = get_uint64,
@ -411,6 +436,13 @@ const PropertyInfo qdev_prop_uint64 = {
.set_default_value = set_default_value_uint, .set_default_value = set_default_value_uint,
}; };
const PropertyInfo qdev_prop_int64 = {
.name = "int64",
.get = get_int64,
.set = set_int64,
.set_default_value = set_default_value_int,
};
/* --- string --- */ /* --- string --- */
static void release_string(Object *obj, const char *name, void *opaque) static void release_string(Object *obj, const char *name, void *opaque)

View File

@ -13,6 +13,7 @@ extern const PropertyInfo qdev_prop_uint16;
extern const PropertyInfo qdev_prop_uint32; extern const PropertyInfo qdev_prop_uint32;
extern const PropertyInfo qdev_prop_int32; extern const PropertyInfo qdev_prop_int32;
extern const PropertyInfo qdev_prop_uint64; extern const PropertyInfo qdev_prop_uint64;
extern const PropertyInfo qdev_prop_int64;
extern const PropertyInfo qdev_prop_size; extern const PropertyInfo qdev_prop_size;
extern const PropertyInfo qdev_prop_string; extern const PropertyInfo qdev_prop_string;
extern const PropertyInfo qdev_prop_chr; extern const PropertyInfo qdev_prop_chr;
@ -157,6 +158,8 @@ extern const PropertyInfo qdev_prop_link;
DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_int32, int32_t) DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_int32, int32_t)
#define DEFINE_PROP_UINT64(_n, _s, _f, _d) \ #define DEFINE_PROP_UINT64(_n, _s, _f, _d) \
DEFINE_PROP_UNSIGNED(_n, _s, _f, _d, qdev_prop_uint64, uint64_t) DEFINE_PROP_UNSIGNED(_n, _s, _f, _d, qdev_prop_uint64, uint64_t)
#define DEFINE_PROP_INT64(_n, _s, _f, _d) \
DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_int64, int64_t)
#define DEFINE_PROP_SIZE(_n, _s, _f, _d) \ #define DEFINE_PROP_SIZE(_n, _s, _f, _d) \
DEFINE_PROP_UNSIGNED(_n, _s, _f, _d, qdev_prop_size, uint64_t) DEFINE_PROP_UNSIGNED(_n, _s, _f, _d, qdev_prop_size, uint64_t)
#define DEFINE_PROP_PCI_DEVFN(_n, _s, _f, _d) \ #define DEFINE_PROP_PCI_DEVFN(_n, _s, _f, _d) \

View File

@ -15,7 +15,6 @@
#include "qemu-common.h" #include "qemu-common.h"
bool colo_supported(void);
void colo_info_init(void); void colo_info_init(void);
void migrate_start_colo_process(MigrationState *s); void migrate_start_colo_process(MigrationState *s);

View File

@ -29,11 +29,6 @@ static bool vmstate_loading;
#define COLO_BUFFER_BASE_SIZE (4 * 1024 * 1024) #define COLO_BUFFER_BASE_SIZE (4 * 1024 * 1024)
bool colo_supported(void)
{
return true;
}
bool migration_in_colo_state(void) bool migration_in_colo_state(void)
{ {
MigrationState *s = migrate_get_current(); MigrationState *s = migrate_get_current();

View File

@ -102,14 +102,22 @@ enum mig_rp_message_type {
static MigrationState *current_migration; static MigrationState *current_migration;
static bool migration_object_check(MigrationState *ms, Error **errp);
void migration_object_init(void) void migration_object_init(void)
{ {
MachineState *ms = MACHINE(qdev_get_machine()); MachineState *ms = MACHINE(qdev_get_machine());
Error *err = NULL;
/* This can only be called once. */ /* This can only be called once. */
assert(!current_migration); assert(!current_migration);
current_migration = MIGRATION_OBJ(object_new(TYPE_MIGRATION)); current_migration = MIGRATION_OBJ(object_new(TYPE_MIGRATION));
if (!migration_object_check(current_migration, &err)) {
error_report_err(err);
exit(1);
}
/* /*
* We cannot really do this in migration_instance_init() since at * We cannot really do this in migration_instance_init() since at
* that time global properties are not yet applied, then this * that time global properties are not yet applied, then this
@ -348,6 +356,7 @@ static void process_incoming_migration_co(void *opaque)
migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE, migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE,
MIGRATION_STATUS_FAILED); MIGRATION_STATUS_FAILED);
error_report("load of migration failed: %s", strerror(-ret)); error_report("load of migration failed: %s", strerror(-ret));
qemu_fclose(mis->from_src_file);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
mis->bh = qemu_bh_new(process_incoming_migration_bh, mis); mis->bh = qemu_bh_new(process_incoming_migration_bh, mis);
@ -403,9 +412,6 @@ MigrationCapabilityStatusList *qmp_query_migrate_capabilities(Error **errp)
continue; continue;
} }
#endif #endif
if (i == MIGRATION_CAPABILITY_X_COLO && !colo_supported()) {
continue;
}
if (head == NULL) { if (head == NULL) {
head = g_malloc0(sizeof(*caps)); head = g_malloc0(sizeof(*caps));
caps = head; caps = head;
@ -582,51 +588,49 @@ MigrationInfo *qmp_query_migrate(Error **errp)
return info; return info;
} }
void qmp_migrate_set_capabilities(MigrationCapabilityStatusList *params, /**
Error **errp) * @migration_caps_check - check capability validity
*
* @cap_list: old capability list, array of bool
* @params: new capabilities to be applied soon
* @errp: set *errp if the check failed, with reason
*
* Returns true if check passed, otherwise false.
*/
static bool migrate_caps_check(bool *cap_list,
MigrationCapabilityStatusList *params,
Error **errp)
{ {
MigrationState *s = migrate_get_current();
MigrationCapabilityStatusList *cap; MigrationCapabilityStatusList *cap;
bool old_postcopy_cap = migrate_postcopy_ram(); bool old_postcopy_cap;
if (migration_is_setup_or_active(s->state)) { old_postcopy_cap = cap_list[MIGRATION_CAPABILITY_POSTCOPY_RAM];
error_setg(errp, QERR_MIGRATION_ACTIVE);
return;
}
for (cap = params; cap; cap = cap->next) { for (cap = params; cap; cap = cap->next) {
#ifndef CONFIG_LIVE_BLOCK_MIGRATION cap_list[cap->value->capability] = cap->value->state;
if (cap->value->capability == MIGRATION_CAPABILITY_BLOCK
&& cap->value->state) {
error_setg(errp, "QEMU compiled without old-style (blk/-b, inc/-i) "
"block migration");
error_append_hint(errp, "Use drive_mirror+NBD instead.\n");
continue;
}
#endif
if (cap->value->capability == MIGRATION_CAPABILITY_X_COLO) {
if (!colo_supported()) {
error_setg(errp, "COLO is not currently supported, please"
" configure with --enable-colo option in order to"
" support COLO feature");
continue;
}
}
s->enabled_capabilities[cap->value->capability] = cap->value->state;
} }
if (migrate_postcopy_ram()) { #ifndef CONFIG_LIVE_BLOCK_MIGRATION
if (migrate_use_compression()) { if (cap_list[MIGRATION_CAPABILITY_BLOCK]) {
error_setg(errp, "QEMU compiled without old-style (blk/-b, inc/-i) "
"block migration");
error_append_hint(errp, "Use drive_mirror+NBD instead.\n");
return false;
}
#endif
if (cap_list[MIGRATION_CAPABILITY_POSTCOPY_RAM]) {
if (cap_list[MIGRATION_CAPABILITY_COMPRESS]) {
/* The decompression threads asynchronously write into RAM /* The decompression threads asynchronously write into RAM
* rather than use the atomic copies needed to avoid * rather than use the atomic copies needed to avoid
* userfaulting. It should be possible to fix the decompression * userfaulting. It should be possible to fix the decompression
* threads for compatibility in future. * threads for compatibility in future.
*/ */
error_report("Postcopy is not currently compatible with " error_setg(errp, "Postcopy is not currently compatible "
"compression"); "with compression");
s->enabled_capabilities[MIGRATION_CAPABILITY_POSTCOPY_RAM] = return false;
false;
} }
/* This check is reasonably expensive, so only when it's being /* This check is reasonably expensive, so only when it's being
* set the first time, also it's only the destination that needs * set the first time, also it's only the destination that needs
* special support. * special support.
@ -636,96 +640,141 @@ void qmp_migrate_set_capabilities(MigrationCapabilityStatusList *params,
/* postcopy_ram_supported_by_host will have emitted a more /* postcopy_ram_supported_by_host will have emitted a more
* detailed message * detailed message
*/ */
error_report("Postcopy is not supported"); error_setg(errp, "Postcopy is not supported");
s->enabled_capabilities[MIGRATION_CAPABILITY_POSTCOPY_RAM] = return false;
false;
} }
} }
return true;
}
void qmp_migrate_set_capabilities(MigrationCapabilityStatusList *params,
Error **errp)
{
MigrationState *s = migrate_get_current();
MigrationCapabilityStatusList *cap;
if (migration_is_setup_or_active(s->state)) {
error_setg(errp, QERR_MIGRATION_ACTIVE);
return;
}
if (!migrate_caps_check(s->enabled_capabilities, params, errp)) {
return;
}
for (cap = params; cap; cap = cap->next) {
s->enabled_capabilities[cap->value->capability] = cap->value->state;
}
} }
void qmp_migrate_set_parameters(MigrationParameters *params, Error **errp) /*
* Check whether the parameters are valid. Error will be put into errp
* (if provided). Return true if valid, otherwise false.
*/
static bool migrate_params_check(MigrationParameters *params, Error **errp)
{ {
MigrationState *s = migrate_get_current();
if (params->has_compress_level && if (params->has_compress_level &&
(params->compress_level < 0 || params->compress_level > 9)) { (params->compress_level < 0 || params->compress_level > 9)) {
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "compress_level", error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "compress_level",
"is invalid, it should be in the range of 0 to 9"); "is invalid, it should be in the range of 0 to 9");
return; return false;
} }
if (params->has_compress_threads && if (params->has_compress_threads &&
(params->compress_threads < 1 || params->compress_threads > 255)) { (params->compress_threads < 1 || params->compress_threads > 255)) {
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
"compress_threads", "compress_threads",
"is invalid, it should be in the range of 1 to 255"); "is invalid, it should be in the range of 1 to 255");
return; return false;
} }
if (params->has_decompress_threads && if (params->has_decompress_threads &&
(params->decompress_threads < 1 || params->decompress_threads > 255)) { (params->decompress_threads < 1 || params->decompress_threads > 255)) {
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
"decompress_threads", "decompress_threads",
"is invalid, it should be in the range of 1 to 255"); "is invalid, it should be in the range of 1 to 255");
return; return false;
} }
if (params->has_cpu_throttle_initial && if (params->has_cpu_throttle_initial &&
(params->cpu_throttle_initial < 1 || (params->cpu_throttle_initial < 1 ||
params->cpu_throttle_initial > 99)) { params->cpu_throttle_initial > 99)) {
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
"cpu_throttle_initial", "cpu_throttle_initial",
"an integer in the range of 1 to 99"); "an integer in the range of 1 to 99");
return; return false;
} }
if (params->has_cpu_throttle_increment && if (params->has_cpu_throttle_increment &&
(params->cpu_throttle_increment < 1 || (params->cpu_throttle_increment < 1 ||
params->cpu_throttle_increment > 99)) { params->cpu_throttle_increment > 99)) {
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
"cpu_throttle_increment", "cpu_throttle_increment",
"an integer in the range of 1 to 99"); "an integer in the range of 1 to 99");
return; return false;
} }
if (params->has_max_bandwidth && if (params->has_max_bandwidth &&
(params->max_bandwidth < 0 || params->max_bandwidth > SIZE_MAX)) { (params->max_bandwidth < 0 || params->max_bandwidth > SIZE_MAX)) {
error_setg(errp, "Parameter 'max_bandwidth' expects an integer in the" error_setg(errp, "Parameter 'max_bandwidth' expects an integer in the"
" range of 0 to %zu bytes/second", SIZE_MAX); " range of 0 to %zu bytes/second", SIZE_MAX);
return; return false;
} }
if (params->has_downtime_limit && if (params->has_downtime_limit &&
(params->downtime_limit < 0 || (params->downtime_limit < 0 ||
params->downtime_limit > MAX_MIGRATE_DOWNTIME)) { params->downtime_limit > MAX_MIGRATE_DOWNTIME)) {
error_setg(errp, "Parameter 'downtime_limit' expects an integer in " error_setg(errp, "Parameter 'downtime_limit' expects an integer in "
"the range of 0 to %d milliseconds", "the range of 0 to %d milliseconds",
MAX_MIGRATE_DOWNTIME); MAX_MIGRATE_DOWNTIME);
return; return false;
} }
if (params->has_x_checkpoint_delay && (params->x_checkpoint_delay < 0)) { if (params->has_x_checkpoint_delay && (params->x_checkpoint_delay < 0)) {
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
"x_checkpoint_delay", "x_checkpoint_delay",
"is invalid, it should be positive"); "is invalid, it should be positive");
return false;
} }
return true;
}
static void migrate_params_apply(MigrationParameters *params)
{
MigrationState *s = migrate_get_current();
if (params->has_compress_level) { if (params->has_compress_level) {
s->parameters.compress_level = params->compress_level; s->parameters.compress_level = params->compress_level;
} }
if (params->has_compress_threads) { if (params->has_compress_threads) {
s->parameters.compress_threads = params->compress_threads; s->parameters.compress_threads = params->compress_threads;
} }
if (params->has_decompress_threads) { if (params->has_decompress_threads) {
s->parameters.decompress_threads = params->decompress_threads; s->parameters.decompress_threads = params->decompress_threads;
} }
if (params->has_cpu_throttle_initial) { if (params->has_cpu_throttle_initial) {
s->parameters.cpu_throttle_initial = params->cpu_throttle_initial; s->parameters.cpu_throttle_initial = params->cpu_throttle_initial;
} }
if (params->has_cpu_throttle_increment) { if (params->has_cpu_throttle_increment) {
s->parameters.cpu_throttle_increment = params->cpu_throttle_increment; s->parameters.cpu_throttle_increment = params->cpu_throttle_increment;
} }
if (params->has_tls_creds) { if (params->has_tls_creds) {
g_free(s->parameters.tls_creds); g_free(s->parameters.tls_creds);
s->parameters.tls_creds = g_strdup(params->tls_creds); s->parameters.tls_creds = g_strdup(params->tls_creds);
} }
if (params->has_tls_hostname) { if (params->has_tls_hostname) {
g_free(s->parameters.tls_hostname); g_free(s->parameters.tls_hostname);
s->parameters.tls_hostname = g_strdup(params->tls_hostname); s->parameters.tls_hostname = g_strdup(params->tls_hostname);
} }
if (params->has_max_bandwidth) { if (params->has_max_bandwidth) {
s->parameters.max_bandwidth = params->max_bandwidth; s->parameters.max_bandwidth = params->max_bandwidth;
if (s->to_dst_file) { if (s->to_dst_file) {
@ -733,6 +782,7 @@ void qmp_migrate_set_parameters(MigrationParameters *params, Error **errp)
s->parameters.max_bandwidth / XFER_LIMIT_RATIO); s->parameters.max_bandwidth / XFER_LIMIT_RATIO);
} }
} }
if (params->has_downtime_limit) { if (params->has_downtime_limit) {
s->parameters.downtime_limit = params->downtime_limit; s->parameters.downtime_limit = params->downtime_limit;
} }
@ -743,11 +793,22 @@ void qmp_migrate_set_parameters(MigrationParameters *params, Error **errp)
colo_checkpoint_notify(s); colo_checkpoint_notify(s);
} }
} }
if (params->has_block_incremental) { if (params->has_block_incremental) {
s->parameters.block_incremental = params->block_incremental; s->parameters.block_incremental = params->block_incremental;
} }
} }
void qmp_migrate_set_parameters(MigrationParameters *params, Error **errp)
{
if (!migrate_params_check(params, errp)) {
/* Invalid parameter */
return;
}
migrate_params_apply(params);
}
void qmp_migrate_start_postcopy(Error **errp) void qmp_migrate_start_postcopy(Error **errp)
{ {
@ -781,14 +842,27 @@ void migrate_set_state(int *state, int old_state, int new_state)
} }
} }
void migrate_set_block_enabled(bool value, Error **errp) static MigrationCapabilityStatusList *migrate_cap_add(
MigrationCapabilityStatusList *list,
MigrationCapability index,
bool state)
{ {
MigrationCapabilityStatusList *cap; MigrationCapabilityStatusList *cap;
cap = g_new0(MigrationCapabilityStatusList, 1); cap = g_new0(MigrationCapabilityStatusList, 1);
cap->value = g_new0(MigrationCapabilityStatus, 1); cap->value = g_new0(MigrationCapabilityStatus, 1);
cap->value->capability = MIGRATION_CAPABILITY_BLOCK; cap->value->capability = index;
cap->value->state = value; cap->value->state = state;
cap->next = list;
return cap;
}
void migrate_set_block_enabled(bool value, Error **errp)
{
MigrationCapabilityStatusList *cap;
cap = migrate_cap_add(NULL, MIGRATION_CAPABILITY_BLOCK, value);
qmp_migrate_set_capabilities(cap, errp); qmp_migrate_set_capabilities(cap, errp);
qapi_free_MigrationCapabilityStatusList(cap); qapi_free_MigrationCapabilityStatusList(cap);
} }
@ -2001,6 +2075,9 @@ void migration_global_dump(Monitor *mon)
ms->send_configuration, ms->send_section_footer); ms->send_configuration, ms->send_section_footer);
} }
#define DEFINE_PROP_MIG_CAP(name, x) \
DEFINE_PROP_BOOL(name, MigrationState, enabled_capabilities[x], false)
static Property migration_properties[] = { static Property migration_properties[] = {
DEFINE_PROP_BOOL("store-global-state", MigrationState, DEFINE_PROP_BOOL("store-global-state", MigrationState,
store_global_state, true), store_global_state, true),
@ -2009,6 +2086,45 @@ static Property migration_properties[] = {
send_configuration, true), send_configuration, true),
DEFINE_PROP_BOOL("send-section-footer", MigrationState, DEFINE_PROP_BOOL("send-section-footer", MigrationState,
send_section_footer, true), send_section_footer, true),
/* Migration parameters */
DEFINE_PROP_INT64("x-compress-level", MigrationState,
parameters.compress_level,
DEFAULT_MIGRATE_COMPRESS_LEVEL),
DEFINE_PROP_INT64("x-compress-threads", MigrationState,
parameters.compress_threads,
DEFAULT_MIGRATE_COMPRESS_THREAD_COUNT),
DEFINE_PROP_INT64("x-decompress-threads", MigrationState,
parameters.decompress_threads,
DEFAULT_MIGRATE_DECOMPRESS_THREAD_COUNT),
DEFINE_PROP_INT64("x-cpu-throttle-initial", MigrationState,
parameters.cpu_throttle_initial,
DEFAULT_MIGRATE_CPU_THROTTLE_INITIAL),
DEFINE_PROP_INT64("x-cpu-throttle-increment", MigrationState,
parameters.cpu_throttle_increment,
DEFAULT_MIGRATE_CPU_THROTTLE_INCREMENT),
DEFINE_PROP_INT64("x-max-bandwidth", MigrationState,
parameters.max_bandwidth, MAX_THROTTLE),
DEFINE_PROP_INT64("x-downtime-limit", MigrationState,
parameters.downtime_limit,
DEFAULT_MIGRATE_SET_DOWNTIME),
DEFINE_PROP_INT64("x-checkpoint-delay", MigrationState,
parameters.x_checkpoint_delay,
DEFAULT_MIGRATE_X_CHECKPOINT_DELAY),
/* Migration capabilities */
DEFINE_PROP_MIG_CAP("x-xbzrle", MIGRATION_CAPABILITY_XBZRLE),
DEFINE_PROP_MIG_CAP("x-rdma-pin-all", MIGRATION_CAPABILITY_RDMA_PIN_ALL),
DEFINE_PROP_MIG_CAP("x-auto-converge", MIGRATION_CAPABILITY_AUTO_CONVERGE),
DEFINE_PROP_MIG_CAP("x-zero-blocks", MIGRATION_CAPABILITY_ZERO_BLOCKS),
DEFINE_PROP_MIG_CAP("x-compress", MIGRATION_CAPABILITY_COMPRESS),
DEFINE_PROP_MIG_CAP("x-events", MIGRATION_CAPABILITY_EVENTS),
DEFINE_PROP_MIG_CAP("x-postcopy-ram", MIGRATION_CAPABILITY_POSTCOPY_RAM),
DEFINE_PROP_MIG_CAP("x-colo", MIGRATION_CAPABILITY_X_COLO),
DEFINE_PROP_MIG_CAP("x-release-ram", MIGRATION_CAPABILITY_RELEASE_RAM),
DEFINE_PROP_MIG_CAP("x-block", MIGRATION_CAPABILITY_BLOCK),
DEFINE_PROP_MIG_CAP("x-return-path", MIGRATION_CAPABILITY_RETURN_PATH),
DEFINE_PROP_END_OF_LIST(), DEFINE_PROP_END_OF_LIST(),
}; };
@ -2023,22 +2139,54 @@ static void migration_class_init(ObjectClass *klass, void *data)
static void migration_instance_init(Object *obj) static void migration_instance_init(Object *obj)
{ {
MigrationState *ms = MIGRATION_OBJ(obj); MigrationState *ms = MIGRATION_OBJ(obj);
MigrationParameters *params = &ms->parameters;
ms->state = MIGRATION_STATUS_NONE; ms->state = MIGRATION_STATUS_NONE;
ms->xbzrle_cache_size = DEFAULT_MIGRATE_CACHE_SIZE; ms->xbzrle_cache_size = DEFAULT_MIGRATE_CACHE_SIZE;
ms->mbps = -1; ms->mbps = -1;
ms->parameters = (MigrationParameters) {
.compress_level = DEFAULT_MIGRATE_COMPRESS_LEVEL, params->tls_hostname = g_strdup("");
.compress_threads = DEFAULT_MIGRATE_COMPRESS_THREAD_COUNT, params->tls_creds = g_strdup("");
.decompress_threads = DEFAULT_MIGRATE_DECOMPRESS_THREAD_COUNT,
.cpu_throttle_initial = DEFAULT_MIGRATE_CPU_THROTTLE_INITIAL, /* Set has_* up only for parameter checks */
.cpu_throttle_increment = DEFAULT_MIGRATE_CPU_THROTTLE_INCREMENT, params->has_compress_level = true;
.max_bandwidth = MAX_THROTTLE, params->has_compress_threads = true;
.downtime_limit = DEFAULT_MIGRATE_SET_DOWNTIME, params->has_decompress_threads = true;
.x_checkpoint_delay = DEFAULT_MIGRATE_X_CHECKPOINT_DELAY, params->has_cpu_throttle_initial = true;
}; params->has_cpu_throttle_increment = true;
ms->parameters.tls_creds = g_strdup(""); params->has_max_bandwidth = true;
ms->parameters.tls_hostname = g_strdup(""); params->has_downtime_limit = true;
params->has_x_checkpoint_delay = true;
params->has_block_incremental = true;
}
/*
* Return true if check pass, false otherwise. Error will be put
* inside errp if provided.
*/
static bool migration_object_check(MigrationState *ms, Error **errp)
{
MigrationCapabilityStatusList *head = NULL;
/* Assuming all off */
bool cap_list[MIGRATION_CAPABILITY__MAX] = { 0 }, ret;
int i;
if (!migrate_params_check(&ms->parameters, errp)) {
return false;
}
for (i = 0; i < MIGRATION_CAPABILITY__MAX; i++) {
if (ms->enabled_capabilities[i]) {
head = migrate_cap_add(head, i, true);
}
}
ret = migrate_caps_check(cap_list, head, errp);
/* It works with head == NULL */
qapi_free_MigrationCapabilityStatusList(head);
return ret;
} }
static const TypeInfo migration_type = { static const TypeInfo migration_type = {

View File

@ -165,20 +165,6 @@ enum {
RDMA_CONTROL_UNREGISTER_FINISHED, /* unpinning finished */ RDMA_CONTROL_UNREGISTER_FINISHED, /* unpinning finished */
}; };
static const char *control_desc[] = {
[RDMA_CONTROL_NONE] = "NONE",
[RDMA_CONTROL_ERROR] = "ERROR",
[RDMA_CONTROL_READY] = "READY",
[RDMA_CONTROL_QEMU_FILE] = "QEMU FILE",
[RDMA_CONTROL_RAM_BLOCKS_REQUEST] = "RAM BLOCKS REQUEST",
[RDMA_CONTROL_RAM_BLOCKS_RESULT] = "RAM BLOCKS RESULT",
[RDMA_CONTROL_COMPRESS] = "COMPRESS",
[RDMA_CONTROL_REGISTER_REQUEST] = "REGISTER REQUEST",
[RDMA_CONTROL_REGISTER_RESULT] = "REGISTER RESULT",
[RDMA_CONTROL_REGISTER_FINISHED] = "REGISTER FINISHED",
[RDMA_CONTROL_UNREGISTER_REQUEST] = "UNREGISTER REQUEST",
[RDMA_CONTROL_UNREGISTER_FINISHED] = "UNREGISTER FINISHED",
};
/* /*
* Memory and MR structures used to represent an IB Send/Recv work request. * Memory and MR structures used to represent an IB Send/Recv work request.
@ -251,6 +237,30 @@ typedef struct QEMU_PACKED RDMADestBlock {
uint32_t padding; uint32_t padding;
} RDMADestBlock; } RDMADestBlock;
static const char *control_desc(unsigned int rdma_control)
{
static const char *strs[] = {
[RDMA_CONTROL_NONE] = "NONE",
[RDMA_CONTROL_ERROR] = "ERROR",
[RDMA_CONTROL_READY] = "READY",
[RDMA_CONTROL_QEMU_FILE] = "QEMU FILE",
[RDMA_CONTROL_RAM_BLOCKS_REQUEST] = "RAM BLOCKS REQUEST",
[RDMA_CONTROL_RAM_BLOCKS_RESULT] = "RAM BLOCKS RESULT",
[RDMA_CONTROL_COMPRESS] = "COMPRESS",
[RDMA_CONTROL_REGISTER_REQUEST] = "REGISTER REQUEST",
[RDMA_CONTROL_REGISTER_RESULT] = "REGISTER RESULT",
[RDMA_CONTROL_REGISTER_FINISHED] = "REGISTER FINISHED",
[RDMA_CONTROL_UNREGISTER_REQUEST] = "UNREGISTER REQUEST",
[RDMA_CONTROL_UNREGISTER_FINISHED] = "UNREGISTER FINISHED",
};
if (rdma_control > RDMA_CONTROL_UNREGISTER_FINISHED) {
return "??BAD CONTROL VALUE??";
}
return strs[rdma_control];
}
static uint64_t htonll(uint64_t v) static uint64_t htonll(uint64_t v)
{ {
union { uint32_t lv[2]; uint64_t llv; } u; union { uint32_t lv[2]; uint64_t llv; } u;
@ -1466,6 +1476,56 @@ static uint64_t qemu_rdma_poll(RDMAContext *rdma, uint64_t *wr_id_out,
return 0; return 0;
} }
/* Wait for activity on the completion channel.
* Returns 0 on success, none-0 on error.
*/
static int qemu_rdma_wait_comp_channel(RDMAContext *rdma)
{
/*
* Coroutine doesn't start until migration_fd_process_incoming()
* so don't yield unless we know we're running inside of a coroutine.
*/
if (rdma->migration_started_on_destination) {
yield_until_fd_readable(rdma->comp_channel->fd);
} else {
/* This is the source side, we're in a separate thread
* or destination prior to migration_fd_process_incoming()
* we can't yield; so we have to poll the fd.
* But we need to be able to handle 'cancel' or an error
* without hanging forever.
*/
while (!rdma->error_state && !rdma->received_error) {
GPollFD pfds[1];
pfds[0].fd = rdma->comp_channel->fd;
pfds[0].events = G_IO_IN | G_IO_HUP | G_IO_ERR;
/* 0.1s timeout, should be fine for a 'cancel' */
switch (qemu_poll_ns(pfds, 1, 100 * 1000 * 1000)) {
case 1: /* fd active */
return 0;
case 0: /* Timeout, go around again */
break;
default: /* Error of some type -
* I don't trust errno from qemu_poll_ns
*/
error_report("%s: poll failed", __func__);
return -EPIPE;
}
if (migrate_get_current()->state == MIGRATION_STATUS_CANCELLING) {
/* Bail out and let the cancellation happen */
return -EPIPE;
}
}
}
if (rdma->received_error) {
return -EPIPE;
}
return rdma->error_state;
}
/* /*
* Block until the next work request has completed. * Block until the next work request has completed.
* *
@ -1513,22 +1573,21 @@ static int qemu_rdma_block_for_wrid(RDMAContext *rdma, int wrid_requested,
} }
while (1) { while (1) {
/* ret = qemu_rdma_wait_comp_channel(rdma);
* Coroutine doesn't start until migration_fd_process_incoming() if (ret) {
* so don't yield unless we know we're running inside of a coroutine. goto err_block_for_wrid;
*/
if (rdma->migration_started_on_destination) {
yield_until_fd_readable(rdma->comp_channel->fd);
} }
if (ibv_get_cq_event(rdma->comp_channel, &cq, &cq_ctx)) { ret = ibv_get_cq_event(rdma->comp_channel, &cq, &cq_ctx);
if (ret) {
perror("ibv_get_cq_event"); perror("ibv_get_cq_event");
goto err_block_for_wrid; goto err_block_for_wrid;
} }
num_cq_events++; num_cq_events++;
if (ibv_req_notify_cq(cq, 0)) { ret = -ibv_req_notify_cq(cq, 0);
if (ret) {
goto err_block_for_wrid; goto err_block_for_wrid;
} }
@ -1564,6 +1623,8 @@ err_block_for_wrid:
if (num_cq_events) { if (num_cq_events) {
ibv_ack_cq_events(cq, num_cq_events); ibv_ack_cq_events(cq, num_cq_events);
} }
rdma->error_state = ret;
return ret; return ret;
} }
@ -1590,7 +1651,7 @@ static int qemu_rdma_post_send_control(RDMAContext *rdma, uint8_t *buf,
.num_sge = 1, .num_sge = 1,
}; };
trace_qemu_rdma_post_send_control(control_desc[head->type]); trace_qemu_rdma_post_send_control(control_desc(head->type));
/* /*
* We don't actually need to do a memcpy() in here if we used * We don't actually need to do a memcpy() in here if we used
@ -1669,16 +1730,16 @@ static int qemu_rdma_exchange_get_response(RDMAContext *rdma,
network_to_control((void *) rdma->wr_data[idx].control); network_to_control((void *) rdma->wr_data[idx].control);
memcpy(head, rdma->wr_data[idx].control, sizeof(RDMAControlHeader)); memcpy(head, rdma->wr_data[idx].control, sizeof(RDMAControlHeader));
trace_qemu_rdma_exchange_get_response_start(control_desc[expecting]); trace_qemu_rdma_exchange_get_response_start(control_desc(expecting));
if (expecting == RDMA_CONTROL_NONE) { if (expecting == RDMA_CONTROL_NONE) {
trace_qemu_rdma_exchange_get_response_none(control_desc[head->type], trace_qemu_rdma_exchange_get_response_none(control_desc(head->type),
head->type); head->type);
} else if (head->type != expecting || head->type == RDMA_CONTROL_ERROR) { } else if (head->type != expecting || head->type == RDMA_CONTROL_ERROR) {
error_report("Was expecting a %s (%d) control message" error_report("Was expecting a %s (%d) control message"
", but got: %s (%d), length: %d", ", but got: %s (%d), length: %d",
control_desc[expecting], expecting, control_desc(expecting), expecting,
control_desc[head->type], head->type, head->len); control_desc(head->type), head->type, head->len);
if (head->type == RDMA_CONTROL_ERROR) { if (head->type == RDMA_CONTROL_ERROR) {
rdma->received_error = true; rdma->received_error = true;
} }
@ -1788,7 +1849,7 @@ static int qemu_rdma_exchange_send(RDMAContext *rdma, RDMAControlHeader *head,
} }
} }
trace_qemu_rdma_exchange_send_waiting(control_desc[resp->type]); trace_qemu_rdma_exchange_send_waiting(control_desc(resp->type));
ret = qemu_rdma_exchange_get_response(rdma, resp, ret = qemu_rdma_exchange_get_response(rdma, resp,
resp->type, RDMA_WRID_DATA); resp->type, RDMA_WRID_DATA);
@ -1800,7 +1861,7 @@ static int qemu_rdma_exchange_send(RDMAContext *rdma, RDMAControlHeader *head,
if (resp_idx) { if (resp_idx) {
*resp_idx = RDMA_WRID_DATA; *resp_idx = RDMA_WRID_DATA;
} }
trace_qemu_rdma_exchange_send_received(control_desc[resp->type]); trace_qemu_rdma_exchange_send_received(control_desc(resp->type));
} }
rdma->control_ready_expected = 1; rdma->control_ready_expected = 1;
@ -2208,7 +2269,9 @@ static void qemu_rdma_cleanup(RDMAContext *rdma)
int ret, idx; int ret, idx;
if (rdma->cm_id && rdma->connected) { if (rdma->cm_id && rdma->connected) {
if (rdma->error_state && !rdma->received_error) { if ((rdma->error_state ||
migrate_get_current()->state == MIGRATION_STATUS_CANCELLING) &&
!rdma->received_error) {
RDMAControlHeader head = { .len = 0, RDMAControlHeader head = { .len = 0,
.type = RDMA_CONTROL_ERROR, .type = RDMA_CONTROL_ERROR,
.repeat = 1, .repeat = 1,
@ -2365,6 +2428,12 @@ static int qemu_rdma_connect(RDMAContext *rdma, Error **errp)
caps_to_network(&cap); caps_to_network(&cap);
ret = qemu_rdma_post_recv_control(rdma, RDMA_WRID_READY);
if (ret) {
ERROR(errp, "posting second control recv");
goto err_rdma_source_connect;
}
ret = rdma_connect(rdma->cm_id, &conn_param); ret = rdma_connect(rdma->cm_id, &conn_param);
if (ret) { if (ret) {
perror("rdma_connect"); perror("rdma_connect");
@ -2405,12 +2474,6 @@ static int qemu_rdma_connect(RDMAContext *rdma, Error **errp)
rdma_ack_cm_event(cm_event); rdma_ack_cm_event(cm_event);
ret = qemu_rdma_post_recv_control(rdma, RDMA_WRID_READY);
if (ret) {
ERROR(errp, "posting second control recv!");
goto err_rdma_source_connect;
}
rdma->control_ready_expected = 1; rdma->control_ready_expected = 1;
rdma->nb_sent = 0; rdma->nb_sent = 0;
return 0; return 0;
@ -3350,7 +3413,7 @@ static int qemu_rdma_registration_handle(QEMUFile *f, void *opaque)
ret = -EIO; ret = -EIO;
goto out; goto out;
default: default:
error_report("Unknown control message %s", control_desc[head.type]); error_report("Unknown control message %s", control_desc(head.type));
ret = -EIO; ret = -EIO;
goto out; goto out;
} }