Skip to content

Commit

Permalink
Merge remote-tracking branch 'quintela/migration-anthony-v2' into sta…
Browse files Browse the repository at this point in the history
…ging

* 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
  • Loading branch information
Anthony Liguori committed Jul 9, 2012
2 parents 537cf40 + 45f33f0 commit 3f6e9a5
Show file tree
Hide file tree
Showing 14 changed files with 198 additions and 88 deletions.
111 changes: 75 additions & 36 deletions arch_init.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;

Expand All @@ -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;
}
Expand All @@ -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)
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;

Expand All @@ -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) {
Expand Down Expand Up @@ -376,9 +395,16 @@ int ram_save_live(QEMUFile *f, int stage, void *opaque)

qemu_put_be64(f, RAM_SAVE_FLAG_EOS);

expected_time = ram_save_remaining() * TARGET_PAGE_SIZE / bwidth;
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,
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -445,16 +474,19 @@ 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;
}
}

if (!block) {
fprintf(stderr, "Unknown ramblock \"%s\", cannot "
"accept migration\n", id);
return -EINVAL;
ret = -EINVAL;
goto done;
}

total_ram_bytes -= length;
Expand Down Expand Up @@ -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
Expand Down
8 changes: 4 additions & 4 deletions block-migration.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions cpu-all.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
50 changes: 30 additions & 20 deletions exec-obsolete.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,74 +45,84 @@ 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,
ram_addr_t length,
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,
int dirty_flags)
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);
}
}

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);
}
}

Expand Down
Loading

0 comments on commit 3f6e9a5

Please sign in to comment.