Commit 3f6e9a5f authored by Anthony Liguori's avatar Anthony Liguori
Browse files

Merge remote-tracking branch 'quintela/migration-anthony-v2' into staging

* quintela/migration-anthony-v2:
  Maintain the number of dirty pages
  dirty bitmap: abstract its use
  Exit loop if we have been there too long
  Only calculate expected_time for stage 2
  Only TCG needs TLB handling
  No need to iterate if we already are over the limit
  Add tracepoints for savevm section start/end
  Add spent time for migration
  Add migration_end function
  Add debugging infrastructure
  Add save_block_hdr function
  Add MigrationParams structure
  Add missing check for host_from_stream_offset return value for RAM_SAVE_FLAG_PAGE
parents 537cf409 45f33f01
Loading
Loading
Loading
Loading
+75 −36
Original line number Diff line number Diff line
@@ -44,6 +44,14 @@
#include "exec-memory.h"
#include "hw/pcspk.h"

#ifdef DEBUG_ARCH_INIT
#define DPRINTF(fmt, ...) \
    do { fprintf(stdout, "arch_init: " fmt, ## __VA_ARGS__); } while (0)
#else
#define DPRINTF(fmt, ...) \
    do { } while (0)
#endif

#ifdef TARGET_SPARC
int graphic_width = 1024;
int graphic_height = 768;
@@ -161,6 +169,18 @@ static int is_dup_page(uint8_t *page)
    return 1;
}

static void save_block_hdr(QEMUFile *f, RAMBlock *block, ram_addr_t offset,
        int cont, int flag)
{
        qemu_put_be64(f, offset | cont | flag);
        if (!cont) {
                qemu_put_byte(f, strlen(block->idstr));
                qemu_put_buffer(f, (uint8_t *)block->idstr,
                                strlen(block->idstr));
        }

}

static RAMBlock *last_block;
static ram_addr_t last_offset;

@@ -187,21 +207,11 @@ static int ram_save_block(QEMUFile *f)
            p = memory_region_get_ram_ptr(mr) + offset;

            if (is_dup_page(p)) {
                qemu_put_be64(f, offset | cont | RAM_SAVE_FLAG_COMPRESS);
                if (!cont) {
                    qemu_put_byte(f, strlen(block->idstr));
                    qemu_put_buffer(f, (uint8_t *)block->idstr,
                                    strlen(block->idstr));
                }
                save_block_hdr(f, block, offset, cont, RAM_SAVE_FLAG_COMPRESS);
                qemu_put_byte(f, *p);
                bytes_sent = 1;
            } else {
                qemu_put_be64(f, offset | cont | RAM_SAVE_FLAG_PAGE);
                if (!cont) {
                    qemu_put_byte(f, strlen(block->idstr));
                    qemu_put_buffer(f, (uint8_t *)block->idstr,
                                    strlen(block->idstr));
                }
                save_block_hdr(f, block, offset, cont, RAM_SAVE_FLAG_PAGE);
                qemu_put_buffer(f, p, TARGET_PAGE_SIZE);
                bytes_sent = TARGET_PAGE_SIZE;
            }
@@ -228,20 +238,7 @@ static uint64_t bytes_transferred;

static ram_addr_t ram_save_remaining(void)
{
    RAMBlock *block;
    ram_addr_t count = 0;

    QLIST_FOREACH(block, &ram_list.blocks, next) {
        ram_addr_t addr;
        for (addr = 0; addr < block->length; addr += TARGET_PAGE_SIZE) {
            if (memory_region_get_dirty(block->mr, addr, TARGET_PAGE_SIZE,
                                        DIRTY_MEMORY_MIGRATION)) {
                count++;
            }
        }
    }

    return count;
    return ram_list.dirty_pages;
}

uint64_t ram_bytes_remaining(void)
@@ -294,16 +291,23 @@ static void sort_ram_list(void)
    g_free(blocks);
}

static void migration_end(void)
{
    memory_global_dirty_log_stop();
}

#define MAX_WAIT 50 /* ms, half buffered_file limit */

