Commit 17ad9b35 authored by Orit Wasserman's avatar Orit Wasserman Committed by Juan Quintela
Browse files

Add XBZRLE to ram_save_block and ram_save_live



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: default avatarBenoit Hudzia <benoit.hudzia@sap.com>
Signed-off-by: default avatarPetter Svard <petters@cs.umu.se>
Signed-off-by: default avatarAidan Shribman <aidan.shribman@sap.com>
Signed-off-by: default avatarOrit Wasserman <owasserm@redhat.com>

Reviewed-by: default avatarLuiz Capitulino <lcapitulino@redhat.com>
Reviewed-by: default avatarEric Blake <eblake@redhat.com>
parent 302dfbeb
Loading
Loading
Loading
Loading
+156 −2
Original line number Diff line number Diff line
@@ -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, ...) \
@@ -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>
@@ -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)
{
@@ -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;

@@ -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);
@@ -220,14 +288,25 @@ 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;
            }

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

        offset += TARGET_PAGE_SIZE;
        if (offset >= block->length) {
@@ -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)
@@ -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) {
@@ -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)
@@ -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) {
+24 −0
Original line number Diff line number Diff line
@@ -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);

@@ -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;
@@ -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));
@@ -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;
@@ -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 −0
Original line number Diff line number Diff line
@@ -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);
@@ -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