mirror of https://github.com/proxmox/mirror_qemu
Migration:
- many compression/decompression fixes - trace improvements - static checker fix for detecting size mismatch in unused fields - fix VM save after snapshot -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQIcBAABCAAGBQJXY/RAAAoJEOsLTfxlfvZwBhMQAKo8L6sA3ExBHR8+452HNxTu dHqWyiNFi+M5fbBOOpiTZYDH2SvsE7NoCWdQbllacYq1mtq36q1BpwJBd2FXozsb MOxFZfo5prpWqkel9DvfARUNjE/6tLJ21PypFQtBAczocVOVEtkab1MHHsk+H++V JTyAZcfxzxuDK3ByBRtkQd8nYYmnIqihngqPPSQQ4ywkv5SR1g5VNfYLaWx5Btyj dYDMW6BgOl3CAbsk6c2H6pKBylAockpj3oam3VFBiKOAAdgmbXNxbzoI5Pac4aa+ BwrjD1C8Q9dBnHj9XRmr8ASIWVslCkmGWXxZEz4wI3HG9KIqKQdW484K8f2JOpuT O6/b+H2NdnrhG3AgmbqVEsnz01rVRqpmUrBAn4dTwiX19+bbQnLbKrAkPC7uHtzo XDhf0ypV2MChfJaOWEFZvS8Gxm8zauJCfwtH9+bOQFPIWFzbnDyLanLrdAvVoMgw oFEmV9X3b/3D4iIirYIylHiw8cfGjEiTnO7DnMvo1frPIm1gKR7QPLWf3qK4BO5K 7NR309SNcB7BFmJPRUjBxqhxxljjdTkfYkbckGn/7lDnUc3hdnZEHpteIL7i3VaT XtTss/rd3GSHTduxO7NaWIZ9+nwMiCJg3L1dgQl4EppcFS1FQIE45vdwfJbgbm3T 9FwD+2HjKSl84yo+Fo/Y =eoPk -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/amit-migration/tags/migration-for-2.7-5' into staging Migration: - many compression/decompression fixes - trace improvements - static checker fix for detecting size mismatch in unused fields - fix VM save after snapshot # gpg: Signature made Fri 17 Jun 2016 13:59:44 BST # gpg: using RSA key 0xEB0B4DFC657EF670 # gpg: Good signature from "Amit Shah <amit@amitshah.net>" # gpg: aka "Amit Shah <amit@kernel.org>" # gpg: aka "Amit Shah <amitshah@gmx.net>" # Primary key fingerprint: 48CA 3722 5FE7 F4A8 B337 2735 1E9A 3B5F 8540 83B6 # Subkey fingerprint: CC63 D332 AB8F 4617 4529 6534 EB0B 4DFC 657E F670 * remotes/amit-migration/tags/migration-for-2.7-5: vmstate-static-checker: fix size mismatch detection in unused fields migration: code clean up migration: refine the decompression code migration: refine the compression code migration: protect the quit flag by lock migration: refine ram_save_compressed_page qemu-file: Fix qemu_put_compression_data flaw migration: remove useless code migration: Fix a potential issue migration: Fix multi-thread compression bug migration: fix inability to save VM after snapshot migration: Trace improvements migration: Don't use *_to_cpup() and cpu_to_*w() Signed-off-by: Peter Maydell <peter.maydell@linaro.org>master
commit
98b5b7422f
|
@ -1384,7 +1384,7 @@ static void *source_return_path_thread(void *opaque)
|
||||||
/* OK, we have the message and the data */
|
/* OK, we have the message and the data */
|
||||||
switch (header_type) {
|
switch (header_type) {
|
||||||
case MIG_RP_MSG_SHUT:
|
case MIG_RP_MSG_SHUT:
|
||||||
sibling_error = be32_to_cpup((uint32_t *)buf);
|
sibling_error = ldl_be_p(buf);
|
||||||
trace_source_return_path_thread_shut(sibling_error);
|
trace_source_return_path_thread_shut(sibling_error);
|
||||||
if (sibling_error) {
|
if (sibling_error) {
|
||||||
error_report("RP: Sibling indicated error %d", sibling_error);
|
error_report("RP: Sibling indicated error %d", sibling_error);
|
||||||
|
@ -1398,13 +1398,13 @@ static void *source_return_path_thread(void *opaque)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
case MIG_RP_MSG_PONG:
|
case MIG_RP_MSG_PONG:
|
||||||
tmp32 = be32_to_cpup((uint32_t *)buf);
|
tmp32 = ldl_be_p(buf);
|
||||||
trace_source_return_path_thread_pong(tmp32);
|
trace_source_return_path_thread_pong(tmp32);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MIG_RP_MSG_REQ_PAGES:
|
case MIG_RP_MSG_REQ_PAGES:
|
||||||
start = be64_to_cpup((uint64_t *)buf);
|
start = ldq_be_p(buf);
|
||||||
len = be32_to_cpup((uint32_t *)(buf + 8));
|
len = ldl_be_p(buf + 8);
|
||||||
migrate_handle_rp_req_pages(ms, NULL, start, len);
|
migrate_handle_rp_req_pages(ms, NULL, start, len);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -1412,8 +1412,8 @@ static void *source_return_path_thread(void *opaque)
|
||||||
expected_len = 12 + 1; /* header + termination */
|
expected_len = 12 + 1; /* header + termination */
|
||||||
|
|
||||||
if (header_len >= expected_len) {
|
if (header_len >= expected_len) {
|
||||||
start = be64_to_cpup((uint64_t *)buf);
|
start = ldq_be_p(buf);
|
||||||
len = be32_to_cpup((uint32_t *)(buf + 8));
|
len = ldl_be_p(buf + 8);
|
||||||
/* Now we expect an idstr */
|
/* Now we expect an idstr */
|
||||||
tmp32 = buf[12]; /* Length of the following idstr */
|
tmp32 = buf[12]; /* Length of the following idstr */
|
||||||
buf[13 + tmp32] = '\0';
|
buf[13 + tmp32] = '\0';
|
||||||
|
|
|
@ -615,8 +615,14 @@ uint64_t qemu_get_be64(QEMUFile *f)
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* compress size bytes of data start at p with specific compression
|
/* Compress size bytes of data start at p with specific compression
|
||||||
* level and store the compressed data to the buffer of f.
|
* level and store the compressed data to the buffer of f.
|
||||||
|
*
|
||||||
|
* When f is not writable, return -1 if f has no space to save the
|
||||||
|
* compressed data.
|
||||||
|
* When f is wirtable and it has no space to save the compressed data,
|
||||||
|
* do fflush first, if f still has no space to save the compressed
|
||||||
|
* data, return -1.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
ssize_t qemu_put_compression_data(QEMUFile *f, const uint8_t *p, size_t size,
|
ssize_t qemu_put_compression_data(QEMUFile *f, const uint8_t *p, size_t size,
|
||||||
|
@ -625,7 +631,14 @@ ssize_t qemu_put_compression_data(QEMUFile *f, const uint8_t *p, size_t size,
|
||||||
ssize_t blen = IO_BUF_SIZE - f->buf_index - sizeof(int32_t);
|
ssize_t blen = IO_BUF_SIZE - f->buf_index - sizeof(int32_t);
|
||||||
|
|
||||||
if (blen < compressBound(size)) {
|
if (blen < compressBound(size)) {
|
||||||
return 0;
|
if (!qemu_file_is_writable(f)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
qemu_fflush(f);
|
||||||
|
blen = IO_BUF_SIZE - sizeof(int32_t);
|
||||||
|
if (blen < compressBound(size)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (compress2(f->buf + f->buf_index + sizeof(int32_t), (uLongf *)&blen,
|
if (compress2(f->buf + f->buf_index + sizeof(int32_t), (uLongf *)&blen,
|
||||||
(Bytef *)p, size, level) != Z_OK) {
|
(Bytef *)p, size, level) != Z_OK) {
|
||||||
|
@ -633,7 +646,13 @@ ssize_t qemu_put_compression_data(QEMUFile *f, const uint8_t *p, size_t size,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
qemu_put_be32(f, blen);
|
qemu_put_be32(f, blen);
|
||||||
|
if (f->ops->writev_buffer) {
|
||||||
|
add_to_iovec(f, f->buf + f->buf_index, blen);
|
||||||
|
}
|
||||||
f->buf_index += blen;
|
f->buf_index += blen;
|
||||||
|
if (f->buf_index == IO_BUF_SIZE) {
|
||||||
|
qemu_fflush(f);
|
||||||
|
}
|
||||||
return blen + sizeof(int32_t);
|
return blen + sizeof(int32_t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
251
migration/ram.c
251
migration/ram.c
|
@ -253,8 +253,8 @@ static struct BitmapRcu {
|
||||||
} *migration_bitmap_rcu;
|
} *migration_bitmap_rcu;
|
||||||
|
|
||||||
struct CompressParam {
|
struct CompressParam {
|
||||||
bool start;
|
|
||||||
bool done;
|
bool done;
|
||||||
|
bool quit;
|
||||||
QEMUFile *file;
|
QEMUFile *file;
|
||||||
QemuMutex mutex;
|
QemuMutex mutex;
|
||||||
QemuCond cond;
|
QemuCond cond;
|
||||||
|
@ -264,7 +264,8 @@ struct CompressParam {
|
||||||
typedef struct CompressParam CompressParam;
|
typedef struct CompressParam CompressParam;
|
||||||
|
|
||||||
struct DecompressParam {
|
struct DecompressParam {
|
||||||
bool start;
|
bool done;
|
||||||
|
bool quit;
|
||||||
QemuMutex mutex;
|
QemuMutex mutex;
|
||||||
QemuCond cond;
|
QemuCond cond;
|
||||||
void *des;
|
void *des;
|
||||||
|
@ -279,45 +280,47 @@ static QemuThread *compress_threads;
|
||||||
* one of the compression threads has finished the compression.
|
* one of the compression threads has finished the compression.
|
||||||
* comp_done_lock is used to co-work with comp_done_cond.
|
* comp_done_lock is used to co-work with comp_done_cond.
|
||||||
*/
|
*/
|
||||||
static QemuMutex *comp_done_lock;
|
static QemuMutex comp_done_lock;
|
||||||
static QemuCond *comp_done_cond;
|
static QemuCond comp_done_cond;
|
||||||
/* The empty QEMUFileOps will be used by file in CompressParam */
|
/* The empty QEMUFileOps will be used by file in CompressParam */
|
||||||
static const QEMUFileOps empty_ops = { };
|
static const QEMUFileOps empty_ops = { };
|
||||||
|
|
||||||
static bool compression_switch;
|
static bool compression_switch;
|
||||||
static bool quit_comp_thread;
|
|
||||||
static bool quit_decomp_thread;
|
|
||||||
static DecompressParam *decomp_param;
|
static DecompressParam *decomp_param;
|
||||||
static QemuThread *decompress_threads;
|
static QemuThread *decompress_threads;
|
||||||
|
static QemuMutex decomp_done_lock;
|
||||||
|
static QemuCond decomp_done_cond;
|
||||||
|
|
||||||
static int do_compress_ram_page(CompressParam *param);
|
static int do_compress_ram_page(QEMUFile *f, RAMBlock *block,
|
||||||
|
ram_addr_t offset);
|
||||||
|
|
||||||
static void *do_data_compress(void *opaque)
|
static void *do_data_compress(void *opaque)
|
||||||
{
|
{
|
||||||
CompressParam *param = opaque;
|
CompressParam *param = opaque;
|
||||||
|
RAMBlock *block;
|
||||||
|
ram_addr_t offset;
|
||||||
|
|
||||||
while (!quit_comp_thread) {
|
qemu_mutex_lock(¶m->mutex);
|
||||||
qemu_mutex_lock(¶m->mutex);
|
while (!param->quit) {
|
||||||
/* Re-check the quit_comp_thread in case of
|
if (param->block) {
|
||||||
* terminate_compression_threads is called just before
|
block = param->block;
|
||||||
* qemu_mutex_lock(¶m->mutex) and after
|
offset = param->offset;
|
||||||
* while(!quit_comp_thread), re-check it here can make
|
param->block = NULL;
|
||||||
* sure the compression thread terminate as expected.
|
qemu_mutex_unlock(¶m->mutex);
|
||||||
*/
|
|
||||||
while (!param->start && !quit_comp_thread) {
|
do_compress_ram_page(param->file, block, offset);
|
||||||
|
|
||||||
|
qemu_mutex_lock(&comp_done_lock);
|
||||||
|
param->done = true;
|
||||||
|
qemu_cond_signal(&comp_done_cond);
|
||||||
|
qemu_mutex_unlock(&comp_done_lock);
|
||||||
|
|
||||||
|
qemu_mutex_lock(¶m->mutex);
|
||||||
|
} else {
|
||||||
qemu_cond_wait(¶m->cond, ¶m->mutex);
|
qemu_cond_wait(¶m->cond, ¶m->mutex);
|
||||||
}
|
}
|
||||||
if (!quit_comp_thread) {
|
|
||||||
do_compress_ram_page(param);
|
|
||||||
}
|
|
||||||
param->start = false;
|
|
||||||
qemu_mutex_unlock(¶m->mutex);
|
|
||||||
|
|
||||||
qemu_mutex_lock(comp_done_lock);
|
|
||||||
param->done = true;
|
|
||||||
qemu_cond_signal(comp_done_cond);
|
|
||||||
qemu_mutex_unlock(comp_done_lock);
|
|
||||||
}
|
}
|
||||||
|
qemu_mutex_unlock(¶m->mutex);
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -327,9 +330,9 @@ static inline void terminate_compression_threads(void)
|
||||||
int idx, thread_count;
|
int idx, thread_count;
|
||||||
|
|
||||||
thread_count = migrate_compress_threads();
|
thread_count = migrate_compress_threads();
|
||||||
quit_comp_thread = true;
|
|
||||||
for (idx = 0; idx < thread_count; idx++) {
|
for (idx = 0; idx < thread_count; idx++) {
|
||||||
qemu_mutex_lock(&comp_param[idx].mutex);
|
qemu_mutex_lock(&comp_param[idx].mutex);
|
||||||
|
comp_param[idx].quit = true;
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
@ -350,16 +353,12 @@ void migrate_compress_threads_join(void)
|
||||||
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);
|
||||||
}
|
}
|
||||||
qemu_mutex_destroy(comp_done_lock);
|
qemu_mutex_destroy(&comp_done_lock);
|
||||||
qemu_cond_destroy(comp_done_cond);
|
qemu_cond_destroy(&comp_done_cond);
|
||||||
g_free(compress_threads);
|
g_free(compress_threads);
|
||||||
g_free(comp_param);
|
g_free(comp_param);
|
||||||
g_free(comp_done_cond);
|
|
||||||
g_free(comp_done_lock);
|
|
||||||
compress_threads = NULL;
|
compress_threads = NULL;
|
||||||
comp_param = NULL;
|
comp_param = NULL;
|
||||||
comp_done_cond = NULL;
|
|
||||||
comp_done_lock = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void migrate_compress_threads_create(void)
|
void migrate_compress_threads_create(void)
|
||||||
|
@ -369,21 +368,19 @@ void migrate_compress_threads_create(void)
|
||||||
if (!migrate_use_compression()) {
|
if (!migrate_use_compression()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
quit_comp_thread = false;
|
|
||||||
compression_switch = true;
|
compression_switch = true;
|
||||||
thread_count = migrate_compress_threads();
|
thread_count = migrate_compress_threads();
|
||||||
compress_threads = g_new0(QemuThread, thread_count);
|
compress_threads = g_new0(QemuThread, thread_count);
|
||||||
comp_param = g_new0(CompressParam, thread_count);
|
comp_param = g_new0(CompressParam, thread_count);
|
||||||
comp_done_cond = g_new0(QemuCond, 1);
|
qemu_cond_init(&comp_done_cond);
|
||||||
comp_done_lock = g_new0(QemuMutex, 1);
|
qemu_mutex_init(&comp_done_lock);
|
||||||
qemu_cond_init(comp_done_cond);
|
|
||||||
qemu_mutex_init(comp_done_lock);
|
|
||||||
for (i = 0; i < thread_count; i++) {
|
for (i = 0; i < thread_count; i++) {
|
||||||
/* com_param[i].file is just used as a dummy buffer to save data, set
|
/* com_param[i].file is just used as a dummy buffer to save data, set
|
||||||
* it's ops to empty.
|
* it's ops to empty.
|
||||||
*/
|
*/
|
||||||
comp_param[i].file = qemu_fopen_ops(NULL, &empty_ops);
|
comp_param[i].file = qemu_fopen_ops(NULL, &empty_ops);
|
||||||
comp_param[i].done = true;
|
comp_param[i].done = true;
|
||||||
|
comp_param[i].quit = false;
|
||||||
qemu_mutex_init(&comp_param[i].mutex);
|
qemu_mutex_init(&comp_param[i].mutex);
|
||||||
qemu_cond_init(&comp_param[i].cond);
|
qemu_cond_init(&comp_param[i].cond);
|
||||||
qemu_thread_create(compress_threads + i, "compress",
|
qemu_thread_create(compress_threads + i, "compress",
|
||||||
|
@ -805,41 +802,27 @@ static int ram_save_page(QEMUFile *f, PageSearchStatus *pss,
|
||||||
return pages;
|
return pages;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int do_compress_ram_page(CompressParam *param)
|
static int do_compress_ram_page(QEMUFile *f, RAMBlock *block,
|
||||||
|
ram_addr_t offset)
|
||||||
{
|
{
|
||||||
int bytes_sent, blen;
|
int bytes_sent, blen;
|
||||||
uint8_t *p;
|
uint8_t *p = block->host + (offset & TARGET_PAGE_MASK);
|
||||||
RAMBlock *block = param->block;
|
|
||||||
ram_addr_t offset = param->offset;
|
|
||||||
|
|
||||||
p = block->host + (offset & TARGET_PAGE_MASK);
|
bytes_sent = save_page_header(f, block, offset |
|
||||||
|
|
||||||
bytes_sent = save_page_header(param->file, block, offset |
|
|
||||||
RAM_SAVE_FLAG_COMPRESS_PAGE);
|
RAM_SAVE_FLAG_COMPRESS_PAGE);
|
||||||
blen = qemu_put_compression_data(param->file, p, TARGET_PAGE_SIZE,
|
blen = qemu_put_compression_data(f, p, TARGET_PAGE_SIZE,
|
||||||
migrate_compress_level());
|
migrate_compress_level());
|
||||||
bytes_sent += blen;
|
if (blen < 0) {
|
||||||
|
bytes_sent = 0;
|
||||||
|
qemu_file_set_error(migrate_get_current()->to_dst_file, blen);
|
||||||
|
error_report("compressed data failed!");
|
||||||
|
} else {
|
||||||
|
bytes_sent += blen;
|
||||||
|
}
|
||||||
|
|
||||||
return bytes_sent;
|
return bytes_sent;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void start_compression(CompressParam *param)
|
|
||||||
{
|
|
||||||
param->done = false;
|
|
||||||
qemu_mutex_lock(¶m->mutex);
|
|
||||||
param->start = true;
|
|
||||||
qemu_cond_signal(¶m->cond);
|
|
||||||
qemu_mutex_unlock(¶m->mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void start_decompression(DecompressParam *param)
|
|
||||||
{
|
|
||||||
qemu_mutex_lock(¶m->mutex);
|
|
||||||
param->start = true;
|
|
||||||
qemu_cond_signal(¶m->cond);
|
|
||||||
qemu_mutex_unlock(¶m->mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint64_t bytes_transferred;
|
static uint64_t bytes_transferred;
|
||||||
|
|
||||||
static void flush_compressed_data(QEMUFile *f)
|
static void flush_compressed_data(QEMUFile *f)
|
||||||
|
@ -850,18 +833,22 @@ static void flush_compressed_data(QEMUFile *f)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
thread_count = migrate_compress_threads();
|
thread_count = migrate_compress_threads();
|
||||||
|
|
||||||
|
qemu_mutex_lock(&comp_done_lock);
|
||||||
for (idx = 0; idx < thread_count; idx++) {
|
for (idx = 0; idx < thread_count; idx++) {
|
||||||
if (!comp_param[idx].done) {
|
while (!comp_param[idx].done) {
|
||||||
qemu_mutex_lock(comp_done_lock);
|
qemu_cond_wait(&comp_done_cond, &comp_done_lock);
|
||||||
while (!comp_param[idx].done && !quit_comp_thread) {
|
|
||||||
qemu_cond_wait(comp_done_cond, comp_done_lock);
|
|
||||||
}
|
|
||||||
qemu_mutex_unlock(comp_done_lock);
|
|
||||||
}
|
}
|
||||||
if (!quit_comp_thread) {
|
}
|
||||||
|
qemu_mutex_unlock(&comp_done_lock);
|
||||||
|
|
||||||
|
for (idx = 0; idx < thread_count; idx++) {
|
||||||
|
qemu_mutex_lock(&comp_param[idx].mutex);
|
||||||
|
if (!comp_param[idx].quit) {
|
||||||
len = qemu_put_qemu_file(f, comp_param[idx].file);
|
len = qemu_put_qemu_file(f, comp_param[idx].file);
|
||||||
bytes_transferred += len;
|
bytes_transferred += len;
|
||||||
}
|
}
|
||||||
|
qemu_mutex_unlock(&comp_param[idx].mutex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -879,13 +866,16 @@ static int compress_page_with_multi_thread(QEMUFile *f, RAMBlock *block,
|
||||||
int idx, thread_count, bytes_xmit = -1, pages = -1;
|
int idx, thread_count, bytes_xmit = -1, pages = -1;
|
||||||
|
|
||||||
thread_count = migrate_compress_threads();
|
thread_count = migrate_compress_threads();
|
||||||
qemu_mutex_lock(comp_done_lock);
|
qemu_mutex_lock(&comp_done_lock);
|
||||||
while (true) {
|
while (true) {
|
||||||
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;
|
||||||
bytes_xmit = qemu_put_qemu_file(f, comp_param[idx].file);
|
bytes_xmit = qemu_put_qemu_file(f, comp_param[idx].file);
|
||||||
|
qemu_mutex_lock(&comp_param[idx].mutex);
|
||||||
set_compress_params(&comp_param[idx], block, offset);
|
set_compress_params(&comp_param[idx], block, offset);
|
||||||
start_compression(&comp_param[idx]);
|
qemu_cond_signal(&comp_param[idx].cond);
|
||||||
|
qemu_mutex_unlock(&comp_param[idx].mutex);
|
||||||
pages = 1;
|
pages = 1;
|
||||||
acct_info.norm_pages++;
|
acct_info.norm_pages++;
|
||||||
*bytes_transferred += bytes_xmit;
|
*bytes_transferred += bytes_xmit;
|
||||||
|
@ -895,10 +885,10 @@ static int compress_page_with_multi_thread(QEMUFile *f, RAMBlock *block,
|
||||||
if (pages > 0) {
|
if (pages > 0) {
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
qemu_cond_wait(comp_done_cond, comp_done_lock);
|
qemu_cond_wait(&comp_done_cond, &comp_done_lock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
qemu_mutex_unlock(comp_done_lock);
|
qemu_mutex_unlock(&comp_done_lock);
|
||||||
|
|
||||||
return pages;
|
return pages;
|
||||||
}
|
}
|
||||||
|
@ -919,24 +909,20 @@ static int ram_save_compressed_page(QEMUFile *f, PageSearchStatus *pss,
|
||||||
uint64_t *bytes_transferred)
|
uint64_t *bytes_transferred)
|
||||||
{
|
{
|
||||||
int pages = -1;
|
int pages = -1;
|
||||||
uint64_t bytes_xmit;
|
uint64_t bytes_xmit = 0;
|
||||||
uint8_t *p;
|
uint8_t *p;
|
||||||
int ret;
|
int ret, blen;
|
||||||
RAMBlock *block = pss->block;
|
RAMBlock *block = pss->block;
|
||||||
ram_addr_t offset = pss->offset;
|
ram_addr_t offset = pss->offset;
|
||||||
|
|
||||||
p = block->host + offset;
|
p = block->host + offset;
|
||||||
|
|
||||||
bytes_xmit = 0;
|
|
||||||
ret = ram_control_save_page(f, block->offset,
|
ret = ram_control_save_page(f, block->offset,
|
||||||
offset, TARGET_PAGE_SIZE, &bytes_xmit);
|
offset, TARGET_PAGE_SIZE, &bytes_xmit);
|
||||||
if (bytes_xmit) {
|
if (bytes_xmit) {
|
||||||
*bytes_transferred += bytes_xmit;
|
*bytes_transferred += bytes_xmit;
|
||||||
pages = 1;
|
pages = 1;
|
||||||
}
|
}
|
||||||
if (block == last_sent_block) {
|
|
||||||
offset |= RAM_SAVE_FLAG_CONTINUE;
|
|
||||||
}
|
|
||||||
if (ret != RAM_SAVE_CONTROL_NOT_SUPP) {
|
if (ret != RAM_SAVE_CONTROL_NOT_SUPP) {
|
||||||
if (ret != RAM_SAVE_CONTROL_DELAYED) {
|
if (ret != RAM_SAVE_CONTROL_DELAYED) {
|
||||||
if (bytes_xmit > 0) {
|
if (bytes_xmit > 0) {
|
||||||
|
@ -956,17 +942,22 @@ static int ram_save_compressed_page(QEMUFile *f, PageSearchStatus *pss,
|
||||||
flush_compressed_data(f);
|
flush_compressed_data(f);
|
||||||
pages = save_zero_page(f, block, offset, p, bytes_transferred);
|
pages = save_zero_page(f, block, offset, p, bytes_transferred);
|
||||||
if (pages == -1) {
|
if (pages == -1) {
|
||||||
set_compress_params(&comp_param[0], block, offset);
|
/* Make sure the first page is sent out before other pages */
|
||||||
/* Use the qemu thread to compress the data to make sure the
|
bytes_xmit = save_page_header(f, block, offset |
|
||||||
* first page is sent out before other pages
|
RAM_SAVE_FLAG_COMPRESS_PAGE);
|
||||||
*/
|
blen = qemu_put_compression_data(f, p, TARGET_PAGE_SIZE,
|
||||||
bytes_xmit = do_compress_ram_page(&comp_param[0]);
|
migrate_compress_level());
|
||||||
acct_info.norm_pages++;
|
if (blen > 0) {
|
||||||
qemu_put_qemu_file(f, comp_param[0].file);
|
*bytes_transferred += bytes_xmit + blen;
|
||||||
*bytes_transferred += bytes_xmit;
|
acct_info.norm_pages++;
|
||||||
pages = 1;
|
pages = 1;
|
||||||
|
} else {
|
||||||
|
qemu_file_set_error(f, blen);
|
||||||
|
error_report("compressed data failed!");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
offset |= RAM_SAVE_FLAG_CONTINUE;
|
||||||
pages = save_zero_page(f, block, offset, p, bytes_transferred);
|
pages = save_zero_page(f, block, offset, p, bytes_transferred);
|
||||||
if (pages == -1) {
|
if (pages == -1) {
|
||||||
pages = compress_page_with_multi_thread(f, block, offset,
|
pages = compress_page_with_multi_thread(f, block, offset,
|
||||||
|
@ -2191,29 +2182,59 @@ static void *do_data_decompress(void *opaque)
|
||||||
{
|
{
|
||||||
DecompressParam *param = opaque;
|
DecompressParam *param = opaque;
|
||||||
unsigned long pagesize;
|
unsigned long pagesize;
|
||||||
|
uint8_t *des;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
qemu_mutex_lock(¶m->mutex);
|
||||||
|
while (!param->quit) {
|
||||||
|
if (param->des) {
|
||||||
|
des = param->des;
|
||||||
|
len = param->len;
|
||||||
|
param->des = 0;
|
||||||
|
qemu_mutex_unlock(¶m->mutex);
|
||||||
|
|
||||||
while (!quit_decomp_thread) {
|
|
||||||
qemu_mutex_lock(¶m->mutex);
|
|
||||||
while (!param->start && !quit_decomp_thread) {
|
|
||||||
qemu_cond_wait(¶m->cond, ¶m->mutex);
|
|
||||||
pagesize = TARGET_PAGE_SIZE;
|
pagesize = TARGET_PAGE_SIZE;
|
||||||
if (!quit_decomp_thread) {
|
/* uncompress() will return failed in some case, especially
|
||||||
/* uncompress() will return failed in some case, especially
|
* when the page is dirted when doing the compression, it's
|
||||||
* when the page is dirted when doing the compression, it's
|
* not a problem because the dirty page will be retransferred
|
||||||
* not a problem because the dirty page will be retransferred
|
* and uncompress() won't break the data in other pages.
|
||||||
* and uncompress() won't break the data in other pages.
|
*/
|
||||||
*/
|
uncompress((Bytef *)des, &pagesize,
|
||||||
uncompress((Bytef *)param->des, &pagesize,
|
(const Bytef *)param->compbuf, len);
|
||||||
(const Bytef *)param->compbuf, param->len);
|
|
||||||
}
|
qemu_mutex_lock(&decomp_done_lock);
|
||||||
param->start = false;
|
param->done = true;
|
||||||
|
qemu_cond_signal(&decomp_done_cond);
|
||||||
|
qemu_mutex_unlock(&decomp_done_lock);
|
||||||
|
|
||||||
|
qemu_mutex_lock(¶m->mutex);
|
||||||
|
} else {
|
||||||
|
qemu_cond_wait(¶m->cond, ¶m->mutex);
|
||||||
}
|
}
|
||||||
qemu_mutex_unlock(¶m->mutex);
|
|
||||||
}
|
}
|
||||||
|
qemu_mutex_unlock(¶m->mutex);
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void wait_for_decompress_done(void)
|
||||||
|
{
|
||||||
|
int idx, thread_count;
|
||||||
|
|
||||||
|
if (!migrate_use_compression()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
thread_count = migrate_decompress_threads();
|
||||||
|
qemu_mutex_lock(&decomp_done_lock);
|
||||||
|
for (idx = 0; idx < thread_count; idx++) {
|
||||||
|
while (!decomp_param[idx].done) {
|
||||||
|
qemu_cond_wait(&decomp_done_cond, &decomp_done_lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
qemu_mutex_unlock(&decomp_done_lock);
|
||||||
|
}
|
||||||
|
|
||||||
void migrate_decompress_threads_create(void)
|
void migrate_decompress_threads_create(void)
|
||||||
{
|
{
|
||||||
int i, thread_count;
|
int i, thread_count;
|
||||||
|
@ -2221,11 +2242,14 @@ void migrate_decompress_threads_create(void)
|
||||||
thread_count = migrate_decompress_threads();
|
thread_count = migrate_decompress_threads();
|
||||||
decompress_threads = g_new0(QemuThread, thread_count);
|
decompress_threads = g_new0(QemuThread, thread_count);
|
||||||
decomp_param = g_new0(DecompressParam, thread_count);
|
decomp_param = g_new0(DecompressParam, thread_count);
|
||||||
quit_decomp_thread = false;
|
qemu_mutex_init(&decomp_done_lock);
|
||||||
|
qemu_cond_init(&decomp_done_cond);
|
||||||
for (i = 0; i < thread_count; i++) {
|
for (i = 0; i < thread_count; i++) {
|
||||||
qemu_mutex_init(&decomp_param[i].mutex);
|
qemu_mutex_init(&decomp_param[i].mutex);
|
||||||
qemu_cond_init(&decomp_param[i].cond);
|
qemu_cond_init(&decomp_param[i].cond);
|
||||||
decomp_param[i].compbuf = g_malloc0(compressBound(TARGET_PAGE_SIZE));
|
decomp_param[i].compbuf = g_malloc0(compressBound(TARGET_PAGE_SIZE));
|
||||||
|
decomp_param[i].done = true;
|
||||||
|
decomp_param[i].quit = false;
|
||||||
qemu_thread_create(decompress_threads + i, "decompress",
|
qemu_thread_create(decompress_threads + i, "decompress",
|
||||||
do_data_decompress, decomp_param + i,
|
do_data_decompress, decomp_param + i,
|
||||||
QEMU_THREAD_JOINABLE);
|
QEMU_THREAD_JOINABLE);
|
||||||
|
@ -2236,10 +2260,10 @@ void migrate_decompress_threads_join(void)
|
||||||
{
|
{
|
||||||
int i, thread_count;
|
int i, thread_count;
|
||||||
|
|
||||||
quit_decomp_thread = true;
|
|
||||||
thread_count = migrate_decompress_threads();
|
thread_count = migrate_decompress_threads();
|
||||||
for (i = 0; i < thread_count; i++) {
|
for (i = 0; i < thread_count; i++) {
|
||||||
qemu_mutex_lock(&decomp_param[i].mutex);
|
qemu_mutex_lock(&decomp_param[i].mutex);
|
||||||
|
decomp_param[i].quit = true;
|
||||||
qemu_cond_signal(&decomp_param[i].cond);
|
qemu_cond_signal(&decomp_param[i].cond);
|
||||||
qemu_mutex_unlock(&decomp_param[i].mutex);
|
qemu_mutex_unlock(&decomp_param[i].mutex);
|
||||||
}
|
}
|
||||||
|
@ -2261,20 +2285,27 @@ static void decompress_data_with_multi_threads(QEMUFile *f,
|
||||||
int idx, thread_count;
|
int idx, thread_count;
|
||||||
|
|
||||||
thread_count = migrate_decompress_threads();
|
thread_count = migrate_decompress_threads();
|
||||||
|
qemu_mutex_lock(&decomp_done_lock);
|
||||||
while (true) {
|
while (true) {
|
||||||
for (idx = 0; idx < thread_count; idx++) {
|
for (idx = 0; idx < thread_count; idx++) {
|
||||||
if (!decomp_param[idx].start) {
|
if (decomp_param[idx].done) {
|
||||||
|
decomp_param[idx].done = false;
|
||||||
|
qemu_mutex_lock(&decomp_param[idx].mutex);
|
||||||
qemu_get_buffer(f, decomp_param[idx].compbuf, len);
|
qemu_get_buffer(f, decomp_param[idx].compbuf, len);
|
||||||
decomp_param[idx].des = host;
|
decomp_param[idx].des = host;
|
||||||
decomp_param[idx].len = len;
|
decomp_param[idx].len = len;
|
||||||
start_decompression(&decomp_param[idx]);
|
qemu_cond_signal(&decomp_param[idx].cond);
|
||||||
|
qemu_mutex_unlock(&decomp_param[idx].mutex);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (idx < thread_count) {
|
if (idx < thread_count) {
|
||||||
break;
|
break;
|
||||||
|
} else {
|
||||||
|
qemu_cond_wait(&decomp_done_cond, &decomp_done_lock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
qemu_mutex_unlock(&decomp_done_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -2325,7 +2356,6 @@ static int ram_load_postcopy(QEMUFile *f)
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
page_buffer = host;
|
|
||||||
/*
|
/*
|
||||||
* Postcopy requires that we place whole host pages atomically.
|
* Postcopy requires that we place whole host pages atomically.
|
||||||
* To make it atomic, the data is read into a temporary page
|
* To make it atomic, the data is read into a temporary page
|
||||||
|
@ -2541,6 +2571,7 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wait_for_decompress_done();
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
DPRINTF("Completed load of VM with exit code %d seq iteration "
|
DPRINTF("Completed load of VM with exit code %d seq iteration "
|
||||||
"%" PRIu64 "\n", ret, seq_iter);
|
"%" PRIu64 "\n", ret, seq_iter);
|
||||||
|
|
|
@ -823,9 +823,9 @@ void qemu_savevm_send_postcopy_ram_discard(QEMUFile *f, const char *name,
|
||||||
buf[tmplen++] = '\0';
|
buf[tmplen++] = '\0';
|
||||||
|
|
||||||
for (t = 0; t < len; t++) {
|
for (t = 0; t < len; t++) {
|
||||||
cpu_to_be64w((uint64_t *)(buf + tmplen), start_list[t]);
|
stq_be_p(buf + tmplen, start_list[t]);
|
||||||
tmplen += 8;
|
tmplen += 8;
|
||||||
cpu_to_be64w((uint64_t *)(buf + tmplen), length_list[t]);
|
stq_be_p(buf + tmplen, length_list[t]);
|
||||||
tmplen += 8;
|
tmplen += 8;
|
||||||
}
|
}
|
||||||
qemu_savevm_command_send(f, MIG_CMD_POSTCOPY_RAM_DISCARD, tmplen, buf);
|
qemu_savevm_command_send(f, MIG_CMD_POSTCOPY_RAM_DISCARD, tmplen, buf);
|
||||||
|
@ -1150,10 +1150,12 @@ static int qemu_savevm_state(QEMUFile *f, Error **errp)
|
||||||
.shared = 0
|
.shared = 0
|
||||||
};
|
};
|
||||||
MigrationState *ms = migrate_init(¶ms);
|
MigrationState *ms = migrate_init(¶ms);
|
||||||
|
MigrationStatus status;
|
||||||
ms->to_dst_file = f;
|
ms->to_dst_file = f;
|
||||||
|
|
||||||
if (migration_is_blocked(errp)) {
|
if (migration_is_blocked(errp)) {
|
||||||
return -EINVAL;
|
ret = -EINVAL;
|
||||||
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
qemu_mutex_unlock_iothread();
|
qemu_mutex_unlock_iothread();
|
||||||
|
@ -1176,6 +1178,14 @@ static int qemu_savevm_state(QEMUFile *f, Error **errp)
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
error_setg_errno(errp, -ret, "Error while writing VM state");
|
error_setg_errno(errp, -ret, "Error while writing VM state");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
|
if (ret != 0) {
|
||||||
|
status = MIGRATION_STATUS_FAILED;
|
||||||
|
} else {
|
||||||
|
status = MIGRATION_STATUS_COMPLETED;
|
||||||
|
}
|
||||||
|
migrate_set_state(&ms->state, MIGRATION_STATUS_SETUP, status);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,7 @@ static int vmstate_n_elems(void *opaque, VMStateField *field)
|
||||||
n_elems *= field->num;
|
n_elems *= field->num;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trace_vmstate_n_elems(field->name, n_elems);
|
||||||
return n_elems;
|
return n_elems;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -381,25 +382,25 @@ static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd,
|
||||||
len = qemu_peek_byte(f, 1);
|
len = qemu_peek_byte(f, 1);
|
||||||
if (len < strlen(vmsd->name) + 1) {
|
if (len < strlen(vmsd->name) + 1) {
|
||||||
/* subsection name has be be "section_name/a" */
|
/* subsection name has be be "section_name/a" */
|
||||||
trace_vmstate_subsection_load_bad(vmsd->name, "(short)");
|
trace_vmstate_subsection_load_bad(vmsd->name, "(short)", "");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
size = qemu_peek_buffer(f, (uint8_t **)&idstr_ret, len, 2);
|
size = qemu_peek_buffer(f, (uint8_t **)&idstr_ret, len, 2);
|
||||||
if (size != len) {
|
if (size != len) {
|
||||||
trace_vmstate_subsection_load_bad(vmsd->name, "(peek fail)");
|
trace_vmstate_subsection_load_bad(vmsd->name, "(peek fail)", "");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
memcpy(idstr, idstr_ret, size);
|
memcpy(idstr, idstr_ret, size);
|
||||||
idstr[size] = 0;
|
idstr[size] = 0;
|
||||||
|
|
||||||
if (strncmp(vmsd->name, idstr, strlen(vmsd->name)) != 0) {
|
if (strncmp(vmsd->name, idstr, strlen(vmsd->name)) != 0) {
|
||||||
trace_vmstate_subsection_load_bad(vmsd->name, idstr);
|
trace_vmstate_subsection_load_bad(vmsd->name, idstr, "(prefix)");
|
||||||
/* it don't have a valid subsection name */
|
/* it doesn't have a valid subsection name */
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
sub_vmsd = vmstate_get_subsection(vmsd->subsections, idstr);
|
sub_vmsd = vmstate_get_subsection(vmsd->subsections, idstr);
|
||||||
if (sub_vmsd == NULL) {
|
if (sub_vmsd == NULL) {
|
||||||
trace_vmstate_subsection_load_bad(vmsd->name, "(lookup)");
|
trace_vmstate_subsection_load_bad(vmsd->name, idstr, "(lookup)");
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
qemu_file_skip(f, 1); /* subsection */
|
qemu_file_skip(f, 1); /* subsection */
|
||||||
|
@ -409,7 +410,7 @@ static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd,
|
||||||
|
|
||||||
ret = vmstate_load_state(f, sub_vmsd, opaque, version_id);
|
ret = vmstate_load_state(f, sub_vmsd, opaque, version_id);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
trace_vmstate_subsection_load_bad(vmsd->name, "(child)");
|
trace_vmstate_subsection_load_bad(vmsd->name, idstr, "(child)");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -185,7 +185,7 @@ def check_fields(src_fields, dest_fields, desc, sec):
|
||||||
if unused_count == 0:
|
if unused_count == 0:
|
||||||
advance_dest = True
|
advance_dest = True
|
||||||
|
|
||||||
if unused_count > 0:
|
if unused_count != 0:
|
||||||
if advance_dest == False:
|
if advance_dest == False:
|
||||||
unused_count = unused_count - s_item["size"]
|
unused_count = unused_count - s_item["size"]
|
||||||
if unused_count == 0:
|
if unused_count == 0:
|
||||||
|
|
|
@ -1271,8 +1271,9 @@ vmstate_load_field_error(const char *field, int ret) "field \"%s\" load failed,
|
||||||
vmstate_load_state(const char *name, int version_id) "%s v%d"
|
vmstate_load_state(const char *name, int version_id) "%s v%d"
|
||||||
vmstate_load_state_end(const char *name, const char *reason, int val) "%s %s/%d"
|
vmstate_load_state_end(const char *name, const char *reason, int val) "%s %s/%d"
|
||||||
vmstate_load_state_field(const char *name, const char *field) "%s:%s"
|
vmstate_load_state_field(const char *name, const char *field) "%s:%s"
|
||||||
|
vmstate_n_elems(const char *name, int n_elems) "%s: %d"
|
||||||
vmstate_subsection_load(const char *parent) "%s"
|
vmstate_subsection_load(const char *parent) "%s"
|
||||||
vmstate_subsection_load_bad(const char *parent, const char *sub) "%s: %s"
|
vmstate_subsection_load_bad(const char *parent, const char *sub, const char *sub2) "%s: %s/%s"
|
||||||
vmstate_subsection_load_good(const char *parent) "%s"
|
vmstate_subsection_load_good(const char *parent) "%s"
|
||||||
|
|
||||||
# qemu-file.c
|
# qemu-file.c
|
||||||
|
|
Loading…
Reference in New Issue