mirror of https://github.com/xemu-project/xemu.git
Migration pull 2018-09-26
This supercedes Juan's pull from the 13th -----BEGIN PGP SIGNATURE----- iQIcBAABAgAGBQJbq7zSAAoJEAUWMx68W/3nrSAP/jU3MAg5/Cx+AMbNjQntTllN 0kd1lJPNOW9IBmoKKwu+mJ0SRHRi5kghs5NXuGBYTMcpVoZk0I0Vf8/koq+Yn+2g lbZw4Nv7a8h0aOoXo6lNa1u/VIRMCwgRvLWzP3HVIjlVf1Uup+45zTMynt6QnnMi w42ctSJXVl5asNp11od8BUJCSZ4C9OI2Uu6Z54F3q2q3GdCEKH0wKkxD19WSBimf /j82TSXGctdJGrwWCqEh2yapTG0cYeaYPCTx6Nb8mc/+mqR/gdvquM8plIHTVqEP 0eBFl/rZp1gnPqN+TIpTBqngPiIO1XezQvg/vXQThbnUaaaz1axnAAefXeXabz/W /JPWZdDue5MX2MTtD5uoz/9RKQNfOWwCB+phTDJreqkdSNjeQmrIxItDXksPaD8n diNVJd0Erg377E3mt3wn2mJH4PscwJtTk5s8dhLECAqypybqwGRMqKpomXKfodQj /bIjjQpsqV0NyCNCcKSWOrTAnZl7KDohUL4KTPi49CgLMTO+J6YqSsGrhoVyU541 m9uV8xfKLBfX2ebOqEvpu9gty2t21yVFXNHDenJA5sdiF63LvJ5MvD6Hi2zCuf3S tRrjSCqioao4BFhGjuQRc20Plsv8YHpfKLy/+w/SN8QF0Gmv49e/WSWcFuE1GuN8 loI3NEahqd0VrpW/x/HD =JP00 -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/dgilbert/tags/pull-migration-20180926a' into staging Migration pull 2018-09-26 This supercedes Juan's pull from the 13th # gpg: Signature made Wed 26 Sep 2018 18:07:30 BST # gpg: using RSA key 0516331EBC5BFDE7 # gpg: Good signature from "Dr. David Alan Gilbert (RH2) <dgilbert@redhat.com>" # Primary key fingerprint: 45F5 C71B 4A0C B7FB 977A 9FA9 0516 331E BC5B FDE7 * remotes/dgilbert/tags/pull-migration-20180926a: migration/ram.c: Avoid taking address of fields in packed MultiFDInit_t struct migration: fix the compression code migration: fix QEMUFile leak tests/migration: Speed up the test on ppc64 migration: cleanup in error paths in loadvm migration/postcopy: Clear have_listen_thread tests/migration: Add migration-test header file tests/migration: Support cross compilation in generating boot header file tests/migration: Convert x86 boot block compilation script into Makefile migration: use save_page_use_compression in flush_compressed_data migration: show the statistics of compression migration: do not flush_compressed_data at the end of iteration Add a hint message to loadvm and exits on failure migration: handle the error condition properly migration: fix calculating xbzrle_counters.cache_miss_rate migration/rdma: Fix uninitialised rdma_return_path Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
042938f46e
13
hmp.c
13
hmp.c
|
@ -271,6 +271,19 @@ void hmp_info_migrate(Monitor *mon, const QDict *qdict)
|
||||||
info->xbzrle_cache->overflow);
|
info->xbzrle_cache->overflow);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (info->has_compression) {
|
||||||
|
monitor_printf(mon, "compression pages: %" PRIu64 " pages\n",
|
||||||
|
info->compression->pages);
|
||||||
|
monitor_printf(mon, "compression busy: %" PRIu64 "\n",
|
||||||
|
info->compression->busy);
|
||||||
|
monitor_printf(mon, "compression busy rate: %0.2f\n",
|
||||||
|
info->compression->busy_rate);
|
||||||
|
monitor_printf(mon, "compressed size: %" PRIu64 "\n",
|
||||||
|
info->compression->compressed_size);
|
||||||
|
monitor_printf(mon, "compression rate: %0.2f\n",
|
||||||
|
info->compression->compression_rate);
|
||||||
|
}
|
||||||
|
|
||||||
if (info->has_cpu_throttle_percentage) {
|
if (info->has_cpu_throttle_percentage) {
|
||||||
monitor_printf(mon, "cpu throttle percentage: %" PRIu64 "\n",
|
monitor_printf(mon, "cpu throttle percentage: %" PRIu64 "\n",
|
||||||
info->cpu_throttle_percentage);
|
info->cpu_throttle_percentage);
|
||||||
|
|
|
@ -758,6 +758,18 @@ static void populate_ram_info(MigrationInfo *info, MigrationState *s)
|
||||||
info->xbzrle_cache->overflow = xbzrle_counters.overflow;
|
info->xbzrle_cache->overflow = xbzrle_counters.overflow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (migrate_use_compression()) {
|
||||||
|
info->has_compression = true;
|
||||||
|
info->compression = g_malloc0(sizeof(*info->compression));
|
||||||
|
info->compression->pages = compression_counters.pages;
|
||||||
|
info->compression->busy = compression_counters.busy;
|
||||||
|
info->compression->busy_rate = compression_counters.busy_rate;
|
||||||
|
info->compression->compressed_size =
|
||||||
|
compression_counters.compressed_size;
|
||||||
|
info->compression->compression_rate =
|
||||||
|
compression_counters.compression_rate;
|
||||||
|
}
|
||||||
|
|
||||||
if (cpu_throttle_active()) {
|
if (cpu_throttle_active()) {
|
||||||
info->has_cpu_throttle_percentage = true;
|
info->has_cpu_throttle_percentage = true;
|
||||||
info->cpu_throttle_percentage = cpu_throttle_get_percentage();
|
info->cpu_throttle_percentage = cpu_throttle_get_percentage();
|
||||||
|
@ -2268,7 +2280,10 @@ out:
|
||||||
*/
|
*/
|
||||||
if (postcopy_pause_return_path_thread(ms)) {
|
if (postcopy_pause_return_path_thread(ms)) {
|
||||||
/* Reload rp, reset the rest */
|
/* Reload rp, reset the rest */
|
||||||
|
if (rp != ms->rp_state.from_dst_file) {
|
||||||
|
qemu_fclose(rp);
|
||||||
rp = ms->rp_state.from_dst_file;
|
rp = ms->rp_state.from_dst_file;
|
||||||
|
}
|
||||||
ms->rp_state.error = false;
|
ms->rp_state.error = false;
|
||||||
goto retry;
|
goto retry;
|
||||||
}
|
}
|
||||||
|
|
133
migration/ram.c
133
migration/ram.c
|
@ -301,10 +301,19 @@ struct RAMState {
|
||||||
uint64_t num_dirty_pages_period;
|
uint64_t num_dirty_pages_period;
|
||||||
/* xbzrle misses since the beginning of the period */
|
/* xbzrle misses since the beginning of the period */
|
||||||
uint64_t xbzrle_cache_miss_prev;
|
uint64_t xbzrle_cache_miss_prev;
|
||||||
/* number of iterations at the beginning of period */
|
|
||||||
uint64_t iterations_prev;
|
/* compression statistics since the beginning of the period */
|
||||||
/* Iterations since start */
|
/* amount of count that no free thread to compress data */
|
||||||
uint64_t iterations;
|
uint64_t compress_thread_busy_prev;
|
||||||
|
/* amount bytes after compression */
|
||||||
|
uint64_t compressed_size_prev;
|
||||||
|
/* amount of compressed pages */
|
||||||
|
uint64_t compress_pages_prev;
|
||||||
|
|
||||||
|
/* total handled target pages at the beginning of period */
|
||||||
|
uint64_t target_page_count_prev;
|
||||||
|
/* total handled target pages since start */
|
||||||
|
uint64_t target_page_count;
|
||||||
/* number of dirty bits in the bitmap */
|
/* number of dirty bits in the bitmap */
|
||||||
uint64_t migration_dirty_pages;
|
uint64_t migration_dirty_pages;
|
||||||
/* protects modification of the bitmap */
|
/* protects modification of the bitmap */
|
||||||
|
@ -338,6 +347,8 @@ struct PageSearchStatus {
|
||||||
};
|
};
|
||||||
typedef struct PageSearchStatus PageSearchStatus;
|
typedef struct PageSearchStatus PageSearchStatus;
|
||||||
|
|
||||||
|
CompressionStats compression_counters;
|
||||||
|
|
||||||
struct CompressParam {
|
struct CompressParam {
|
||||||
bool done;
|
bool done;
|
||||||
bool quit;
|
bool quit;
|
||||||
|
@ -420,28 +431,14 @@ static void *do_data_compress(void *opaque)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void terminate_compression_threads(void)
|
|
||||||
{
|
|
||||||
int idx, thread_count;
|
|
||||||
|
|
||||||
thread_count = migrate_compress_threads();
|
|
||||||
|
|
||||||
for (idx = 0; idx < thread_count; idx++) {
|
|
||||||
qemu_mutex_lock(&comp_param[idx].mutex);
|
|
||||||
comp_param[idx].quit = true;
|
|
||||||
qemu_cond_signal(&comp_param[idx].cond);
|
|
||||||
qemu_mutex_unlock(&comp_param[idx].mutex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void compress_threads_save_cleanup(void)
|
static void compress_threads_save_cleanup(void)
|
||||||
{
|
{
|
||||||
int i, thread_count;
|
int i, thread_count;
|
||||||
|
|
||||||
if (!migrate_use_compression()) {
|
if (!migrate_use_compression() || !comp_param) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
terminate_compression_threads();
|
|
||||||
thread_count = migrate_compress_threads();
|
thread_count = migrate_compress_threads();
|
||||||
for (i = 0; i < thread_count; i++) {
|
for (i = 0; i < thread_count; i++) {
|
||||||
/*
|
/*
|
||||||
|
@ -451,6 +448,12 @@ static void compress_threads_save_cleanup(void)
|
||||||
if (!comp_param[i].file) {
|
if (!comp_param[i].file) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qemu_mutex_lock(&comp_param[i].mutex);
|
||||||
|
comp_param[i].quit = true;
|
||||||
|
qemu_cond_signal(&comp_param[i].cond);
|
||||||
|
qemu_mutex_unlock(&comp_param[i].mutex);
|
||||||
|
|
||||||
qemu_thread_join(compress_threads + i);
|
qemu_thread_join(compress_threads + i);
|
||||||
qemu_mutex_destroy(&comp_param[i].mutex);
|
qemu_mutex_destroy(&comp_param[i].mutex);
|
||||||
qemu_cond_destroy(&comp_param[i].cond);
|
qemu_cond_destroy(&comp_param[i].cond);
|
||||||
|
@ -648,8 +651,8 @@ static int multifd_recv_initial_packet(QIOChannel *c, Error **errp)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
be32_to_cpus(&msg.magic);
|
msg.magic = be32_to_cpu(msg.magic);
|
||||||
be32_to_cpus(&msg.version);
|
msg.version = be32_to_cpu(msg.version);
|
||||||
|
|
||||||
if (msg.magic != MULTIFD_MAGIC) {
|
if (msg.magic != MULTIFD_MAGIC) {
|
||||||
error_setg(errp, "multifd: received packet magic %x "
|
error_setg(errp, "multifd: received packet magic %x "
|
||||||
|
@ -734,7 +737,7 @@ static int multifd_recv_unfill_packet(MultiFDRecvParams *p, Error **errp)
|
||||||
RAMBlock *block;
|
RAMBlock *block;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
be32_to_cpus(&packet->magic);
|
packet->magic = be32_to_cpu(packet->magic);
|
||||||
if (packet->magic != MULTIFD_MAGIC) {
|
if (packet->magic != MULTIFD_MAGIC) {
|
||||||
error_setg(errp, "multifd: received packet "
|
error_setg(errp, "multifd: received packet "
|
||||||
"magic %x and expected magic %x",
|
"magic %x and expected magic %x",
|
||||||
|
@ -742,7 +745,7 @@ static int multifd_recv_unfill_packet(MultiFDRecvParams *p, Error **errp)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
be32_to_cpus(&packet->version);
|
packet->version = be32_to_cpu(packet->version);
|
||||||
if (packet->version != MULTIFD_VERSION) {
|
if (packet->version != MULTIFD_VERSION) {
|
||||||
error_setg(errp, "multifd: received packet "
|
error_setg(errp, "multifd: received packet "
|
||||||
"version %d and expected version %d",
|
"version %d and expected version %d",
|
||||||
|
@ -752,7 +755,7 @@ static int multifd_recv_unfill_packet(MultiFDRecvParams *p, Error **errp)
|
||||||
|
|
||||||
p->flags = be32_to_cpu(packet->flags);
|
p->flags = be32_to_cpu(packet->flags);
|
||||||
|
|
||||||
be32_to_cpus(&packet->size);
|
packet->size = be32_to_cpu(packet->size);
|
||||||
if (packet->size > migrate_multifd_page_count()) {
|
if (packet->size > migrate_multifd_page_count()) {
|
||||||
error_setg(errp, "multifd: received packet "
|
error_setg(errp, "multifd: received packet "
|
||||||
"with size %d and expected maximum size %d",
|
"with size %d and expected maximum size %d",
|
||||||
|
@ -1592,21 +1595,42 @@ uint64_t ram_pagesize_summary(void)
|
||||||
|
|
||||||
static void migration_update_rates(RAMState *rs, int64_t end_time)
|
static void migration_update_rates(RAMState *rs, int64_t end_time)
|
||||||
{
|
{
|
||||||
uint64_t iter_count = rs->iterations - rs->iterations_prev;
|
uint64_t page_count = rs->target_page_count - rs->target_page_count_prev;
|
||||||
|
double compressed_size;
|
||||||
|
|
||||||
/* calculate period counters */
|
/* calculate period counters */
|
||||||
ram_counters.dirty_pages_rate = rs->num_dirty_pages_period * 1000
|
ram_counters.dirty_pages_rate = rs->num_dirty_pages_period * 1000
|
||||||
/ (end_time - rs->time_last_bitmap_sync);
|
/ (end_time - rs->time_last_bitmap_sync);
|
||||||
|
|
||||||
if (!iter_count) {
|
if (!page_count) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (migrate_use_xbzrle()) {
|
if (migrate_use_xbzrle()) {
|
||||||
xbzrle_counters.cache_miss_rate = (double)(xbzrle_counters.cache_miss -
|
xbzrle_counters.cache_miss_rate = (double)(xbzrle_counters.cache_miss -
|
||||||
rs->xbzrle_cache_miss_prev) / iter_count;
|
rs->xbzrle_cache_miss_prev) / page_count;
|
||||||
rs->xbzrle_cache_miss_prev = xbzrle_counters.cache_miss;
|
rs->xbzrle_cache_miss_prev = xbzrle_counters.cache_miss;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (migrate_use_compression()) {
|
||||||
|
compression_counters.busy_rate = (double)(compression_counters.busy -
|
||||||
|
rs->compress_thread_busy_prev) / page_count;
|
||||||
|
rs->compress_thread_busy_prev = compression_counters.busy;
|
||||||
|
|
||||||
|
compressed_size = compression_counters.compressed_size -
|
||||||
|
rs->compressed_size_prev;
|
||||||
|
if (compressed_size) {
|
||||||
|
double uncompressed_size = (compression_counters.pages -
|
||||||
|
rs->compress_pages_prev) * TARGET_PAGE_SIZE;
|
||||||
|
|
||||||
|
/* Compression-Ratio = Uncompressed-size / Compressed-size */
|
||||||
|
compression_counters.compression_rate =
|
||||||
|
uncompressed_size / compressed_size;
|
||||||
|
|
||||||
|
rs->compress_pages_prev = compression_counters.pages;
|
||||||
|
rs->compressed_size_prev = compression_counters.compressed_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void migration_bitmap_sync(RAMState *rs)
|
static void migration_bitmap_sync(RAMState *rs)
|
||||||
|
@ -1662,7 +1686,7 @@ static void migration_bitmap_sync(RAMState *rs)
|
||||||
|
|
||||||
migration_update_rates(rs, end_time);
|
migration_update_rates(rs, end_time);
|
||||||
|
|
||||||
rs->iterations_prev = rs->iterations;
|
rs->target_page_count_prev = rs->target_page_count;
|
||||||
|
|
||||||
/* reset period counters */
|
/* reset period counters */
|
||||||
rs->time_last_bitmap_sync = end_time;
|
rs->time_last_bitmap_sync = end_time;
|
||||||
|
@ -1888,17 +1912,25 @@ exit:
|
||||||
static void
|
static void
|
||||||
update_compress_thread_counts(const CompressParam *param, int bytes_xmit)
|
update_compress_thread_counts(const CompressParam *param, int bytes_xmit)
|
||||||
{
|
{
|
||||||
|
ram_counters.transferred += bytes_xmit;
|
||||||
|
|
||||||
if (param->zero_page) {
|
if (param->zero_page) {
|
||||||
ram_counters.duplicate++;
|
ram_counters.duplicate++;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
ram_counters.transferred += bytes_xmit;
|
|
||||||
|
/* 8 means a header with RAM_SAVE_FLAG_CONTINUE. */
|
||||||
|
compression_counters.compressed_size += bytes_xmit - 8;
|
||||||
|
compression_counters.pages++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool save_page_use_compression(RAMState *rs);
|
||||||
|
|
||||||
static void flush_compressed_data(RAMState *rs)
|
static void flush_compressed_data(RAMState *rs)
|
||||||
{
|
{
|
||||||
int idx, len, thread_count;
|
int idx, len, thread_count;
|
||||||
|
|
||||||
if (!migrate_use_compression()) {
|
if (!save_page_use_compression(rs)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
thread_count = migrate_compress_threads();
|
thread_count = migrate_compress_threads();
|
||||||
|
@ -1996,17 +2028,22 @@ static bool find_dirty_block(RAMState *rs, PageSearchStatus *pss, bool *again)
|
||||||
pss->page = 0;
|
pss->page = 0;
|
||||||
pss->block = QLIST_NEXT_RCU(pss->block, next);
|
pss->block = QLIST_NEXT_RCU(pss->block, next);
|
||||||
if (!pss->block) {
|
if (!pss->block) {
|
||||||
|
/*
|
||||||
|
* If memory migration starts over, we will meet a dirtied page
|
||||||
|
* which may still exists in compression threads's ring, so we
|
||||||
|
* should flush the compressed data to make sure the new page
|
||||||
|
* is not overwritten by the old one in the destination.
|
||||||
|
*
|
||||||
|
* Also If xbzrle is on, stop using the data compression at this
|
||||||
|
* point. In theory, xbzrle can do better than compression.
|
||||||
|
*/
|
||||||
|
flush_compressed_data(rs);
|
||||||
|
|
||||||
/* Hit the end of the list */
|
/* Hit the end of the list */
|
||||||
pss->block = QLIST_FIRST_RCU(&ram_list.blocks);
|
pss->block = QLIST_FIRST_RCU(&ram_list.blocks);
|
||||||
/* Flag that we've looped */
|
/* Flag that we've looped */
|
||||||
pss->complete_round = true;
|
pss->complete_round = true;
|
||||||
rs->ram_bulk_stage = false;
|
rs->ram_bulk_stage = false;
|
||||||
if (migrate_use_xbzrle()) {
|
|
||||||
/* If xbzrle is on, stop using the data compression at this
|
|
||||||
* point. In theory, xbzrle can do better than compression.
|
|
||||||
*/
|
|
||||||
flush_compressed_data(rs);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
/* Didn't find anything this time, but try again on the new block */
|
/* Didn't find anything this time, but try again on the new block */
|
||||||
*again = true;
|
*again = true;
|
||||||
|
@ -2259,6 +2296,7 @@ static bool save_compress_page(RAMState *rs, RAMBlock *block, ram_addr_t offset)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
compression_counters.busy++;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2372,7 +2410,8 @@ static int ram_save_host_page(RAMState *rs, PageSearchStatus *pss,
|
||||||
*
|
*
|
||||||
* Called within an RCU critical section.
|
* Called within an RCU critical section.
|
||||||
*
|
*
|
||||||
* Returns the number of pages written where zero means no dirty pages
|
* Returns the number of pages written where zero means no dirty pages,
|
||||||
|
* or negative on error
|
||||||
*
|
*
|
||||||
* @rs: current RAM state
|
* @rs: current RAM state
|
||||||
* @last_stage: if we are at the completion stage
|
* @last_stage: if we are at the completion stage
|
||||||
|
@ -3196,7 +3235,13 @@ static int ram_save_iterate(QEMUFile *f, void *opaque)
|
||||||
done = 1;
|
done = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
rs->iterations++;
|
|
||||||
|
if (pages < 0) {
|
||||||
|
qemu_file_set_error(f, pages);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
rs->target_page_count += pages;
|
||||||
|
|
||||||
/* we want to check in the 1st loop, just in case it was the 1st time
|
/* we want to check in the 1st loop, just in case it was the 1st time
|
||||||
and we had to sync the dirty bitmap.
|
and we had to sync the dirty bitmap.
|
||||||
|
@ -3212,7 +3257,6 @@ static int ram_save_iterate(QEMUFile *f, void *opaque)
|
||||||
}
|
}
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
flush_compressed_data(rs);
|
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -3238,7 +3282,7 @@ out:
|
||||||
/**
|
/**
|
||||||
* ram_save_complete: function called to send the remaining amount of ram
|
* ram_save_complete: function called to send the remaining amount of ram
|
||||||
*
|
*
|
||||||
* Returns zero to indicate success
|
* Returns zero to indicate success or negative on error
|
||||||
*
|
*
|
||||||
* Called with iothread lock
|
* Called with iothread lock
|
||||||
*
|
*
|
||||||
|
@ -3249,6 +3293,7 @@ static int ram_save_complete(QEMUFile *f, void *opaque)
|
||||||
{
|
{
|
||||||
RAMState **temp = opaque;
|
RAMState **temp = opaque;
|
||||||
RAMState *rs = *temp;
|
RAMState *rs = *temp;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
|
|
||||||
|
@ -3269,6 +3314,10 @@ static int ram_save_complete(QEMUFile *f, void *opaque)
|
||||||
if (pages == 0) {
|
if (pages == 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (pages < 0) {
|
||||||
|
ret = pages;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
flush_compressed_data(rs);
|
flush_compressed_data(rs);
|
||||||
|
@ -3280,7 +3329,7 @@ static int ram_save_complete(QEMUFile *f, void *opaque)
|
||||||
qemu_put_be64(f, RAM_SAVE_FLAG_EOS);
|
qemu_put_be64(f, RAM_SAVE_FLAG_EOS);
|
||||||
qemu_fflush(f);
|
qemu_fflush(f);
|
||||||
|
|
||||||
return 0;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ram_save_pending(QEMUFile *f, void *opaque, uint64_t max_size,
|
static void ram_save_pending(QEMUFile *f, void *opaque, uint64_t max_size,
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
|
|
||||||
extern MigrationStats ram_counters;
|
extern MigrationStats ram_counters;
|
||||||
extern XBZRLECacheStats xbzrle_counters;
|
extern XBZRLECacheStats xbzrle_counters;
|
||||||
|
extern CompressionStats compression_counters;
|
||||||
|
|
||||||
int xbzrle_cache_resize(int64_t new_size, Error **errp);
|
int xbzrle_cache_resize(int64_t new_size, Error **errp);
|
||||||
uint64_t ram_bytes_remaining(void);
|
uint64_t ram_bytes_remaining(void);
|
||||||
|
|
|
@ -4012,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, *rdma_return_path;
|
RDMAContext *rdma, *rdma_return_path = NULL;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
|
|
||||||
trace_rdma_start_incoming_migration();
|
trace_rdma_start_incoming_migration();
|
||||||
|
|
|
@ -1679,6 +1679,7 @@ static void *postcopy_ram_listen_thread(void *opaque)
|
||||||
qemu_loadvm_state_cleanup();
|
qemu_loadvm_state_cleanup();
|
||||||
|
|
||||||
rcu_unregister_thread();
|
rcu_unregister_thread();
|
||||||
|
mis->have_listen_thread = false;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2078,7 +2079,9 @@ qemu_loadvm_section_start_full(QEMUFile *f, MigrationIncomingState *mis)
|
||||||
/* Find savevm section */
|
/* Find savevm section */
|
||||||
se = find_se(idstr, instance_id);
|
se = find_se(idstr, instance_id);
|
||||||
if (se == NULL) {
|
if (se == NULL) {
|
||||||
error_report("Unknown savevm section or instance '%s' %d",
|
error_report("Unknown savevm section or instance '%s' %d. "
|
||||||
|
"Make sure that your current VM setup matches your "
|
||||||
|
"saved VM setup, including any hotplugged devices",
|
||||||
idstr, instance_id);
|
idstr, instance_id);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
@ -2330,11 +2333,13 @@ int qemu_loadvm_state(QEMUFile *f)
|
||||||
if (migrate_get_current()->send_configuration) {
|
if (migrate_get_current()->send_configuration) {
|
||||||
if (qemu_get_byte(f) != QEMU_VM_CONFIGURATION) {
|
if (qemu_get_byte(f) != QEMU_VM_CONFIGURATION) {
|
||||||
error_report("Configuration section missing");
|
error_report("Configuration section missing");
|
||||||
|
qemu_loadvm_state_cleanup();
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
ret = vmstate_load_state(f, &vmstate_configuration, &savevm_state, 0);
|
ret = vmstate_load_state(f, &vmstate_configuration, &savevm_state, 0);
|
||||||
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
qemu_loadvm_state_cleanup();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,6 +75,27 @@
|
||||||
'cache-miss': 'int', 'cache-miss-rate': 'number',
|
'cache-miss': 'int', 'cache-miss-rate': 'number',
|
||||||
'overflow': 'int' } }
|
'overflow': 'int' } }
|
||||||
|
|
||||||
|
##
|
||||||
|
# @CompressionStats:
|
||||||
|
#
|
||||||
|
# Detailed migration compression statistics
|
||||||
|
#
|
||||||
|
# @pages: amount of pages compressed and transferred to the target VM
|
||||||
|
#
|
||||||
|
# @busy: count of times that no free thread was available to compress data
|
||||||
|
#
|
||||||
|
# @busy-rate: rate of thread busy
|
||||||
|
#
|
||||||
|
# @compressed-size: amount of bytes after compression
|
||||||
|
#
|
||||||
|
# @compression-rate: rate of compressed size
|
||||||
|
#
|
||||||
|
# Since: 3.1
|
||||||
|
##
|
||||||
|
{ 'struct': 'CompressionStats',
|
||||||
|
'data': {'pages': 'int', 'busy': 'int', 'busy-rate': 'number',
|
||||||
|
'compressed-size': 'int', 'compression-rate': 'number' } }
|
||||||
|
|
||||||
##
|
##
|
||||||
# @MigrationStatus:
|
# @MigrationStatus:
|
||||||
#
|
#
|
||||||
|
@ -172,6 +193,8 @@
|
||||||
# only present when the postcopy-blocktime migration capability
|
# only present when the postcopy-blocktime migration capability
|
||||||
# is enabled. (Since 3.0)
|
# is enabled. (Since 3.0)
|
||||||
#
|
#
|
||||||
|
# @compression: migration compression statistics, only returned if compression
|
||||||
|
# feature is on and status is 'active' or 'completed' (Since 3.1)
|
||||||
#
|
#
|
||||||
# Since: 0.14.0
|
# Since: 0.14.0
|
||||||
##
|
##
|
||||||
|
@ -186,7 +209,8 @@
|
||||||
'*cpu-throttle-percentage': 'int',
|
'*cpu-throttle-percentage': 'int',
|
||||||
'*error-desc': 'str',
|
'*error-desc': 'str',
|
||||||
'*postcopy-blocktime' : 'uint32',
|
'*postcopy-blocktime' : 'uint32',
|
||||||
'*postcopy-vcpu-blocktime': ['uint32']} }
|
'*postcopy-vcpu-blocktime': ['uint32'],
|
||||||
|
'*compression': 'CompressionStats'} }
|
||||||
|
|
||||||
##
|
##
|
||||||
# @query-migrate:
|
# @query-migrate:
|
||||||
|
|
|
@ -21,11 +21,13 @@
|
||||||
#include "chardev/char.h"
|
#include "chardev/char.h"
|
||||||
#include "sysemu/sysemu.h"
|
#include "sysemu/sysemu.h"
|
||||||
|
|
||||||
|
#include "migration/migration-test.h"
|
||||||
|
|
||||||
/* TODO actually test the results and get rid of this */
|
/* TODO actually test the results and get rid of this */
|
||||||
#define qtest_qmp_discard_response(...) qobject_unref(qtest_qmp(__VA_ARGS__))
|
#define qtest_qmp_discard_response(...) qobject_unref(qtest_qmp(__VA_ARGS__))
|
||||||
|
|
||||||
const unsigned start_address = 1024 * 1024;
|
unsigned start_address;
|
||||||
const unsigned end_address = 100 * 1024 * 1024;
|
unsigned end_address;
|
||||||
bool got_stop;
|
bool got_stop;
|
||||||
static bool uffd_feature_thread_id;
|
static bool uffd_feature_thread_id;
|
||||||
|
|
||||||
|
@ -80,10 +82,10 @@ static bool ufd_version_check(void)
|
||||||
|
|
||||||
static const char *tmpfs;
|
static const char *tmpfs;
|
||||||
|
|
||||||
/* A simple PC boot sector that modifies memory (1-100MB) quickly
|
/* The boot file modifies memory area in [start_address, end_address)
|
||||||
* outputting a 'B' every so often if it's still running.
|
* repeatedly. It outputs a 'B' at a fixed rate while it's still running.
|
||||||
*/
|
*/
|
||||||
#include "tests/migration/x86-a-b-bootblock.h"
|
#include "tests/migration/i386/a-b-bootblock.h"
|
||||||
|
|
||||||
static void init_bootfile_x86(const char *bootpath)
|
static void init_bootfile_x86(const char *bootpath)
|
||||||
{
|
{
|
||||||
|
@ -270,11 +272,11 @@ static void wait_for_migration_pass(QTestState *who)
|
||||||
static void check_guests_ram(QTestState *who)
|
static void check_guests_ram(QTestState *who)
|
||||||
{
|
{
|
||||||
/* Our ASM test will have been incrementing one byte from each page from
|
/* Our ASM test will have been incrementing one byte from each page from
|
||||||
* 1MB to <100MB in order.
|
* start_address to < end_address in order. This gives us a constraint
|
||||||
* This gives us a constraint that any page's byte should be equal or less
|
* that any page's byte should be equal or less than the previous pages
|
||||||
* than the previous pages byte (mod 256); and they should all be equal
|
* byte (mod 256); and they should all be equal except for one transition
|
||||||
* except for one transition at the point where we meet the incrementer.
|
* at the point where we meet the incrementer. (We're running this with
|
||||||
* (We're running this with the guest stopped).
|
* the guest stopped).
|
||||||
*/
|
*/
|
||||||
unsigned address;
|
unsigned address;
|
||||||
uint8_t first_byte;
|
uint8_t first_byte;
|
||||||
|
@ -285,7 +287,8 @@ static void check_guests_ram(QTestState *who)
|
||||||
qtest_memread(who, start_address, &first_byte, 1);
|
qtest_memread(who, start_address, &first_byte, 1);
|
||||||
last_byte = first_byte;
|
last_byte = first_byte;
|
||||||
|
|
||||||
for (address = start_address + 4096; address < end_address; address += 4096)
|
for (address = start_address + TEST_MEM_PAGE_SIZE; address < end_address;
|
||||||
|
address += TEST_MEM_PAGE_SIZE)
|
||||||
{
|
{
|
||||||
uint8_t b;
|
uint8_t b;
|
||||||
qtest_memread(who, address, &b, 1);
|
qtest_memread(who, address, &b, 1);
|
||||||
|
@ -437,6 +440,8 @@ static int test_migrate_start(QTestState **from, QTestState **to,
|
||||||
" -drive file=%s,format=raw"
|
" -drive file=%s,format=raw"
|
||||||
" -incoming %s",
|
" -incoming %s",
|
||||||
accel, tmpfs, bootpath, uri);
|
accel, tmpfs, bootpath, uri);
|
||||||
|
start_address = X86_TEST_MEM_START;
|
||||||
|
end_address = X86_TEST_MEM_END;
|
||||||
} else if (strcmp(arch, "ppc64") == 0) {
|
} else if (strcmp(arch, "ppc64") == 0) {
|
||||||
cmd_src = g_strdup_printf("-machine accel=%s -m 256M -nodefaults"
|
cmd_src = g_strdup_printf("-machine accel=%s -m 256M -nodefaults"
|
||||||
" -name source,debug-threads=on"
|
" -name source,debug-threads=on"
|
||||||
|
@ -451,6 +456,9 @@ static int test_migrate_start(QTestState **from, QTestState **to,
|
||||||
" -serial file:%s/dest_serial"
|
" -serial file:%s/dest_serial"
|
||||||
" -incoming %s",
|
" -incoming %s",
|
||||||
accel, tmpfs, uri);
|
accel, tmpfs, uri);
|
||||||
|
|
||||||
|
start_address = PPC_TEST_MEM_START;
|
||||||
|
end_address = PPC_TEST_MEM_END;
|
||||||
} else {
|
} else {
|
||||||
g_assert_not_reached();
|
g_assert_not_reached();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
#
|
||||||
|
# Copyright (c) 2018 Red Hat, Inc. and/or its affiliates
|
||||||
|
#
|
||||||
|
# This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||||
|
# See the COPYING file in the top-level directory.
|
||||||
|
#
|
||||||
|
|
||||||
|
TARGET_LIST = i386
|
||||||
|
|
||||||
|
SRC_PATH = ../..
|
||||||
|
|
||||||
|
override define __note
|
||||||
|
/* This file is automatically generated from the assembly file in
|
||||||
|
* tests/migration/$@. Edit that file and then run "make all"
|
||||||
|
* inside tests/migration to update, and then remember to send both
|
||||||
|
* the header and the assembler differences in your patch submission.
|
||||||
|
*/
|
||||||
|
endef
|
||||||
|
export __note
|
||||||
|
|
||||||
|
find-arch-cross-cc = $(lastword $(shell grep -h "CROSS_CC_GUEST=" $(wildcard $(SRC_PATH)/$(patsubst i386,*86*,$(1))-softmmu/config-target.mak) /dev/null))
|
||||||
|
parse-cross-prefix = $(subst gcc,,$(patsubst cc,gcc,$(patsubst CROSS_CC_GUEST="%",%,$(call find-arch-cross-cc,$(1)))))
|
||||||
|
gen-cross-prefix = $(patsubst %-,CROSS_PREFIX=%-,$(call parse-cross-prefix,$(1)))
|
||||||
|
|
||||||
|
.PHONY: all $(TARGET_LIST)
|
||||||
|
|
||||||
|
all: $(TARGET_LIST)
|
||||||
|
|
||||||
|
$(TARGET_LIST):
|
||||||
|
$(MAKE) -C $@ $(call gen-cross-prefix,$@)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
for target in $(TARGET_LIST); do \
|
||||||
|
$(MAKE) -C $$target clean; \
|
||||||
|
done
|
|
@ -0,0 +1,22 @@
|
||||||
|
# To specify cross compiler prefix, use CROSS_PREFIX=
|
||||||
|
# $ make CROSS_PREFIX=x86_64-linux-gnu-
|
||||||
|
|
||||||
|
.PHONY: all clean
|
||||||
|
all: a-b-bootblock.h
|
||||||
|
|
||||||
|
a-b-bootblock.h: x86.bootsect
|
||||||
|
echo "$$__note" > header.tmp
|
||||||
|
xxd -i $< | sed -e 's/.*int.*//' >> header.tmp
|
||||||
|
mv header.tmp $@
|
||||||
|
|
||||||
|
x86.bootsect: x86.boot
|
||||||
|
dd if=$< of=$@ bs=256 count=2 skip=124
|
||||||
|
|
||||||
|
x86.boot: x86.o
|
||||||
|
$(CROSS_PREFIX)objcopy -O binary $< $@
|
||||||
|
|
||||||
|
x86.o: a-b-bootblock.S
|
||||||
|
$(CROSS_PREFIX)gcc -m32 -march=i486 -c $< -o $@
|
||||||
|
|
||||||
|
clean:
|
||||||
|
@rm -rf *.boot *.o *.bootsect
|
|
@ -3,10 +3,6 @@
|
||||||
# range.
|
# range.
|
||||||
# Outputs an initial 'A' on serial followed by repeated 'B's
|
# Outputs an initial 'A' on serial followed by repeated 'B's
|
||||||
#
|
#
|
||||||
# run tests/migration/rebuild-x86-bootblock.sh
|
|
||||||
# to regenerate the hex, and remember to include both the .h and .s
|
|
||||||
# in any patches.
|
|
||||||
#
|
|
||||||
# Copyright (c) 2016 Red Hat, Inc. and/or its affiliates
|
# Copyright (c) 2016 Red Hat, Inc. and/or its affiliates
|
||||||
# This work is licensed under the terms of the GNU GPL, version 2 or later.
|
# This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||||
# See the COPYING file in the top-level directory.
|
# See the COPYING file in the top-level directory.
|
|
@ -1,7 +1,7 @@
|
||||||
/* This file is automatically generated from
|
/* This file is automatically generated from the assembly file in
|
||||||
* tests/migration/x86-a-b-bootblock.s, edit that and then run
|
* tests/migration/i386. Edit that file and then run "make all"
|
||||||
* tests/migration/rebuild-x86-bootblock.sh to update,
|
* inside tests/migration to update, and then remember to send both
|
||||||
* and then remember to send both in your patch submission.
|
* the header and the assembler differences in your patch submission.
|
||||||
*/
|
*/
|
||||||
unsigned char x86_bootsect[] = {
|
unsigned char x86_bootsect[] = {
|
||||||
0xfa, 0x0f, 0x01, 0x16, 0x74, 0x7c, 0x66, 0xb8, 0x01, 0x00, 0x00, 0x00,
|
0xfa, 0x0f, 0x01, 0x16, 0x74, 0x7c, 0x66, 0xb8, 0x01, 0x00, 0x00, 0x00,
|
|
@ -0,0 +1,21 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Red Hat, Inc. and/or its affiliates
|
||||||
|
*
|
||||||
|
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||||
|
* See the COPYING file in the top-level directory.
|
||||||
|
*/
|
||||||
|
#ifndef _TEST_MIGRATION_H_
|
||||||
|
#define _TEST_MIGRATION_H_
|
||||||
|
|
||||||
|
/* Common */
|
||||||
|
#define TEST_MEM_PAGE_SIZE 4096
|
||||||
|
|
||||||
|
/* x86 */
|
||||||
|
#define X86_TEST_MEM_START (1 * 1024 * 1024)
|
||||||
|
#define X86_TEST_MEM_END (100 * 1024 * 1024)
|
||||||
|
|
||||||
|
/* PPC */
|
||||||
|
#define PPC_TEST_MEM_START (1 * 1024 * 1024)
|
||||||
|
#define PPC_TEST_MEM_END (100 * 1024 * 1024)
|
||||||
|
|
||||||
|
#endif /* _TEST_MIGRATION_H_ */
|
|
@ -1,33 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
# Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates
|
|
||||||
# This work is licensed under the terms of the GNU GPL, version 2 or later.
|
|
||||||
# See the COPYING file in the top-level directory.
|
|
||||||
#
|
|
||||||
# Author: dgilbert@redhat.com
|
|
||||||
|
|
||||||
ASMFILE=$PWD/tests/migration/x86-a-b-bootblock.s
|
|
||||||
HEADER=$PWD/tests/migration/x86-a-b-bootblock.h
|
|
||||||
|
|
||||||
if [ ! -e "$ASMFILE" ]
|
|
||||||
then
|
|
||||||
echo "Couldn't find $ASMFILE" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
ASM_WORK_DIR=$(mktemp -d --tmpdir X86BB.XXXXXX)
|
|
||||||
cd "$ASM_WORK_DIR" &&
|
|
||||||
as --32 -march=i486 "$ASMFILE" -o x86.o &&
|
|
||||||
objcopy -O binary x86.o x86.boot &&
|
|
||||||
dd if=x86.boot of=x86.bootsect bs=256 count=2 skip=124 &&
|
|
||||||
xxd -i x86.bootsect |
|
|
||||||
sed -e 's/.*int.*//' > x86.hex &&
|
|
||||||
cat - x86.hex <<HERE > "$HEADER"
|
|
||||||
/* This file is automatically generated from
|
|
||||||
* tests/migration/x86-a-b-bootblock.s, edit that and then run
|
|
||||||
* tests/migration/rebuild-x86-bootblock.sh to update,
|
|
||||||
* and then remember to send both in your patch submission.
|
|
||||||
*/
|
|
||||||
HERE
|
|
||||||
|
|
||||||
rm x86.hex x86.bootsect x86.boot x86.o
|
|
||||||
cd .. && rmdir "$ASM_WORK_DIR"
|
|
Loading…
Reference in New Issue