* [Qemu-devel] [PATCH v8 01/10] Add cache handling functions
2012-04-05 10:47 [Qemu-devel] [PATCH v8 00/10] XBZRLE delta for live migration of large memory app Orit Wasserman
@ 2012-04-05 10:47 ` Orit Wasserman
2012-04-05 10:47 ` [Qemu-devel] [PATCH v8 02/10] Add uleb encoding/decoding functions Orit Wasserman
` (8 subsequent siblings)
9 siblings, 0 replies; 13+ messages in thread
From: Orit Wasserman @ 2012-04-05 10:47 UTC (permalink / raw)
To: qemu-devel; +Cc: aliguori, quintela, stefanha, blauwirbel, Orit Wasserman, avi
Add LRU page cache mechanism.
The page are accessed by their address.
Signed-off-by: Orit Wasserman <owasserm@redhat.com>
---
arch_init.c | 174 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 174 insertions(+), 0 deletions(-)
diff --git a/arch_init.c b/arch_init.c
index 595badf..42d4f17 100644
--- a/arch_init.c
+++ b/arch_init.c
@@ -28,6 +28,7 @@
#include <sys/types.h>
#include <sys/mman.h>
#endif
+#include <assert.h>
#include "config.h"
#include "monitor.h"
#include "sysemu.h"
@@ -44,6 +45,14 @@
#include "exec-memory.h"
#include "hw/pcspk.h"
+#ifdef DEBUG_ARCH_INIT
+#define DPRINTF(fmt, ...) \
+ do { fprintf(stdout, "arch_init: " fmt, ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) \
+ do { } while (0)
+#endif
+
#ifdef TARGET_SPARC
int graphic_width = 1024;
int graphic_height = 768;
@@ -127,6 +136,171 @@ static int is_dup_page(uint8_t *page)
return 1;
}
+/***********************************************************/
+/* Page cache for storing previous pages as basis for XBZRLE compression */
+#define CACHE_N_WAY 2 /* 2-way assossiative cache */
+
+typedef struct CacheItem {
+ ram_addr_t it_addr;
+ unsigned long it_age;
+ uint8_t *it_data;
+} CacheItem;
+
+typedef struct CacheBucket {
+ CacheItem bkt_item[CACHE_N_WAY];
+} CacheBucket;
+
+static CacheBucket *page_cache;
+static int64_t cache_num_buckets;
+static uint64_t cache_max_item_age;
+static int64_t cache_num_items;
+
+static void cache_init(ssize_t num_buckets);
+static void cache_fini(void);
+static int cache_is_cached(ram_addr_t addr);
+static int cache_get_oldest(CacheBucket *buck);
+static int cache_get_newest(CacheBucket *buck, ram_addr_t addr);
+static void cache_insert(ram_addr_t id, uint8_t *pdata);
+static unsigned long cache_get_cache_pos(ram_addr_t address);
+static CacheItem *cache_item_get(unsigned long pos, int item);
+
+/***********************************************************/
+/* XBRLE page cache implementation */
+static CacheItem *cache_item_get(unsigned long pos, int item)
+{
+ assert(page_cache);
+ return &page_cache[pos].bkt_item[item];
+}
+
+static void cache_init(int64_t num_bytes)
+{
+ int i;
+
+ cache_num_items = 0;
+ cache_max_item_age = 0;
+ cache_num_buckets = num_bytes / (TARGET_PAGE_SIZE * CACHE_N_WAY);
+ assert(cache_num_buckets);
+ DPRINTF("Setting cache buckets to %lu\n", cache_num_buckets);
+
+ assert(!page_cache);
+ page_cache = (CacheBucket *)g_malloc((cache_num_buckets) *
+ sizeof(CacheBucket));
+
+ for (i = 0; i < cache_num_buckets; i++) {
+ int j;
+ for (j = 0; j < CACHE_N_WAY; j++) {
+ CacheItem *it = cache_item_get(i, j);
+ it->it_data = NULL;
+ it->it_age = 0;
+ it->it_addr = -1;
+ }
+ }
+}
+
+static void cache_fini(void)
+{
+ int i;
+
+ assert(page_cache);
+
+ for (i = 0; i < cache_num_buckets; i++) {
+ int j;
+ for (j = 0; j < CACHE_N_WAY; j++) {
+ CacheItem *it = cache_item_get(i, j);
+ g_free(it->it_data);
+ it->it_data = 0;
+ }
+ }
+
+ g_free(page_cache);
+ page_cache = NULL;
+}
+
+static unsigned long cache_get_cache_pos(ram_addr_t address)
+{
+ unsigned long pos;
+
+ assert(cache_num_buckets);
+ pos = (address/TARGET_PAGE_SIZE) & (cache_num_buckets - 1);
+ return pos;
+}
+
+static int cache_get_newest(CacheBucket *buck, ram_addr_t addr)
+{
+ unsigned long big = 0;
+ int big_pos = -1;
+ int j;
+
+ assert(page_cache);
+
+ for (j = 0; j < CACHE_N_WAY; j++) {
+ CacheItem *it = &buck->bkt_item[j];
+
+ if (it->it_addr != addr) {
+ continue;
+ }
+
+ if (!j || it->it_age > big) {
+ big = it->it_age;
+ big_pos = j;
+ }
+ }
+
+ return big_pos;
+}
+
+static int cache_get_oldest(CacheBucket *buck)
+{
+ unsigned long small = 0;
+ int small_pos = -1;
+ int j;
+
+ assert(page_cache);
+
+ for (j = 0; j < CACHE_N_WAY; j++) {
+ CacheItem *it = &buck->bkt_item[j];
+
+ if (!j || it->it_age < small) {
+ small = it->it_age;
+ small_pos = j;
+ }
+ }
+
+ return small_pos;
+}
+
+static int cache_is_cached(ram_addr_t addr)
+{
+ unsigned long pos = cache_get_cache_pos(addr);
+
+ assert(page_cache);
+ CacheBucket *bucket = &page_cache[pos];
+ return cache_get_newest(bucket, addr);
+}
+
+static void cache_insert(unsigned long addr, uint8_t *pdata)
+{
+ unsigned long pos;
+ int slot = -1;
+ CacheBucket *bucket;
+
+ pos = cache_get_cache_pos(addr);
+ assert(page_cache);
+ bucket = &page_cache[pos];
+ slot = cache_get_oldest(bucket); /* evict LRU */
+
+ /* actual update of entry */
+ CacheItem *it = cache_item_get(pos, slot);
+ if (!it->it_data) {
+ it->it_data = g_malloc(TARGET_PAGE_SIZE);
+ cache_num_items++;
+ }
+
+ memcpy(it->it_data, pdata, TARGET_PAGE_SIZE);
+ it->it_age = ++cache_max_item_age;
+ it->it_addr = addr;
+}
+
static RAMBlock *last_block;
static ram_addr_t last_offset;
--
1.7.7.6
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [Qemu-devel] [PATCH v8 02/10] Add uleb encoding/decoding functions
2012-04-05 10:47 [Qemu-devel] [PATCH v8 00/10] XBZRLE delta for live migration of large memory app Orit Wasserman
2012-04-05 10:47 ` [Qemu-devel] [PATCH v8 01/10] Add cache handling functions Orit Wasserman
@ 2012-04-05 10:47 ` Orit Wasserman
2012-04-05 10:47 ` [Qemu-devel] [PATCH v8 03/10] Add save_block_hdr function Orit Wasserman
` (7 subsequent siblings)
9 siblings, 0 replies; 13+ messages in thread
From: Orit Wasserman @ 2012-04-05 10:47 UTC (permalink / raw)
To: qemu-devel; +Cc: aliguori, quintela, stefanha, blauwirbel, Orit Wasserman, avi
Implement Unsigned Little Endian Base 128.
Signed-off-by: Orit Wasserman <owasserm@redhat.com>
---
migration.h | 4 ++++
savevm.c | 28 ++++++++++++++++++++++++++++
2 files changed, 32 insertions(+), 0 deletions(-)
diff --git a/migration.h b/migration.h
index 691b367..d798fac 100644
--- a/migration.h
+++ b/migration.h
@@ -92,4 +92,8 @@ void migrate_add_blocker(Error *reason);
*/
void migrate_del_blocker(Error *reason);
+/* ULEB128 */
+int uleb128_encode_small(uint8_t *out, uint32_t n);
+int uleb128_decode_small(const uint8 *in, uint32_t *n);
+
#endif
diff --git a/savevm.c b/savevm.c
index 12fb209..0b2fe38 100644
--- a/savevm.c
+++ b/savevm.c
@@ -2368,3 +2368,31 @@ void vmstate_register_ram_global(MemoryRegion *mr)
{
vmstate_register_ram(mr, NULL);
}
+
+/* ULEB128 */
+int uleb128_encode_small(uint8_t *out, uint32_t n)
+{
+ assert(n <= 0x3fff);
+ if (n < 0x80) {
+ *out++ = n;
+ return 1;
+ } else {
+ *out++ = (n & 0x7f) | 0x80;
+ *out++ = n >> 7;
+ return 2;
+ }
+}
+
+int uleb128_decode_small(const uint8 *in, uint32_t *n)
+{
+ if (!(*in & 0x80)) {
+ *n = *in++;
+ return 1;
+ } else {
+ *n = *in++ & 0x7f;
+ assert(!(*in & 0x80));
+ *n |= *in++ << 7;
+ return 2;
+ }
+}
+
--
1.7.7.6
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [Qemu-devel] [PATCH v8 03/10] Add save_block_hdr function
2012-04-05 10:47 [Qemu-devel] [PATCH v8 00/10] XBZRLE delta for live migration of large memory app Orit Wasserman
2012-04-05 10:47 ` [Qemu-devel] [PATCH v8 01/10] Add cache handling functions Orit Wasserman
2012-04-05 10:47 ` [Qemu-devel] [PATCH v8 02/10] Add uleb encoding/decoding functions Orit Wasserman
@ 2012-04-05 10:47 ` Orit Wasserman
2012-04-05 10:47 ` [Qemu-devel] [PATCH v8 04/10] Add host_from_stream_offset_versioned function Orit Wasserman
` (6 subsequent siblings)
9 siblings, 0 replies; 13+ messages in thread
From: Orit Wasserman @ 2012-04-05 10:47 UTC (permalink / raw)
To: qemu-devel; +Cc: aliguori, quintela, stefanha, blauwirbel, Orit Wasserman, avi
Signed-off-by: Orit Wasserman <owasserm@redhat.com>
---
arch_init.c | 25 +++++++++++++------------
1 files changed, 13 insertions(+), 12 deletions(-)
diff --git a/arch_init.c b/arch_init.c
index 42d4f17..5f60fa9 100644
--- a/arch_init.c
+++ b/arch_init.c
@@ -301,6 +301,17 @@ static void cache_insert(unsigned long addr, uint8_t *pdata)
it->it_addr = addr;
}
+static void save_block_hdr(QEMUFile *f, RAMBlock *block, ram_addr_t offset,
+ int cont, int flag)
+{
+ qemu_put_be64(f, offset | cont | flag);
+ if (!cont) {
+ qemu_put_byte(f, strlen(block->idstr));
+ qemu_put_buffer(f, (uint8_t *)block->idstr,
+ strlen(block->idstr));
+ }
+}
+
static RAMBlock *last_block;
static ram_addr_t last_offset;
@@ -327,21 +338,11 @@ static int ram_save_block(QEMUFile *f)
p = memory_region_get_ram_ptr(mr) + offset;
if (is_dup_page(p)) {
- qemu_put_be64(f, offset | cont | RAM_SAVE_FLAG_COMPRESS);
- if (!cont) {
- qemu_put_byte(f, strlen(block->idstr));
- qemu_put_buffer(f, (uint8_t *)block->idstr,
- strlen(block->idstr));
- }
+ save_block_hdr(f, block, offset, cont, RAM_SAVE_FLAG_COMPRESS);
qemu_put_byte(f, *p);
bytes_sent = 1;
} else {
- qemu_put_be64(f, offset | cont | RAM_SAVE_FLAG_PAGE);
- if (!cont) {
- qemu_put_byte(f, strlen(block->idstr));
- qemu_put_buffer(f, (uint8_t *)block->idstr,
- strlen(block->idstr));
- }
+ save_block_hdr(f, block, offset, cont, RAM_SAVE_FLAG_PAGE);
qemu_put_buffer(f, p, TARGET_PAGE_SIZE);
bytes_sent = TARGET_PAGE_SIZE;
}
--
1.7.7.6
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [Qemu-devel] [PATCH v8 04/10] Add host_from_stream_offset_versioned function
2012-04-05 10:47 [Qemu-devel] [PATCH v8 00/10] XBZRLE delta for live migration of large memory app Orit Wasserman
` (2 preceding siblings ...)
2012-04-05 10:47 ` [Qemu-devel] [PATCH v8 03/10] Add save_block_hdr function Orit Wasserman
@ 2012-04-05 10:47 ` Orit Wasserman
2012-04-05 10:47 ` [Qemu-devel] [PATCH v8 05/10] Add MigrationParams structure Orit Wasserman
` (5 subsequent siblings)
9 siblings, 0 replies; 13+ messages in thread
From: Orit Wasserman @ 2012-04-05 10:47 UTC (permalink / raw)
To: qemu-devel; +Cc: aliguori, quintela, stefanha, blauwirbel, Orit Wasserman, avi
Signed-off-by: Orit Wasserman <owasserm@redhat.com>
---
arch_init.c | 26 +++++++++++++++++++++++---
1 files changed, 23 insertions(+), 3 deletions(-)
diff --git a/arch_init.c b/arch_init.c
index 5f60fa9..cef5d6f 100644
--- a/arch_init.c
+++ b/arch_init.c
@@ -552,6 +552,18 @@ static inline void *host_from_stream_offset(QEMUFile *f,
return NULL;
}
+static inline void *host_from_stream_offset_versioned(int version_id,
+ QEMUFile *f, ram_addr_t offset, int flags)
+{
+ void *host;
+ if (version_id == 3) {
+ host = qemu_get_ram_ptr(offset);
+ } else {
+ host = host_from_stream_offset(f, offset, flags);
+ }
+ return host;
+}
+
int ram_load(QEMUFile *f, void *opaque, int version_id)
{
ram_addr_t addr;
@@ -607,8 +619,11 @@ int ram_load(QEMUFile *f, void *opaque, int version_id)
void *host;
uint8_t ch;
- host = host_from_stream_offset(f, addr, flags);
+ host = host_from_stream_offset_versioned(version_id,
+ f, addr, flags);
if (!host) {
+ fprintf(stderr, "Failed to convert RAM address to host"
+ " for offset " RAM_ADDR_FMT "\n", addr);
return -EINVAL;
}
@@ -623,8 +638,13 @@ int ram_load(QEMUFile *f, void *opaque, int version_id)
} else if (flags & RAM_SAVE_FLAG_PAGE) {
void *host;
- host = host_from_stream_offset(f, addr, flags);
-
+ host = host_from_stream_offset_versioned(version_id,
+ f, addr, flags);
+ if (!host) {
+ fprintf(stderr, "Failed to convert RAM address to host"
+ " for offset " RAM_ADDR_FMT "\n", addr);
+ return -EINVAL;
+ }
qemu_get_buffer(f, host, TARGET_PAGE_SIZE);
}
error = qemu_file_get_error(f);
--
1.7.7.6
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [Qemu-devel] [PATCH v8 05/10] Add MigrationParams structure
2012-04-05 10:47 [Qemu-devel] [PATCH v8 00/10] XBZRLE delta for live migration of large memory app Orit Wasserman
` (3 preceding siblings ...)
2012-04-05 10:47 ` [Qemu-devel] [PATCH v8 04/10] Add host_from_stream_offset_versioned function Orit Wasserman
@ 2012-04-05 10:47 ` Orit Wasserman
2012-04-05 10:47 ` [Qemu-devel] [PATCH v8 06/10] Add XBZRLE to ram_save_block and ram_save_live Orit Wasserman
` (4 subsequent siblings)
9 siblings, 0 replies; 13+ messages in thread
From: Orit Wasserman @ 2012-04-05 10:47 UTC (permalink / raw)
To: qemu-devel
Cc: aliguori, quintela, stefanha, blauwirbel, Orit Wasserman, avi,
Isaku Yamahata
Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
---
block-migration.c | 8 ++++----
migration.c | 13 ++++++++-----
migration.h | 9 +++++++--
qemu-common.h | 1 +
savevm.c | 11 ++++++++---
sysemu.h | 3 ++-
vmstate.h | 2 +-
7 files changed, 31 insertions(+), 16 deletions(-)
diff --git a/block-migration.c b/block-migration.c
index fd2ffff..b95b4e1 100644
--- a/block-migration.c
+++ b/block-migration.c
@@ -700,13 +700,13 @@ static int block_load(QEMUFile *f, void *opaque, int version_id)
return 0;
}
-static void block_set_params(int blk_enable, int shared_base, void *opaque)
+static void block_set_params(const MigrationParams *params, void *opaque)
{
- block_mig_state.blk_enable = blk_enable;
- block_mig_state.shared_base = shared_base;
+ block_mig_state.blk_enable = params->blk;
+ block_mig_state.shared_base = params->shared;
/* shared base means that blk_enable = 1 */
- block_mig_state.blk_enable |= shared_base;
+ block_mig_state.blk_enable |= params->shared;
}
void blk_mig_init(void)
diff --git a/migration.c b/migration.c
index 8c119ba..1bdac1e 100644
--- a/migration.c
+++ b/migration.c
@@ -350,7 +350,7 @@ void migrate_fd_connect(MigrationState *s)
migrate_fd_close);
DPRINTF("beginning savevm\n");
- ret = qemu_savevm_state_begin(s->file, s->blk, s->shared);
+ ret = qemu_savevm_state_begin(s->file, &s->params);
if (ret < 0) {
DPRINTF("failed, %d\n", ret);
migrate_fd_error(s);
@@ -359,15 +359,14 @@ void migrate_fd_connect(MigrationState *s)
migrate_fd_put_ready(s);
}
-static MigrationState *migrate_init(int blk, int inc)
+static MigrationState *migrate_init(const MigrationParams *params)
{
MigrationState *s = migrate_get_current();
int64_t bandwidth_limit = s->bandwidth_limit;
memset(s, 0, sizeof(*s));
s->bandwidth_limit = bandwidth_limit;
- s->blk = blk;
- s->shared = inc;
+ s->params = *params;
s->bandwidth_limit = bandwidth_limit;
s->state = MIG_STATE_SETUP;
@@ -392,9 +391,13 @@ void qmp_migrate(const char *uri, bool has_blk, bool blk,
Error **errp)
{
MigrationState *s = migrate_get_current();
+ MigrationParams params;
const char *p;
int ret;
+ params.blk = blk;
+ params.shared = inc;
+
if (s->state == MIG_STATE_ACTIVE) {
error_set(errp, QERR_MIGRATION_ACTIVE);
return;
@@ -409,7 +412,7 @@ void qmp_migrate(const char *uri, bool has_blk, bool blk,
return;
}
- s = migrate_init(blk, inc);
+ s = migrate_init(¶ms);
if (strstart(uri, "tcp:", &p)) {
ret = tcp_start_outgoing_migration(s, p);
diff --git a/migration.h b/migration.h
index d798fac..b2097a9 100644
--- a/migration.h
+++ b/migration.h
@@ -19,6 +19,11 @@
#include "notify.h"
#include "error.h"
+struct MigrationParams {
+ int blk;
+ int shared;
+};
+
typedef struct MigrationState MigrationState;
struct MigrationState
@@ -31,10 +36,10 @@ struct MigrationState
int (*close)(MigrationState *s);
int (*write)(MigrationState *s, const void *buff, size_t size);
void *opaque;
- int blk;
- int shared;
+ MigrationParams params;
};
+
void process_incoming_migration(QEMUFile *f);
int qemu_start_incoming_migration(const char *uri);
diff --git a/qemu-common.h b/qemu-common.h
index 4647dd9..c7d5571 100644
--- a/qemu-common.h
+++ b/qemu-common.h
@@ -17,6 +17,7 @@ typedef struct DeviceState DeviceState;
struct Monitor;
typedef struct Monitor Monitor;
+typedef struct MigrationParams MigrationParams;
/* we put basic includes here to avoid repeating them in device drivers */
#include <stdlib.h>
diff --git a/savevm.c b/savevm.c
index 0b2fe38..4736784 100644
--- a/savevm.c
+++ b/savevm.c
@@ -1561,7 +1561,8 @@ bool qemu_savevm_state_blocked(Error **errp)
return false;
}
-int qemu_savevm_state_begin(QEMUFile *f, int blk_enable, int shared)
+int qemu_savevm_state_begin(QEMUFile *f,
+ const MigrationParams *params)
{
SaveStateEntry *se;
int ret;
@@ -1570,7 +1571,7 @@ int qemu_savevm_state_begin(QEMUFile *f, int blk_enable, int shared)
if(se->set_params == NULL) {
continue;
}
- se->set_params(blk_enable, shared, se->opaque);
+ se->set_params(params, se->opaque);
}
qemu_put_be32(f, QEMU_VM_FILE_MAGIC);
@@ -1708,13 +1709,17 @@ void qemu_savevm_state_cancel(QEMUFile *f)
static int qemu_savevm_state(QEMUFile *f)
{
int ret;
+ MigrationParams params = {
+ .blk = 0,
+ .shared = 0
+ };
if (qemu_savevm_state_blocked(NULL)) {
ret = -EINVAL;
goto out;
}
- ret = qemu_savevm_state_begin(f, 0, 0);
+ ret = qemu_savevm_state_begin(f, ¶ms);
if (ret < 0)
goto out;
diff --git a/sysemu.h b/sysemu.h
index bc2c788..6540c79 100644
--- a/sysemu.h
+++ b/sysemu.h
@@ -77,7 +77,8 @@ void do_info_snapshots(Monitor *mon);
void qemu_announce_self(void);
bool qemu_savevm_state_blocked(Error **errp);
-int qemu_savevm_state_begin(QEMUFile *f, int blk_enable, int shared);
+int qemu_savevm_state_begin(QEMUFile *f,
+ const MigrationParams *params);
int qemu_savevm_state_iterate(QEMUFile *f);
int qemu_savevm_state_complete(QEMUFile *f);
void qemu_savevm_state_cancel(QEMUFile *f);
diff --git a/vmstate.h b/vmstate.h
index 82d97ae..5af45e0 100644
--- a/vmstate.h
+++ b/vmstate.h
@@ -26,7 +26,7 @@
#ifndef QEMU_VMSTATE_H
#define QEMU_VMSTATE_H 1
-typedef void SaveSetParamsHandler(int blk_enable, int shared, void * opaque);
+typedef void SaveSetParamsHandler(const MigrationParams *params, void * opaque);
typedef void SaveStateHandler(QEMUFile *f, void *opaque);
typedef int SaveLiveStateHandler(QEMUFile *f, int stage, void *opaque);
typedef int LoadStateHandler(QEMUFile *f, void *opaque, int version_id);
--
1.7.7.6
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [Qemu-devel] [PATCH v8 06/10] Add XBZRLE to ram_save_block and ram_save_live
2012-04-05 10:47 [Qemu-devel] [PATCH v8 00/10] XBZRLE delta for live migration of large memory app Orit Wasserman
` (4 preceding siblings ...)
2012-04-05 10:47 ` [Qemu-devel] [PATCH v8 05/10] Add MigrationParams structure Orit Wasserman
@ 2012-04-05 10:47 ` Orit Wasserman
2012-04-05 10:47 ` [Qemu-devel] [PATCH v8 07/10] Add XBZRLE option to migrate command Orit Wasserman
` (3 subsequent siblings)
9 siblings, 0 replies; 13+ messages in thread
From: Orit Wasserman @ 2012-04-05 10:47 UTC (permalink / raw)
To: qemu-devel; +Cc: aliguori, quintela, stefanha, blauwirbel, Orit Wasserman, avi
Add migration state to store XBRLE params (enablement and cache size).
In the outgoing migration check to see if the page is cached and
changed than send compressed page by using save_xbrle_page function.
In the incoming migration check to see if RAM_SAVE_FLAG_XBRLE is set
and decompress the page (by using load_xbrle function).
Signed-off-by: Orit Wasserman <owasserm@redhat.com>
---
arch_init.c | 175 +++++++++++++++++++++++++++++++++++++++++++++++++++++++----
migration.c | 18 ++++++
migration.h | 8 +++
savevm.c | 94 +++++++++++++++++++++++++++++++-
4 files changed, 282 insertions(+), 13 deletions(-)
diff --git a/arch_init.c b/arch_init.c
index cef5d6f..d728e5a 100644
--- a/arch_init.c
+++ b/arch_init.c
@@ -104,6 +104,7 @@ const uint32_t arch_type = QEMU_ARCH;
#define RAM_SAVE_FLAG_PAGE 0x08
#define RAM_SAVE_FLAG_EOS 0x10
#define RAM_SAVE_FLAG_CONTINUE 0x20
+#define RAM_SAVE_FLAG_XBZRLE 0x40
#ifdef __ALTIVEC__
#include <altivec.h>
@@ -164,6 +165,15 @@ static void cache_insert(ram_addr_t id, uint8_t *pdata);
static unsigned long cache_get_cache_pos(ram_addr_t address);
static CacheItem *cache_item_get(unsigned long pos, int item);
+/* XBZRLE (Xor Based Zero Length Encoding */
+typedef struct __attribute__((packed)) XBZRLEHeader {
+ uint8_t xh_flags;
+ uint16_t xh_len;
+ uint32_t xh_cksum;
+} XBZRLEHeader;
+
+static uint8 *prev_cache_page;
+
/***********************************************************/
/* XBRLE page cache implementation */
static CacheItem *cache_item_get(unsigned long pos, int item)
@@ -195,6 +205,8 @@ static void cache_init(int64_t num_bytes)
it->it_addr = -1;
}
}
+
+ prev_cache_page = g_malloc(TARGET_PAGE_SIZE);
}
static void cache_fini(void)
@@ -214,6 +226,9 @@ static void cache_fini(void)
g_free(page_cache);
page_cache = NULL;
+
+ g_free(prev_cache_page);
+ prev_cache_page = NULL;
}
static unsigned long cache_get_cache_pos(ram_addr_t address)
@@ -312,19 +327,73 @@ static void save_block_hdr(QEMUFile *f, RAMBlock *block, ram_addr_t offset,
}
}
+#define ENCODING_FLAG_XBZRLE 0x1
+
+static int save_xbzrle_page(QEMUFile *f, uint8_t *current_data,
+ ram_addr_t current_addr, RAMBlock *block,
+ ram_addr_t offset, int cont)
+{
+ int cache_location = -1, slot = -1, encoded_len = 0, bytes_sent = 0;
+ XBZRLEHeader hdr = {0};
+ CacheItem *it;
+ uint8_t *encoded_buf = NULL;
+
+ /* get location */
+ slot = cache_is_cached(current_addr);
+ if (slot == -1) {
+ cache_insert(current_addr, current_data);
+ goto done;
+ }
+ cache_location = cache_get_cache_pos(current_addr);
+
+ /* abort if page changed too much */
+ it = cache_item_get(cache_location, slot);
+ /* we need to copy the current data before encoding so it won't change
+ during encoding. cache_insert copies the data.
+ */
+ memcpy(it->it_data, prev_cache_page, TARGET_PAGE_SIZE);
+ cache_insert(current_addr, current_data);
+
+ /* XBZRLE encoding (if there is no overflow) */
+ encoded_buf = (uint8_t *) g_malloc(TARGET_PAGE_SIZE);
+ encoded_len = encode_page(prev_cache_page, it->it_data, TARGET_PAGE_SIZE,
+ encoded_buf, TARGET_PAGE_SIZE);
+ if (encoded_len < 0) {
+ DPRINTF("Unmodifed page - skipping\n");
+ goto done;
+ }
+
+ hdr.xh_len = encoded_len;
+ hdr.xh_flags |= ENCODING_FLAG_XBZRLE;
+
+ /* Send XBZRLE based compressed page */
+ save_block_hdr(f, block, offset, cont, RAM_SAVE_FLAG_XBZRLE);
+ qemu_put_buffer(f, (uint8_t *) &hdr, sizeof(hdr));
+ qemu_put_buffer(f, encoded_buf, encoded_len);
+ bytes_sent = encoded_len + sizeof(hdr);
+
+done:
+ g_free(encoded_buf);
+ return bytes_sent;
+}
+
static RAMBlock *last_block;
static ram_addr_t last_offset;
-static int ram_save_block(QEMUFile *f)
+static int ram_save_block(QEMUFile *f, int stage)
{
RAMBlock *block = last_block;
ram_addr_t offset = last_offset;
int bytes_sent = 0;
MemoryRegion *mr;
+ ram_addr_t current_addr;
+ int use_xbzrle = migrate_use_xbzrle();
if (!block)
block = QLIST_FIRST(&ram_list.blocks);
+ current_addr = block->offset + offset;
+
do {
mr = block->mr;
if (memory_region_get_dirty(mr, offset, TARGET_PAGE_SIZE,
@@ -341,7 +410,14 @@ static int ram_save_block(QEMUFile *f)
save_block_hdr(f, block, offset, cont, RAM_SAVE_FLAG_COMPRESS);
qemu_put_byte(f, *p);
bytes_sent = 1;
- } else {
+ } else if (stage == 2 && use_xbzrle) {
+ bytes_sent = save_xbzrle_page(f, p, current_addr, block,
+ offset, cont);
+ } else if (use_xbzrle && stage == 1) {
+ cache_insert(current_addr, p);
+ }
+
+ if (!bytes_sent) {
save_block_hdr(f, block, offset, cont, RAM_SAVE_FLAG_PAGE);
qemu_put_buffer(f, p, TARGET_PAGE_SIZE);
bytes_sent = TARGET_PAGE_SIZE;
@@ -445,6 +521,9 @@ int ram_save_live(QEMUFile *f, int stage, void *opaque)
if (stage < 0) {
memory_global_dirty_log_stop();
+ if (migrate_use_xbzrle()) {
+ cache_fini();
+ }
return 0;
}
@@ -457,6 +536,10 @@ int ram_save_live(QEMUFile *f, int stage, void *opaque)
last_offset = 0;
sort_ram_list();
+ if (migrate_use_xbzrle()) {
+ cache_init(migrate_xbzrle_cache_size());
+ }
+
/* Make sure all dirty bits are set */
QLIST_FOREACH(block, &ram_list.blocks, next) {
for (addr = 0; addr < block->length; addr += TARGET_PAGE_SIZE) {
@@ -484,9 +567,11 @@ int ram_save_live(QEMUFile *f, int stage, void *opaque)
while ((ret = qemu_file_rate_limit(f)) == 0) {
int bytes_sent;
- bytes_sent = ram_save_block(f);
- bytes_transferred += bytes_sent;
- if (bytes_sent == 0) { /* no more blocks */
+ bytes_sent = ram_save_block(f, stage);
+ /* bytes_sent -1 represent unchanged page */
+ if (bytes_sent > 0) {
+ bytes_transferred += bytes_sent;
+ } else if (bytes_sent == 0) { /* no more blocks */
break;
}
}
@@ -509,19 +594,67 @@ int ram_save_live(QEMUFile *f, int stage, void *opaque)
int bytes_sent;
/* flush all remaining blocks regardless of rate limiting */
- while ((bytes_sent = ram_save_block(f)) != 0) {
+ while ((bytes_sent = ram_save_block(f, stage)) != 0) {
bytes_transferred += bytes_sent;
}
memory_global_dirty_log_stop();
+ if (migrate_use_xbzrle()) {
+ cache_fini();
+ }
}
qemu_put_be64(f, RAM_SAVE_FLAG_EOS);
expected_time = ram_save_remaining() * TARGET_PAGE_SIZE / bwidth;
+ DPRINTF("ram_save_live: expected(%ld) <= max(%ld)?\n", expected_time,
+ migrate_max_downtime());
+
return (stage == 2) && (expected_time <= migrate_max_downtime());
}
+static int load_xbzrle(QEMUFile *f, ram_addr_t addr, void *host)
+{
+ int ret, rc = -1;
+ uint8_t *xbzrle_buf = NULL;
+ XBZRLEHeader hdr = {0};
+
+ /* extract RLE header */
+ qemu_get_buffer(f, (uint8_t *) &hdr, sizeof(hdr));
+ if (!(hdr.xh_flags & ENCODING_FLAG_XBZRLE)) {
+ fprintf(stderr, "Failed to load XBZRLE page - wrong compression!\n");
+ goto done;
+ }
+
+ if (hdr.xh_len > TARGET_PAGE_SIZE) {
+ fprintf(stderr, "Failed to load XBZRLE page - len overflow!\n");
+ goto done;
+ }
+
+ /* load data and decode */
+ xbzrle_buf = (uint8_t *) g_malloc0(TARGET_PAGE_SIZE);
+ qemu_get_buffer(f, xbzrle_buf, hdr.xh_len);
+
+ /* decode RLE */
+ ret = decode_page(xbzrle_buf, hdr.xh_len, host, TARGET_PAGE_SIZE);
+ if (ret == -1) {
+ fprintf(stderr, "Failed to load XBZRLE page - decode error!\n");
+ goto done;
+ }
+
+ if (ret > TARGET_PAGE_SIZE) {
+ fprintf(stderr, "Failed to load XBZRLE page - size %d exceeds %d!\n",
+ ret, TARGET_PAGE_SIZE);
+ goto done;
+ }
+
+ rc = 0;
+
+done:
+ g_free(xbzrle_buf);
+ return rc;
+}
+
static inline void *host_from_stream_offset(QEMUFile *f,
ram_addr_t offset,
int flags)
@@ -567,14 +700,18 @@ static inline void *host_from_stream_offset_versioned(int version_id,
int ram_load(QEMUFile *f, void *opaque, int version_id)
{
ram_addr_t addr;
- int flags;
+ int flags, ret = 0;
int error;
+ static uint64_t seq_iter;
+
+ seq_iter++;
if (version_id < 4 || version_id > 4) {
return -EINVAL;
}
do {
+ void *host;
addr = qemu_get_be64(f);
flags = addr & ~TARGET_PAGE_MASK;
@@ -598,8 +735,10 @@ int ram_load(QEMUFile *f, void *opaque, int version_id)
QLIST_FOREACH(block, &ram_list.blocks, next) {
if (!strncmp(id, block->idstr, sizeof(id))) {
- if (block->length != length)
- return -EINVAL;
+ if (block->length != length) {
+ ret = -EINVAL;
+ goto done;
+ }
break;
}
}
@@ -607,7 +746,8 @@ int ram_load(QEMUFile *f, void *opaque, int version_id)
if (!block) {
fprintf(stderr, "Unknown ramblock \"%s\", cannot "
"accept migration\n", id);
- return -EINVAL;
+ ret = -EINVAL;
+ goto done;
}
total_ram_bytes -= length;
@@ -646,14 +786,25 @@ int ram_load(QEMUFile *f, void *opaque, int version_id)
return -EINVAL;
}
qemu_get_buffer(f, host, TARGET_PAGE_SIZE);
+ } else if (flags & RAM_SAVE_FLAG_XBZRLE) {
+ host = host_from_stream_offset_versioned(version_id,
+ f, addr, flags);
+ if (load_xbzrle(f, addr, host) < 0) {
+ ret = -EINVAL;
+ goto done;
+ }
}
error = qemu_file_get_error(f);
if (error) {
- return error;
+ ret = error;
+ goto done;
}
} while (!(flags & RAM_SAVE_FLAG_EOS));
- return 0;
+done:
+ DPRINTF("Completed load of VM with exit code %d seq iteration %ld\n",
+ ret, seq_iter);
+ return ret;
}
#ifdef HAS_AUDIO
diff --git a/migration.c b/migration.c
index 1bdac1e..4e2c3b3 100644
--- a/migration.c
+++ b/migration.c
@@ -463,3 +463,21 @@ void qmp_migrate_set_downtime(double value, Error **errp)
value = MAX(0, MIN(UINT64_MAX, value));
max_downtime = (uint64_t)value;
}
+
+int migrate_use_xbzrle(void)
+{
+ MigrationState *s;
+
+ s = migrate_get_current();
+
+ return s->params.use_xbzrle;
+}
+
+int64_t migrate_xbzrle_cache_size(void)
+{
+ MigrationState *s;
+
+ s = migrate_get_current();
+
+ return s->params.xbzrle_cache_size;
+}
diff --git a/migration.h b/migration.h
index b2097a9..06ce8ab 100644
--- a/migration.h
+++ b/migration.h
@@ -22,6 +22,8 @@
struct MigrationParams {
int blk;
int shared;
+ int use_xbzrle;
+ int64_t xbzrle_cache_size;
};
typedef struct MigrationState MigrationState;
@@ -100,5 +102,11 @@ void migrate_del_blocker(Error *reason);
/* ULEB128 */
int uleb128_encode_small(uint8_t *out, uint32_t n);
int uleb128_decode_small(const uint8 *in, uint32_t *n);
+int encode_page(uint8_t *old_buf, uint8_t *new_buf, int slen,
+ uint8_t *dst, int dlen);
+int decode_page(uint8_t *src, int slen, uint8_t *dst, int dlen);
+
+int migrate_use_xbzrle(void);
+int64_t migrate_xbzrle_cache_size(void);
#endif
diff --git a/savevm.c b/savevm.c
index 4736784..fbf1903 100644
--- a/savevm.c
+++ b/savevm.c
@@ -1711,7 +1711,9 @@ static int qemu_savevm_state(QEMUFile *f)
int ret;
MigrationParams params = {
.blk = 0,
- .shared = 0
+ .shared = 0,
+ .use_xbzrle = 0,
+ .xbzrle_cache_size = 0
};
if (qemu_savevm_state_blocked(NULL)) {
@@ -2401,3 +2403,93 @@ int uleb128_decode_small(const uint8 *in, uint32_t *n)
}
}
+/*
+ page = zrun nzrun
+ | zrun nzrun page
+
+ zrun = length
+
+ nzrun = length byte...
+
+ length = uleb128 encoded integer
+ */
+int encode_page(uint8_t *old_buf, uint8_t *new_buf, int slen, uint8_t *dst,
+ int dlen)
+{
+ uint32_t zrun_len = 0, nzrun_len = 0;
+ int d = 0 , i = 0;
+ uint8_t *nzrun_start = NULL;
+
+ while (i < slen) {
+ /* overflow */
+ if (d + 2 > dlen) {
+ return 0;
+ }
+
+ while (!(old_buf[i] ^ new_buf[i]) && ++i <= slen) {
+ zrun_len++;
+ }
+
+ /* buffer unchanged */
+ if (zrun_len == slen) {
+ return -1;
+ }
+
+ /* skip last zero run */
+ if (i == slen + 1) {
+ return d;
+ }
+
+ d += uleb128_encode_small(dst + d, zrun_len);
+
+ zrun_len = 0;
+ nzrun_start = new_buf + i;
+ while ((old_buf[i] ^ new_buf[i]) != 0 && ++i <= slen) {
+ nzrun_len++;
+ }
+
+ /* overflow */
+ if (d + nzrun_len + 2 > dlen) {
+ return 0;
+ }
+
+ d += uleb128_encode_small(dst + d, nzrun_len);
+ memcpy(dst + d, nzrun_start, nzrun_len);
+ d += nzrun_len;
+ nzrun_len = 0;
+ }
+
+ return d;
+}
+
+int decode_page(uint8_t *src, int slen, uint8_t *dst, int dlen)
+{
+ int i = 0, d = 0;
+ uint32_t count = 0;
+
+ while (i < slen) {
+
+ /* zrun */
+ i += uleb128_decode_small(src + i, &count);
+ d += count;
+
+ /* overflow */
+ assert(d <= dlen);
+
+ /* completed decoding */
+ if (i == slen - 1) {
+ return d;
+ }
+
+ /* nzrun */
+ i += uleb128_decode_small(src + i, &count);
+
+ assert(d + count <= dlen);
+
+ memcpy(dst + d , src + i, count);
+ d += count;
+ i += count;
+ }
+
+ return d;
+}
--
1.7.7.6
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [Qemu-devel] [PATCH v8 07/10] Add XBZRLE option to migrate command
2012-04-05 10:47 [Qemu-devel] [PATCH v8 00/10] XBZRLE delta for live migration of large memory app Orit Wasserman
` (5 preceding siblings ...)
2012-04-05 10:47 ` [Qemu-devel] [PATCH v8 06/10] Add XBZRLE to ram_save_block and ram_save_live Orit Wasserman
@ 2012-04-05 10:47 ` Orit Wasserman
2012-04-05 10:47 ` [Qemu-devel] [PATCH v8 08/10] Add migration capabilites Orit Wasserman
` (2 subsequent siblings)
9 siblings, 0 replies; 13+ messages in thread
From: Orit Wasserman @ 2012-04-05 10:47 UTC (permalink / raw)
To: qemu-devel; +Cc: aliguori, quintela, stefanha, blauwirbel, Orit Wasserman, avi
Signed-off-by: Orit Wasserman <owasserm@redhat.com>
---
hmp-commands.hx | 20 ++++++++++++--------
hmp.c | 4 +++-
migration.c | 9 +++++++++
qapi-schema.json | 2 +-
qmp-commands.hx | 4 +++-
5 files changed, 28 insertions(+), 11 deletions(-)
diff --git a/hmp-commands.hx b/hmp-commands.hx
index bd35a3e..88e345a 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -799,23 +799,27 @@ ETEXI
{
.name = "migrate",
- .args_type = "detach:-d,blk:-b,inc:-i,uri:s",
- .params = "[-d] [-b] [-i] uri",
- .help = "migrate to URI (using -d to not wait for completion)"
- "\n\t\t\t -b for migration without shared storage with"
- " full copy of disk\n\t\t\t -i for migration without "
- "shared storage with incremental copy of disk "
- "(base image shared between src and destination)",
+ .args_type = "detach:-d,blk:-b,inc:-i,xbzrle:-x,uri:s",
+ .params = "[-d] [-b] [-i] [-x] uri",
+ .help = "migrate to URI"
+ "\n\t -d to not wait for completion"
+ "\n\t -b for migration without shared storage with"
+ " full copy of disk"
+ "\n\t -i for migration without"
+ " shared storage with incremental copy of disk"
+ " (base image shared between source and destination)"
+ "\n\t -x to use XBZRLE page delta compression",
.mhandler.cmd = hmp_migrate,
},
STEXI
-@item migrate [-d] [-b] [-i] @var{uri}
+@item migrate [-d] [-b] [-i] [-x] @var{uri}
@findex migrate
Migrate to @var{uri} (using -d to not wait for completion).
-b for migration with full copy of disk
-i for migration with incremental copy of disk (base image is shared)
+ -x to use XBZRLE page delta compression
ETEXI
{
diff --git a/hmp.c b/hmp.c
index 9cf2d13..c2f8c31 100644
--- a/hmp.c
+++ b/hmp.c
@@ -907,10 +907,12 @@ void hmp_migrate(Monitor *mon, const QDict *qdict)
int detach = qdict_get_try_bool(qdict, "detach", 0);
int blk = qdict_get_try_bool(qdict, "blk", 0);
int inc = qdict_get_try_bool(qdict, "inc", 0);
+ int xbzrle = qdict_get_try_bool(qdict, "xbzrle", 0);
const char *uri = qdict_get_str(qdict, "uri");
Error *err = NULL;
- qmp_migrate(uri, !!blk, blk, !!inc, inc, false, false, &err);
+ qmp_migrate(uri, !!blk, blk, !!inc, inc, false, false, !!xbzrle, xbzrle,
+ &err);
if (err) {
monitor_printf(mon, "migrate: %s\n", error_get_pretty(err));
error_free(err);
diff --git a/migration.c b/migration.c
index 4e2c3b3..66238de 100644
--- a/migration.c
+++ b/migration.c
@@ -43,6 +43,11 @@ enum {
#define MAX_THROTTLE (32 << 20) /* Migration speed throttling */
+/* Migration XBZRLE cache size */
+#define DEFAULT_MIGRATE_CACHE_SIZE (64 * 1024 * 1024)
+
+static int64_t migrate_cache_size = DEFAULT_MIGRATE_CACHE_SIZE;
+
static NotifierList migration_state_notifiers =
NOTIFIER_LIST_INITIALIZER(migration_state_notifiers);
@@ -388,6 +393,7 @@ void migrate_del_blocker(Error *reason)
void qmp_migrate(const char *uri, bool has_blk, bool blk,
bool has_inc, bool inc, bool has_detach, bool detach,
+ bool has_xbzrle, bool xbzrle,
Error **errp)
{
MigrationState *s = migrate_get_current();
@@ -398,6 +404,9 @@ void qmp_migrate(const char *uri, bool has_blk, bool blk,
params.blk = blk;
params.shared = inc;
+ params.use_xbzrle = xbzrle;
+ params.xbzrle_cache_size = migrate_cache_size;
+
if (s->state == MIG_STATE_ACTIVE) {
error_set(errp, QERR_MIGRATION_ACTIVE);
return;
diff --git a/qapi-schema.json b/qapi-schema.json
index 0d11d6e..8b1c78a 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -1683,7 +1683,7 @@
# Since: 0.14.0
##
{ 'command': 'migrate',
- 'data': {'uri': 'str', '*blk': 'bool', '*inc': 'bool', '*detach': 'bool' } }
+ 'data': {'uri': 'str', '*blk': 'bool', '*inc': 'bool', '*detach': 'bool', '*xbzrle': 'bool' } }
# @xen-save-devices-state:
#
diff --git a/qmp-commands.hx b/qmp-commands.hx
index 9447871..5d3714f 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -472,7 +472,8 @@ EQMP
{
.name = "migrate",
- .args_type = "detach:-d,blk:-b,inc:-i,uri:s",
+ .args_type = "detach:-d,blk:-b,inc:-i,xbzrle:-x,uri:s",
+ .params = "[-d] [-b] [-i] [-x] uri",
.mhandler.cmd_new = qmp_marshal_input_migrate,
},
@@ -487,6 +488,7 @@ Arguments:
- "blk": block migration, full disk copy (json-bool, optional)
- "inc": incremental disk copy (json-bool, optional)
- "uri": Destination URI (json-string)
+- "xbzrle": to use XBZRLE page delta compression
Example:
--
1.7.7.6
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [Qemu-devel] [PATCH v8 08/10] Add migration capabilites
2012-04-05 10:47 [Qemu-devel] [PATCH v8 00/10] XBZRLE delta for live migration of large memory app Orit Wasserman
` (6 preceding siblings ...)
2012-04-05 10:47 ` [Qemu-devel] [PATCH v8 07/10] Add XBZRLE option to migrate command Orit Wasserman
@ 2012-04-05 10:47 ` Orit Wasserman
2012-04-05 10:47 ` [Qemu-devel] [PATCH v8 09/10] Add set_cachesize command Orit Wasserman
2012-04-05 10:47 ` [Qemu-devel] [PATCH v8 10/10] Add XBZRLE statstics information Orit Wasserman
9 siblings, 0 replies; 13+ messages in thread
From: Orit Wasserman @ 2012-04-05 10:47 UTC (permalink / raw)
To: qemu-devel; +Cc: aliguori, quintela, stefanha, blauwirbel, Orit Wasserman, avi
Add migration capabiltes that can be queried by the management.
The managment can query to source and the destination in order to
verify both support some maigration capability (currently only XBZRLE).
Signed-off-by: Orit Wasserman <owasserm@redhat.com>
---
hmp.c | 18 ++++++++++++++++++
hmp.h | 1 +
migration.c | 11 +++++++++++
monitor.c | 7 +++++++
qapi-schema.json | 24 ++++++++++++++++++++++++
qmp-commands.hx | 24 ++++++++++++++++++++++++
savevm.c | 2 +-
7 files changed, 86 insertions(+), 1 deletions(-)
diff --git a/hmp.c b/hmp.c
index c2f8c31..186a119 100644
--- a/hmp.c
+++ b/hmp.c
@@ -156,6 +156,24 @@ void hmp_info_migrate(Monitor *mon)
qapi_free_MigrationInfo(info);
}
+void hmp_info_migration_caps(Monitor *mon)
+{
+ MigrationCapList *caps_list, *cap;
+
+ caps_list = qmp_query_migration_caps(NULL);
+ if (!caps_list) {
+ monitor_printf(mon, "No migration capabilities found\n");
+ return;
+ }
+
+ for (cap = caps_list; cap; cap = cap->next) {
+ monitor_printf(mon, "%s\n", cap->value->name);
+ }
+
+ qapi_free_MigrationCapList(caps_list);
+
+}
+
void hmp_info_cpus(Monitor *mon)
{
CpuInfoList *cpu_list, *cpu;
diff --git a/hmp.h b/hmp.h
index 8807853..1c53d35 100644
--- a/hmp.h
+++ b/hmp.h
@@ -25,6 +25,7 @@ void hmp_info_uuid(Monitor *mon);
void hmp_info_chardev(Monitor *mon);
void hmp_info_mice(Monitor *mon);
void hmp_info_migrate(Monitor *mon);
+void hmp_info_migration_caps(Monitor *mon);
void hmp_info_cpus(Monitor *mon);
void hmp_info_block(Monitor *mon);
void hmp_info_blockstats(Monitor *mon);
diff --git a/migration.c b/migration.c
index 66238de..db8c3d8 100644
--- a/migration.c
+++ b/migration.c
@@ -161,6 +161,17 @@ MigrationInfo *qmp_query_migrate(Error **errp)
return info;
}
+MigrationCapList *qmp_query_migration_caps(Error **errp)
+{
+ MigrationCapList *caps_list = g_malloc0(sizeof(*caps_list));
+
+ caps_list->value = g_malloc(sizeof(*caps_list->value));
+ caps_list->value->name = g_strdup("uleb");
+ caps_list->next = NULL;
+
+ return caps_list;
+}
+
/* shared migration helpers */
static int migrate_fd_cleanup(MigrationState *s)
diff --git a/monitor.c b/monitor.c
index 8946a10..ff434e0 100644
--- a/monitor.c
+++ b/monitor.c
@@ -2558,6 +2558,13 @@ static mon_cmd_t info_cmds[] = {
.mhandler.info = hmp_info_migrate,
},
{
+ .name = "migration_caps",
+ .args_type = "",
+ .params = "",
+ .help = "show migration capabilties",
+ .mhandler.info = hmp_info_migration_caps,
+ },
+ {
.name = "balloon",
.args_type = "",
.params = "",
diff --git a/qapi-schema.json b/qapi-schema.json
index 8b1c78a..e49ec43 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -276,6 +276,30 @@
{ 'command': 'query-migrate', 'returns': 'MigrationInfo' }
##
+# @MigrationCap
+#
+# Information about current migration capabilites.
+#
+# @xbzrle: true if the current migration supports xbzrle
+#
+# Since: 1.1
+##
+{ 'type': 'MigrationCap',
+ 'data': { 'name': 'str'} }
+
+##
+# @query-migration-caps
+#
+# Returns information about current migration process capabilties.
+#
+# Returns: @MigrationCap
+#
+# Since: 1.1
+##
+{ 'command': 'query-migration-caps', 'returns': ['MigrationCap'] }
+
+
+##
# @MouseInfo:
#
# Information about a mouse device.
diff --git a/qmp-commands.hx b/qmp-commands.hx
index 5d3714f..c21ec1c 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -2073,6 +2073,30 @@ EQMP
},
SQMP
+query-migration-caps
+-------
+
+Query migration capabilties
+
+- "xbzrle": xbzrle support
+
+Arguments:
+
+Example:
+
+-> { "execute": "query-migration-caps"}
+<- { "return": { "xbzrle" : true } }
+
+EQMP
+
+ {
+ .name = "query_migration_caps",
+ .args_type = "",
+ .mhandler.cmd_new = qmp_marshal_input_query_migration_caps,
+ },
+
+
+SQMP
query-balloon
-------------
diff --git a/savevm.c b/savevm.c
index fbf1903..3c0b7cc 100644
--- a/savevm.c
+++ b/savevm.c
@@ -1573,7 +1573,7 @@ int qemu_savevm_state_begin(QEMUFile *f,
}
se->set_params(params, se->opaque);
}
-
+
qemu_put_be32(f, QEMU_VM_FILE_MAGIC);
qemu_put_be32(f, QEMU_VM_FILE_VERSION);
--
1.7.7.6
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [Qemu-devel] [PATCH v8 09/10] Add set_cachesize command
2012-04-05 10:47 [Qemu-devel] [PATCH v8 00/10] XBZRLE delta for live migration of large memory app Orit Wasserman
` (7 preceding siblings ...)
2012-04-05 10:47 ` [Qemu-devel] [PATCH v8 08/10] Add migration capabilites Orit Wasserman
@ 2012-04-05 10:47 ` Orit Wasserman
2012-04-05 12:15 ` Avi Kivity
2012-04-05 10:47 ` [Qemu-devel] [PATCH v8 10/10] Add XBZRLE statstics information Orit Wasserman
9 siblings, 1 reply; 13+ messages in thread
From: Orit Wasserman @ 2012-04-05 10:47 UTC (permalink / raw)
To: qemu-devel; +Cc: aliguori, quintela, stefanha, blauwirbel, Orit Wasserman, avi
Change XBZRLE cache size in MB (the size should be a power of 2).
Signed-off-by: Orit Wasserman <owasserm@redhat.com>
---
arch_init.c | 8 ++++++++
hmp-commands.hx | 15 +++++++++++++++
hmp.c | 13 +++++++++++++
hmp.h | 1 +
migration.c | 31 ++++++++++++++++++++++++++++++-
migration.h | 2 ++
qapi-schema.json | 13 +++++++++++++
qmp-commands.hx | 22 ++++++++++++++++++++++
8 files changed, 104 insertions(+), 1 deletions(-)
diff --git a/arch_init.c b/arch_init.c
index d728e5a..9df9d1c 100644
--- a/arch_init.c
+++ b/arch_init.c
@@ -174,6 +174,14 @@ typedef struct __attribute__((packed)) XBZRLEHeader {
static uint8 *prev_cache_page;
+void xbzrle_cache_resize(int64_t new_size)
+{
+ if (page_cache) {
+ cache_fini();
+ cache_init(new_size);
+ }
+}
+
/***********************************************************/
/* XBRLE page cache implementation */
static CacheItem *cache_item_get(unsigned long pos, int item)
diff --git a/hmp-commands.hx b/hmp-commands.hx
index 88e345a..31a5eb7 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -834,6 +834,21 @@ STEXI
@item migrate_cancel
@findex migrate_cancel
Cancel the current VM migration.
+
+ETEXI
+
+ {
+ .name = "migrate_set_cachesize",
+ .args_type = "value:o",
+ .params = "value",
+ .help = "set cache size (in MB) for XBZRLE migrations",
+ .mhandler.cmd = hmp_migrate_set_cachesize,
+ },
+
+STEXI
+@item migrate_set_cachesize @var{value}
+@findex migrate_set_cache
+Set cache size to @var{value} (in MB) for xbzrle migrations.
ETEXI
{
diff --git a/hmp.c b/hmp.c
index 186a119..c1b5380 100644
--- a/hmp.c
+++ b/hmp.c
@@ -740,6 +740,19 @@ void hmp_migrate_set_downtime(Monitor *mon, const QDict *qdict)
qmp_migrate_set_downtime(value, NULL);
}
+void hmp_migrate_set_cachesize(Monitor *mon, const QDict *qdict)
+{
+ int64_t value = qdict_get_int(qdict, "value");
+ Error *err = NULL;
+
+ qmp_migrate_set_cachesize(value, &err);
+ if (err) {
+ monitor_printf(mon, "%s\n", error_get_pretty(err));
+ error_free(err);
+ return;
+ }
+}
+
void hmp_migrate_set_speed(Monitor *mon, const QDict *qdict)
{
int64_t value = qdict_get_int(qdict, "value");
diff --git a/hmp.h b/hmp.h
index 1c53d35..02a10ee 100644
--- a/hmp.h
+++ b/hmp.h
@@ -52,6 +52,7 @@ void hmp_snapshot_blkdev(Monitor *mon, const QDict *qdict);
void hmp_migrate_cancel(Monitor *mon, const QDict *qdict);
void hmp_migrate_set_downtime(Monitor *mon, const QDict *qdict);
void hmp_migrate_set_speed(Monitor *mon, const QDict *qdict);
+void hmp_migrate_set_cachesize(Monitor *mon, const QDict *qdict);
void hmp_set_password(Monitor *mon, const QDict *qdict);
void hmp_expire_password(Monitor *mon, const QDict *qdict);
void hmp_eject(Monitor *mon, const QDict *qdict);
diff --git a/migration.c b/migration.c
index db8c3d8..ec51ad3 100644
--- a/migration.c
+++ b/migration.c
@@ -166,7 +166,7 @@ MigrationCapList *qmp_query_migration_caps(Error **errp)
MigrationCapList *caps_list = g_malloc0(sizeof(*caps_list));
caps_list->value = g_malloc(sizeof(*caps_list->value));
- caps_list->value->name = g_strdup("uleb");
+ caps_list->value->name = g_strdup("xbzrle");
caps_list->next = NULL;
return caps_list;
@@ -464,6 +464,35 @@ void qmp_migrate_cancel(Error **errp)
migrate_fd_cancel(migrate_get_current());
}
+void qmp_migrate_set_cachesize(int64_t value, Error **errp)
+{
+ MigrationState *s;
+
+ /* On 32-bit hosts, QEMU is limited by virtual address space */
+ if (value > (2047 << 20) && HOST_LONG_BITS == 32) {
+ error_set(errp, QERR_INVALID_PARAMETER_VALUE, "cache size",
+ "exceeding address space");
+ return;
+ }
+
+ /* power of 2 */
+ if (value != 1 && (value & (value - 1))) {
+ error_set(errp, QERR_INVALID_PARAMETER_VALUE, "cache size",
+ "needs to be power of 2");
+ return;
+ }
+
+ value = MIN(UINT64_MAX, value);
+ if (value == migrate_cache_size) {
+ return;
+ }
+
+ migrate_cache_size = value;
+ s = migrate_get_current();
+ s->params.xbzrle_cache_size = value;
+ xbzrle_cache_resize(value);
+}
+
void qmp_migrate_set_speed(int64_t value, Error **errp)
{
MigrationState *s;
diff --git a/migration.h b/migration.h
index 06ce8ab..3426c94 100644
--- a/migration.h
+++ b/migration.h
@@ -109,4 +109,6 @@ int decode_page(uint8_t *src, int slen, uint8_t *dst, int dlen);
int migrate_use_xbzrle(void);
int64_t migrate_xbzrle_cache_size(void);
+void xbzrle_cache_resize(int64_t new_size);
+
#endif
diff --git a/qapi-schema.json b/qapi-schema.json
index e49ec43..f055ec6 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -1310,6 +1310,19 @@
{ 'command': 'migrate_set_speed', 'data': {'value': 'int'} }
##
+# @migrate_set_cachesize
+#
+# Set XBZRLE cache size
+#
+# @value: cache size in bytes
+#
+# Returns: nothing on success
+#
+# Since: 1.1
+##
+{ 'command': 'migrate_set_cachesize', 'data': {'value': 'int'} }
+
+##
# @ObjectPropertyInfo:
#
# @name: the name of the property
diff --git a/qmp-commands.hx b/qmp-commands.hx
index c21ec1c..5aa26ba 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -525,6 +525,28 @@ Example:
<- { "return": {} }
EQMP
+{
+ .name = "migrate_set_cachesize",
+ .args_type = "value:o",
+ .mhandler.cmd_new = qmp_marshal_input_migrate_set_cachesize,
+ },
+
+SQMP
+migrate_set_cachesize
+---------------------
+
+Set cache size to be used by XBZRLE migration
+
+Arguments:
+
+- "value": cache size in bytes (json-int)
+
+Example:
+
+-> { "execute": "migrate_set_cachesize", "arguments": { "value": 512 } }
+<- { "return": {} }
+
+EQMP
{
.name = "migrate_set_speed",
--
1.7.7.6
^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [Qemu-devel] [PATCH v8 09/10] Add set_cachesize command
2012-04-05 10:47 ` [Qemu-devel] [PATCH v8 09/10] Add set_cachesize command Orit Wasserman
@ 2012-04-05 12:15 ` Avi Kivity
2012-04-05 12:17 ` Avi Kivity
0 siblings, 1 reply; 13+ messages in thread
From: Avi Kivity @ 2012-04-05 12:15 UTC (permalink / raw)
To: Orit Wasserman; +Cc: blauwirbel, stefanha, aliguori, qemu-devel, quintela
On 04/05/2012 01:47 PM, Orit Wasserman wrote:
> Change XBZRLE cache size in MB (the size should be a power of 2).
In bytes
>
> +void xbzrle_cache_resize(int64_t new_size)
> +{
> + if (page_cache) {
> + cache_fini();
> + cache_init(new_size);
> + }
> +}
A little sad to drop the cache, especially if we're enlarging it. But
this can be improved later.
> +
> +ETEXI
> +
> + {
> + .name = "migrate_set_cachesize",
> + .args_type = "value:o",
> + .params = "value",
> + .help = "set cache size (in MB) for XBZRLE migrations",
In bytes.
> + .mhandler.cmd = hmp_migrate_set_cachesize,
> + },
> +
> +STEXI
> +@item migrate_set_cachesize @var{value}
> +@findex migrate_set_cache
> +Set cache size to @var{value} (in MB) for xbzrle migrations.
Need to document the constraints, and say something about how a larger
cache size can reduce the needed bandwidth.
We need to either document the default or (better) add a command to get
the current cache size (perhaps with some statistics about hit rate and
average data reduction).
>
> +void qmp_migrate_set_cachesize(int64_t value, Error **errp)
> +{
> + MigrationState *s;
> +
> + /* On 32-bit hosts, QEMU is limited by virtual address space */
> + if (value > (2047 << 20) && HOST_LONG_BITS == 32) {
Could be made clearer by using
/* Check for truncation */
if (value != (size_t)value)) {
...
(assumes value is in bytes)
> ##
> +# @migrate_set_cachesize
> +#
> +# Set XBZRLE cache size
> +#
> +# @value: cache size in bytes
Here it's in bytes, good.
For the human monitor we can use MB as the unit, or allow suffixes as we
do for -m <memory>.
--
error compiling committee.c: too many arguments to function
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [Qemu-devel] [PATCH v8 09/10] Add set_cachesize command
2012-04-05 12:15 ` Avi Kivity
@ 2012-04-05 12:17 ` Avi Kivity
0 siblings, 0 replies; 13+ messages in thread
From: Avi Kivity @ 2012-04-05 12:17 UTC (permalink / raw)
To: Orit Wasserman; +Cc: blauwirbel, stefanha, aliguori, qemu-devel, quintela
On 04/05/2012 03:15 PM, Avi Kivity wrote:
> We need to either document the default or (better) add a command to get
> the current cache size (perhaps with some statistics about hit rate and
> average data reduction).
>
I see there already is a stats command, you can add the cache size to that.
--
error compiling committee.c: too many arguments to function
^ permalink raw reply [flat|nested] 13+ messages in thread
* [Qemu-devel] [PATCH v8 10/10] Add XBZRLE statstics information
2012-04-05 10:47 [Qemu-devel] [PATCH v8 00/10] XBZRLE delta for live migration of large memory app Orit Wasserman
` (8 preceding siblings ...)
2012-04-05 10:47 ` [Qemu-devel] [PATCH v8 09/10] Add set_cachesize command Orit Wasserman
@ 2012-04-05 10:47 ` Orit Wasserman
9 siblings, 0 replies; 13+ messages in thread
From: Orit Wasserman @ 2012-04-05 10:47 UTC (permalink / raw)
To: qemu-devel; +Cc: aliguori, quintela, stefanha, blauwirbel, Orit Wasserman, avi
Signed-off-by: Orit Wasserman <owasserm@redhat.com>
---
arch_init.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
hmp.c | 11 ++++++++
migration.c | 11 ++++++++
migration.h | 9 +++++++
qapi-schema.json | 21 +++++++++++++++-
5 files changed, 118 insertions(+), 2 deletions(-)
diff --git a/arch_init.c b/arch_init.c
index 9df9d1c..8c0eb37 100644
--- a/arch_init.c
+++ b/arch_init.c
@@ -182,6 +182,64 @@ void xbzrle_cache_resize(int64_t new_size)
}
}
+/* accounting */
+typedef struct AccountingInfo {
+ uint64_t dup_pages;
+ uint64_t norm_pages;
+ uint64_t xbzrle_bytes;
+ uint64_t xbzrle_pages;
+ uint64_t xbzrle_cache_miss;
+ uint64_t iterations;
+ uint64_t xbzrle_overflows;
+} AccountingInfo;
+
+static AccountingInfo acct_info;
+
+static void acct_clear(void)
+{
+ memset(&acct_info, 0, sizeof(acct_info));
+}
+
+uint64_t dup_mig_bytes_transferred(void)
+{
+ return acct_info.dup_pages * TARGET_PAGE_SIZE;
+}
+
+uint64_t dup_mig_pages_transferred(void)
+{
+ return acct_info.dup_pages;
+}
+
+uint64_t norm_mig_bytes_transferred(void)
+{
+ return acct_info.norm_pages * TARGET_PAGE_SIZE;
+}
+
+uint64_t norm_mig_pages_transferred(void)
+{
+ return acct_info.norm_pages;
+}
+
+uint64_t xbzrle_mig_bytes_transferred(void)
+{
+ return acct_info.xbzrle_bytes;
+}
+
+uint64_t xbzrle_mig_pages_transferred(void)
+{
+ return acct_info.xbzrle_pages;
+}
+
+uint64_t xbzrle_mig_pages_cache_miss(void)
+{
+ return acct_info.xbzrle_cache_miss;
+}
+
+uint64_t xbzrle_mig_pages_overflow(void)
+{
+ return acct_info.xbzrle_overflows;
+}
+
/***********************************************************/
/* XBRLE page cache implementation */
static CacheItem *cache_item_get(unsigned long pos, int item)
@@ -350,6 +408,7 @@ static int save_xbzrle_page(QEMUFile *f, uint8_t *current_data,
slot = cache_is_cached(current_addr);
if (slot == -1) {
cache_insert(current_addr, current_data);
+ acct_info.xbzrle_cache_miss++;
goto done;
}
cache_location = cache_get_cache_pos(current_addr);
@@ -369,6 +428,9 @@ static int save_xbzrle_page(QEMUFile *f, uint8_t *current_data,
if (encoded_len < 0) {
DPRINTF("Unmodifed page - skipping\n");
goto done;
+ } else if (encoded_len == 0) {
+ DPRINTF("Overflow - sending orginal page\n");
+ acct_info.xbzrle_overflows++;
}
hdr.xh_len = encoded_len;
@@ -378,7 +440,9 @@ static int save_xbzrle_page(QEMUFile *f, uint8_t *current_data,
save_block_hdr(f, block, offset, cont, RAM_SAVE_FLAG_XBZRLE);
qemu_put_buffer(f, (uint8_t *) &hdr, sizeof(hdr));
qemu_put_buffer(f, encoded_buf, encoded_len);
+ acct_info.xbzrle_pages++;
bytes_sent = encoded_len + sizeof(hdr);
+ acct_info.xbzrle_bytes += bytes_sent;
done:
g_free(encoded_buf);
@@ -418,6 +482,7 @@ static int ram_save_block(QEMUFile *f, int stage)
save_block_hdr(f, block, offset, cont, RAM_SAVE_FLAG_COMPRESS);
qemu_put_byte(f, *p);
bytes_sent = 1;
+ acct_info.dup_pages++;
} else if (stage == 2 && use_xbzrle) {
bytes_sent = save_xbzrle_page(f, p, current_addr, block,
offset, cont);
@@ -429,6 +494,7 @@ static int ram_save_block(QEMUFile *f, int stage)
save_block_hdr(f, block, offset, cont, RAM_SAVE_FLAG_PAGE);
qemu_put_buffer(f, p, TARGET_PAGE_SIZE);
bytes_sent = TARGET_PAGE_SIZE;
+ acct_info.norm_pages++;
}
break;
@@ -546,6 +612,7 @@ int ram_save_live(QEMUFile *f, int stage, void *opaque)
if (migrate_use_xbzrle()) {
cache_init(migrate_xbzrle_cache_size());
+ acct_clear();
}
/* Make sure all dirty bits are set */
@@ -579,6 +646,7 @@ int ram_save_live(QEMUFile *f, int stage, void *opaque)
/* bytes_sent -1 represent unchanged page */
if (bytes_sent > 0) {
bytes_transferred += bytes_sent;
+ acct_info.iterations++;
} else if (bytes_sent == 0) { /* no more blocks */
break;
}
diff --git a/hmp.c b/hmp.c
index c1b5380..f1c6439 100644
--- a/hmp.c
+++ b/hmp.c
@@ -153,6 +153,17 @@ void hmp_info_migrate(Monitor *mon)
info->disk->total >> 10);
}
+ if (info->has_cache) {
+ monitor_printf(mon, "xbzrle transferred: %" PRIu64 " kbytes\n",
+ info->cache->xbzrle_bytes >> 10);
+ monitor_printf(mon, "xbzrle pages: %" PRIu64 " pages\n",
+ info->cache->xbzrle_pages);
+ monitor_printf(mon, "xbzrle cache miss: %" PRIu64 "\n",
+ info->cache->xbzrle_cache_miss);
+ monitor_printf(mon, "xbzrle overflow : %" PRIu64 "\n",
+ info->cache->xbzrle_overflow);
+ }
+
qapi_free_MigrationInfo(info);
}
diff --git a/migration.c b/migration.c
index ec51ad3..561ff65 100644
--- a/migration.c
+++ b/migration.c
@@ -135,6 +135,8 @@ MigrationInfo *qmp_query_migrate(Error **errp)
info->ram->transferred = ram_bytes_transferred();
info->ram->remaining = ram_bytes_remaining();
info->ram->total = ram_bytes_total();
+ info->ram->duplicate = dup_mig_pages_transferred();
+ info->ram->norm = norm_mig_pages_transferred();
if (blk_mig_active()) {
info->has_disk = true;
@@ -143,6 +145,15 @@ MigrationInfo *qmp_query_migrate(Error **errp)
info->disk->remaining = blk_mig_bytes_remaining();
info->disk->total = blk_mig_bytes_total();
}
+
+ if (s->params.use_xbzrle) {
+ info->has_cache = true;
+ info->cache = g_malloc0(sizeof(*info->cache));
+ info->cache->xbzrle_bytes = xbzrle_mig_bytes_transferred();
+ info->cache->xbzrle_pages = xbzrle_mig_pages_transferred();
+ info->cache->xbzrle_cache_miss = xbzrle_mig_pages_cache_miss();
+ info->cache->xbzrle_overflow = xbzrle_mig_pages_overflow();
+ }
break;
case MIG_STATE_COMPLETED:
info->has_status = true;
diff --git a/migration.h b/migration.h
index 3426c94..03f116d 100644
--- a/migration.h
+++ b/migration.h
@@ -82,6 +82,15 @@ uint64_t ram_bytes_remaining(void);
uint64_t ram_bytes_transferred(void);
uint64_t ram_bytes_total(void);
+uint64_t dup_mig_bytes_transferred(void);
+uint64_t dup_mig_pages_transferred(void);
+uint64_t norm_mig_bytes_transferred(void);
+uint64_t norm_mig_pages_transferred(void);
+uint64_t xbzrle_mig_bytes_transferred(void);
+uint64_t xbzrle_mig_pages_transferred(void);
+uint64_t xbzrle_mig_pages_overflow(void);
+uint64_t xbzrle_mig_pages_cache_miss(void);
+
int ram_save_live(QEMUFile *f, int stage, void *opaque);
int ram_load(QEMUFile *f, void *opaque, int version_id);
diff --git a/qapi-schema.json b/qapi-schema.json
index f055ec6..20ba316 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -239,7 +239,24 @@
# Since: 0.14.0.
##
{ 'type': 'MigrationStats',
- 'data': {'transferred': 'int', 'remaining': 'int', 'total': 'int' } }
+ 'data': {'transferred': 'int', 'remaining': 'int', 'total': 'int', 'duplicate': 'int', 'norm': 'int' } }
+
+##
+# @CacheStats
+#
+# Detailed XBZRLE migration cache statistics
+#
+# @xbzrle_bytes: amount of bytes already transferred to the target VM
+#
+# @xbzrle_pages: amount of pages transferred to the target VM
+#
+# @xbzrle_cache_miss: numer of cache miss
+#
+# Since: 1.1
+##
+{ 'type': 'CacheStats',
+ 'data': {'xbzrle_bytes': 'int', 'xbzrle_pages': 'int', 'xbzrle_cache_miss': 'int',
+ 'xbzrle_overflow': 'int' } }
##
# @MigrationInfo
@@ -262,7 +279,7 @@
##
{ 'type': 'MigrationInfo',
'data': {'*status': 'str', '*ram': 'MigrationStats',
- '*disk': 'MigrationStats'} }
+ '*disk': 'MigrationStats', '*cache': 'CacheStats'} }
##
# @query-migrate
--
1.7.7.6
^ permalink raw reply related [flat|nested] 13+ messages in thread