Skip to content

Commit

Permalink
Add XBZRLE to ram_save_block and ram_save_live
Browse files Browse the repository at this point in the history
In the outgoing migration check to see if the page is cached and
changed, then send compressed page by using save_xbrle_page function.
In the incoming migration check to see if RAM_SAVE_FLAG_XBZRLE is set
and decompress the page (by using load_xbrle function).

Signed-off-by: Benoit Hudzia <benoit.hudzia@sap.com>
Signed-off-by: Petter Svard <petters@cs.umu.se>
Signed-off-by: Aidan Shribman <aidan.shribman@sap.com>
Signed-off-by: Orit Wasserman <owasserm@redhat.com>

Reviewed-by: Luiz Capitulino <lcapitulino@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
  • Loading branch information
oritwas authored and Juan Quintela committed Aug 8, 2012
1 parent 302dfbe commit 17ad9b3
Show file tree
Hide file tree
Showing 3 changed files with 184 additions and 2 deletions.
158 changes: 156 additions & 2 deletions arch_init.c
Expand Up @@ -43,6 +43,7 @@
#include "hw/smbios.h"
#include "exec-memory.h"
#include "hw/pcspk.h"
#include "qemu/page_cache.h"

#ifdef DEBUG_ARCH_INIT
#define DPRINTF(fmt, ...) \
Expand Down Expand Up @@ -104,6 +105,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>
Expand Down Expand Up @@ -171,6 +173,24 @@ static int is_dup_page(uint8_t *page)
return 1;
}

/* struct contains XBZRLE cache and a static page
used by the compression */
static struct {
/* buffer used for XBZRLE encoding */
uint8_t *encoded_buf;
/* buffer for storing page content */
uint8_t *current_buf;
/* buffer used for XBZRLE decoding */
uint8_t *decoded_buf;
/* Cache for XBZRLE */
PageCache *cache;
} XBZRLE = {
.encoded_buf = NULL,
.current_buf = NULL,
.decoded_buf = NULL,
.cache = NULL,
};

static void save_block_hdr(QEMUFile *f, RAMBlock *block, ram_addr_t offset,
int cont, int flag)
{
Expand All @@ -183,6 +203,53 @@ 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 encoded_len = 0, bytes_sent = -1;
uint8_t *prev_cached_page;

if (!cache_is_cached(XBZRLE.cache, current_addr)) {
cache_insert(XBZRLE.cache, current_addr,
g_memdup(current_data, TARGET_PAGE_SIZE));
return -1;
}

prev_cached_page = get_cached_data(XBZRLE.cache, current_addr);

/* save current buffer into memory */
memcpy(XBZRLE.current_buf, current_data, TARGET_PAGE_SIZE);

/* XBZRLE encoding (if there is no overflow) */
encoded_len = xbzrle_encode_buffer(prev_cached_page, XBZRLE.current_buf,
TARGET_PAGE_SIZE, XBZRLE.encoded_buf,
TARGET_PAGE_SIZE);
if (encoded_len == 0) {
DPRINTF("Skipping unmodified page\n");
return 0;
} else if (encoded_len == -1) {
DPRINTF("Overflow\n");
/* update data in the cache */
memcpy(prev_cached_page, current_data, TARGET_PAGE_SIZE);
return -1;
}

/* we need to update the data in the cache, in order to get the same data */
memcpy(prev_cached_page, XBZRLE.current_buf, TARGET_PAGE_SIZE);

/* Send XBZRLE based compressed page */
save_block_hdr(f, block, offset, cont, RAM_SAVE_FLAG_XBZRLE);
qemu_put_byte(f, ENCODING_FLAG_XBZRLE);
qemu_put_be16(f, encoded_len);
qemu_put_buffer(f, XBZRLE.encoded_buf, encoded_len);
bytes_sent = encoded_len + 1 + 2;

return bytes_sent;
}

static RAMBlock *last_block;
static ram_addr_t last_offset;

Expand All @@ -200,6 +267,7 @@ static int ram_save_block(QEMUFile *f)
ram_addr_t offset = last_offset;
int bytes_sent = -1;
MemoryRegion *mr;
ram_addr_t current_addr;

if (!block)
block = QLIST_FIRST(&ram_list.blocks);
Expand All @@ -220,13 +288,24 @@ 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 (migrate_use_xbzrle()) {
current_addr = block->offset + offset;
bytes_sent = save_xbzrle_page(f, p, current_addr, block,
offset, cont);
p = get_cached_data(XBZRLE.cache, current_addr);
}

/* either we didn't send yet (we may have had XBZRLE overflow) */
if (bytes_sent == -1) {
save_block_hdr(f, block, offset, cont, RAM_SAVE_FLAG_PAGE);
qemu_put_buffer(f, p, TARGET_PAGE_SIZE);
bytes_sent = TARGET_PAGE_SIZE;
}

break;
/* if page is unmodified, continue to the next */
if (bytes_sent != 0) {
break;
}
}

offset += TARGET_PAGE_SIZE;
Expand Down Expand Up @@ -304,6 +383,15 @@ static void sort_ram_list(void)
static void migration_end(void)
{
memory_global_dirty_log_stop();

if (migrate_use_xbzrle()) {
cache_fini(XBZRLE.cache);
g_free(XBZRLE.cache);
g_free(XBZRLE.encoded_buf);
g_free(XBZRLE.current_buf);
g_free(XBZRLE.decoded_buf);
XBZRLE.cache = NULL;
}
}