int ram_save_live(QEMUFile *f, int stage, void *opaque)
{
    ram_addr_t addr;
    uint64_t bytes_transferred_last;
    double bwidth = 0;
    uint64_t expected_time = 0;
    int ret;
    int i;

    if (stage < 0) {
        memory_global_dirty_log_stop();
        migration_end();
        return 0;
    }

@@ -340,6 +344,7 @@ int ram_save_live(QEMUFile *f, int stage, void *opaque)
    bytes_transferred_last = bytes_transferred;
    bwidth = qemu_get_clock_ns(rt_clock);

    i = 0;
    while ((ret = qemu_file_rate_limit(f)) == 0) {
        int bytes_sent;

@@ -348,6 +353,20 @@ int ram_save_live(QEMUFile *f, int stage, void *opaque)
        if (bytes_sent == 0) { /* no more blocks */
            break;
        }
        /* we want to check in the 1st loop, just in case it was the 1st time
           and we had to sync the dirty bitmap.
           qemu_get_clock_ns() is a bit expensive, so we only check each some
           iterations
        */
        if ((i & 63) == 0) {
            uint64_t t1 = (qemu_get_clock_ns(rt_clock) - bwidth) / 1000000;
            if (t1 > MAX_WAIT) {
                DPRINTF("big wait: " PRIu64 " milliseconds, %d iterations\n",
                        t1, i);
                break;
            }
        }
        i++;
    }

    if (ret < 0) {
@@ -376,9 +395,16 @@ int ram_save_live(QEMUFile *f, int stage, void *opaque)

    qemu_put_be64(f, RAM_SAVE_FLAG_EOS);

    if (stage == 2) {
        uint64_t expected_time;
        expected_time = ram_save_remaining() * TARGET_PAGE_SIZE / bwidth;

    return (stage == 2) && (expected_time <= migrate_max_downtime());
        DPRINTF("ram_save_live: expected(" PRIu64 ") <= max(" PRIu64 ")?\n",
                expected_time, migrate_max_downtime());

        return expected_time <= migrate_max_downtime();
    }
    return 0;
}

static inline void *host_from_stream_offset(QEMUFile *f,
@@ -414,8 +440,11 @@ static inline void *host_from_stream_offset(QEMUFile *f,
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 < 4 || version_id > 4) {
        return -EINVAL;
@@ -445,8 +474,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;
                        }
                    }
@@ -454,7 +485,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;
@@ -483,16 +515,23 @@ int ram_load(QEMUFile *f, void *opaque, int version_id)
            void *host;

            host = host_from_stream_offset(f, addr, flags);
            if (!host) {
                return -EINVAL;
            }

            qemu_get_buffer(f, host, TARGET_PAGE_SIZE);
        }
        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 " PRIu64 "\n",
            ret, seq_iter);
    return ret;
}

#ifdef HAS_AUDIO
+4 −4
Original line number Diff line number Diff line
@@ -700,13 +700,13 @@ static int block_load(QEMUFile *f, void *opaque, int version_id)
    return 0;
}

static void block_set_params(int blk_enable, int shared_base, void *opaque)
static void block_set_params(const MigrationParams *params, void *opaque)
{
    block_mig_state.blk_enable = blk_enable;
    block_mig_state.shared_base = shared_base;
    block_mig_state.blk_enable = params->blk;
    block_mig_state.shared_base = params->shared;

    /* shared base means that blk_enable = 1 */
    block_mig_state.blk_enable |= shared_base;
    block_mig_state.blk_enable |= params->shared;
}

