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 <owass...@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