static void ram_migration_cancel(void *opaque)
Expand All @@ -323,6 +411,18 @@ static int ram_save_setup(QEMUFile *f, void *opaque)
last_offset = 0;
sort_ram_list();

if (migrate_use_xbzrle()) {
XBZRLE.cache = cache_init(migrate_xbzrle_cache_size() /
TARGET_PAGE_SIZE,
TARGET_PAGE_SIZE);
if (!XBZRLE.cache) {
DPRINTF("Error creating cache\n");
return -1;
}
XBZRLE.encoded_buf = g_malloc0(TARGET_PAGE_SIZE);
XBZRLE.current_buf = g_malloc(TARGET_PAGE_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) {
Expand Down Expand Up @@ -438,6 +538,47 @@ static int ram_save_complete(QEMUFile *f, void *opaque)
return 0;
}

static int load_xbzrle(QEMUFile *f, ram_addr_t addr, void *host)
{
int ret, rc = 0;
unsigned int xh_len;
int xh_flags;

if (!XBZRLE.decoded_buf) {
XBZRLE.decoded_buf = g_malloc(TARGET_PAGE_SIZE);
}

/* extract RLE header */
xh_flags = qemu_get_byte(f);
xh_len = qemu_get_be16(f);

if (xh_flags != ENCODING_FLAG_XBZRLE) {
fprintf(stderr, "Failed to load XBZRLE page - wrong compression!\n");
return -1;
}

if (xh_len > TARGET_PAGE_SIZE) {
fprintf(stderr, "Failed to load XBZRLE page - len overflow!\n");
return -1;
}
/* load data and decode */
qemu_get_buffer(f, XBZRLE.decoded_buf, xh_len);

/* decode RLE */
ret = xbzrle_decode_buffer(XBZRLE.decoded_buf, xh_len, host,
TARGET_PAGE_SIZE);
if (ret == -1) {
fprintf(stderr, "Failed to load XBZRLE page - decode error!\n");
rc = -1;
} else if (ret > TARGET_PAGE_SIZE) {
fprintf(stderr, "Failed to load XBZRLE page - size %d exceeds %d!\n",
ret, TARGET_PAGE_SIZE);
abort();
}

return rc;
}

static inline void *host_from_stream_offset(QEMUFile *f,
ram_addr_t offset,
int flags)
Expand Down Expand Up @@ -551,6 +692,19 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id)
}

qemu_get_buffer(f, host, TARGET_PAGE_SIZE);
} else if (flags & RAM_SAVE_FLAG_XBZRLE) {
if (!migrate_use_xbzrle()) {
return -EINVAL;
}
void *host = host_from_stream_offset(f, addr, flags);
if (!host) {
return -EINVAL;
}

if (load_xbzrle(f, addr, host) < 0) {
ret = -EINVAL;
goto done;
}
}
error = qemu_file_get_error(f);
if (error) {
Expand Down
24 changes: 24 additions & 0 deletions migration.c
Expand Up @@ -43,6 +43,9 @@ enum {

#define MAX_THROTTLE (32 << 20) /* Migration speed throttling */

/* Migration XBZRLE default cache size */
#define DEFAULT_MIGRATE_CACHE_SIZE (64 * 1024 * 1024)

static NotifierList migration_state_notifiers =
NOTIFIER_LIST_INITIALIZER(migration_state_notifiers);

Expand All @@ -55,6 +58,7 @@ static MigrationState *migrate_get_current(void)
static MigrationState current_migration = {
.state = MIG_STATE_SETUP,
.bandwidth_limit = MAX_THROTTLE,
.xbzrle_cache_size = DEFAULT_MIGRATE_CACHE_SIZE,
};

return &current_migration;
Expand Down Expand Up @@ -416,6 +420,7 @@ static MigrationState *migrate_init(const MigrationParams *params)
MigrationState *s = migrate_get_current();
int64_t bandwidth_limit = s->bandwidth_limit;
bool enabled_capabilities[MIGRATION_CAPABILITY_MAX];
int64_t xbzrle_cache_size = s->xbzrle_cache_size;

memcpy(enabled_capabilities, s->enabled_capabilities,
sizeof(enabled_capabilities));
Expand All @@ -425,6 +430,7 @@ static MigrationState *migrate_init(const MigrationParams *params)
s->params = *params;
memcpy(s->enabled_capabilities, enabled_capabilities,
sizeof(enabled_capabilities));
s->xbzrle_cache_size = xbzrle_cache_size;

s->bandwidth_limit = bandwidth_limit;
s->state = MIG_STATE_SETUP;
Expand Down Expand Up @@ -524,3 +530,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->enabled_capabilities[MIGRATION_CAPABILITY_XBZRLE];
}

int64_t migrate_xbzrle_cache_size(void)
{
MigrationState *s;

s = migrate_get_current();

return s->xbzrle_cache_size;
}
4 changes: 4 additions & 0 deletions migration.h
Expand Up @@ -41,6 +41,7 @@ struct MigrationState
MigrationParams params;
int64_t total_time;
bool enabled_capabilities[MIGRATION_CAPABILITY_MAX];
int64_t xbzrle_cache_size;
};

void process_incoming_migration(QEMUFile *f);
Expand Down Expand Up @@ -104,4 +105,7 @@ int xbzrle_encode_buffer(uint8_t *old_buf, uint8_t *new_buf, int slen,
uint8_t *dst, int dlen);
int xbzrle_decode_buffer(uint8_t *src, int slen, uint8_t *dst, int dlen);

int migrate_use_xbzrle(void);
int64_t migrate_xbzrle_cache_size(void);

#endif

0 comments on commit 17ad9b3

Please sign in to comment.