mirror of https://gitee.com/openkylin/qemu.git
migration/next for 20180822
-----BEGIN PGP SIGNATURE----- iQIcBAABCAAGBQJbfUQPAAoJEPSH7xhYctcjlRcP/0EVbPvoYzNuHFgDyXFRZO1L manVFlVRHgHIFg9Wf0D0f6uB+2iLgdbK9cxG+L7jbzRQeyfqYBjOiCIxV8bXrvQX DHxKHhrEYXZMY9Ofx3M0kdM4I1XeoLpP3Sm5nwkkEioShRcnGeSvqibAGT9zJbNN a4xWEIJTnlcoeqZQSuo9utVnTt2Igx0hV2Zfs2SHLzh1s3vEi4EttWfGkK/Z/Qu6 hf0YtB44ykdLIH2Cuu+J/QiWouJLLRY3lX2fpttAMaEFtZFWBU/WdM4eH9+MpuJC XrBRBS8JzqS2QHpQGlmPpCicCaBkiKHtSlfQshO78mW0qMKh4I0mNJILFFQBVSp0 9F4U7vytF73dmJWVRmvWWjZUG0bwXfOUu+CyNKbffQOZIkv80NKKykcm72BxWLL+ gGBkYoCa2i8IRu2z5u1c01oPi0oN0fs3uNPBwSzDhpfzTm4+024wMXSRE9qg/OxA ++JBz2DynkKi2S6c1WKvTe31sez5JA2OiAo2qqzST2sxf1cXMiwarjZ5MQn7DNCf XsIUfLlwe5+CXVR/w5rB24CwSYRYPF9iUWAya6Oc3ss9ngux8cReJmYQr1PK433F rSpvaFxwozZ6vrbHUWwloa/JDJfBehetq2xSwrSTK3xF28jYi6+lIaXYB2oHZJeU nY/mNxvpzOcLUQIgr8kH =GExd -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/juanquintela/tags/migration/20180822-1' into staging migration/next for 20180822 # gpg: Signature made Wed 22 Aug 2018 12:07:59 BST # gpg: using RSA key F487EF185872D723 # 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/20180822-1: migration: hold the lock only if it is really needed migration: move handle of zero page to the thread migration: drop the return value of do_compress_ram_page migration: introduce save_zero_page_to_file migration: fix counting normal page for compression migration: do not wait for free thread migration: poll the cm event for destination qemu tests/migration-test: Silence the kvm_hv message by default migration: implement the shutdown for RDMA QIOChannel migration: poll the cm event while wait RDMA work request completion migration: invoke qio_channel_yield only when qemu_in_coroutine() migration: implement io_set_aio_fd_handler function for RDMA QIOChannel migration: Stop rdma yielding during incoming postcopy migration: implement bi-directional RDMA QIOChannel migration: create a dedicated connection for rdma return path migration: disable RDMA WRITE after postcopy started migrate/cpu-throttle: Add max-cpu-throttle migration parameter docs/migration: Clarify pre_load in subsections migration: Correctly handle subsections with no 'needed' function qapi/migration.json: fix the description for "query-migrate" output Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
54906fe0ce
|
@ -240,10 +240,13 @@ should succeed even with the data missing. To support this the
|
||||||
subsection can be connected to a device property and from there
|
subsection can be connected to a device property and from there
|
||||||
to a versioned machine type.
|
to a versioned machine type.
|
||||||
|
|
||||||
One important note is that the post_load() function is called "after"
|
The 'pre_load' and 'post_load' functions on subsections are only
|
||||||
loading all subsections, because a newer subsection could change same
|
called if the subsection is loaded.
|
||||||
value that it uses. A flag, and the combination of pre_load and post_load
|
|
||||||
can be used to detect whether a subsection was loaded, and to
|
One important note is that the outer post_load() function is called "after"
|
||||||
|
loading all subsections, because a newer subsection could change the same
|
||||||
|
value that it uses. A flag, and the combination of outer pre_load and
|
||||||
|
post_load can be used to detect whether a subsection was loaded, and to
|
||||||
fall back on default behaviour when the subsection isn't present.
|
fall back on default behaviour when the subsection isn't present.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
@ -315,8 +318,8 @@ For example:
|
||||||
the property to false.
|
the property to false.
|
||||||
c) Add a static bool support_foo function that tests the property.
|
c) Add a static bool support_foo function that tests the property.
|
||||||
d) Add a subsection with a .needed set to the support_foo function
|
d) Add a subsection with a .needed set to the support_foo function
|
||||||
e) (potentially) Add a pre_load that sets up a default value for 'foo'
|
e) (potentially) Add an outer pre_load that sets up a default value
|
||||||
to be used if the subsection isn't loaded.
|
for 'foo' to be used if the subsection isn't loaded.
|
||||||
|
|
||||||
Now that subsection will not be generated when using an older
|
Now that subsection will not be generated when using an older
|
||||||
machine type and the migration stream will be accepted by older
|
machine type and the migration stream will be accepted by older
|
||||||
|
|
16
hmp.c
16
hmp.c
|
@ -327,6 +327,10 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict)
|
||||||
monitor_printf(mon, "%s: %u\n",
|
monitor_printf(mon, "%s: %u\n",
|
||||||
MigrationParameter_str(MIGRATION_PARAMETER_COMPRESS_THREADS),
|
MigrationParameter_str(MIGRATION_PARAMETER_COMPRESS_THREADS),
|
||||||
params->compress_threads);
|
params->compress_threads);
|
||||||
|
assert(params->has_compress_wait_thread);
|
||||||
|
monitor_printf(mon, "%s: %s\n",
|
||||||
|
MigrationParameter_str(MIGRATION_PARAMETER_COMPRESS_WAIT_THREAD),
|
||||||
|
params->compress_wait_thread ? "on" : "off");
|
||||||
assert(params->has_decompress_threads);
|
assert(params->has_decompress_threads);
|
||||||
monitor_printf(mon, "%s: %u\n",
|
monitor_printf(mon, "%s: %u\n",
|
||||||
MigrationParameter_str(MIGRATION_PARAMETER_DECOMPRESS_THREADS),
|
MigrationParameter_str(MIGRATION_PARAMETER_DECOMPRESS_THREADS),
|
||||||
|
@ -339,6 +343,10 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict)
|
||||||
monitor_printf(mon, "%s: %u\n",
|
monitor_printf(mon, "%s: %u\n",
|
||||||
MigrationParameter_str(MIGRATION_PARAMETER_CPU_THROTTLE_INCREMENT),
|
MigrationParameter_str(MIGRATION_PARAMETER_CPU_THROTTLE_INCREMENT),
|
||||||
params->cpu_throttle_increment);
|
params->cpu_throttle_increment);
|
||||||
|
assert(params->has_max_cpu_throttle);
|
||||||
|
monitor_printf(mon, "%s: %u\n",
|
||||||
|
MigrationParameter_str(MIGRATION_PARAMETER_MAX_CPU_THROTTLE),
|
||||||
|
params->max_cpu_throttle);
|
||||||
assert(params->has_tls_creds);
|
assert(params->has_tls_creds);
|
||||||
monitor_printf(mon, "%s: '%s'\n",
|
monitor_printf(mon, "%s: '%s'\n",
|
||||||
MigrationParameter_str(MIGRATION_PARAMETER_TLS_CREDS),
|
MigrationParameter_str(MIGRATION_PARAMETER_TLS_CREDS),
|
||||||
|
@ -1647,6 +1655,10 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict)
|
||||||
p->has_compress_threads = true;
|
p->has_compress_threads = true;
|
||||||
visit_type_int(v, param, &p->compress_threads, &err);
|
visit_type_int(v, param, &p->compress_threads, &err);
|
||||||
break;
|
break;
|
||||||
|
case MIGRATION_PARAMETER_COMPRESS_WAIT_THREAD:
|
||||||
|
p->has_compress_wait_thread = true;
|
||||||
|
visit_type_bool(v, param, &p->compress_wait_thread, &err);
|
||||||
|
break;
|
||||||
case MIGRATION_PARAMETER_DECOMPRESS_THREADS:
|
case MIGRATION_PARAMETER_DECOMPRESS_THREADS:
|
||||||
p->has_decompress_threads = true;
|
p->has_decompress_threads = true;
|
||||||
visit_type_int(v, param, &p->decompress_threads, &err);
|
visit_type_int(v, param, &p->decompress_threads, &err);
|
||||||
|
@ -1659,6 +1671,10 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict)
|
||||||
p->has_cpu_throttle_increment = true;
|
p->has_cpu_throttle_increment = true;
|
||||||
visit_type_int(v, param, &p->cpu_throttle_increment, &err);
|
visit_type_int(v, param, &p->cpu_throttle_increment, &err);
|
||||||
break;
|
break;
|
||||||
|
case MIGRATION_PARAMETER_MAX_CPU_THROTTLE:
|
||||||
|
p->has_max_cpu_throttle = true;
|
||||||
|
visit_type_int(v, param, &p->max_cpu_throttle, &err);
|
||||||
|
break;
|
||||||
case MIGRATION_PARAMETER_TLS_CREDS:
|
case MIGRATION_PARAMETER_TLS_CREDS:
|
||||||
p->has_tls_creds = true;
|
p->has_tls_creds = true;
|
||||||
p->tls_creds = g_new0(StrOrNull, 1);
|
p->tls_creds = g_new0(StrOrNull, 1);
|
||||||
|
|
|
@ -341,6 +341,7 @@ struct { \
|
||||||
/*
|
/*
|
||||||
* Simple queue access methods.
|
* Simple queue access methods.
|
||||||
*/
|
*/
|
||||||
|
#define QSIMPLEQ_EMPTY_ATOMIC(head) (atomic_read(&((head)->sqh_first)) == NULL)
|
||||||
#define QSIMPLEQ_EMPTY(head) ((head)->sqh_first == NULL)
|
#define QSIMPLEQ_EMPTY(head) ((head)->sqh_first == NULL)
|
||||||
#define QSIMPLEQ_FIRST(head) ((head)->sqh_first)
|
#define QSIMPLEQ_FIRST(head) ((head)->sqh_first)
|
||||||
#define QSIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
|
#define QSIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
|
||||||
|
|
|
@ -534,6 +534,7 @@ void *colo_process_incoming_thread(void *opaque)
|
||||||
uint64_t value;
|
uint64_t value;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
|
|
||||||
|
rcu_register_thread();
|
||||||
qemu_sem_init(&mis->colo_incoming_sem, 0);
|
qemu_sem_init(&mis->colo_incoming_sem, 0);
|
||||||
|
|
||||||
migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE,
|
migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE,
|
||||||
|
@ -666,5 +667,6 @@ out:
|
||||||
}
|
}
|
||||||
migration_incoming_exit_colo();
|
migration_incoming_exit_colo();
|
||||||
|
|
||||||
|
rcu_unregister_thread();
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,6 +71,7 @@
|
||||||
/* Define default autoconverge cpu throttle migration parameters */
|
/* Define default autoconverge cpu throttle migration parameters */
|
||||||
#define DEFAULT_MIGRATE_CPU_THROTTLE_INITIAL 20
|
#define DEFAULT_MIGRATE_CPU_THROTTLE_INITIAL 20
|
||||||
#define DEFAULT_MIGRATE_CPU_THROTTLE_INCREMENT 10
|
#define DEFAULT_MIGRATE_CPU_THROTTLE_INCREMENT 10
|
||||||
|
#define DEFAULT_MIGRATE_MAX_CPU_THROTTLE 99
|
||||||
|
|
||||||
/* Migration XBZRLE default cache size */
|
/* Migration XBZRLE default cache size */
|
||||||
#define DEFAULT_MIGRATE_XBZRLE_CACHE_SIZE (64 * 1024 * 1024)
|
#define DEFAULT_MIGRATE_XBZRLE_CACHE_SIZE (64 * 1024 * 1024)
|
||||||
|
@ -389,6 +390,7 @@ static void process_incoming_migration_co(void *opaque)
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
assert(mis->from_src_file);
|
assert(mis->from_src_file);
|
||||||
|
mis->migration_incoming_co = qemu_coroutine_self();
|
||||||
mis->largest_page_size = qemu_ram_pagesize_largest();
|
mis->largest_page_size = qemu_ram_pagesize_largest();
|
||||||
postcopy_state_set(POSTCOPY_INCOMING_NONE);
|
postcopy_state_set(POSTCOPY_INCOMING_NONE);
|
||||||
migrate_set_state(&mis->state, MIGRATION_STATUS_NONE,
|
migrate_set_state(&mis->state, MIGRATION_STATUS_NONE,
|
||||||
|
@ -418,7 +420,6 @@ static void process_incoming_migration_co(void *opaque)
|
||||||
|
|
||||||
/* we get COLO info, and know if we are in COLO mode */
|
/* we get COLO info, and know if we are in COLO mode */
|
||||||
if (!ret && migration_incoming_enable_colo()) {
|
if (!ret && migration_incoming_enable_colo()) {
|
||||||
mis->migration_incoming_co = qemu_coroutine_self();
|
|
||||||
qemu_thread_create(&mis->colo_incoming_thread, "COLO incoming",
|
qemu_thread_create(&mis->colo_incoming_thread, "COLO incoming",
|
||||||
colo_process_incoming_thread, mis, QEMU_THREAD_JOINABLE);
|
colo_process_incoming_thread, mis, QEMU_THREAD_JOINABLE);
|
||||||
mis->have_colo_incoming_thread = true;
|
mis->have_colo_incoming_thread = true;
|
||||||
|
@ -442,6 +443,7 @@ static void process_incoming_migration_co(void *opaque)
|
||||||
}
|
}
|
||||||
mis->bh = qemu_bh_new(process_incoming_migration_bh, mis);
|
mis->bh = qemu_bh_new(process_incoming_migration_bh, mis);
|
||||||
qemu_bh_schedule(mis->bh);
|
qemu_bh_schedule(mis->bh);
|
||||||
|
mis->migration_incoming_co = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void migration_incoming_setup(QEMUFile *f)
|
static void migration_incoming_setup(QEMUFile *f)
|
||||||
|
@ -671,6 +673,8 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp)
|
||||||
params->compress_level = s->parameters.compress_level;
|
params->compress_level = s->parameters.compress_level;
|
||||||
params->has_compress_threads = true;
|
params->has_compress_threads = true;
|
||||||
params->compress_threads = s->parameters.compress_threads;
|
params->compress_threads = s->parameters.compress_threads;
|
||||||
|
params->has_compress_wait_thread = true;
|
||||||
|
params->compress_wait_thread = s->parameters.compress_wait_thread;
|
||||||
params->has_decompress_threads = true;
|
params->has_decompress_threads = true;
|
||||||
params->decompress_threads = s->parameters.decompress_threads;
|
params->decompress_threads = s->parameters.decompress_threads;
|
||||||
params->has_cpu_throttle_initial = true;
|
params->has_cpu_throttle_initial = true;
|
||||||
|
@ -697,6 +701,8 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp)
|
||||||
params->xbzrle_cache_size = s->parameters.xbzrle_cache_size;
|
params->xbzrle_cache_size = s->parameters.xbzrle_cache_size;
|
||||||
params->has_max_postcopy_bandwidth = true;
|
params->has_max_postcopy_bandwidth = true;
|
||||||
params->max_postcopy_bandwidth = s->parameters.max_postcopy_bandwidth;
|
params->max_postcopy_bandwidth = s->parameters.max_postcopy_bandwidth;
|
||||||
|
params->has_max_cpu_throttle = true;
|
||||||
|
params->max_cpu_throttle = s->parameters.max_cpu_throttle;
|
||||||
|
|
||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
@ -1043,6 +1049,15 @@ static bool migrate_params_check(MigrationParameters *params, Error **errp)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (params->has_max_cpu_throttle &&
|
||||||
|
(params->max_cpu_throttle < params->cpu_throttle_initial ||
|
||||||
|
params->max_cpu_throttle > 99)) {
|
||||||
|
error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
|
||||||
|
"max_cpu_throttle",
|
||||||
|
"an integer in the range of cpu_throttle_initial to 99");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1061,6 +1076,10 @@ static void migrate_params_test_apply(MigrateSetParameters *params,
|
||||||
dest->compress_threads = params->compress_threads;
|
dest->compress_threads = params->compress_threads;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (params->has_compress_wait_thread) {
|
||||||
|
dest->compress_wait_thread = params->compress_wait_thread;
|
||||||
|
}
|
||||||
|
|
||||||
if (params->has_decompress_threads) {
|
if (params->has_decompress_threads) {
|
||||||
dest->decompress_threads = params->decompress_threads;
|
dest->decompress_threads = params->decompress_threads;
|
||||||
}
|
}
|
||||||
|
@ -1110,6 +1129,9 @@ static void migrate_params_test_apply(MigrateSetParameters *params,
|
||||||
if (params->has_max_postcopy_bandwidth) {
|
if (params->has_max_postcopy_bandwidth) {
|
||||||
dest->max_postcopy_bandwidth = params->max_postcopy_bandwidth;
|
dest->max_postcopy_bandwidth = params->max_postcopy_bandwidth;
|
||||||
}
|
}
|
||||||
|
if (params->has_max_cpu_throttle) {
|
||||||
|
dest->max_cpu_throttle = params->max_cpu_throttle;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void migrate_params_apply(MigrateSetParameters *params, Error **errp)
|
static void migrate_params_apply(MigrateSetParameters *params, Error **errp)
|
||||||
|
@ -1126,6 +1148,10 @@ static void migrate_params_apply(MigrateSetParameters *params, Error **errp)
|
||||||
s->parameters.compress_threads = params->compress_threads;
|
s->parameters.compress_threads = params->compress_threads;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (params->has_compress_wait_thread) {
|
||||||
|
s->parameters.compress_wait_thread = params->compress_wait_thread;
|
||||||
|
}
|
||||||
|
|
||||||
if (params->has_decompress_threads) {
|
if (params->has_decompress_threads) {
|
||||||
s->parameters.decompress_threads = params->decompress_threads;
|
s->parameters.decompress_threads = params->decompress_threads;
|
||||||
}
|
}
|
||||||
|
@ -1185,6 +1211,9 @@ static void migrate_params_apply(MigrateSetParameters *params, Error **errp)
|
||||||
if (params->has_max_postcopy_bandwidth) {
|
if (params->has_max_postcopy_bandwidth) {
|
||||||
s->parameters.max_postcopy_bandwidth = params->max_postcopy_bandwidth;
|
s->parameters.max_postcopy_bandwidth = params->max_postcopy_bandwidth;
|
||||||
}
|
}
|
||||||
|
if (params->has_max_cpu_throttle) {
|
||||||
|
s->parameters.max_cpu_throttle = params->max_cpu_throttle;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void qmp_migrate_set_parameters(MigrateSetParameters *params, Error **errp)
|
void qmp_migrate_set_parameters(MigrateSetParameters *params, Error **errp)
|
||||||
|
@ -1871,6 +1900,15 @@ int migrate_compress_threads(void)
|
||||||
return s->parameters.compress_threads;
|
return s->parameters.compress_threads;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int migrate_compress_wait_thread(void)
|
||||||
|
{
|
||||||
|
MigrationState *s;
|
||||||
|
|
||||||
|
s = migrate_get_current();
|
||||||
|
|
||||||
|
return s->parameters.compress_wait_thread;
|
||||||
|
}
|
||||||
|
|
||||||
int migrate_decompress_threads(void)
|
int migrate_decompress_threads(void)
|
||||||
{
|
{
|
||||||
MigrationState *s;
|
MigrationState *s;
|
||||||
|
@ -1962,7 +2000,6 @@ static int64_t migrate_max_postcopy_bandwidth(void)
|
||||||
return s->parameters.max_postcopy_bandwidth;
|
return s->parameters.max_postcopy_bandwidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool migrate_use_block(void)
|
bool migrate_use_block(void)
|
||||||
{
|
{
|
||||||
MigrationState *s;
|
MigrationState *s;
|
||||||
|
@ -2104,6 +2141,7 @@ static void *source_return_path_thread(void *opaque)
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
trace_source_return_path_thread_entry();
|
trace_source_return_path_thread_entry();
|
||||||
|
rcu_register_thread();
|
||||||
|
|
||||||
retry:
|
retry:
|
||||||
while (!ms->rp_state.error && !qemu_file_get_error(rp) &&
|
while (!ms->rp_state.error && !qemu_file_get_error(rp) &&
|
||||||
|
@ -2243,6 +2281,7 @@ out:
|
||||||
trace_source_return_path_thread_end();
|
trace_source_return_path_thread_end();
|
||||||
ms->rp_state.from_dst_file = NULL;
|
ms->rp_state.from_dst_file = NULL;
|
||||||
qemu_fclose(rp);
|
qemu_fclose(rp);
|
||||||
|
rcu_unregister_thread();
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3131,6 +3170,8 @@ static Property migration_properties[] = {
|
||||||
DEFINE_PROP_UINT8("x-compress-threads", MigrationState,
|
DEFINE_PROP_UINT8("x-compress-threads", MigrationState,
|
||||||
parameters.compress_threads,
|
parameters.compress_threads,
|
||||||
DEFAULT_MIGRATE_COMPRESS_THREAD_COUNT),
|
DEFAULT_MIGRATE_COMPRESS_THREAD_COUNT),
|
||||||
|
DEFINE_PROP_BOOL("x-compress-wait-thread", MigrationState,
|
||||||
|
parameters.compress_wait_thread, true),
|
||||||
DEFINE_PROP_UINT8("x-decompress-threads", MigrationState,
|
DEFINE_PROP_UINT8("x-decompress-threads", MigrationState,
|
||||||
parameters.decompress_threads,
|
parameters.decompress_threads,
|
||||||
DEFAULT_MIGRATE_DECOMPRESS_THREAD_COUNT),
|
DEFAULT_MIGRATE_DECOMPRESS_THREAD_COUNT),
|
||||||
|
@ -3160,6 +3201,9 @@ static Property migration_properties[] = {
|
||||||
DEFINE_PROP_SIZE("max-postcopy-bandwidth", MigrationState,
|
DEFINE_PROP_SIZE("max-postcopy-bandwidth", MigrationState,
|
||||||
parameters.max_postcopy_bandwidth,
|
parameters.max_postcopy_bandwidth,
|
||||||
DEFAULT_MIGRATE_MAX_POSTCOPY_BANDWIDTH),
|
DEFAULT_MIGRATE_MAX_POSTCOPY_BANDWIDTH),
|
||||||
|
DEFINE_PROP_UINT8("max-cpu-throttle", MigrationState,
|
||||||
|
parameters.max_cpu_throttle,
|
||||||
|
DEFAULT_MIGRATE_MAX_CPU_THROTTLE),
|
||||||
|
|
||||||
/* Migration capabilities */
|
/* Migration capabilities */
|
||||||
DEFINE_PROP_MIG_CAP("x-xbzrle", MIGRATION_CAPABILITY_XBZRLE),
|
DEFINE_PROP_MIG_CAP("x-xbzrle", MIGRATION_CAPABILITY_XBZRLE),
|
||||||
|
@ -3230,6 +3274,7 @@ static void migration_instance_init(Object *obj)
|
||||||
params->has_x_multifd_page_count = true;
|
params->has_x_multifd_page_count = true;
|
||||||
params->has_xbzrle_cache_size = true;
|
params->has_xbzrle_cache_size = true;
|
||||||
params->has_max_postcopy_bandwidth = true;
|
params->has_max_postcopy_bandwidth = true;
|
||||||
|
params->has_max_cpu_throttle = true;
|
||||||
|
|
||||||
qemu_sem_init(&ms->postcopy_pause_sem, 0);
|
qemu_sem_init(&ms->postcopy_pause_sem, 0);
|
||||||
qemu_sem_init(&ms->postcopy_pause_rp_sem, 0);
|
qemu_sem_init(&ms->postcopy_pause_rp_sem, 0);
|
||||||
|
|
|
@ -266,11 +266,13 @@ bool migrate_colo_enabled(void);
|
||||||
|
|
||||||
bool migrate_use_block(void);
|
bool migrate_use_block(void);
|
||||||
bool migrate_use_block_incremental(void);
|
bool migrate_use_block_incremental(void);
|
||||||
|
int migrate_max_cpu_throttle(void);
|
||||||
bool migrate_use_return_path(void);
|
bool migrate_use_return_path(void);
|
||||||
|
|
||||||
bool migrate_use_compression(void);
|
bool migrate_use_compression(void);
|
||||||
int migrate_compress_level(void);
|
int migrate_compress_level(void);
|
||||||
int migrate_compress_threads(void);
|
int migrate_compress_threads(void);
|
||||||
|
int migrate_compress_wait_thread(void);
|
||||||
int migrate_decompress_threads(void);
|
int migrate_decompress_threads(void);
|
||||||
bool migrate_use_events(void);
|
bool migrate_use_events(void);
|
||||||
bool migrate_postcopy_blocktime(void);
|
bool migrate_postcopy_blocktime(void);
|
||||||
|
|
|
@ -853,6 +853,7 @@ static void *postcopy_ram_fault_thread(void *opaque)
|
||||||
RAMBlock *rb = NULL;
|
RAMBlock *rb = NULL;
|
||||||
|
|
||||||
trace_postcopy_ram_fault_thread_entry();
|
trace_postcopy_ram_fault_thread_entry();
|
||||||
|
rcu_register_thread();
|
||||||
mis->last_rb = NULL; /* last RAMBlock we sent part of */
|
mis->last_rb = NULL; /* last RAMBlock we sent part of */
|
||||||
qemu_sem_post(&mis->fault_thread_sem);
|
qemu_sem_post(&mis->fault_thread_sem);
|
||||||
|
|
||||||
|
@ -1059,6 +1060,7 @@ retry:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
rcu_unregister_thread();
|
||||||
trace_postcopy_ram_fault_thread_exit();
|
trace_postcopy_ram_fault_thread_exit();
|
||||||
g_free(pfd);
|
g_free(pfd);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
|
@ -49,7 +49,11 @@ static ssize_t channel_writev_buffer(void *opaque,
|
||||||
ssize_t len;
|
ssize_t len;
|
||||||
len = qio_channel_writev(ioc, local_iov, nlocal_iov, NULL);
|
len = qio_channel_writev(ioc, local_iov, nlocal_iov, NULL);
|
||||||
if (len == QIO_CHANNEL_ERR_BLOCK) {
|
if (len == QIO_CHANNEL_ERR_BLOCK) {
|
||||||
qio_channel_wait(ioc, G_IO_OUT);
|
if (qemu_in_coroutine()) {
|
||||||
|
qio_channel_yield(ioc, G_IO_OUT);
|
||||||
|
} else {
|
||||||
|
qio_channel_wait(ioc, G_IO_OUT);
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (len < 0) {
|
if (len < 0) {
|
||||||
|
@ -80,7 +84,11 @@ static ssize_t channel_get_buffer(void *opaque,
|
||||||
ret = qio_channel_read(ioc, (char *)buf, size, NULL);
|
ret = qio_channel_read(ioc, (char *)buf, size, NULL);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
if (ret == QIO_CHANNEL_ERR_BLOCK) {
|
if (ret == QIO_CHANNEL_ERR_BLOCK) {
|
||||||
qio_channel_yield(ioc, G_IO_IN);
|
if (qemu_in_coroutine()) {
|
||||||
|
qio_channel_yield(ioc, G_IO_IN);
|
||||||
|
} else {
|
||||||
|
qio_channel_wait(ioc, G_IO_IN);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
/* XXX handle Error * object */
|
/* XXX handle Error * object */
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
|
|
@ -253,8 +253,12 @@ size_t ram_control_save_page(QEMUFile *f, ram_addr_t block_offset,
|
||||||
if (f->hooks && f->hooks->save_page) {
|
if (f->hooks && f->hooks->save_page) {
|
||||||
int ret = f->hooks->save_page(f, f->opaque, block_offset,
|
int ret = f->hooks->save_page(f, f->opaque, block_offset,
|
||||||
offset, size, bytes_sent);
|
offset, size, bytes_sent);
|
||||||
f->bytes_xfer += size;
|
if (ret != RAM_SAVE_CONTROL_NOT_SUPP) {
|
||||||
if (ret != RAM_SAVE_CONTROL_DELAYED) {
|
f->bytes_xfer += size;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret != RAM_SAVE_CONTROL_DELAYED &&
|
||||||
|
ret != RAM_SAVE_CONTROL_NOT_SUPP) {
|
||||||
if (bytes_sent && *bytes_sent > 0) {
|
if (bytes_sent && *bytes_sent > 0) {
|
||||||
qemu_update_position(f, *bytes_sent);
|
qemu_update_position(f, *bytes_sent);
|
||||||
} else if (ret < 0) {
|
} else if (ret < 0) {
|
||||||
|
|
202
migration/ram.c
202
migration/ram.c
|
@ -341,6 +341,7 @@ typedef struct PageSearchStatus PageSearchStatus;
|
||||||
struct CompressParam {
|
struct CompressParam {
|
||||||
bool done;
|
bool done;
|
||||||
bool quit;
|
bool quit;
|
||||||
|
bool zero_page;
|
||||||
QEMUFile *file;
|
QEMUFile *file;
|
||||||
QemuMutex mutex;
|
QemuMutex mutex;
|
||||||
QemuCond cond;
|
QemuCond cond;
|
||||||
|
@ -382,14 +383,15 @@ static QemuThread *decompress_threads;
|
||||||
static QemuMutex decomp_done_lock;
|
static QemuMutex decomp_done_lock;
|
||||||
static QemuCond decomp_done_cond;
|
static QemuCond decomp_done_cond;
|
||||||
|
|
||||||
static int do_compress_ram_page(QEMUFile *f, z_stream *stream, RAMBlock *block,
|
static bool do_compress_ram_page(QEMUFile *f, z_stream *stream, RAMBlock *block,
|
||||||
ram_addr_t offset, uint8_t *source_buf);
|
ram_addr_t offset, uint8_t *source_buf);
|
||||||
|
|
||||||
static void *do_data_compress(void *opaque)
|
static void *do_data_compress(void *opaque)
|
||||||
{
|
{
|
||||||
CompressParam *param = opaque;
|
CompressParam *param = opaque;
|
||||||
RAMBlock *block;
|
RAMBlock *block;
|
||||||
ram_addr_t offset;
|
ram_addr_t offset;
|
||||||
|
bool zero_page;
|
||||||
|
|
||||||
qemu_mutex_lock(¶m->mutex);
|
qemu_mutex_lock(¶m->mutex);
|
||||||
while (!param->quit) {
|
while (!param->quit) {
|
||||||
|
@ -399,11 +401,12 @@ static void *do_data_compress(void *opaque)
|
||||||
param->block = NULL;
|
param->block = NULL;
|
||||||
qemu_mutex_unlock(¶m->mutex);
|
qemu_mutex_unlock(¶m->mutex);
|
||||||
|
|
||||||
do_compress_ram_page(param->file, ¶m->stream, block, offset,
|
zero_page = do_compress_ram_page(param->file, ¶m->stream,
|
||||||
param->originbuf);
|
block, offset, param->originbuf);
|
||||||
|
|
||||||
qemu_mutex_lock(&comp_done_lock);
|
qemu_mutex_lock(&comp_done_lock);
|
||||||
param->done = true;
|
param->done = true;
|
||||||
|
param->zero_page = zero_page;
|
||||||
qemu_cond_signal(&comp_done_cond);
|
qemu_cond_signal(&comp_done_cond);
|
||||||
qemu_mutex_unlock(&comp_done_lock);
|
qemu_mutex_unlock(&comp_done_lock);
|
||||||
|
|
||||||
|
@ -989,6 +992,7 @@ static void *multifd_send_thread(void *opaque)
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
trace_multifd_send_thread_start(p->id);
|
trace_multifd_send_thread_start(p->id);
|
||||||
|
rcu_register_thread();
|
||||||
|
|
||||||
if (multifd_send_initial_packet(p, &local_err) < 0) {
|
if (multifd_send_initial_packet(p, &local_err) < 0) {
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -1051,6 +1055,7 @@ out:
|
||||||
p->running = false;
|
p->running = false;
|
||||||
qemu_mutex_unlock(&p->mutex);
|
qemu_mutex_unlock(&p->mutex);
|
||||||
|
|
||||||
|
rcu_unregister_thread();
|
||||||
trace_multifd_send_thread_end(p->id, p->num_packets, p->num_pages);
|
trace_multifd_send_thread_end(p->id, p->num_packets, p->num_pages);
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -1220,6 +1225,7 @@ static void *multifd_recv_thread(void *opaque)
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
trace_multifd_recv_thread_start(p->id);
|
trace_multifd_recv_thread_start(p->id);
|
||||||
|
rcu_register_thread();
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
uint32_t used;
|
uint32_t used;
|
||||||
|
@ -1266,6 +1272,7 @@ static void *multifd_recv_thread(void *opaque)
|
||||||
p->running = false;
|
p->running = false;
|
||||||
qemu_mutex_unlock(&p->mutex);
|
qemu_mutex_unlock(&p->mutex);
|
||||||
|
|
||||||
|
rcu_unregister_thread();
|
||||||
trace_multifd_recv_thread_end(p->id, p->num_packets, p->num_pages);
|
trace_multifd_recv_thread_end(p->id, p->num_packets, p->num_pages);
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -1391,13 +1398,15 @@ static void mig_throttle_guest_down(void)
|
||||||
MigrationState *s = migrate_get_current();
|
MigrationState *s = migrate_get_current();
|
||||||
uint64_t pct_initial = s->parameters.cpu_throttle_initial;
|
uint64_t pct_initial = s->parameters.cpu_throttle_initial;
|
||||||
uint64_t pct_icrement = s->parameters.cpu_throttle_increment;
|
uint64_t pct_icrement = s->parameters.cpu_throttle_increment;
|
||||||
|
int pct_max = s->parameters.max_cpu_throttle;
|
||||||
|
|
||||||
/* We have not started throttling yet. Let's start it. */
|
/* We have not started throttling yet. Let's start it. */
|
||||||
if (!cpu_throttle_active()) {
|
if (!cpu_throttle_active()) {
|
||||||
cpu_throttle_set(pct_initial);
|
cpu_throttle_set(pct_initial);
|
||||||
} else {
|
} else {
|
||||||
/* Throttling already on, just increase the rate */
|
/* Throttling already on, just increase the rate */
|
||||||
cpu_throttle_set(cpu_throttle_get_percentage() + pct_icrement);
|
cpu_throttle_set(MIN(cpu_throttle_get_percentage() + pct_icrement,
|
||||||
|
pct_max));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1665,6 +1674,31 @@ static void migration_bitmap_sync(RAMState *rs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* save_zero_page_to_file: send the zero page to the file
|
||||||
|
*
|
||||||
|
* Returns the size of data written to the file, 0 means the page is not
|
||||||
|
* a zero page
|
||||||
|
*
|
||||||
|
* @rs: current RAM state
|
||||||
|
* @file: the file where the data is saved
|
||||||
|
* @block: block that contains the page we want to send
|
||||||
|
* @offset: offset inside the block for the page
|
||||||
|
*/
|
||||||
|
static int save_zero_page_to_file(RAMState *rs, QEMUFile *file,
|
||||||
|
RAMBlock *block, ram_addr_t offset)
|
||||||
|
{
|
||||||
|
uint8_t *p = block->host + offset;
|
||||||
|
int len = 0;
|
||||||
|
|
||||||
|
if (is_zero_range(p, TARGET_PAGE_SIZE)) {
|
||||||
|
len += save_page_header(rs, file, block, offset | RAM_SAVE_FLAG_ZERO);
|
||||||
|
qemu_put_byte(file, 0);
|
||||||
|
len += 1;
|
||||||
|
}
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* save_zero_page: send the zero page to the stream
|
* save_zero_page: send the zero page to the stream
|
||||||
*
|
*
|
||||||
|
@ -1676,19 +1710,14 @@ static void migration_bitmap_sync(RAMState *rs)
|
||||||
*/
|
*/
|
||||||
static int save_zero_page(RAMState *rs, RAMBlock *block, ram_addr_t offset)
|
static int save_zero_page(RAMState *rs, RAMBlock *block, ram_addr_t offset)
|
||||||
{
|
{
|
||||||
uint8_t *p = block->host + offset;
|
int len = save_zero_page_to_file(rs, rs->f, block, offset);
|
||||||
int pages = -1;
|
|
||||||
|
|
||||||
if (is_zero_range(p, TARGET_PAGE_SIZE)) {
|
if (len) {
|
||||||
ram_counters.duplicate++;
|
ram_counters.duplicate++;
|
||||||
ram_counters.transferred +=
|
ram_counters.transferred += len;
|
||||||
save_page_header(rs, rs->f, block, offset | RAM_SAVE_FLAG_ZERO);
|
return 1;
|
||||||
qemu_put_byte(rs->f, 0);
|
|
||||||
ram_counters.transferred += 1;
|
|
||||||
pages = 1;
|
|
||||||
}
|
}
|
||||||
|
return -1;
|
||||||
return pages;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ram_release_pages(const char *rbname, uint64_t offset, int pages)
|
static void ram_release_pages(const char *rbname, uint64_t offset, int pages)
|
||||||
|
@ -1823,15 +1852,20 @@ static int ram_save_multifd_page(RAMState *rs, RAMBlock *block,
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int do_compress_ram_page(QEMUFile *f, z_stream *stream, RAMBlock *block,
|
static bool do_compress_ram_page(QEMUFile *f, z_stream *stream, RAMBlock *block,
|
||||||
ram_addr_t offset, uint8_t *source_buf)
|
ram_addr_t offset, uint8_t *source_buf)
|
||||||
{
|
{
|
||||||
RAMState *rs = ram_state;
|
RAMState *rs = ram_state;
|
||||||
int bytes_sent, blen;
|
|
||||||
uint8_t *p = block->host + (offset & TARGET_PAGE_MASK);
|
uint8_t *p = block->host + (offset & TARGET_PAGE_MASK);
|
||||||
|
bool zero_page = false;
|
||||||
|
int ret;
|
||||||
|
|
||||||
bytes_sent = save_page_header(rs, f, block, offset |
|
if (save_zero_page_to_file(rs, f, block, offset)) {
|
||||||
RAM_SAVE_FLAG_COMPRESS_PAGE);
|
zero_page = true;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
save_page_header(rs, f, block, offset | RAM_SAVE_FLAG_COMPRESS_PAGE);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* copy it to a internal buffer to avoid it being modified by VM
|
* copy it to a internal buffer to avoid it being modified by VM
|
||||||
|
@ -1839,17 +1873,25 @@ static int do_compress_ram_page(QEMUFile *f, z_stream *stream, RAMBlock *block,
|
||||||
* decompression
|
* decompression
|
||||||
*/
|
*/
|
||||||
memcpy(source_buf, p, TARGET_PAGE_SIZE);
|
memcpy(source_buf, p, TARGET_PAGE_SIZE);
|
||||||
blen = qemu_put_compression_data(f, stream, source_buf, TARGET_PAGE_SIZE);
|
ret = qemu_put_compression_data(f, stream, source_buf, TARGET_PAGE_SIZE);
|
||||||
if (blen < 0) {
|
if (ret < 0) {
|
||||||
bytes_sent = 0;
|
qemu_file_set_error(migrate_get_current()->to_dst_file, ret);
|
||||||
qemu_file_set_error(migrate_get_current()->to_dst_file, blen);
|
|
||||||
error_report("compressed data failed!");
|
error_report("compressed data failed!");
|
||||||
} else {
|
return false;
|
||||||
bytes_sent += blen;
|
|
||||||
ram_release_pages(block->idstr, offset & TARGET_PAGE_MASK, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return bytes_sent;
|
exit:
|
||||||
|
ram_release_pages(block->idstr, offset & TARGET_PAGE_MASK, 1);
|
||||||
|
return zero_page;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
update_compress_thread_counts(const CompressParam *param, int bytes_xmit)
|
||||||
|
{
|
||||||
|
if (param->zero_page) {
|
||||||
|
ram_counters.duplicate++;
|
||||||
|
}
|
||||||
|
ram_counters.transferred += bytes_xmit;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void flush_compressed_data(RAMState *rs)
|
static void flush_compressed_data(RAMState *rs)
|
||||||
|
@ -1873,7 +1915,12 @@ static void flush_compressed_data(RAMState *rs)
|
||||||
qemu_mutex_lock(&comp_param[idx].mutex);
|
qemu_mutex_lock(&comp_param[idx].mutex);
|
||||||
if (!comp_param[idx].quit) {
|
if (!comp_param[idx].quit) {
|
||||||
len = qemu_put_qemu_file(rs->f, comp_param[idx].file);
|
len = qemu_put_qemu_file(rs->f, comp_param[idx].file);
|
||||||
ram_counters.transferred += len;
|
/*
|
||||||
|
* it's safe to fetch zero_page without holding comp_done_lock
|
||||||
|
* as there is no further request submitted to the thread,
|
||||||
|
* i.e, the thread should be waiting for a request at this point.
|
||||||
|
*/
|
||||||
|
update_compress_thread_counts(&comp_param[idx], len);
|
||||||
}
|
}
|
||||||
qemu_mutex_unlock(&comp_param[idx].mutex);
|
qemu_mutex_unlock(&comp_param[idx].mutex);
|
||||||
}
|
}
|
||||||
|
@ -1890,30 +1937,33 @@ static int compress_page_with_multi_thread(RAMState *rs, RAMBlock *block,
|
||||||
ram_addr_t offset)
|
ram_addr_t offset)
|
||||||
{
|
{
|
||||||
int idx, thread_count, bytes_xmit = -1, pages = -1;
|
int idx, thread_count, bytes_xmit = -1, pages = -1;
|
||||||
|
bool wait = migrate_compress_wait_thread();
|
||||||
|
|
||||||
thread_count = migrate_compress_threads();
|
thread_count = migrate_compress_threads();
|
||||||
qemu_mutex_lock(&comp_done_lock);
|
qemu_mutex_lock(&comp_done_lock);
|
||||||
while (true) {
|
retry:
|
||||||
for (idx = 0; idx < thread_count; idx++) {
|
for (idx = 0; idx < thread_count; idx++) {
|
||||||
if (comp_param[idx].done) {
|
if (comp_param[idx].done) {
|
||||||
comp_param[idx].done = false;
|
comp_param[idx].done = false;
|
||||||
bytes_xmit = qemu_put_qemu_file(rs->f, comp_param[idx].file);
|
bytes_xmit = qemu_put_qemu_file(rs->f, comp_param[idx].file);
|
||||||
qemu_mutex_lock(&comp_param[idx].mutex);
|
qemu_mutex_lock(&comp_param[idx].mutex);
|
||||||
set_compress_params(&comp_param[idx], block, offset);
|
set_compress_params(&comp_param[idx], block, offset);
|
||||||
qemu_cond_signal(&comp_param[idx].cond);
|
qemu_cond_signal(&comp_param[idx].cond);
|
||||||
qemu_mutex_unlock(&comp_param[idx].mutex);
|
qemu_mutex_unlock(&comp_param[idx].mutex);
|
||||||
pages = 1;
|
pages = 1;
|
||||||
ram_counters.normal++;
|
update_compress_thread_counts(&comp_param[idx], bytes_xmit);
|
||||||
ram_counters.transferred += bytes_xmit;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (pages > 0) {
|
|
||||||
break;
|
break;
|
||||||
} else {
|
|
||||||
qemu_cond_wait(&comp_done_cond, &comp_done_lock);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* wait for the free thread if the user specifies 'compress-wait-thread',
|
||||||
|
* otherwise we will post the page out in the main thread as normal page.
|
||||||
|
*/
|
||||||
|
if (pages < 0 && wait) {
|
||||||
|
qemu_cond_wait(&comp_done_cond, &comp_done_lock);
|
||||||
|
goto retry;
|
||||||
|
}
|
||||||
qemu_mutex_unlock(&comp_done_lock);
|
qemu_mutex_unlock(&comp_done_lock);
|
||||||
|
|
||||||
return pages;
|
return pages;
|
||||||
|
@ -1983,6 +2033,10 @@ static RAMBlock *unqueue_page(RAMState *rs, ram_addr_t *offset)
|
||||||
{
|
{
|
||||||
RAMBlock *block = NULL;
|
RAMBlock *block = NULL;
|
||||||
|
|
||||||
|
if (QSIMPLEQ_EMPTY_ATOMIC(&rs->src_page_requests)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
qemu_mutex_lock(&rs->src_page_req_mutex);
|
qemu_mutex_lock(&rs->src_page_req_mutex);
|
||||||
if (!QSIMPLEQ_EMPTY(&rs->src_page_requests)) {
|
if (!QSIMPLEQ_EMPTY(&rs->src_page_requests)) {
|
||||||
struct RAMSrcPageRequest *entry =
|
struct RAMSrcPageRequest *entry =
|
||||||
|
@ -2175,6 +2229,39 @@ static bool save_page_use_compression(RAMState *rs)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* try to compress the page before posting it out, return true if the page
|
||||||
|
* has been properly handled by compression, otherwise needs other
|
||||||
|
* paths to handle it
|
||||||
|
*/
|
||||||
|
static bool save_compress_page(RAMState *rs, RAMBlock *block, ram_addr_t offset)
|
||||||
|
{
|
||||||
|
if (!save_page_use_compression(rs)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When starting the process of a new block, the first page of
|
||||||
|
* the block should be sent out before other pages in the same
|
||||||
|
* block, and all the pages in last block should have been sent
|
||||||
|
* out, keeping this order is important, because the 'cont' flag
|
||||||
|
* is used to avoid resending the block name.
|
||||||
|
*
|
||||||
|
* We post the fist page as normal page as compression will take
|
||||||
|
* much CPU resource.
|
||||||
|
*/
|
||||||
|
if (block != rs->last_sent_block) {
|
||||||
|
flush_compressed_data(rs);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (compress_page_with_multi_thread(rs, block, offset) > 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ram_save_target_page: save one target page
|
* ram_save_target_page: save one target page
|
||||||
*
|
*
|
||||||
|
@ -2195,15 +2282,8 @@ static int ram_save_target_page(RAMState *rs, PageSearchStatus *pss,
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
if (save_compress_page(rs, block, offset)) {
|
||||||
* When starting the process of a new block, the first page of
|
return 1;
|
||||||
* the block should be sent out before other pages in the same
|
|
||||||
* block, and all the pages in last block should have been sent
|
|
||||||
* out, keeping this order is important, because the 'cont' flag
|
|
||||||
* is used to avoid resending the block name.
|
|
||||||
*/
|
|
||||||
if (block != rs->last_sent_block && save_page_use_compression(rs)) {
|
|
||||||
flush_compressed_data(rs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
res = save_zero_page(rs, block, offset);
|
res = save_zero_page(rs, block, offset);
|
||||||
|
@ -2221,14 +2301,10 @@ static int ram_save_target_page(RAMState *rs, PageSearchStatus *pss,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Make sure the first page is sent out before other pages.
|
* do not use multifd for compression as the first page in the new
|
||||||
*
|
* block should be posted out before sending the compressed page
|
||||||
* we post it as normal page as compression will take much
|
|
||||||
* CPU resource.
|
|
||||||
*/
|
*/
|
||||||
if (block == rs->last_sent_block && save_page_use_compression(rs)) {
|
if (!save_page_use_compression(rs) && migrate_use_multifd()) {
|
||||||
return compress_page_with_multi_thread(rs, block, offset);
|
|
||||||
} else if (migrate_use_multifd()) {
|
|
||||||
return ram_save_multifd_page(rs, block, offset);
|
return ram_save_multifd_page(rs, block, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
425
migration/rdma.c
425
migration/rdma.c
|
@ -86,6 +86,7 @@ static uint32_t known_capabilities = RDMA_CAPABILITY_PIN_ALL;
|
||||||
" to abort!"); \
|
" to abort!"); \
|
||||||
rdma->error_reported = 1; \
|
rdma->error_reported = 1; \
|
||||||
} \
|
} \
|
||||||
|
rcu_read_unlock(); \
|
||||||
return rdma->error_state; \
|
return rdma->error_state; \
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
@ -387,6 +388,10 @@ typedef struct RDMAContext {
|
||||||
uint64_t unregistrations[RDMA_SIGNALED_SEND_MAX];
|
uint64_t unregistrations[RDMA_SIGNALED_SEND_MAX];
|
||||||
|
|
||||||
GHashTable *blockmap;
|
GHashTable *blockmap;
|
||||||
|
|
||||||
|
/* the RDMAContext for return path */
|
||||||
|
struct RDMAContext *return_path;
|
||||||
|
bool is_return_path;
|
||||||
} RDMAContext;
|
} RDMAContext;
|
||||||
|
|
||||||
#define TYPE_QIO_CHANNEL_RDMA "qio-channel-rdma"
|
#define TYPE_QIO_CHANNEL_RDMA "qio-channel-rdma"
|
||||||
|
@ -398,7 +403,8 @@ typedef struct QIOChannelRDMA QIOChannelRDMA;
|
||||||
|
|
||||||
struct QIOChannelRDMA {
|
struct QIOChannelRDMA {
|
||||||
QIOChannel parent;
|
QIOChannel parent;
|
||||||
RDMAContext *rdma;
|
RDMAContext *rdmain;
|
||||||
|
RDMAContext *rdmaout;
|
||||||
QEMUFile *file;
|
QEMUFile *file;
|
||||||
bool blocking; /* XXX we don't actually honour this yet */
|
bool blocking; /* XXX we don't actually honour this yet */
|
||||||
};
|
};
|
||||||
|
@ -1483,27 +1489,56 @@ static uint64_t qemu_rdma_poll(RDMAContext *rdma, uint64_t *wr_id_out,
|
||||||
*/
|
*/
|
||||||
static int qemu_rdma_wait_comp_channel(RDMAContext *rdma)
|
static int qemu_rdma_wait_comp_channel(RDMAContext *rdma)
|
||||||
{
|
{
|
||||||
|
struct rdma_cm_event *cm_event;
|
||||||
|
int ret = -1;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Coroutine doesn't start until migration_fd_process_incoming()
|
* Coroutine doesn't start until migration_fd_process_incoming()
|
||||||
* so don't yield unless we know we're running inside of a coroutine.
|
* so don't yield unless we know we're running inside of a coroutine.
|
||||||
*/
|
*/
|
||||||
if (rdma->migration_started_on_destination) {
|
if (rdma->migration_started_on_destination &&
|
||||||
|
migration_incoming_get_current()->state == MIGRATION_STATUS_ACTIVE) {
|
||||||
yield_until_fd_readable(rdma->comp_channel->fd);
|
yield_until_fd_readable(rdma->comp_channel->fd);
|
||||||
} else {
|
} else {
|
||||||
/* This is the source side, we're in a separate thread
|
/* This is the source side, we're in a separate thread
|
||||||
* or destination prior to migration_fd_process_incoming()
|
* or destination prior to migration_fd_process_incoming()
|
||||||
|
* after postcopy, the destination also in a seprate thread.
|
||||||
* we can't yield; so we have to poll the fd.
|
* we can't yield; so we have to poll the fd.
|
||||||
* But we need to be able to handle 'cancel' or an error
|
* But we need to be able to handle 'cancel' or an error
|
||||||
* without hanging forever.
|
* without hanging forever.
|
||||||
*/
|
*/
|
||||||
while (!rdma->error_state && !rdma->received_error) {
|
while (!rdma->error_state && !rdma->received_error) {
|
||||||
GPollFD pfds[1];
|
GPollFD pfds[2];
|
||||||
pfds[0].fd = rdma->comp_channel->fd;
|
pfds[0].fd = rdma->comp_channel->fd;
|
||||||
pfds[0].events = G_IO_IN | G_IO_HUP | G_IO_ERR;
|
pfds[0].events = G_IO_IN | G_IO_HUP | G_IO_ERR;
|
||||||
|
pfds[0].revents = 0;
|
||||||
|
|
||||||
|
pfds[1].fd = rdma->channel->fd;
|
||||||
|
pfds[1].events = G_IO_IN | G_IO_HUP | G_IO_ERR;
|
||||||
|
pfds[1].revents = 0;
|
||||||
|
|
||||||
/* 0.1s timeout, should be fine for a 'cancel' */
|
/* 0.1s timeout, should be fine for a 'cancel' */
|
||||||
switch (qemu_poll_ns(pfds, 1, 100 * 1000 * 1000)) {
|
switch (qemu_poll_ns(pfds, 2, 100 * 1000 * 1000)) {
|
||||||
|
case 2:
|
||||||
case 1: /* fd active */
|
case 1: /* fd active */
|
||||||
return 0;
|
if (pfds[0].revents) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pfds[1].revents) {
|
||||||
|
ret = rdma_get_cm_event(rdma->channel, &cm_event);
|
||||||
|
if (!ret) {
|
||||||
|
rdma_ack_cm_event(cm_event);
|
||||||
|
}
|
||||||
|
|
||||||
|
error_report("receive cm event while wait comp channel,"
|
||||||
|
"cm event is %d", cm_event->event);
|
||||||
|
if (cm_event->event == RDMA_CM_EVENT_DISCONNECTED ||
|
||||||
|
cm_event->event == RDMA_CM_EVENT_DEVICE_REMOVAL) {
|
||||||
|
return -EPIPE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case 0: /* Timeout, go around again */
|
case 0: /* Timeout, go around again */
|
||||||
break;
|
break;
|
||||||
|
@ -2323,10 +2358,22 @@ static void qemu_rdma_cleanup(RDMAContext *rdma)
|
||||||
rdma_destroy_id(rdma->cm_id);
|
rdma_destroy_id(rdma->cm_id);
|
||||||
rdma->cm_id = NULL;
|
rdma->cm_id = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* the destination side, listen_id and channel is shared */
|
||||||
if (rdma->listen_id) {
|
if (rdma->listen_id) {
|
||||||
rdma_destroy_id(rdma->listen_id);
|
if (!rdma->is_return_path) {
|
||||||
|
rdma_destroy_id(rdma->listen_id);
|
||||||
|
}
|
||||||
rdma->listen_id = NULL;
|
rdma->listen_id = NULL;
|
||||||
|
|
||||||
|
if (rdma->channel) {
|
||||||
|
if (!rdma->is_return_path) {
|
||||||
|
rdma_destroy_event_channel(rdma->channel);
|
||||||
|
}
|
||||||
|
rdma->channel = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rdma->channel) {
|
if (rdma->channel) {
|
||||||
rdma_destroy_event_channel(rdma->channel);
|
rdma_destroy_event_channel(rdma->channel);
|
||||||
rdma->channel = NULL;
|
rdma->channel = NULL;
|
||||||
|
@ -2555,6 +2602,25 @@ err_dest_init_create_listen_id:
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void qemu_rdma_return_path_dest_init(RDMAContext *rdma_return_path,
|
||||||
|
RDMAContext *rdma)
|
||||||
|
{
|
||||||
|
int idx;
|
||||||
|
|
||||||
|
for (idx = 0; idx < RDMA_WRID_MAX; idx++) {
|
||||||
|
rdma_return_path->wr_data[idx].control_len = 0;
|
||||||
|
rdma_return_path->wr_data[idx].control_curr = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*the CM channel and CM id is shared*/
|
||||||
|
rdma_return_path->channel = rdma->channel;
|
||||||
|
rdma_return_path->listen_id = rdma->listen_id;
|
||||||
|
|
||||||
|
rdma->return_path = rdma_return_path;
|
||||||
|
rdma_return_path->return_path = rdma;
|
||||||
|
rdma_return_path->is_return_path = true;
|
||||||
|
}
|
||||||
|
|
||||||
static void *qemu_rdma_data_init(const char *host_port, Error **errp)
|
static void *qemu_rdma_data_init(const char *host_port, Error **errp)
|
||||||
{
|
{
|
||||||
RDMAContext *rdma = NULL;
|
RDMAContext *rdma = NULL;
|
||||||
|
@ -2595,12 +2661,20 @@ static ssize_t qio_channel_rdma_writev(QIOChannel *ioc,
|
||||||
{
|
{
|
||||||
QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(ioc);
|
QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(ioc);
|
||||||
QEMUFile *f = rioc->file;
|
QEMUFile *f = rioc->file;
|
||||||
RDMAContext *rdma = rioc->rdma;
|
RDMAContext *rdma;
|
||||||
int ret;
|
int ret;
|
||||||
ssize_t done = 0;
|
ssize_t done = 0;
|
||||||
size_t i;
|
size_t i;
|
||||||
size_t len = 0;
|
size_t len = 0;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
rdma = atomic_rcu_read(&rioc->rdmaout);
|
||||||
|
|
||||||
|
if (!rdma) {
|
||||||
|
rcu_read_unlock();
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
CHECK_ERROR_STATE();
|
CHECK_ERROR_STATE();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -2610,6 +2684,7 @@ static ssize_t qio_channel_rdma_writev(QIOChannel *ioc,
|
||||||
ret = qemu_rdma_write_flush(f, rdma);
|
ret = qemu_rdma_write_flush(f, rdma);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
rdma->error_state = ret;
|
rdma->error_state = ret;
|
||||||
|
rcu_read_unlock();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2629,6 +2704,7 @@ static ssize_t qio_channel_rdma_writev(QIOChannel *ioc,
|
||||||
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
rdma->error_state = ret;
|
rdma->error_state = ret;
|
||||||
|
rcu_read_unlock();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2637,6 +2713,7 @@ static ssize_t qio_channel_rdma_writev(QIOChannel *ioc,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rcu_read_unlock();
|
||||||
return done;
|
return done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2670,12 +2747,20 @@ static ssize_t qio_channel_rdma_readv(QIOChannel *ioc,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(ioc);
|
QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(ioc);
|
||||||
RDMAContext *rdma = rioc->rdma;
|
RDMAContext *rdma;
|
||||||
RDMAControlHeader head;
|
RDMAControlHeader head;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
ssize_t i;
|
ssize_t i;
|
||||||
size_t done = 0;
|
size_t done = 0;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
rdma = atomic_rcu_read(&rioc->rdmain);
|
||||||
|
|
||||||
|
if (!rdma) {
|
||||||
|
rcu_read_unlock();
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
CHECK_ERROR_STATE();
|
CHECK_ERROR_STATE();
|
||||||
|
|
||||||
for (i = 0; i < niov; i++) {
|
for (i = 0; i < niov; i++) {
|
||||||
|
@ -2687,7 +2772,7 @@ static ssize_t qio_channel_rdma_readv(QIOChannel *ioc,
|
||||||
* were given and dish out the bytes until we run
|
* were given and dish out the bytes until we run
|
||||||
* out of bytes.
|
* out of bytes.
|
||||||
*/
|
*/
|
||||||
ret = qemu_rdma_fill(rioc->rdma, data, want, 0);
|
ret = qemu_rdma_fill(rdma, data, want, 0);
|
||||||
done += ret;
|
done += ret;
|
||||||
want -= ret;
|
want -= ret;
|
||||||
/* Got what we needed, so go to next iovec */
|
/* Got what we needed, so go to next iovec */
|
||||||
|
@ -2709,25 +2794,28 @@ static ssize_t qio_channel_rdma_readv(QIOChannel *ioc,
|
||||||
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
rdma->error_state = ret;
|
rdma->error_state = ret;
|
||||||
|
rcu_read_unlock();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* SEND was received with new bytes, now try again.
|
* SEND was received with new bytes, now try again.
|
||||||
*/
|
*/
|
||||||
ret = qemu_rdma_fill(rioc->rdma, data, want, 0);
|
ret = qemu_rdma_fill(rdma, data, want, 0);
|
||||||
done += ret;
|
done += ret;
|
||||||
want -= ret;
|
want -= ret;
|
||||||
|
|
||||||
/* Still didn't get enough, so lets just return */
|
/* Still didn't get enough, so lets just return */
|
||||||
if (want) {
|
if (want) {
|
||||||
if (done == 0) {
|
if (done == 0) {
|
||||||
|
rcu_read_unlock();
|
||||||
return QIO_CHANNEL_ERR_BLOCK;
|
return QIO_CHANNEL_ERR_BLOCK;
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
rcu_read_unlock();
|
||||||
return done;
|
return done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2779,15 +2867,29 @@ qio_channel_rdma_source_prepare(GSource *source,
|
||||||
gint *timeout)
|
gint *timeout)
|
||||||
{
|
{
|
||||||
QIOChannelRDMASource *rsource = (QIOChannelRDMASource *)source;
|
QIOChannelRDMASource *rsource = (QIOChannelRDMASource *)source;
|
||||||
RDMAContext *rdma = rsource->rioc->rdma;
|
RDMAContext *rdma;
|
||||||
GIOCondition cond = 0;
|
GIOCondition cond = 0;
|
||||||
*timeout = -1;
|
*timeout = -1;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
if (rsource->condition == G_IO_IN) {
|
||||||
|
rdma = atomic_rcu_read(&rsource->rioc->rdmain);
|
||||||
|
} else {
|
||||||
|
rdma = atomic_rcu_read(&rsource->rioc->rdmaout);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rdma) {
|
||||||
|
error_report("RDMAContext is NULL when prepare Gsource");
|
||||||
|
rcu_read_unlock();
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
if (rdma->wr_data[0].control_len) {
|
if (rdma->wr_data[0].control_len) {
|
||||||
cond |= G_IO_IN;
|
cond |= G_IO_IN;
|
||||||
}
|
}
|
||||||
cond |= G_IO_OUT;
|
cond |= G_IO_OUT;
|
||||||
|
|
||||||
|
rcu_read_unlock();
|
||||||
return cond & rsource->condition;
|
return cond & rsource->condition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2795,14 +2897,28 @@ static gboolean
|
||||||
qio_channel_rdma_source_check(GSource *source)
|
qio_channel_rdma_source_check(GSource *source)
|
||||||
{
|
{
|
||||||
QIOChannelRDMASource *rsource = (QIOChannelRDMASource *)source;
|
QIOChannelRDMASource *rsource = (QIOChannelRDMASource *)source;
|
||||||
RDMAContext *rdma = rsource->rioc->rdma;
|
RDMAContext *rdma;
|
||||||
GIOCondition cond = 0;
|
GIOCondition cond = 0;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
if (rsource->condition == G_IO_IN) {
|
||||||
|
rdma = atomic_rcu_read(&rsource->rioc->rdmain);
|
||||||
|
} else {
|
||||||
|
rdma = atomic_rcu_read(&rsource->rioc->rdmaout);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rdma) {
|
||||||
|
error_report("RDMAContext is NULL when check Gsource");
|
||||||
|
rcu_read_unlock();
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
if (rdma->wr_data[0].control_len) {
|
if (rdma->wr_data[0].control_len) {
|
||||||
cond |= G_IO_IN;
|
cond |= G_IO_IN;
|
||||||
}
|
}
|
||||||
cond |= G_IO_OUT;
|
cond |= G_IO_OUT;
|
||||||
|
|
||||||
|
rcu_read_unlock();
|
||||||
return cond & rsource->condition;
|
return cond & rsource->condition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2813,14 +2929,28 @@ qio_channel_rdma_source_dispatch(GSource *source,
|
||||||
{
|
{
|
||||||
QIOChannelFunc func = (QIOChannelFunc)callback;
|
QIOChannelFunc func = (QIOChannelFunc)callback;
|
||||||
QIOChannelRDMASource *rsource = (QIOChannelRDMASource *)source;
|
QIOChannelRDMASource *rsource = (QIOChannelRDMASource *)source;
|
||||||
RDMAContext *rdma = rsource->rioc->rdma;
|
RDMAContext *rdma;
|
||||||
GIOCondition cond = 0;
|
GIOCondition cond = 0;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
if (rsource->condition == G_IO_IN) {
|
||||||
|
rdma = atomic_rcu_read(&rsource->rioc->rdmain);
|
||||||
|
} else {
|
||||||
|
rdma = atomic_rcu_read(&rsource->rioc->rdmaout);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rdma) {
|
||||||
|
error_report("RDMAContext is NULL when dispatch Gsource");
|
||||||
|
rcu_read_unlock();
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
if (rdma->wr_data[0].control_len) {
|
if (rdma->wr_data[0].control_len) {
|
||||||
cond |= G_IO_IN;
|
cond |= G_IO_IN;
|
||||||
}
|
}
|
||||||
cond |= G_IO_OUT;
|
cond |= G_IO_OUT;
|
||||||
|
|
||||||
|
rcu_read_unlock();
|
||||||
return (*func)(QIO_CHANNEL(rsource->rioc),
|
return (*func)(QIO_CHANNEL(rsource->rioc),
|
||||||
(cond & rsource->condition),
|
(cond & rsource->condition),
|
||||||
user_data);
|
user_data);
|
||||||
|
@ -2860,20 +2990,91 @@ static GSource *qio_channel_rdma_create_watch(QIOChannel *ioc,
|
||||||
return source;
|
return source;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void qio_channel_rdma_set_aio_fd_handler(QIOChannel *ioc,
|
||||||
|
AioContext *ctx,
|
||||||
|
IOHandler *io_read,
|
||||||
|
IOHandler *io_write,
|
||||||
|
void *opaque)
|
||||||
|
{
|
||||||
|
QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(ioc);
|
||||||
|
if (io_read) {
|
||||||
|
aio_set_fd_handler(ctx, rioc->rdmain->comp_channel->fd,
|
||||||
|
false, io_read, io_write, NULL, opaque);
|
||||||
|
} else {
|
||||||
|
aio_set_fd_handler(ctx, rioc->rdmaout->comp_channel->fd,
|
||||||
|
false, io_read, io_write, NULL, opaque);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int qio_channel_rdma_close(QIOChannel *ioc,
|
static int qio_channel_rdma_close(QIOChannel *ioc,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(ioc);
|
QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(ioc);
|
||||||
|
RDMAContext *rdmain, *rdmaout;
|
||||||
trace_qemu_rdma_close();
|
trace_qemu_rdma_close();
|
||||||
if (rioc->rdma) {
|
|
||||||
if (!rioc->rdma->error_state) {
|
rdmain = rioc->rdmain;
|
||||||
rioc->rdma->error_state = qemu_file_get_error(rioc->file);
|
if (rdmain) {
|
||||||
}
|
atomic_rcu_set(&rioc->rdmain, NULL);
|
||||||
qemu_rdma_cleanup(rioc->rdma);
|
|
||||||
g_free(rioc->rdma);
|
|
||||||
rioc->rdma = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rdmaout = rioc->rdmaout;
|
||||||
|
if (rdmaout) {
|
||||||
|
atomic_rcu_set(&rioc->rdmaout, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronize_rcu();
|
||||||
|
|
||||||
|
if (rdmain) {
|
||||||
|
qemu_rdma_cleanup(rdmain);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rdmaout) {
|
||||||
|
qemu_rdma_cleanup(rdmaout);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free(rdmain);
|
||||||
|
g_free(rdmaout);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
qio_channel_rdma_shutdown(QIOChannel *ioc,
|
||||||
|
QIOChannelShutdown how,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(ioc);
|
||||||
|
RDMAContext *rdmain, *rdmaout;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
|
||||||
|
rdmain = atomic_rcu_read(&rioc->rdmain);
|
||||||
|
rdmaout = atomic_rcu_read(&rioc->rdmain);
|
||||||
|
|
||||||
|
switch (how) {
|
||||||
|
case QIO_CHANNEL_SHUTDOWN_READ:
|
||||||
|
if (rdmain) {
|
||||||
|
rdmain->error_state = -1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case QIO_CHANNEL_SHUTDOWN_WRITE:
|
||||||
|
if (rdmaout) {
|
||||||
|
rdmaout->error_state = -1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case QIO_CHANNEL_SHUTDOWN_BOTH:
|
||||||
|
default:
|
||||||
|
if (rdmain) {
|
||||||
|
rdmain->error_state = -1;
|
||||||
|
}
|
||||||
|
if (rdmaout) {
|
||||||
|
rdmaout->error_state = -1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
rcu_read_unlock();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2916,11 +3117,24 @@ static size_t qemu_rdma_save_page(QEMUFile *f, void *opaque,
|
||||||
size_t size, uint64_t *bytes_sent)
|
size_t size, uint64_t *bytes_sent)
|
||||||
{
|
{
|
||||||
QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(opaque);
|
QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(opaque);
|
||||||
RDMAContext *rdma = rioc->rdma;
|
RDMAContext *rdma;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
rdma = atomic_rcu_read(&rioc->rdmaout);
|
||||||
|
|
||||||
|
if (!rdma) {
|
||||||
|
rcu_read_unlock();
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
CHECK_ERROR_STATE();
|
CHECK_ERROR_STATE();
|
||||||
|
|
||||||
|
if (migrate_get_current()->state == MIGRATION_STATUS_POSTCOPY_ACTIVE) {
|
||||||
|
rcu_read_unlock();
|
||||||
|
return RAM_SAVE_CONTROL_NOT_SUPP;
|
||||||
|
}
|
||||||
|
|
||||||
qemu_fflush(f);
|
qemu_fflush(f);
|
||||||
|
|
||||||
if (size > 0) {
|
if (size > 0) {
|
||||||
|
@ -3002,12 +3216,45 @@ static size_t qemu_rdma_save_page(QEMUFile *f, void *opaque,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rcu_read_unlock();
|
||||||
return RAM_SAVE_CONTROL_DELAYED;
|
return RAM_SAVE_CONTROL_DELAYED;
|
||||||
err:
|
err:
|
||||||
rdma->error_state = ret;
|
rdma->error_state = ret;
|
||||||
|
rcu_read_unlock();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void rdma_accept_incoming_migration(void *opaque);
|
||||||
|
|
||||||
|
static void rdma_cm_poll_handler(void *opaque)
|
||||||
|
{
|
||||||
|
RDMAContext *rdma = opaque;
|
||||||
|
int ret;
|
||||||
|
struct rdma_cm_event *cm_event;
|
||||||
|
MigrationIncomingState *mis = migration_incoming_get_current();
|
||||||
|
|
||||||
|
ret = rdma_get_cm_event(rdma->channel, &cm_event);
|
||||||
|
if (ret) {
|
||||||
|
error_report("get_cm_event failed %d", errno);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rdma_ack_cm_event(cm_event);
|
||||||
|
|
||||||
|
if (cm_event->event == RDMA_CM_EVENT_DISCONNECTED ||
|
||||||
|
cm_event->event == RDMA_CM_EVENT_DEVICE_REMOVAL) {
|
||||||
|
error_report("receive cm event, cm event is %d", cm_event->event);
|
||||||
|
rdma->error_state = -EPIPE;
|
||||||
|
if (rdma->return_path) {
|
||||||
|
rdma->return_path->error_state = -EPIPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mis->migration_incoming_co) {
|
||||||
|
qemu_coroutine_enter(mis->migration_incoming_co);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int qemu_rdma_accept(RDMAContext *rdma)
|
static int qemu_rdma_accept(RDMAContext *rdma)
|
||||||
{
|
{
|
||||||
RDMACapabilities cap;
|
RDMACapabilities cap;
|
||||||
|
@ -3102,7 +3349,15 @@ static int qemu_rdma_accept(RDMAContext *rdma)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
qemu_set_fd_handler(rdma->channel->fd, NULL, NULL, NULL);
|
/* Accept the second connection request for return path */
|
||||||
|
if (migrate_postcopy() && !rdma->is_return_path) {
|
||||||
|
qemu_set_fd_handler(rdma->channel->fd, rdma_accept_incoming_migration,
|
||||||
|
NULL,
|
||||||
|
(void *)(intptr_t)rdma->return_path);
|
||||||
|
} else {
|
||||||
|
qemu_set_fd_handler(rdma->channel->fd, rdma_cm_poll_handler,
|
||||||
|
NULL, rdma);
|
||||||
|
}
|
||||||
|
|
||||||
ret = rdma_accept(rdma->cm_id, &conn_param);
|
ret = rdma_accept(rdma->cm_id, &conn_param);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
@ -3171,8 +3426,8 @@ static int qemu_rdma_registration_handle(QEMUFile *f, void *opaque)
|
||||||
RDMAControlHeader blocks = { .type = RDMA_CONTROL_RAM_BLOCKS_RESULT,
|
RDMAControlHeader blocks = { .type = RDMA_CONTROL_RAM_BLOCKS_RESULT,
|
||||||
.repeat = 1 };
|
.repeat = 1 };
|
||||||
QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(opaque);
|
QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(opaque);
|
||||||
RDMAContext *rdma = rioc->rdma;
|
RDMAContext *rdma;
|
||||||
RDMALocalBlocks *local = &rdma->local_ram_blocks;
|
RDMALocalBlocks *local;
|
||||||
RDMAControlHeader head;
|
RDMAControlHeader head;
|
||||||
RDMARegister *reg, *registers;
|
RDMARegister *reg, *registers;
|
||||||
RDMACompress *comp;
|
RDMACompress *comp;
|
||||||
|
@ -3185,8 +3440,17 @@ static int qemu_rdma_registration_handle(QEMUFile *f, void *opaque)
|
||||||
int count = 0;
|
int count = 0;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
rdma = atomic_rcu_read(&rioc->rdmain);
|
||||||
|
|
||||||
|
if (!rdma) {
|
||||||
|
rcu_read_unlock();
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
CHECK_ERROR_STATE();
|
CHECK_ERROR_STATE();
|
||||||
|
|
||||||
|
local = &rdma->local_ram_blocks;
|
||||||
do {
|
do {
|
||||||
trace_qemu_rdma_registration_handle_wait();
|
trace_qemu_rdma_registration_handle_wait();
|
||||||
|
|
||||||
|
@ -3420,6 +3684,7 @@ out:
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
rdma->error_state = ret;
|
rdma->error_state = ret;
|
||||||
}
|
}
|
||||||
|
rcu_read_unlock();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3433,10 +3698,18 @@ out:
|
||||||
static int
|
static int
|
||||||
rdma_block_notification_handle(QIOChannelRDMA *rioc, const char *name)
|
rdma_block_notification_handle(QIOChannelRDMA *rioc, const char *name)
|
||||||
{
|
{
|
||||||
RDMAContext *rdma = rioc->rdma;
|
RDMAContext *rdma;
|
||||||
int curr;
|
int curr;
|
||||||
int found = -1;
|
int found = -1;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
rdma = atomic_rcu_read(&rioc->rdmain);
|
||||||
|
|
||||||
|
if (!rdma) {
|
||||||
|
rcu_read_unlock();
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
/* Find the matching RAMBlock in our local list */
|
/* Find the matching RAMBlock in our local list */
|
||||||
for (curr = 0; curr < rdma->local_ram_blocks.nb_blocks; curr++) {
|
for (curr = 0; curr < rdma->local_ram_blocks.nb_blocks; curr++) {
|
||||||
if (!strcmp(rdma->local_ram_blocks.block[curr].block_name, name)) {
|
if (!strcmp(rdma->local_ram_blocks.block[curr].block_name, name)) {
|
||||||
|
@ -3447,6 +3720,7 @@ rdma_block_notification_handle(QIOChannelRDMA *rioc, const char *name)
|
||||||
|
|
||||||
if (found == -1) {
|
if (found == -1) {
|
||||||
error_report("RAMBlock '%s' not found on destination", name);
|
error_report("RAMBlock '%s' not found on destination", name);
|
||||||
|
rcu_read_unlock();
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3454,6 +3728,7 @@ rdma_block_notification_handle(QIOChannelRDMA *rioc, const char *name)
|
||||||
trace_rdma_block_notification_handle(name, rdma->next_src_index);
|
trace_rdma_block_notification_handle(name, rdma->next_src_index);
|
||||||
rdma->next_src_index++;
|
rdma->next_src_index++;
|
||||||
|
|
||||||
|
rcu_read_unlock();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3476,14 +3751,27 @@ static int qemu_rdma_registration_start(QEMUFile *f, void *opaque,
|
||||||
uint64_t flags, void *data)
|
uint64_t flags, void *data)
|
||||||
{
|
{
|
||||||
QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(opaque);
|
QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(opaque);
|
||||||
RDMAContext *rdma = rioc->rdma;
|
RDMAContext *rdma;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
rdma = atomic_rcu_read(&rioc->rdmaout);
|
||||||
|
if (!rdma) {
|
||||||
|
rcu_read_unlock();
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
CHECK_ERROR_STATE();
|
CHECK_ERROR_STATE();
|
||||||
|
|
||||||
|
if (migrate_get_current()->state == MIGRATION_STATUS_POSTCOPY_ACTIVE) {
|
||||||
|
rcu_read_unlock();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
trace_qemu_rdma_registration_start(flags);
|
trace_qemu_rdma_registration_start(flags);
|
||||||
qemu_put_be64(f, RAM_SAVE_FLAG_HOOK);
|
qemu_put_be64(f, RAM_SAVE_FLAG_HOOK);
|
||||||
qemu_fflush(f);
|
qemu_fflush(f);
|
||||||
|
|
||||||
|
rcu_read_unlock();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3496,12 +3784,24 @@ static int qemu_rdma_registration_stop(QEMUFile *f, void *opaque,
|
||||||
{
|
{
|
||||||
Error *local_err = NULL, **errp = &local_err;
|
Error *local_err = NULL, **errp = &local_err;
|
||||||
QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(opaque);
|
QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(opaque);
|
||||||
RDMAContext *rdma = rioc->rdma;
|
RDMAContext *rdma;
|
||||||
RDMAControlHeader head = { .len = 0, .repeat = 1 };
|
RDMAControlHeader head = { .len = 0, .repeat = 1 };
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
rdma = atomic_rcu_read(&rioc->rdmaout);
|
||||||
|
if (!rdma) {
|
||||||
|
rcu_read_unlock();
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
CHECK_ERROR_STATE();
|
CHECK_ERROR_STATE();
|
||||||
|
|
||||||
|
if (migrate_get_current()->state == MIGRATION_STATUS_POSTCOPY_ACTIVE) {
|
||||||
|
rcu_read_unlock();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
qemu_fflush(f);
|
qemu_fflush(f);
|
||||||
ret = qemu_rdma_drain_cq(f, rdma);
|
ret = qemu_rdma_drain_cq(f, rdma);
|
||||||
|
|
||||||
|
@ -3530,6 +3830,7 @@ static int qemu_rdma_registration_stop(QEMUFile *f, void *opaque,
|
||||||
qemu_rdma_reg_whole_ram_blocks : NULL);
|
qemu_rdma_reg_whole_ram_blocks : NULL);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
ERROR(errp, "receiving remote info!");
|
ERROR(errp, "receiving remote info!");
|
||||||
|
rcu_read_unlock();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3553,6 +3854,7 @@ static int qemu_rdma_registration_stop(QEMUFile *f, void *opaque,
|
||||||
"not identical on both the source and destination.",
|
"not identical on both the source and destination.",
|
||||||
local->nb_blocks, nb_dest_blocks);
|
local->nb_blocks, nb_dest_blocks);
|
||||||
rdma->error_state = -EINVAL;
|
rdma->error_state = -EINVAL;
|
||||||
|
rcu_read_unlock();
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3569,6 +3871,7 @@ static int qemu_rdma_registration_stop(QEMUFile *f, void *opaque,
|
||||||
local->block[i].length,
|
local->block[i].length,
|
||||||
rdma->dest_blocks[i].length);
|
rdma->dest_blocks[i].length);
|
||||||
rdma->error_state = -EINVAL;
|
rdma->error_state = -EINVAL;
|
||||||
|
rcu_read_unlock();
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
local->block[i].remote_host_addr =
|
local->block[i].remote_host_addr =
|
||||||
|
@ -3586,9 +3889,11 @@ static int qemu_rdma_registration_stop(QEMUFile *f, void *opaque,
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rcu_read_unlock();
|
||||||
return 0;
|
return 0;
|
||||||
err:
|
err:
|
||||||
rdma->error_state = ret;
|
rdma->error_state = ret;
|
||||||
|
rcu_read_unlock();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3606,10 +3911,15 @@ static const QEMUFileHooks rdma_write_hooks = {
|
||||||
static void qio_channel_rdma_finalize(Object *obj)
|
static void qio_channel_rdma_finalize(Object *obj)
|
||||||
{
|
{
|
||||||
QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(obj);
|
QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(obj);
|
||||||
if (rioc->rdma) {
|
if (rioc->rdmain) {
|
||||||
qemu_rdma_cleanup(rioc->rdma);
|
qemu_rdma_cleanup(rioc->rdmain);
|
||||||
g_free(rioc->rdma);
|
g_free(rioc->rdmain);
|
||||||
rioc->rdma = NULL;
|
rioc->rdmain = NULL;
|
||||||
|
}
|
||||||
|
if (rioc->rdmaout) {
|
||||||
|
qemu_rdma_cleanup(rioc->rdmaout);
|
||||||
|
g_free(rioc->rdmaout);
|
||||||
|
rioc->rdmaout = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3623,6 +3933,8 @@ static void qio_channel_rdma_class_init(ObjectClass *klass,
|
||||||
ioc_klass->io_set_blocking = qio_channel_rdma_set_blocking;
|
ioc_klass->io_set_blocking = qio_channel_rdma_set_blocking;
|
||||||
ioc_klass->io_close = qio_channel_rdma_close;
|
ioc_klass->io_close = qio_channel_rdma_close;
|
||||||
ioc_klass->io_create_watch = qio_channel_rdma_create_watch;
|
ioc_klass->io_create_watch = qio_channel_rdma_create_watch;
|
||||||
|
ioc_klass->io_set_aio_fd_handler = qio_channel_rdma_set_aio_fd_handler;
|
||||||
|
ioc_klass->io_shutdown = qio_channel_rdma_shutdown;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const TypeInfo qio_channel_rdma_info = {
|
static const TypeInfo qio_channel_rdma_info = {
|
||||||
|
@ -3649,13 +3961,16 @@ static QEMUFile *qemu_fopen_rdma(RDMAContext *rdma, const char *mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
rioc = QIO_CHANNEL_RDMA(object_new(TYPE_QIO_CHANNEL_RDMA));
|
rioc = QIO_CHANNEL_RDMA(object_new(TYPE_QIO_CHANNEL_RDMA));
|
||||||
rioc->rdma = rdma;
|
|
||||||
|
|
||||||
if (mode[0] == 'w') {
|
if (mode[0] == 'w') {
|
||||||
rioc->file = qemu_fopen_channel_output(QIO_CHANNEL(rioc));
|
rioc->file = qemu_fopen_channel_output(QIO_CHANNEL(rioc));
|
||||||
|
rioc->rdmaout = rdma;
|
||||||
|
rioc->rdmain = rdma->return_path;
|
||||||
qemu_file_set_hooks(rioc->file, &rdma_write_hooks);
|
qemu_file_set_hooks(rioc->file, &rdma_write_hooks);
|
||||||
} else {
|
} else {
|
||||||
rioc->file = qemu_fopen_channel_input(QIO_CHANNEL(rioc));
|
rioc->file = qemu_fopen_channel_input(QIO_CHANNEL(rioc));
|
||||||
|
rioc->rdmain = rdma;
|
||||||
|
rioc->rdmaout = rdma->return_path;
|
||||||
qemu_file_set_hooks(rioc->file, &rdma_read_hooks);
|
qemu_file_set_hooks(rioc->file, &rdma_read_hooks);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3679,6 +3994,10 @@ static void rdma_accept_incoming_migration(void *opaque)
|
||||||
|
|
||||||
trace_qemu_rdma_accept_incoming_migration_accepted();
|
trace_qemu_rdma_accept_incoming_migration_accepted();
|
||||||
|
|
||||||
|
if (rdma->is_return_path) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
f = qemu_fopen_rdma(rdma, "rb");
|
f = qemu_fopen_rdma(rdma, "rb");
|
||||||
if (f == NULL) {
|
if (f == NULL) {
|
||||||
ERROR(errp, "could not qemu_fopen_rdma!");
|
ERROR(errp, "could not qemu_fopen_rdma!");
|
||||||
|
@ -3693,7 +4012,7 @@ static void rdma_accept_incoming_migration(void *opaque)
|
||||||
void rdma_start_incoming_migration(const char *host_port, Error **errp)
|
void rdma_start_incoming_migration(const char *host_port, Error **errp)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
RDMAContext *rdma;
|
RDMAContext *rdma, *rdma_return_path;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
|
|
||||||
trace_rdma_start_incoming_migration();
|
trace_rdma_start_incoming_migration();
|
||||||
|
@ -3720,12 +4039,24 @@ void rdma_start_incoming_migration(const char *host_port, Error **errp)
|
||||||
|
|
||||||
trace_rdma_start_incoming_migration_after_rdma_listen();
|
trace_rdma_start_incoming_migration_after_rdma_listen();
|
||||||
|
|
||||||
|
/* initialize the RDMAContext for return path */
|
||||||
|
if (migrate_postcopy()) {
|
||||||
|
rdma_return_path = qemu_rdma_data_init(host_port, &local_err);
|
||||||
|
|
||||||
|
if (rdma_return_path == NULL) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
qemu_rdma_return_path_dest_init(rdma_return_path, rdma);
|
||||||
|
}
|
||||||
|
|
||||||
qemu_set_fd_handler(rdma->channel->fd, rdma_accept_incoming_migration,
|
qemu_set_fd_handler(rdma->channel->fd, rdma_accept_incoming_migration,
|
||||||
NULL, (void *)(intptr_t)rdma);
|
NULL, (void *)(intptr_t)rdma);
|
||||||
return;
|
return;
|
||||||
err:
|
err:
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
g_free(rdma);
|
g_free(rdma);
|
||||||
|
g_free(rdma_return_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
void rdma_start_outgoing_migration(void *opaque,
|
void rdma_start_outgoing_migration(void *opaque,
|
||||||
|
@ -3733,6 +4064,7 @@ void rdma_start_outgoing_migration(void *opaque,
|
||||||
{
|
{
|
||||||
MigrationState *s = opaque;
|
MigrationState *s = opaque;
|
||||||
RDMAContext *rdma = qemu_rdma_data_init(host_port, errp);
|
RDMAContext *rdma = qemu_rdma_data_init(host_port, errp);
|
||||||
|
RDMAContext *rdma_return_path = NULL;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
if (rdma == NULL) {
|
if (rdma == NULL) {
|
||||||
|
@ -3753,6 +4085,32 @@ void rdma_start_outgoing_migration(void *opaque,
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* RDMA postcopy need a seprate queue pair for return path */
|
||||||
|
if (migrate_postcopy()) {
|
||||||
|
rdma_return_path = qemu_rdma_data_init(host_port, errp);
|
||||||
|
|
||||||
|
if (rdma_return_path == NULL) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = qemu_rdma_source_init(rdma_return_path,
|
||||||
|
s->enabled_capabilities[MIGRATION_CAPABILITY_RDMA_PIN_ALL], errp);
|
||||||
|
|
||||||
|
if (ret) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = qemu_rdma_connect(rdma_return_path, errp);
|
||||||
|
|
||||||
|
if (ret) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
rdma->return_path = rdma_return_path;
|
||||||
|
rdma_return_path->return_path = rdma;
|
||||||
|
rdma_return_path->is_return_path = true;
|
||||||
|
}
|
||||||
|
|
||||||
trace_rdma_start_outgoing_migration_after_rdma_connect();
|
trace_rdma_start_outgoing_migration_after_rdma_connect();
|
||||||
|
|
||||||
s->to_dst_file = qemu_fopen_rdma(rdma, "wb");
|
s->to_dst_file = qemu_fopen_rdma(rdma, "wb");
|
||||||
|
@ -3760,4 +4118,5 @@ void rdma_start_outgoing_migration(void *opaque,
|
||||||
return;
|
return;
|
||||||
err:
|
err:
|
||||||
g_free(rdma);
|
g_free(rdma);
|
||||||
|
g_free(rdma_return_path);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1622,6 +1622,7 @@ static void *postcopy_ram_listen_thread(void *opaque)
|
||||||
qemu_sem_post(&mis->listen_thread_sem);
|
qemu_sem_post(&mis->listen_thread_sem);
|
||||||
trace_postcopy_ram_listen_thread_start();
|
trace_postcopy_ram_listen_thread_start();
|
||||||
|
|
||||||
|
rcu_register_thread();
|
||||||
/*
|
/*
|
||||||
* Because we're a thread and not a coroutine we can't yield
|
* Because we're a thread and not a coroutine we can't yield
|
||||||
* in qemu_file, and thus we must be blocking now.
|
* in qemu_file, and thus we must be blocking now.
|
||||||
|
@ -1662,6 +1663,7 @@ static void *postcopy_ram_listen_thread(void *opaque)
|
||||||
* to leave the guest running and fire MCEs for pages that never
|
* to leave the guest running and fire MCEs for pages that never
|
||||||
* arrived as a desperate recovery step.
|
* arrived as a desperate recovery step.
|
||||||
*/
|
*/
|
||||||
|
rcu_unregister_thread();
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1676,6 +1678,7 @@ static void *postcopy_ram_listen_thread(void *opaque)
|
||||||
migration_incoming_state_destroy();
|
migration_incoming_state_destroy();
|
||||||
qemu_loadvm_state_cleanup();
|
qemu_loadvm_state_cleanup();
|
||||||
|
|
||||||
|
rcu_unregister_thread();
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -418,7 +418,7 @@ int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd,
|
||||||
static const VMStateDescription *
|
static const VMStateDescription *
|
||||||
vmstate_get_subsection(const VMStateDescription **sub, char *idstr)
|
vmstate_get_subsection(const VMStateDescription **sub, char *idstr)
|
||||||
{
|
{
|
||||||
while (sub && *sub && (*sub)->needed) {
|
while (sub && *sub) {
|
||||||
if (strcmp(idstr, (*sub)->name) == 0) {
|
if (strcmp(idstr, (*sub)->name) == 0) {
|
||||||
return *sub;
|
return *sub;
|
||||||
}
|
}
|
||||||
|
@ -486,8 +486,8 @@ static int vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd,
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
trace_vmstate_subsection_save_top(vmsd->name);
|
trace_vmstate_subsection_save_top(vmsd->name);
|
||||||
while (sub && *sub && (*sub)->needed) {
|
while (sub && *sub) {
|
||||||
if ((*sub)->needed(opaque)) {
|
if (vmstate_save_needed(*sub, opaque)) {
|
||||||
const VMStateDescription *vmsdsub = *sub;
|
const VMStateDescription *vmsdsub = *sub;
|
||||||
uint8_t len;
|
uint8_t len;
|
||||||
|
|
||||||
|
|
|
@ -212,13 +212,13 @@
|
||||||
# -> { "execute": "query-migrate" }
|
# -> { "execute": "query-migrate" }
|
||||||
# <- { "return": {
|
# <- { "return": {
|
||||||
# "status": "completed",
|
# "status": "completed",
|
||||||
|
# "total-time":12345,
|
||||||
|
# "setup-time":12345,
|
||||||
|
# "downtime":12345,
|
||||||
# "ram":{
|
# "ram":{
|
||||||
# "transferred":123,
|
# "transferred":123,
|
||||||
# "remaining":123,
|
# "remaining":123,
|
||||||
# "total":246,
|
# "total":246,
|
||||||
# "total-time":12345,
|
|
||||||
# "setup-time":12345,
|
|
||||||
# "downtime":12345,
|
|
||||||
# "duplicate":123,
|
# "duplicate":123,
|
||||||
# "normal":123,
|
# "normal":123,
|
||||||
# "normal-bytes":123456,
|
# "normal-bytes":123456,
|
||||||
|
@ -238,13 +238,13 @@
|
||||||
# <- {
|
# <- {
|
||||||
# "return":{
|
# "return":{
|
||||||
# "status":"active",
|
# "status":"active",
|
||||||
|
# "total-time":12345,
|
||||||
|
# "setup-time":12345,
|
||||||
|
# "expected-downtime":12345,
|
||||||
# "ram":{
|
# "ram":{
|
||||||
# "transferred":123,
|
# "transferred":123,
|
||||||
# "remaining":123,
|
# "remaining":123,
|
||||||
# "total":246,
|
# "total":246,
|
||||||
# "total-time":12345,
|
|
||||||
# "setup-time":12345,
|
|
||||||
# "expected-downtime":12345,
|
|
||||||
# "duplicate":123,
|
# "duplicate":123,
|
||||||
# "normal":123,
|
# "normal":123,
|
||||||
# "normal-bytes":123456,
|
# "normal-bytes":123456,
|
||||||
|
@ -259,13 +259,13 @@
|
||||||
# <- {
|
# <- {
|
||||||
# "return":{
|
# "return":{
|
||||||
# "status":"active",
|
# "status":"active",
|
||||||
|
# "total-time":12345,
|
||||||
|
# "setup-time":12345,
|
||||||
|
# "expected-downtime":12345,
|
||||||
# "ram":{
|
# "ram":{
|
||||||
# "total":1057024,
|
# "total":1057024,
|
||||||
# "remaining":1053304,
|
# "remaining":1053304,
|
||||||
# "transferred":3720,
|
# "transferred":3720,
|
||||||
# "total-time":12345,
|
|
||||||
# "setup-time":12345,
|
|
||||||
# "expected-downtime":12345,
|
|
||||||
# "duplicate":123,
|
# "duplicate":123,
|
||||||
# "normal":123,
|
# "normal":123,
|
||||||
# "normal-bytes":123456,
|
# "normal-bytes":123456,
|
||||||
|
@ -285,14 +285,13 @@
|
||||||
# <- {
|
# <- {
|
||||||
# "return":{
|
# "return":{
|
||||||
# "status":"active",
|
# "status":"active",
|
||||||
# "capabilities" : [ { "capability": "xbzrle", "state" : true } ],
|
# "total-time":12345,
|
||||||
|
# "setup-time":12345,
|
||||||
|
# "expected-downtime":12345,
|
||||||
# "ram":{
|
# "ram":{
|
||||||
# "total":1057024,
|
# "total":1057024,
|
||||||
# "remaining":1053304,
|
# "remaining":1053304,
|
||||||
# "transferred":3720,
|
# "transferred":3720,
|
||||||
# "total-time":12345,
|
|
||||||
# "setup-time":12345,
|
|
||||||
# "expected-downtime":12345,
|
|
||||||
# "duplicate":10,
|
# "duplicate":10,
|
||||||
# "normal":3333,
|
# "normal":3333,
|
||||||
# "normal-bytes":3412992,
|
# "normal-bytes":3412992,
|
||||||
|
@ -462,6 +461,11 @@
|
||||||
# @compress-threads: Set compression thread count to be used in live migration,
|
# @compress-threads: Set compression thread count to be used in live migration,
|
||||||
# the compression thread count is an integer between 1 and 255.
|
# the compression thread count is an integer between 1 and 255.
|
||||||
#
|
#
|
||||||
|
# @compress-wait-thread: Controls behavior when all compression threads are
|
||||||
|
# currently busy. If true (default), wait for a free
|
||||||
|
# compression thread to become available; otherwise,
|
||||||
|
# send the page uncompressed. (Since 3.1)
|
||||||
|
#
|
||||||
# @decompress-threads: Set decompression thread count to be used in live
|
# @decompress-threads: Set decompression thread count to be used in live
|
||||||
# migration, the decompression thread count is an integer between 1
|
# migration, the decompression thread count is an integer between 1
|
||||||
# and 255. Usually, decompression is at least 4 times as fast as
|
# and 255. Usually, decompression is at least 4 times as fast as
|
||||||
|
@ -523,15 +527,20 @@
|
||||||
# @max-postcopy-bandwidth: Background transfer bandwidth during postcopy.
|
# @max-postcopy-bandwidth: Background transfer bandwidth during postcopy.
|
||||||
# Defaults to 0 (unlimited). In bytes per second.
|
# Defaults to 0 (unlimited). In bytes per second.
|
||||||
# (Since 3.0)
|
# (Since 3.0)
|
||||||
|
#
|
||||||
|
# @max-cpu-throttle: maximum cpu throttle percentage.
|
||||||
|
# Defaults to 99. (Since 3.1)
|
||||||
# Since: 2.4
|
# Since: 2.4
|
||||||
##
|
##
|
||||||
{ 'enum': 'MigrationParameter',
|
{ 'enum': 'MigrationParameter',
|
||||||
'data': ['compress-level', 'compress-threads', 'decompress-threads',
|
'data': ['compress-level', 'compress-threads', 'decompress-threads',
|
||||||
|
'compress-wait-thread',
|
||||||
'cpu-throttle-initial', 'cpu-throttle-increment',
|
'cpu-throttle-initial', 'cpu-throttle-increment',
|
||||||
'tls-creds', 'tls-hostname', 'max-bandwidth',
|
'tls-creds', 'tls-hostname', 'max-bandwidth',
|
||||||
'downtime-limit', 'x-checkpoint-delay', 'block-incremental',
|
'downtime-limit', 'x-checkpoint-delay', 'block-incremental',
|
||||||
'x-multifd-channels', 'x-multifd-page-count',
|
'x-multifd-channels', 'x-multifd-page-count',
|
||||||
'xbzrle-cache-size', 'max-postcopy-bandwidth' ] }
|
'xbzrle-cache-size', 'max-postcopy-bandwidth',
|
||||||
|
'max-cpu-throttle' ] }
|
||||||
|
|
||||||
##
|
##
|
||||||
# @MigrateSetParameters:
|
# @MigrateSetParameters:
|
||||||
|
@ -540,6 +549,11 @@
|
||||||
#
|
#
|
||||||
# @compress-threads: compression thread count
|
# @compress-threads: compression thread count
|
||||||
#
|
#
|
||||||
|
# @compress-wait-thread: Controls behavior when all compression threads are
|
||||||
|
# currently busy. If true (default), wait for a free
|
||||||
|
# compression thread to become available; otherwise,
|
||||||
|
# send the page uncompressed. (Since 3.1)
|
||||||
|
#
|
||||||
# @decompress-threads: decompression thread count
|
# @decompress-threads: decompression thread count
|
||||||
#
|
#
|
||||||
# @cpu-throttle-initial: Initial percentage of time guest cpus are
|
# @cpu-throttle-initial: Initial percentage of time guest cpus are
|
||||||
|
@ -603,6 +617,10 @@
|
||||||
# @max-postcopy-bandwidth: Background transfer bandwidth during postcopy.
|
# @max-postcopy-bandwidth: Background transfer bandwidth during postcopy.
|
||||||
# Defaults to 0 (unlimited). In bytes per second.
|
# Defaults to 0 (unlimited). In bytes per second.
|
||||||
# (Since 3.0)
|
# (Since 3.0)
|
||||||
|
#
|
||||||
|
# @max-cpu-throttle: maximum cpu throttle percentage.
|
||||||
|
# The default value is 99. (Since 3.1)
|
||||||
|
#
|
||||||
# Since: 2.4
|
# Since: 2.4
|
||||||
##
|
##
|
||||||
# TODO either fuse back into MigrationParameters, or make
|
# TODO either fuse back into MigrationParameters, or make
|
||||||
|
@ -610,6 +628,7 @@
|
||||||
{ 'struct': 'MigrateSetParameters',
|
{ 'struct': 'MigrateSetParameters',
|
||||||
'data': { '*compress-level': 'int',
|
'data': { '*compress-level': 'int',
|
||||||
'*compress-threads': 'int',
|
'*compress-threads': 'int',
|
||||||
|
'*compress-wait-thread': 'bool',
|
||||||
'*decompress-threads': 'int',
|
'*decompress-threads': 'int',
|
||||||
'*cpu-throttle-initial': 'int',
|
'*cpu-throttle-initial': 'int',
|
||||||
'*cpu-throttle-increment': 'int',
|
'*cpu-throttle-increment': 'int',
|
||||||
|
@ -622,7 +641,8 @@
|
||||||
'*x-multifd-channels': 'int',
|
'*x-multifd-channels': 'int',
|
||||||
'*x-multifd-page-count': 'int',
|
'*x-multifd-page-count': 'int',
|
||||||
'*xbzrle-cache-size': 'size',
|
'*xbzrle-cache-size': 'size',
|
||||||
'*max-postcopy-bandwidth': 'size' } }
|
'*max-postcopy-bandwidth': 'size',
|
||||||
|
'*max-cpu-throttle': 'int' } }
|
||||||
|
|
||||||
##
|
##
|
||||||
# @migrate-set-parameters:
|
# @migrate-set-parameters:
|
||||||
|
@ -649,6 +669,11 @@
|
||||||
#
|
#
|
||||||
# @compress-threads: compression thread count
|
# @compress-threads: compression thread count
|
||||||
#
|
#
|
||||||
|
# @compress-wait-thread: Controls behavior when all compression threads are
|
||||||
|
# currently busy. If true (default), wait for a free
|
||||||
|
# compression thread to become available; otherwise,
|
||||||
|
# send the page uncompressed. (Since 3.1)
|
||||||
|
#
|
||||||
# @decompress-threads: decompression thread count
|
# @decompress-threads: decompression thread count
|
||||||
#
|
#
|
||||||
# @cpu-throttle-initial: Initial percentage of time guest cpus are
|
# @cpu-throttle-initial: Initial percentage of time guest cpus are
|
||||||
|
@ -709,11 +734,17 @@
|
||||||
# @max-postcopy-bandwidth: Background transfer bandwidth during postcopy.
|
# @max-postcopy-bandwidth: Background transfer bandwidth during postcopy.
|
||||||
# Defaults to 0 (unlimited). In bytes per second.
|
# Defaults to 0 (unlimited). In bytes per second.
|
||||||
# (Since 3.0)
|
# (Since 3.0)
|
||||||
|
#
|
||||||
|
# @max-cpu-throttle: maximum cpu throttle percentage.
|
||||||
|
# Defaults to 99.
|
||||||
|
# (Since 3.1)
|
||||||
|
#
|
||||||
# Since: 2.4
|
# Since: 2.4
|
||||||
##
|
##
|
||||||
{ 'struct': 'MigrationParameters',
|
{ 'struct': 'MigrationParameters',
|
||||||
'data': { '*compress-level': 'uint8',
|
'data': { '*compress-level': 'uint8',
|
||||||
'*compress-threads': 'uint8',
|
'*compress-threads': 'uint8',
|
||||||
|
'*compress-wait-thread': 'bool',
|
||||||
'*decompress-threads': 'uint8',
|
'*decompress-threads': 'uint8',
|
||||||
'*cpu-throttle-initial': 'uint8',
|
'*cpu-throttle-initial': 'uint8',
|
||||||
'*cpu-throttle-increment': 'uint8',
|
'*cpu-throttle-increment': 'uint8',
|
||||||
|
@ -726,7 +757,8 @@
|
||||||
'*x-multifd-channels': 'uint8',
|
'*x-multifd-channels': 'uint8',
|
||||||
'*x-multifd-page-count': 'uint32',
|
'*x-multifd-page-count': 'uint32',
|
||||||
'*xbzrle-cache-size': 'size',
|
'*xbzrle-cache-size': 'size',
|
||||||
'*max-postcopy-bandwidth': 'size' } }
|
'*max-postcopy-bandwidth': 'size',
|
||||||
|
'*max-cpu-throttle':'uint8'} }
|
||||||
|
|
||||||
##
|
##
|
||||||
# @query-migrate-parameters:
|
# @query-migrate-parameters:
|
||||||
|
|
Loading…
Reference in New Issue