+ 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)
@@ -196,6 +206,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)
@@ -215,6 +227,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)
@@ -359,19 +374,74 @@ 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, 0);
+ 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, 0);
+
+ /* 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,
@@ -388,7 +458,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, 0);
+ }
+
+ 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;
@@ -492,6 +569,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;
}
@@ -504,6 +584,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) {
@@ -531,9 +615,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;
}
}
@@ -556,19 +642,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)
@@ -614,14 +748,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;
@@ -645,8 +783,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;
}
}
@@ -654,7 +794,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;
@@ -693,14 +834,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 fc9511c..a524d5a 100644
--- a/migration.c
+++ b/migration.c
@@ -464,3 +464,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;
};