From patchwork Tue Jan 3 15:34:35 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Orit Wasserman X-Patchwork-Id: 134028 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [140.186.70.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 6D268B6F9A for ; Wed, 4 Jan 2012 02:58:14 +1100 (EST) Received: from localhost ([::1]:52939 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Ri6Pf-00085T-TN for incoming@patchwork.ozlabs.org; Tue, 03 Jan 2012 10:36:31 -0500 Received: from eggs.gnu.org ([140.186.70.92]:34611) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Ri6Ou-0005z7-74 for qemu-devel@nongnu.org; Tue, 03 Jan 2012 10:35:50 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1Ri6Op-0006yg-Ik for qemu-devel@nongnu.org; Tue, 03 Jan 2012 10:35:44 -0500 Received: from mx1.redhat.com ([209.132.183.28]:20460) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Ri6Oo-0006yQ-JN for qemu-devel@nongnu.org; Tue, 03 Jan 2012 10:35:39 -0500 Received: from int-mx12.intmail.prod.int.phx2.redhat.com (int-mx12.intmail.prod.int.phx2.redhat.com [10.5.11.25]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id q03FZaJ0026665 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Tue, 3 Jan 2012 10:35:36 -0500 Received: from dhcp-1-120.tlv.redhat.com (vpn-202-200.tlv.redhat.com [10.35.202.200]) by int-mx12.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id q03FZNo0003527; Tue, 3 Jan 2012 10:35:35 -0500 From: Orit Wasserman To: qemu-devel@nongnu.org Date: Tue, 3 Jan 2012 17:34:35 +0200 Message-Id: <1325604879-15862-6-git-send-email-owasserm@redhat.com> In-Reply-To: <1325604879-15862-1-git-send-email-owasserm@redhat.com> References: <1325604879-15862-1-git-send-email-owasserm@redhat.com> X-Scanned-By: MIMEDefang 2.68 on 10.5.11.25 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6 (newer, 3) X-Received-From: 209.132.183.28 Cc: blauwirbel@gmail.com, stefanha@gmail.com, Orit Wasserman , quintela@redhat.com Subject: [Qemu-devel] [PATCH v5 5/9] Add XBRLE to ram_save_block and ram_save_live X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org 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 --- 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