mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
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:
@@ -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
|
||||
to a versioned machine type.
|
||||
|
||||
One important note is that the post_load() function is called "after"
|
||||
loading all subsections, because a newer subsection could change same
|
||||
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
|
||||
The 'pre_load' and 'post_load' functions on subsections are only
|
||||
called if the subsection is loaded.
|
||||
|
||||
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.
|
||||
|
||||
Example:
|
||||
@@ -315,8 +318,8 @@ For example:
|
||||
the property to false.
|
||||
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
|
||||
e) (potentially) Add a pre_load that sets up a default value for 'foo'
|
||||
to be used if the subsection isn't loaded.
|
||||
e) (potentially) Add an outer pre_load that sets up a default value
|
||||
for 'foo' to be used if the subsection isn't loaded.
|
||||
|
||||
Now that subsection will not be generated when using an older
|
||||
machine type and the migration stream will be accepted by older
|
||||
|
||||
@@ -327,6 +327,10 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict)
|
||||
monitor_printf(mon, "%s: %u\n",
|
||||
MigrationParameter_str(MIGRATION_PARAMETER_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);
|
||||
monitor_printf(mon, "%s: %u\n",
|
||||
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",
|
||||
MigrationParameter_str(MIGRATION_PARAMETER_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);
|
||||
monitor_printf(mon, "%s: '%s'\n",
|
||||
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;
|
||||
visit_type_int(v, param, &p->compress_threads, &err);
|
||||
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:
|
||||
p->has_decompress_threads = true;
|
||||
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;
|
||||
visit_type_int(v, param, &p->cpu_throttle_increment, &err);
|
||||
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:
|
||||
p->has_tls_creds = true;
|
||||
p->tls_creds = g_new0(StrOrNull, 1);
|
||||
|
||||
@@ -341,6 +341,7 @@ struct { \
|
||||
/*
|
||||
* 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_FIRST(head) ((head)->sqh_first)
|
||||
#define QSIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
|
||||
|
||||
@@ -534,6 +534,7 @@ void *colo_process_incoming_thread(void *opaque)
|
||||
uint64_t value;
|
||||
Error *local_err = NULL;
|
||||
|
||||
rcu_register_thread();
|
||||
qemu_sem_init(&mis->colo_incoming_sem, 0);
|
||||
|
||||
migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE,
|
||||
@@ -666,5 +667,6 @@ out:
|
||||
}
|
||||
migration_incoming_exit_colo();
|
||||
|
||||
rcu_unregister_thread();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
+47
-2
@@ -71,6 +71,7 @@
|
||||
/* Define default autoconverge cpu throttle migration parameters */
|
||||
#define DEFAULT_MIGRATE_CPU_THROTTLE_INITIAL 20
|
||||
#define DEFAULT_MIGRATE_CPU_THROTTLE_INCREMENT 10
|
||||
#define DEFAULT_MIGRATE_MAX_CPU_THROTTLE 99
|
||||
|
||||
/* Migration XBZRLE default cache size */
|
||||
#define DEFAULT_MIGRATE_XBZRLE_CACHE_SIZE (64 * 1024 * 1024)
|
||||
@@ -389,6 +390,7 @@ static void process_incoming_migration_co(void *opaque)
|
||||
int ret;
|
||||
|
||||
assert(mis->from_src_file);
|
||||
mis->migration_incoming_co = qemu_coroutine_self();
|
||||
mis->largest_page_size = qemu_ram_pagesize_largest();
|
||||
postcopy_state_set(POSTCOPY_INCOMING_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 */
|
||||
if (!ret && migration_incoming_enable_colo()) {
|
||||
mis->migration_incoming_co = qemu_coroutine_self();
|
||||
qemu_thread_create(&mis->colo_incoming_thread, "COLO incoming",
|
||||
colo_process_incoming_thread, mis, QEMU_THREAD_JOINABLE);
|
||||
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);
|
||||
qemu_bh_schedule(mis->bh);
|
||||
mis->migration_incoming_co = NULL;
|
||||
}
|
||||
|
||||
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->has_compress_threads = true;
|
||||
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->decompress_threads = s->parameters.decompress_threads;
|
||||
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->has_max_postcopy_bandwidth = true;
|
||||
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;
|
||||
}
|
||||
@@ -1043,6 +1049,15 @@ static bool migrate_params_check(MigrationParameters *params, Error **errp)
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -1061,6 +1076,10 @@ static void migrate_params_test_apply(MigrateSetParameters *params,
|
||||
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) {
|
||||
dest->decompress_threads = params->decompress_threads;
|
||||
}
|
||||
@@ -1110,6 +1129,9 @@ static void migrate_params_test_apply(MigrateSetParameters *params,
|
||||
if (params->has_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)
|
||||
@@ -1126,6 +1148,10 @@ static void migrate_params_apply(MigrateSetParameters *params, Error **errp)
|
||||
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) {
|
||||
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) {
|
||||
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)
|
||||
@@ -1871,6 +1900,15 @@ int migrate_compress_threads(void)
|
||||
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)
|
||||
{
|
||||
MigrationState *s;
|
||||
@@ -1962,7 +2000,6 @@ static int64_t migrate_max_postcopy_bandwidth(void)
|
||||
return s->parameters.max_postcopy_bandwidth;
|
||||
}
|
||||
|
||||
|
||||
bool migrate_use_block(void)
|
||||
{
|
||||
MigrationState *s;
|
||||
@@ -2104,6 +2141,7 @@ static void *source_return_path_thread(void *opaque)
|
||||
int res;
|
||||
|
||||
trace_source_return_path_thread_entry();
|
||||
rcu_register_thread();
|
||||
|
||||
retry:
|
||||
while (!ms->rp_state.error && !qemu_file_get_error(rp) &&
|
||||
@@ -2243,6 +2281,7 @@ out:
|
||||
trace_source_return_path_thread_end();
|
||||
ms->rp_state.from_dst_file = NULL;
|
||||
qemu_fclose(rp);
|
||||
rcu_unregister_thread();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -3131,6 +3170,8 @@ static Property migration_properties[] = {
|
||||
DEFINE_PROP_UINT8("x-compress-threads", MigrationState,
|
||||
parameters.compress_threads,
|
||||
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,
|
||||
parameters.decompress_threads,
|
||||
DEFAULT_MIGRATE_DECOMPRESS_THREAD_COUNT),
|
||||
@@ -3160,6 +3201,9 @@ static Property migration_properties[] = {
|
||||
DEFINE_PROP_SIZE("max-postcopy-bandwidth", MigrationState,
|
||||
parameters.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 */
|
||||
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_xbzrle_cache_size = 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_rp_sem, 0);
|
||||
|
||||
@@ -266,11 +266,13 @@ bool migrate_colo_enabled(void);
|
||||
|
||||
bool migrate_use_block(void);
|
||||
bool migrate_use_block_incremental(void);
|
||||
int migrate_max_cpu_throttle(void);
|
||||
bool migrate_use_return_path(void);
|
||||
|
||||
bool migrate_use_compression(void);
|
||||
int migrate_compress_level(void);
|
||||
int migrate_compress_threads(void);
|
||||
int migrate_compress_wait_thread(void);
|
||||
int migrate_decompress_threads(void);
|
||||
bool migrate_use_events(void);
|
||||
bool migrate_postcopy_blocktime(void);
|
||||
|
||||
@@ -853,6 +853,7 @@ static void *postcopy_ram_fault_thread(void *opaque)
|
||||
RAMBlock *rb = NULL;
|
||||
|
||||
trace_postcopy_ram_fault_thread_entry();
|
||||
rcu_register_thread();
|
||||
mis->last_rb = NULL; /* last RAMBlock we sent part of */
|
||||
qemu_sem_post(&mis->fault_thread_sem);
|
||||
|
||||
@@ -1059,6 +1060,7 @@ retry:
|
||||
}
|
||||
}
|
||||
}
|
||||
rcu_unregister_thread();
|
||||
trace_postcopy_ram_fault_thread_exit();
|
||||
g_free(pfd);
|
||||
return NULL;
|
||||
|
||||
@@ -49,7 +49,11 @@ static ssize_t channel_writev_buffer(void *opaque,
|
||||
ssize_t len;
|
||||
len = qio_channel_writev(ioc, local_iov, nlocal_iov, NULL);
|
||||
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;
|
||||
}
|
||||
if (len < 0) {
|
||||
@@ -80,7 +84,11 @@ static ssize_t channel_get_buffer(void *opaque,
|
||||
ret = qio_channel_read(ioc, (char *)buf, size, NULL);
|
||||
if (ret < 0) {
|
||||
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 {
|
||||
/* XXX handle Error * object */
|
||||
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) {
|
||||
int ret = f->hooks->save_page(f, f->opaque, block_offset,
|
||||
offset, size, bytes_sent);
|
||||
f->bytes_xfer += size;
|
||||
if (ret != RAM_SAVE_CONTROL_DELAYED) {
|
||||
if (ret != RAM_SAVE_CONTROL_NOT_SUPP) {
|
||||
f->bytes_xfer += size;
|
||||
}
|
||||
|
||||
if (ret != RAM_SAVE_CONTROL_DELAYED &&
|
||||
ret != RAM_SAVE_CONTROL_NOT_SUPP) {
|
||||
if (bytes_sent && *bytes_sent > 0) {
|
||||
qemu_update_position(f, *bytes_sent);
|
||||
} else if (ret < 0) {
|
||||
|
||||
+139
-63
@@ -341,6 +341,7 @@ typedef struct PageSearchStatus PageSearchStatus;
|
||||
struct CompressParam {
|
||||
bool done;
|
||||
bool quit;
|
||||
bool zero_page;
|
||||
QEMUFile *file;
|
||||
QemuMutex mutex;
|
||||
QemuCond cond;
|
||||
@@ -382,14 +383,15 @@ static QemuThread *decompress_threads;
|
||||
static QemuMutex decomp_done_lock;
|
||||
static QemuCond decomp_done_cond;
|
||||
|
||||
static int do_compress_ram_page(QEMUFile *f, z_stream *stream, RAMBlock *block,
|
||||
ram_addr_t offset, uint8_t *source_buf);
|
||||
static bool do_compress_ram_page(QEMUFile *f, z_stream *stream, RAMBlock *block,
|
||||
ram_addr_t offset, uint8_t *source_buf);
|
||||
|
||||
static void *do_data_compress(void *opaque)
|
||||
{
|
||||
CompressParam *param = opaque;
|
||||
RAMBlock *block;
|
||||
ram_addr_t offset;
|
||||
bool zero_page;
|
||||
|
||||
qemu_mutex_lock(¶m->mutex);
|
||||
while (!param->quit) {
|
||||
@@ -399,11 +401,12 @@ static void *do_data_compress(void *opaque)
|
||||
param->block = NULL;
|
||||
qemu_mutex_unlock(¶m->mutex);
|
||||
|
||||
do_compress_ram_page(param->file, ¶m->stream, block, offset,
|
||||
param->originbuf);
|
||||
zero_page = do_compress_ram_page(param->file, ¶m->stream,
|
||||
block, offset, param->originbuf);
|
||||
|
||||
qemu_mutex_lock(&comp_done_lock);
|
||||
param->done = true;
|
||||
param->zero_page = zero_page;
|
||||
qemu_cond_signal(&comp_done_cond);
|
||||
qemu_mutex_unlock(&comp_done_lock);
|
||||
|
||||
@@ -989,6 +992,7 @@ static void *multifd_send_thread(void *opaque)
|
||||
int ret;
|
||||
|
||||
trace_multifd_send_thread_start(p->id);
|
||||
rcu_register_thread();
|
||||
|
||||
if (multifd_send_initial_packet(p, &local_err) < 0) {
|
||||
goto out;
|
||||
@@ -1051,6 +1055,7 @@ out:
|
||||
p->running = false;
|
||||
qemu_mutex_unlock(&p->mutex);
|
||||
|
||||
rcu_unregister_thread();
|
||||
trace_multifd_send_thread_end(p->id, p->num_packets, p->num_pages);
|
||||
|
||||
return NULL;
|
||||
@@ -1220,6 +1225,7 @@ static void *multifd_recv_thread(void *opaque)
|
||||
int ret;
|
||||
|
||||
trace_multifd_recv_thread_start(p->id);
|
||||
rcu_register_thread();
|
||||
|
||||
while (true) {
|
||||
uint32_t used;
|
||||
@@ -1266,6 +1272,7 @@ static void *multifd_recv_thread(void *opaque)
|
||||
p->running = false;
|
||||
qemu_mutex_unlock(&p->mutex);
|
||||
|
||||
rcu_unregister_thread();
|
||||
trace_multifd_recv_thread_end(p->id, p->num_packets, p->num_pages);
|
||||
|
||||
return NULL;
|
||||
@@ -1391,13 +1398,15 @@ static void mig_throttle_guest_down(void)
|
||||
MigrationState *s = migrate_get_current();
|
||||
uint64_t pct_initial = s->parameters.cpu_throttle_initial;
|
||||
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. */
|
||||
if (!cpu_throttle_active()) {
|
||||
cpu_throttle_set(pct_initial);
|
||||
} else {
|
||||
/* 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
|
||||
*
|
||||
@@ -1676,19 +1710,14 @@ static void migration_bitmap_sync(RAMState *rs)
|
||||
*/
|
||||
static int save_zero_page(RAMState *rs, RAMBlock *block, ram_addr_t offset)
|
||||
{
|
||||
uint8_t *p = block->host + offset;
|
||||
int pages = -1;
|
||||
int len = save_zero_page_to_file(rs, rs->f, block, offset);
|
||||
|
||||
if (is_zero_range(p, TARGET_PAGE_SIZE)) {
|
||||
if (len) {
|
||||
ram_counters.duplicate++;
|
||||
ram_counters.transferred +=
|
||||
save_page_header(rs, rs->f, block, offset | RAM_SAVE_FLAG_ZERO);
|
||||
qemu_put_byte(rs->f, 0);
|
||||
ram_counters.transferred += 1;
|
||||
pages = 1;
|
||||
ram_counters.transferred += len;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return pages;
|
||||
return -1;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static int do_compress_ram_page(QEMUFile *f, z_stream *stream, RAMBlock *block,
|
||||
ram_addr_t offset, uint8_t *source_buf)
|
||||
static bool do_compress_ram_page(QEMUFile *f, z_stream *stream, RAMBlock *block,
|
||||
ram_addr_t offset, uint8_t *source_buf)
|
||||
{
|
||||
RAMState *rs = ram_state;
|
||||
int bytes_sent, blen;
|
||||
uint8_t *p = block->host + (offset & TARGET_PAGE_MASK);
|
||||
bool zero_page = false;
|
||||
int ret;
|
||||
|
||||
bytes_sent = save_page_header(rs, f, block, offset |
|
||||
RAM_SAVE_FLAG_COMPRESS_PAGE);
|
||||
if (save_zero_page_to_file(rs, f, block, offset)) {
|
||||
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
|
||||
@@ -1839,17 +1873,25 @@ static int do_compress_ram_page(QEMUFile *f, z_stream *stream, RAMBlock *block,
|
||||
* decompression
|
||||
*/
|
||||
memcpy(source_buf, p, TARGET_PAGE_SIZE);
|
||||
blen = qemu_put_compression_data(f, stream, source_buf, TARGET_PAGE_SIZE);
|
||||
if (blen < 0) {
|
||||
bytes_sent = 0;
|
||||
qemu_file_set_error(migrate_get_current()->to_dst_file, blen);
|
||||
ret = qemu_put_compression_data(f, stream, source_buf, TARGET_PAGE_SIZE);
|
||||
if (ret < 0) {
|
||||
qemu_file_set_error(migrate_get_current()->to_dst_file, ret);
|
||||
error_report("compressed data failed!");
|
||||
} else {
|
||||
bytes_sent += blen;
|
||||
ram_release_pages(block->idstr, offset & TARGET_PAGE_MASK, 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -1873,7 +1915,12 @@ static void flush_compressed_data(RAMState *rs)
|
||||
qemu_mutex_lock(&comp_param[idx].mutex);
|
||||
if (!comp_param[idx].quit) {
|
||||
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);
|
||||
}
|
||||
@@ -1890,30 +1937,33 @@ static int compress_page_with_multi_thread(RAMState *rs, RAMBlock *block,
|
||||
ram_addr_t offset)
|
||||
{
|
||||
int idx, thread_count, bytes_xmit = -1, pages = -1;
|
||||
bool wait = migrate_compress_wait_thread();
|
||||
|
||||
thread_count = migrate_compress_threads();
|
||||
qemu_mutex_lock(&comp_done_lock);
|
||||
while (true) {
|
||||
for (idx = 0; idx < thread_count; idx++) {
|
||||
if (comp_param[idx].done) {
|
||||
comp_param[idx].done = false;
|
||||
bytes_xmit = qemu_put_qemu_file(rs->f, comp_param[idx].file);
|
||||
qemu_mutex_lock(&comp_param[idx].mutex);
|
||||
set_compress_params(&comp_param[idx], block, offset);
|
||||
qemu_cond_signal(&comp_param[idx].cond);
|
||||
qemu_mutex_unlock(&comp_param[idx].mutex);
|
||||
pages = 1;
|
||||
ram_counters.normal++;
|
||||
ram_counters.transferred += bytes_xmit;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (pages > 0) {
|
||||
retry:
|
||||
for (idx = 0; idx < thread_count; idx++) {
|
||||
if (comp_param[idx].done) {
|
||||
comp_param[idx].done = false;
|
||||
bytes_xmit = qemu_put_qemu_file(rs->f, comp_param[idx].file);
|
||||
qemu_mutex_lock(&comp_param[idx].mutex);
|
||||
set_compress_params(&comp_param[idx], block, offset);
|
||||
qemu_cond_signal(&comp_param[idx].cond);
|
||||
qemu_mutex_unlock(&comp_param[idx].mutex);
|
||||
pages = 1;
|
||||
update_compress_thread_counts(&comp_param[idx], bytes_xmit);
|
||||
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);
|
||||
|
||||
return pages;
|
||||
@@ -1983,6 +2033,10 @@ static RAMBlock *unqueue_page(RAMState *rs, ram_addr_t *offset)
|
||||
{
|
||||
RAMBlock *block = NULL;
|
||||
|
||||
if (QSIMPLEQ_EMPTY_ATOMIC(&rs->src_page_requests)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
qemu_mutex_lock(&rs->src_page_req_mutex);
|
||||
if (!QSIMPLEQ_EMPTY(&rs->src_page_requests)) {
|
||||
struct RAMSrcPageRequest *entry =
|
||||
@@ -2175,6 +2229,39 @@ static bool save_page_use_compression(RAMState *rs)
|
||||
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
|
||||
*
|
||||
@@ -2195,15 +2282,8 @@ static int ram_save_target_page(RAMState *rs, PageSearchStatus *pss,
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
if (block != rs->last_sent_block && save_page_use_compression(rs)) {
|
||||
flush_compressed_data(rs);
|
||||
if (save_compress_page(rs, block, offset)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
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.
|
||||
*
|
||||
* we post it as normal page as compression will take much
|
||||
* CPU resource.
|
||||
* do not use multifd for compression as the first page in the new
|
||||
* block should be posted out before sending the compressed page
|
||||
*/
|
||||
if (block == rs->last_sent_block && save_page_use_compression(rs)) {
|
||||
return compress_page_with_multi_thread(rs, block, offset);
|
||||
} else if (migrate_use_multifd()) {
|
||||
if (!save_page_use_compression(rs) && migrate_use_multifd()) {
|
||||
return ram_save_multifd_page(rs, block, offset);
|
||||
}
|
||||
|
||||
|
||||
+392
-33
File diff suppressed because it is too large
Load Diff
@@ -1622,6 +1622,7 @@ static void *postcopy_ram_listen_thread(void *opaque)
|
||||
qemu_sem_post(&mis->listen_thread_sem);
|
||||
trace_postcopy_ram_listen_thread_start();
|
||||
|
||||
rcu_register_thread();
|
||||
/*
|
||||
* Because we're a thread and not a coroutine we can't yield
|
||||
* 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
|
||||
* arrived as a desperate recovery step.
|
||||
*/
|
||||
rcu_unregister_thread();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
@@ -1676,6 +1678,7 @@ static void *postcopy_ram_listen_thread(void *opaque)
|
||||
migration_incoming_state_destroy();
|
||||
qemu_loadvm_state_cleanup();
|
||||
|
||||
rcu_unregister_thread();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -418,7 +418,7 @@ int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd,
|
||||
static const VMStateDescription *
|
||||
vmstate_get_subsection(const VMStateDescription **sub, char *idstr)
|
||||
{
|
||||
while (sub && *sub && (*sub)->needed) {
|
||||
while (sub && *sub) {
|
||||
if (strcmp(idstr, (*sub)->name) == 0) {
|
||||
return *sub;
|
||||
}
|
||||
@@ -486,8 +486,8 @@ static int vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd,
|
||||
int ret = 0;
|
||||
|
||||
trace_vmstate_subsection_save_top(vmsd->name);
|
||||
while (sub && *sub && (*sub)->needed) {
|
||||
if ((*sub)->needed(opaque)) {
|
||||
while (sub && *sub) {
|
||||
if (vmstate_save_needed(*sub, opaque)) {
|
||||
const VMStateDescription *vmsdsub = *sub;
|
||||
uint8_t len;
|
||||
|
||||
|
||||
+48
-16
@@ -212,13 +212,13 @@
|
||||
# -> { "execute": "query-migrate" }
|
||||
# <- { "return": {
|
||||
# "status": "completed",
|
||||
# "total-time":12345,
|
||||
# "setup-time":12345,
|
||||
# "downtime":12345,
|
||||
# "ram":{
|
||||
# "transferred":123,
|
||||
# "remaining":123,
|
||||
# "total":246,
|
||||
# "total-time":12345,
|
||||
# "setup-time":12345,
|
||||
# "downtime":12345,
|
||||
# "duplicate":123,
|
||||
# "normal":123,
|
||||
# "normal-bytes":123456,
|
||||
@@ -238,13 +238,13 @@
|
||||
# <- {
|
||||
# "return":{
|
||||
# "status":"active",
|
||||
# "total-time":12345,
|
||||
# "setup-time":12345,
|
||||
# "expected-downtime":12345,
|
||||
# "ram":{
|
||||
# "transferred":123,
|
||||
# "remaining":123,
|
||||
# "total":246,
|
||||
# "total-time":12345,
|
||||
# "setup-time":12345,
|
||||
# "expected-downtime":12345,
|
||||
# "duplicate":123,
|
||||
# "normal":123,
|
||||
# "normal-bytes":123456,
|
||||
@@ -259,13 +259,13 @@
|
||||
# <- {
|
||||
# "return":{
|
||||
# "status":"active",
|
||||
# "total-time":12345,
|
||||
# "setup-time":12345,
|
||||
# "expected-downtime":12345,
|
||||
# "ram":{
|
||||
# "total":1057024,
|
||||
# "remaining":1053304,
|
||||
# "transferred":3720,
|
||||
# "total-time":12345,
|
||||
# "setup-time":12345,
|
||||
# "expected-downtime":12345,
|
||||
# "duplicate":123,
|
||||
# "normal":123,
|
||||
# "normal-bytes":123456,
|
||||
@@ -285,14 +285,13 @@
|
||||
# <- {
|
||||
# "return":{
|
||||
# "status":"active",
|
||||
# "capabilities" : [ { "capability": "xbzrle", "state" : true } ],
|
||||
# "total-time":12345,
|
||||
# "setup-time":12345,
|
||||
# "expected-downtime":12345,
|
||||
# "ram":{
|
||||
# "total":1057024,
|
||||
# "remaining":1053304,
|
||||
# "transferred":3720,
|
||||
# "total-time":12345,
|
||||
# "setup-time":12345,
|
||||
# "expected-downtime":12345,
|
||||
# "duplicate":10,
|
||||
# "normal":3333,
|
||||
# "normal-bytes":3412992,
|
||||
@@ -462,6 +461,11 @@
|
||||
# @compress-threads: Set compression thread count to be used in live migration,
|
||||
# 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
|
||||
# migration, the decompression thread count is an integer between 1
|
||||
# and 255. Usually, decompression is at least 4 times as fast as
|
||||
@@ -523,15 +527,20 @@
|
||||
# @max-postcopy-bandwidth: Background transfer bandwidth during postcopy.
|
||||
# Defaults to 0 (unlimited). In bytes per second.
|
||||
# (Since 3.0)
|
||||
#
|
||||
# @max-cpu-throttle: maximum cpu throttle percentage.
|
||||
# Defaults to 99. (Since 3.1)
|
||||
# Since: 2.4
|
||||
##
|
||||
{ 'enum': 'MigrationParameter',
|
||||
'data': ['compress-level', 'compress-threads', 'decompress-threads',
|
||||
'compress-wait-thread',
|
||||
'cpu-throttle-initial', 'cpu-throttle-increment',
|
||||
'tls-creds', 'tls-hostname', 'max-bandwidth',
|
||||
'downtime-limit', 'x-checkpoint-delay', 'block-incremental',
|
||||
'x-multifd-channels', 'x-multifd-page-count',
|
||||
'xbzrle-cache-size', 'max-postcopy-bandwidth' ] }
|
||||
'xbzrle-cache-size', 'max-postcopy-bandwidth',
|
||||
'max-cpu-throttle' ] }
|
||||
|
||||
##
|
||||
# @MigrateSetParameters:
|
||||
@@ -540,6 +549,11 @@
|
||||
#
|
||||
# @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
|
||||
#
|
||||
# @cpu-throttle-initial: Initial percentage of time guest cpus are
|
||||
@@ -603,6 +617,10 @@
|
||||
# @max-postcopy-bandwidth: Background transfer bandwidth during postcopy.
|
||||
# Defaults to 0 (unlimited). In bytes per second.
|
||||
# (Since 3.0)
|
||||
#
|
||||
# @max-cpu-throttle: maximum cpu throttle percentage.
|
||||
# The default value is 99. (Since 3.1)
|
||||
#
|
||||
# Since: 2.4
|
||||
##
|
||||
# TODO either fuse back into MigrationParameters, or make
|
||||
@@ -610,6 +628,7 @@
|
||||
{ 'struct': 'MigrateSetParameters',
|
||||
'data': { '*compress-level': 'int',
|
||||
'*compress-threads': 'int',
|
||||
'*compress-wait-thread': 'bool',
|
||||
'*decompress-threads': 'int',
|
||||
'*cpu-throttle-initial': 'int',
|
||||
'*cpu-throttle-increment': 'int',
|
||||
@@ -622,7 +641,8 @@
|
||||
'*x-multifd-channels': 'int',
|
||||
'*x-multifd-page-count': 'int',
|
||||
'*xbzrle-cache-size': 'size',
|
||||
'*max-postcopy-bandwidth': 'size' } }
|
||||
'*max-postcopy-bandwidth': 'size',
|
||||
'*max-cpu-throttle': 'int' } }
|
||||
|
||||
##
|
||||
# @migrate-set-parameters:
|
||||
@@ -649,6 +669,11 @@
|
||||
#
|
||||
# @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
|
||||
#
|
||||
# @cpu-throttle-initial: Initial percentage of time guest cpus are
|
||||
@@ -709,11 +734,17 @@
|
||||
# @max-postcopy-bandwidth: Background transfer bandwidth during postcopy.
|
||||
# Defaults to 0 (unlimited). In bytes per second.
|
||||
# (Since 3.0)
|
||||
#
|
||||
# @max-cpu-throttle: maximum cpu throttle percentage.
|
||||
# Defaults to 99.
|
||||
# (Since 3.1)
|
||||
#
|
||||
# Since: 2.4
|
||||
##
|
||||
{ 'struct': 'MigrationParameters',
|
||||
'data': { '*compress-level': 'uint8',
|
||||
'*compress-threads': 'uint8',
|
||||
'*compress-wait-thread': 'bool',
|
||||
'*decompress-threads': 'uint8',
|
||||
'*cpu-throttle-initial': 'uint8',
|
||||
'*cpu-throttle-increment': 'uint8',
|
||||
@@ -726,7 +757,8 @@
|
||||
'*x-multifd-channels': 'uint8',
|
||||
'*x-multifd-page-count': 'uint32',
|
||||
'*xbzrle-cache-size': 'size',
|
||||
'*max-postcopy-bandwidth': 'size' } }
|
||||
'*max-postcopy-bandwidth': 'size',
|
||||
'*max-cpu-throttle':'uint8'} }
|
||||
|
||||
##
|
||||
# @query-migrate-parameters:
|
||||
|
||||
Reference in New Issue
Block a user