From: Orit Wasserman <owasserm@redhat.com>
To: qemu-devel@nongnu.org
Cc: blauwirbel@gmail.com, stefanha@gmail.com,
Orit Wasserman <owasserm@redhat.com>,
quintela@redhat.com
Subject: [Qemu-devel] [PATCH v5 5/9] Add XBRLE to ram_save_block and ram_save_live
Date: Tue, 3 Jan 2012 17:34:35 +0200 [thread overview]
Message-ID: <1325604879-15862-6-git-send-email-owasserm@redhat.com> (raw)
In-Reply-To: <1325604879-15862-1-git-send-email-owasserm@redhat.com>
Add migration state to store XBRLE params (enablement and cache size).
In the outgoing check to see if the page is cached and
send compressed page by using save_xbrle_page function.
In the incoming migration check to see if RAM_SAVE_FLAG_XBRLE is set
decompress the page (by using load_xbrle function).
Signed-off-by: Orit Wasserman <owasserm@redhat.com>
---
arch_init.c | 173 +++++++++++++++++++++++++++++++++++++++++++++++++++++++----
1 files changed, 163 insertions(+), 10 deletions(-)
diff --git a/arch_init.c b/arch_init.c
index 05b8053..6b839a1 100644
--- a/arch_init.c
+++ b/arch_init.c
@@ -102,6 +102,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_XBRLE 0x40
/***********************************************************/
/* Page cache for storing previous pages as basis for XBRLE compression */
@@ -132,6 +133,22 @@ static unsigned long cache_get_cache_pos(ram_addr_t address);
static CacheItem *cache_item_get(unsigned long pos, int item);
/***********************************************************/
+/* RAM Migration State */
+typedef struct ArchMigrationState {
+ int use_xbrle;
+ int64_t xbrle_cache_size;
+} ArchMigrationState;
+
+static ArchMigrationState arch_mig_state;
+
+void arch_set_params(int blk_enable, int shared_base, int use_xbrle,
+ int64_t xbrle_cache_size, void *opaque)
+{
+ arch_mig_state.use_xbrle = use_xbrle;
+ arch_mig_state.xbrle_cache_size = xbrle_cache_size;
+}
+
+/***********************************************************/
/* XBRLE (Xor Based Run-Length Encoding) */
typedef struct XBRLEHeader {
uint8_t xh_flags;
@@ -346,6 +363,55 @@ static void save_block_hdr(QEMUFile *f, RAMBlock *block, ram_addr_t offset,
}
}
+#define ENCODING_FLAG_XBRLE 0x1
+
+static int save_xbrle_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;
+ XBRLEHeader hdr = {0};
+ CacheItem *it;
+ uint8_t *xor_buf = NULL, *xbrle_buf = NULL;
+
+ /* get location */
+ slot = cache_is_cached(current_addr);
+ if (slot == -1) {
+ goto done;
+ }
+ cache_location = cache_get_cache_pos(current_addr);
+
+ /* abort if page changed too much */
+ it = cache_item_get(cache_location, slot);
+
+ /* XOR encoding */
+ xor_buf = (uint8_t *) g_malloc0(TARGET_PAGE_SIZE);
+ xor_encode(xor_buf, it->it_data, current_data);
+
+ /* XBRLE (XOR+RLE) encoding (if we can ensure a 1/3 ratio) */
+ xbrle_buf = (uint8_t *) g_malloc0(TARGET_PAGE_SIZE);
+ encoded_len = rle_encode(xor_buf, TARGET_PAGE_SIZE, xbrle_buf,
+ TARGET_PAGE_SIZE/3);
+
+ if (encoded_len < 0) {
+ DPRINTF("XBRLE encoding oeverflow - sending uncompressed\n");
+ goto done;
+ }
+
+ hdr.xh_len = encoded_len;
+ hdr.xh_flags |= ENCODING_FLAG_XBRLE;
+
+ /* Send XBRLE compressed page */
+ save_block_hdr(f, block, offset, cont, RAM_SAVE_FLAG_XBRLE);
+ qemu_put_buffer(f, (uint8_t *) &hdr, sizeof(hdr));
+ qemu_put_buffer(f, xbrle_buf, encoded_len);
+ bytes_sent = encoded_len + sizeof(hdr);
+
+done:
+ g_free(xor_buf);
+ g_free(xbrle_buf);
+ return bytes_sent;
+}
+
static int is_dup_page(uint8_t *page, uint8_t ch)
{
uint32_t val = ch << 24 | ch << 16 | ch << 8 | ch;
@@ -364,7 +430,7 @@ static int is_dup_page(uint8_t *page, uint8_t ch)
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;
@@ -391,10 +457,18 @@ 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 if (stage == 2 && arch_mig_state.use_xbrle) {
+ bytes_sent = save_xbrle_page(f, p, current_addr, block,
+ offset, cont);
+ }
+ 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;
}
+ if (arch_mig_state.use_xbrle) {
+ cache_insert(current_addr, p);
+ }
break;
}
@@ -501,6 +575,10 @@ int ram_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque)
if (stage < 0) {
cpu_physical_memory_set_dirty_tracking(0);
+ if (arch_mig_state.use_xbrle) {
+ cache_fini();
+ }
+
return 0;
}
@@ -516,6 +594,10 @@ int ram_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque)
last_offset = 0;
sort_ram_list();
+ if (arch_mig_state.use_xbrle) {
+ cache_init(arch_mig_state.xbrle_cache_size);
+ }
+
/* Make sure all dirty bits are set */
QLIST_FOREACH(block, &ram_list.blocks, next) {
for (addr = block->offset; addr < block->offset + block->length;
@@ -545,7 +627,7 @@ int ram_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque)
while ((ret = qemu_file_rate_limit(f)) == 0) {
int bytes_sent;
- bytes_sent = ram_save_block(f);
+ bytes_sent = ram_save_block(f, stage);
bytes_transferred += bytes_sent;
if (bytes_sent == 0) { /* no more blocks */
break;
@@ -570,19 +652,71 @@ int ram_save_live(Monitor *mon, 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;
}
cpu_physical_memory_set_dirty_tracking(0);
+ if (arch_mig_state.use_xbrle) {
+ 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_xbrle(QEMUFile *f, ram_addr_t addr, void *host)
+{
+ int ret, rc = -1;
+ uint8_t *prev_page, *xor_buf = NULL, *xbrle_buf = NULL;
+ XBRLEHeader hdr = {0};
+
+ /* extract RLE header */
+ qemu_get_buffer(f, (uint8_t *) &hdr, sizeof(hdr));
+ if (!(hdr.xh_flags & ENCODING_FLAG_XBRLE)) {
+ fprintf(stderr, "Failed to load XBRLE page - wrong compression!\n");
+ goto done;
+ }
+
+ if (hdr.xh_len > TARGET_PAGE_SIZE) {
+ fprintf(stderr, "Failed to load XBRLE page - len overflow!\n");
+ goto done;
+ }
+
+ /* load data and decode */
+ xbrle_buf = (uint8_t *) g_malloc0(TARGET_PAGE_SIZE);
+ qemu_get_buffer(f, xbrle_buf, hdr.xh_len);
+
+ /* decode RLE */
+ xor_buf = (uint8_t *) g_malloc0(TARGET_PAGE_SIZE);
+ ret = rle_decode(xbrle_buf, hdr.xh_len, xor_buf, TARGET_PAGE_SIZE);
+ if (ret == -1) {
+ fprintf(stderr, "Failed to load XBRLE page - decode error!\n");
+ goto done;
+ }
+
+ if (ret != TARGET_PAGE_SIZE) {
+ fprintf(stderr, "Failed to load XBRLE page - size %d expected %d!\n",
+ ret, TARGET_PAGE_SIZE);
+ goto done;
+ }
+
+ /* decode XOR delta */
+ prev_page = host;
+ xor_encode(prev_page, prev_page, xor_buf);
+ rc = 0;
+done:
+ g_free(xor_buf);
+ g_free(xbrle_buf);
+ return rc;
+}
+
static inline void *host_from_stream_offset(QEMUFile *f,
ram_addr_t offset,
int flags)
@@ -633,14 +767,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 < 3 || version_id > 4) {
return -EINVAL;
}
do {
+ void *host;
addr = qemu_get_be64(f);
flags = addr & ~TARGET_PAGE_MASK;
@@ -649,7 +787,8 @@ int ram_load(QEMUFile *f, void *opaque, int version_id)
if (flags & RAM_SAVE_FLAG_MEM_SIZE) {
if (version_id == 3) {
if (addr != ram_bytes_total()) {
- return -EINVAL;
+ ret = -EINVAL;
+ goto done;
}
} else {
/* Synchronize RAM block list */
@@ -668,8 +807,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;
}
}
@@ -677,7 +818,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;
@@ -704,14 +846,25 @@ int ram_load(QEMUFile *f, void *opaque, int version_id)
host = host_from_stream_offset_versioned(version_id,
f, addr, flags);
qemu_get_buffer(f, host, TARGET_PAGE_SIZE);
+ } else if (flags & RAM_SAVE_FLAG_XBRLE) {
+ host = host_from_stream_offset_versioned(version_id,
+ f, addr, flags);
+ if (load_xbrle(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
--
1.7.6.5
next prev parent reply other threads:[~2012-01-03 15:35 UTC|newest]
Thread overview: 36+ messages / expand[flat|nested] mbox.gz Atom feed top
2012-01-03 15:34 [Qemu-devel] [PATCH v5 0/9] XBZRLE delta for live migration of large memory apps Orit Wasserman
2012-01-03 15:34 ` [Qemu-devel] [PATCH v5 1/9] Add cache handling functions Orit Wasserman
2012-01-03 19:54 ` Anthony Liguori
2012-01-04 9:29 ` Orit Wasserman
2012-01-04 22:20 ` Michael Roth
2012-01-04 11:46 ` Stefan Hajnoczi
2012-01-04 13:27 ` Orit Wasserman
2012-01-03 15:34 ` [Qemu-devel] [PATCH v5 2/9] Add rle_encode and rle_decode functions Implement Run Length Encoding compression Orit Wasserman
2012-01-03 19:57 ` Anthony Liguori
2012-01-04 9:31 ` Orit Wasserman
2012-01-04 16:52 ` Paolo Bonzini
2012-01-04 12:59 ` Avi Kivity
2012-01-04 13:35 ` Stefan Hajnoczi
2012-01-04 13:45 ` Avi Kivity
2012-01-03 15:34 ` [Qemu-devel] [PATCH v5 3/9] Add save_block_hdr function Orit Wasserman
2012-01-03 15:34 ` [Qemu-devel] [PATCH v5 4/9] Add host_from_stream_offset_versioned function Orit Wasserman
2012-01-04 12:00 ` Stefan Hajnoczi
2012-01-04 20:59 ` Michael Roth
2012-01-03 15:34 ` Orit Wasserman [this message]
2012-01-04 12:14 ` [Qemu-devel] [PATCH v5 5/9] Add XBRLE to ram_save_block and ram_save_live Stefan Hajnoczi
2012-01-04 13:29 ` Orit Wasserman
2012-01-03 15:34 ` [Qemu-devel] [PATCH v5 6/9] Add xbrle parameters to MigrationState Orit Wasserman
2012-01-04 21:17 ` Michael Roth
2012-01-03 15:34 ` [Qemu-devel] [PATCH v5 7/9] Add set_cachesize to change XBRLE cache size Orit Wasserman
2012-01-03 15:34 ` [Qemu-devel] [PATCH v5 8/9] QMP commands changes Orit Wasserman
2012-01-03 15:47 ` Stefan Hajnoczi
2012-01-03 15:57 ` Orit Wasserman
2012-01-03 16:20 ` Stefan Hajnoczi
2012-01-03 15:34 ` [Qemu-devel] [PATCH v5 9/9] Add XBRLE statistics information Orit Wasserman
2012-01-04 22:45 ` Michael Roth
2012-01-07 16:31 ` Blue Swirl
2012-01-03 16:32 ` [Qemu-devel] [PATCH v5 0/9] XBZRLE delta for live migration of large memory apps Anthony Liguori
2012-01-03 17:02 ` Orit Wasserman
2012-01-04 13:02 ` Avi Kivity
2012-01-04 16:03 ` Orit Wasserman
-- strict thread matches above, loose matches on Subject: below --
2012-01-03 13:35 Orit Wasserman
2012-01-03 13:35 ` [Qemu-devel] [PATCH v5 5/9] Add XBRLE to ram_save_block and ram_save_live Orit Wasserman
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1325604879-15862-6-git-send-email-owasserm@redhat.com \
--to=owasserm@redhat.com \
--cc=blauwirbel@gmail.com \
--cc=qemu-devel@nongnu.org \
--cc=quintela@redhat.com \
--cc=stefanha@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).