void blk_mig_init(void)
+1 −0
Original line number Diff line number Diff line
@@ -486,6 +486,7 @@ typedef struct RAMBlock {
typedef struct RAMList {
    uint8_t *phys_dirty;
    QLIST_HEAD(, RAMBlock) blocks;
    uint64_t dirty_pages;
} RAMList;
extern RAMList ram_list;

+30 −20
Original line number Diff line number Diff line
@@ -45,15 +45,15 @@ int cpu_physical_memory_set_dirty_tracking(int enable);
#define CODE_DIRTY_FLAG      0x02
#define MIGRATION_DIRTY_FLAG 0x08

/* read dirty bit (return 0 or 1) */
static inline int cpu_physical_memory_is_dirty(ram_addr_t addr)
static inline int cpu_physical_memory_get_dirty_flags(ram_addr_t addr)
{
    return ram_list.phys_dirty[addr >> TARGET_PAGE_BITS] == 0xff;
    return ram_list.phys_dirty[addr >> TARGET_PAGE_BITS];
}

static inline int cpu_physical_memory_get_dirty_flags(ram_addr_t addr)
/* read dirty bit (return 0 or 1) */
static inline int cpu_physical_memory_is_dirty(ram_addr_t addr)
{
    return ram_list.phys_dirty[addr >> TARGET_PAGE_BITS];
    return cpu_physical_memory_get_dirty_flags(addr) == 0xff;
}

static inline int cpu_physical_memory_get_dirty(ram_addr_t start,
@@ -61,41 +61,55 @@ static inline int cpu_physical_memory_get_dirty(ram_addr_t start,
                                                int dirty_flags)
{
    int ret = 0;
    uint8_t *p;
    ram_addr_t addr, end;

    end = TARGET_PAGE_ALIGN(start + length);
    start &= TARGET_PAGE_MASK;
    p = ram_list.phys_dirty + (start >> TARGET_PAGE_BITS);
    for (addr = start; addr < end; addr += TARGET_PAGE_SIZE) {
        ret |= *p++ & dirty_flags;
        ret |= cpu_physical_memory_get_dirty_flags(addr) & dirty_flags;
    }
    return ret;
}

static inline int cpu_physical_memory_set_dirty_flags(ram_addr_t addr,
                                                      int dirty_flags)
{
    if ((dirty_flags & MIGRATION_DIRTY_FLAG) &&
        !cpu_physical_memory_get_dirty(addr, TARGET_PAGE_SIZE,
                                       MIGRATION_DIRTY_FLAG)) {
        ram_list.dirty_pages++;
    }
    return ram_list.phys_dirty[addr >> TARGET_PAGE_BITS] |= dirty_flags;
}

static inline void cpu_physical_memory_set_dirty(ram_addr_t addr)
{
    ram_list.phys_dirty[addr >> TARGET_PAGE_BITS] = 0xff;
    cpu_physical_memory_set_dirty_flags(addr, 0xff);
}

static inline int cpu_physical_memory_set_dirty_flags(ram_addr_t addr,
static inline int cpu_physical_memory_clear_dirty_flags(ram_addr_t addr,
                                                        int dirty_flags)
{
    return ram_list.phys_dirty[addr >> TARGET_PAGE_BITS] |= dirty_flags;
    int mask = ~dirty_flags;

    if ((dirty_flags & MIGRATION_DIRTY_FLAG) &&
        cpu_physical_memory_get_dirty(addr, TARGET_PAGE_SIZE,
                                      MIGRATION_DIRTY_FLAG)) {
        ram_list.dirty_pages--;
    }
    return ram_list.phys_dirty[addr >> TARGET_PAGE_BITS] &= mask;
}

static inline void cpu_physical_memory_set_dirty_range(ram_addr_t start,
                                                       ram_addr_t length,
                                                       int dirty_flags)
{
    uint8_t *p;
    ram_addr_t addr, end;

    end = TARGET_PAGE_ALIGN(start + length);
    start &= TARGET_PAGE_MASK;
    p = ram_list.phys_dirty + (start >> TARGET_PAGE_BITS);
    for (addr = start; addr < end; addr += TARGET_PAGE_SIZE) {
        *p++ |= dirty_flags;
        cpu_physical_memory_set_dirty_flags(addr, dirty_flags);
    }
}

@@ -103,16 +117,12 @@ static inline void cpu_physical_memory_mask_dirty_range(ram_addr_t start,
                                                        ram_addr_t length,
                                                        int dirty_flags)
{
    int mask;
    uint8_t *p;
    ram_addr_t addr, end;

    end = TARGET_PAGE_ALIGN(start + length);
    start &= TARGET_PAGE_MASK;
    mask = ~dirty_flags;
    p = ram_list.phys_dirty + (start >> TARGET_PAGE_BITS);
    for (addr = start; addr < end; addr += TARGET_PAGE_SIZE) {
        *p++ &= mask;
        cpu_physical_memory_clear_dirty_flags(addr, dirty_flags);
    }
}

+22 −12
Original line number Diff line number Diff line
@@ -1824,11 +1824,29 @@ void tb_flush_jmp_cache(CPUArchState *env, target_ulong addr)
            TB_JMP_PAGE_SIZE * sizeof(TranslationBlock *));
}

static void tlb_reset_dirty_range_all(ram_addr_t start, ram_addr_t end,
                                      uintptr_t length)
{
    uintptr_t start1;

    /* we modify the TLB cache so that the dirty bit will be set again
       when accessing the range */
    start1 = (uintptr_t)qemu_safe_ram_ptr(start);
    /* Check that we don't span multiple blocks - this breaks the
       address comparisons below.  */
    if ((uintptr_t)qemu_safe_ram_ptr(end - 1) - start1
            != (end - 1) - start) {
        abort();
    }
    cpu_tlb_reset_dirty_all(start1, length);

}

/* Note: start and end must be within the same ram block.  */
void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t end,
                                     int dirty_flags)
{
    uintptr_t length, start1;
    uintptr_t length;

    start &= TARGET_PAGE_MASK;
    end = TARGET_PAGE_ALIGN(end);
@@ -1838,16 +1856,9 @@ void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t end,
        return;
    cpu_physical_memory_mask_dirty_range(start, length, dirty_flags);

    /* we modify the TLB cache so that the dirty bit will be set again
       when accessing the range */
    start1 = (uintptr_t)qemu_safe_ram_ptr(start);
    /* Check that we don't span multiple blocks - this breaks the
       address comparisons below.  */
    if ((uintptr_t)qemu_safe_ram_ptr(end - 1) - start1
            != (end - 1) - start) {
        abort();
    if (tcg_enabled()) {
        tlb_reset_dirty_range_all(start, end, length);
    }
    cpu_tlb_reset_dirty_all(start1, length);
}

int cpu_physical_memory_set_dirty_tracking(int enable)
@@ -2554,8 +2565,7 @@ ram_addr_t qemu_ram_alloc_from_ptr(ram_addr_t size, void *host,

    ram_list.phys_dirty = g_realloc(ram_list.phys_dirty,
                                       last_ram_offset() >> TARGET_PAGE_BITS);
    memset(ram_list.phys_dirty + (new_block->offset >> TARGET_PAGE_BITS),
           0xff, size >> TARGET_PAGE_BITS);
    cpu_physical_memory_set_dirty_range(new_block->offset, size, 0xff);

    if (kvm_enabled())
        kvm_setup_guest_memory(new_block->host, size);
Loading