Skip to content

Commit

Permalink
Merge remote-tracking branch 'quintela/migration.next' into staging
Browse files Browse the repository at this point in the history
# By Peter Lieven (9) and others
# Via Juan Quintela
* quintela/migration.next: (22 commits)
  Use qemu_put_buffer_async for guest memory pages
  Add qemu_put_buffer_async
  Use writev ops if available
  Store the data to send also in iovec
  Update bytes_xfer in qemu_put_byte
  Add socket_writev_buffer function
  Add QemuFileWritevBuffer QemuFileOps
  migration: use XBZRLE only after bulk stage
  migration: do not search dirty pages in bulk stage
  migration: do not sent zero pages in bulk stage
  migration: add an indicator for bulk state of ram migration
  migration: search for zero instead of dup pages
  bitops: unroll while loop in find_next_bit()
  buffer_is_zero: use vector optimizations if possible
  cutils: add a function to find non-zero content in a buffer
  move vector definitions to qemu-common.h
  savevm: Fix bugs in the VMSTATE_VBUFFER_MULTIPLY definition
  savevm: Add VMSTATE_STRUCT_VARRAY_POINTER_UINT32
  savevm: Add VMSTATE_FLOAT64 helpers
  savevm: Add VMSTATE_UINTTL_EQUAL helper
  ...
  • Loading branch information
Anthony Liguori committed Mar 26, 2013
2 parents fad5593 + 500f006 commit 18501ae
Show file tree
Hide file tree
Showing 13 changed files with 375 additions and 87 deletions.
76 changes: 36 additions & 40 deletions arch_init.c
Original file line number Diff line number Diff line change
Expand Up @@ -116,26 +116,6 @@ const uint32_t arch_type = QEMU_ARCH;
#define RAM_SAVE_FLAG_CONTINUE 0x20
#define RAM_SAVE_FLAG_XBZRLE 0x40

#ifdef __ALTIVEC__
#include <altivec.h>
#define VECTYPE vector unsigned char
#define SPLAT(p) vec_splat(vec_ld(0, p), 0)
#define ALL_EQ(v1, v2) vec_all_eq(v1, v2)
/* altivec.h may redefine the bool macro as vector type.
* Reset it to POSIX semantics. */
#undef bool
#define bool _Bool
#elif defined __SSE2__
#include <emmintrin.h>
#define VECTYPE __m128i
#define SPLAT(p) _mm_set1_epi8(*(p))
#define ALL_EQ(v1, v2) (_mm_movemask_epi8(_mm_cmpeq_epi8(v1, v2)) == 0xFFFF)
#else
#define VECTYPE unsigned long
#define SPLAT(p) (*(p) * (~0UL / 255))
#define ALL_EQ(v1, v2) ((v1) == (v2))
#endif


static struct defconfig_file {
const char *filename;
Expand Down Expand Up @@ -166,19 +146,10 @@ int qemu_read_default_config_files(bool userconfig)
return 0;
}

static int is_dup_page(uint8_t *page)
static inline bool is_zero_page(uint8_t *p)
{
VECTYPE *p = (VECTYPE *)page;
VECTYPE val = SPLAT(page);
int i;

for (i = 0; i < TARGET_PAGE_SIZE / sizeof(VECTYPE); i++) {
if (!ALL_EQ(val, p[i])) {
return 0;
}
}

return 1;
return buffer_find_nonzero_offset(p, TARGET_PAGE_SIZE) ==
TARGET_PAGE_SIZE;
}

/* struct contains XBZRLE cache and a static page
Expand Down Expand Up @@ -212,6 +183,7 @@ int64_t xbzrle_cache_resize(int64_t new_size)
/* accounting for migration statistics */
typedef struct AccountingInfo {
uint64_t dup_pages;
uint64_t skipped_pages;
uint64_t norm_pages;
uint64_t iterations;
uint64_t xbzrle_bytes;
Expand All @@ -237,6 +209,16 @@ uint64_t dup_mig_pages_transferred(void)
return acct_info.dup_pages;
}

uint64_t skipped_mig_bytes_transferred(void)
{
return acct_info.skipped_pages * TARGET_PAGE_SIZE;
}

uint64_t skipped_mig_pages_transferred(void)
{
return acct_info.skipped_pages;
}

uint64_t norm_mig_bytes_transferred(void)
{
return acct_info.norm_pages * TARGET_PAGE_SIZE;
Expand Down Expand Up @@ -348,6 +330,7 @@ static ram_addr_t last_offset;
static unsigned long *migration_bitmap;
static uint64_t migration_dirty_pages;
static uint32_t last_version;
static bool ram_bulk_stage;

static inline
ram_addr_t migration_bitmap_find_and_reset_dirty(MemoryRegion *mr,
Expand All @@ -357,7 +340,13 @@ ram_addr_t migration_bitmap_find_and_reset_dirty(MemoryRegion *mr,
unsigned long nr = base + (start >> TARGET_PAGE_BITS);
unsigned long size = base + (int128_get64(mr->size) >> TARGET_PAGE_BITS);

unsigned long next = find_next_bit(migration_bitmap, size, nr);
unsigned long next;

if (ram_bulk_stage && nr > base) {
next = nr + 1;
} else {
next = find_next_bit(migration_bitmap, size, nr);
}

if (next < size) {
clear_bit(next, migration_bitmap);
Expand Down Expand Up @@ -455,6 +444,7 @@ static int ram_save_block(QEMUFile *f, bool last_stage)
if (!block) {
block = QTAILQ_FIRST(&ram_list.blocks);
complete_round = true;
ram_bulk_stage = false;
}
} else {
uint8_t *p;
Expand All @@ -465,13 +455,18 @@ static int ram_save_block(QEMUFile *f, bool last_stage)

/* In doubt sent page as normal */
bytes_sent = -1;
if (is_dup_page(p)) {
if (is_zero_page(p)) {
acct_info.dup_pages++;
bytes_sent = save_block_hdr(f, block, offset, cont,
RAM_SAVE_FLAG_COMPRESS);
qemu_put_byte(f, *p);
bytes_sent += 1;
} else if (migrate_use_xbzrle()) {
if (!ram_bulk_stage) {
bytes_sent = save_block_hdr(f, block, offset, cont,
RAM_SAVE_FLAG_COMPRESS);
qemu_put_byte(f, 0);
bytes_sent++;
} else {
acct_info.skipped_pages++;
bytes_sent = 0;
}
} else if (!ram_bulk_stage && migrate_use_xbzrle()) {
current_addr = block->offset + offset;
bytes_sent = save_xbzrle_page(f, p, current_addr, block,
offset, cont, last_stage);
Expand All @@ -483,7 +478,7 @@ static int ram_save_block(QEMUFile *f, bool last_stage)
/* XBZRLE overflow or normal page */
if (bytes_sent == -1) {
bytes_sent = save_block_hdr(f, block, offset, cont, RAM_SAVE_FLAG_PAGE);
qemu_put_buffer(f, p, TARGET_PAGE_SIZE);
qemu_put_buffer_async(f, p, TARGET_PAGE_SIZE);
bytes_sent += TARGET_PAGE_SIZE;
acct_info.norm_pages++;
}
Expand Down Expand Up @@ -558,6 +553,7 @@ static void reset_ram_globals(void)
last_sent_block = NULL;
last_offset = 0;
last_version = ram_list.version;
ram_bulk_stage = true;
}

#define MAX_WAIT 50 /* ms, half buffered_file limit */
Expand Down
2 changes: 2 additions & 0 deletions hmp.c
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,8 @@ void hmp_info_migrate(Monitor *mon, const QDict *qdict)
info->ram->total >> 10);
monitor_printf(mon, "duplicate: %" PRIu64 " pages\n",
info->ram->duplicate);
monitor_printf(mon, "skipped: %" PRIu64 " pages\n",
info->ram->skipped);
monitor_printf(mon, "normal: %" PRIu64 " pages\n",
info->ram->normal);
monitor_printf(mon, "normal bytes: %" PRIu64 " kbytes\n",
Expand Down
6 changes: 6 additions & 0 deletions hw/hw.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,16 +52,22 @@ int qemu_boot_set(const char *boot_devices);
#if TARGET_LONG_BITS == 64
#define VMSTATE_UINTTL_V(_f, _s, _v) \
VMSTATE_UINT64_V(_f, _s, _v)
#define VMSTATE_UINTTL_EQUAL_V(_f, _s, _v) \
VMSTATE_UINT64_EQUAL_V(_f, _s, _v)
#define VMSTATE_UINTTL_ARRAY_V(_f, _s, _n, _v) \
VMSTATE_UINT64_ARRAY_V(_f, _s, _n, _v)
#else
#define VMSTATE_UINTTL_V(_f, _s, _v) \
VMSTATE_UINT32_V(_f, _s, _v)
#define VMSTATE_UINTTL_EQUAL_V(_f, _s, _v) \
VMSTATE_UINT32_EQUAL_V(_f, _s, _v)
#define VMSTATE_UINTTL_ARRAY_V(_f, _s, _n, _v) \
VMSTATE_UINT32_ARRAY_V(_f, _s, _n, _v)
#endif
#define VMSTATE_UINTTL(_f, _s) \
VMSTATE_UINTTL_V(_f, _s, 0)
#define VMSTATE_UINTTL_EQUAL(_f, _s) \
VMSTATE_UINTTL_EQUAL_V(_f, _s, 0)
#define VMSTATE_UINTTL_ARRAY(_f, _s, _n) \
VMSTATE_UINTTL_ARRAY_V(_f, _s, _n, 0)

Expand Down
2 changes: 2 additions & 0 deletions include/migration/migration.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ extern SaveVMHandlers savevm_ram_handlers;

uint64_t dup_mig_bytes_transferred(void);
uint64_t dup_mig_pages_transferred(void);
uint64_t skipped_mig_bytes_transferred(void);
uint64_t skipped_mig_pages_transferred(void);
uint64_t norm_mig_bytes_transferred(void);
uint64_t norm_mig_pages_transferred(void);
uint64_t xbzrle_mig_bytes_transferred(void);
Expand Down
12 changes: 12 additions & 0 deletions include/migration/qemu-file.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,18 @@ typedef int (QEMUFileCloseFunc)(void *opaque);
*/
typedef int (QEMUFileGetFD)(void *opaque);

/*
* This function writes an iovec to file.
*/
typedef ssize_t (QEMUFileWritevBufferFunc)(void *opaque, struct iovec *iov,
int iovcnt);

typedef struct QEMUFileOps {
QEMUFilePutBufferFunc *put_buffer;
QEMUFileGetBufferFunc *get_buffer;
QEMUFileCloseFunc *close;
QEMUFileGetFD *get_fd;
QEMUFileWritevBufferFunc *writev_buffer;
} QEMUFileOps;

QEMUFile *qemu_fopen_ops(void *opaque, const QEMUFileOps *ops);
Expand All @@ -68,6 +75,11 @@ int qemu_fclose(QEMUFile *f);
int64_t qemu_ftell(QEMUFile *f);
void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size);
void qemu_put_byte(QEMUFile *f, int v);
/*
* put_buffer without copying the buffer.
* The buffer should be available till it is sent asynchronously.
*/
void qemu_put_buffer_async(QEMUFile *f, const uint8_t *buf, int size);

static inline void qemu_put_ubyte(QEMUFile *f, unsigned int v)
{
Expand Down
43 changes: 39 additions & 4 deletions include/migration/vmstate.h
Original file line number Diff line number Diff line change
Expand Up @@ -149,13 +149,16 @@ extern const VMStateInfo vmstate_info_uint8_equal;
extern const VMStateInfo vmstate_info_uint16_equal;
extern const VMStateInfo vmstate_info_int32_equal;
extern const VMStateInfo vmstate_info_uint32_equal;
extern const VMStateInfo vmstate_info_uint64_equal;
extern const VMStateInfo vmstate_info_int32_le;

extern const VMStateInfo vmstate_info_uint8;
extern const VMStateInfo vmstate_info_uint16;
extern const VMStateInfo vmstate_info_uint32;
extern const VMStateInfo vmstate_info_uint64;

extern const VMStateInfo vmstate_info_float64;

extern const VMStateInfo vmstate_info_timer;
extern const VMStateInfo vmstate_info_buffer;
extern const VMStateInfo vmstate_info_unused_buffer;
Expand Down Expand Up @@ -340,6 +343,16 @@ extern const VMStateInfo vmstate_info_bitmap;
.offset = vmstate_offset_pointer(_state, _field, _type), \
}

#define VMSTATE_STRUCT_VARRAY_POINTER_UINT32(_field, _state, _field_num, _vmsd, _type) { \
.name = (stringify(_field)), \
.version_id = 0, \
.num_offset = vmstate_offset_value(_state, _field_num, uint32_t),\
.size = sizeof(_type), \
.vmsd = &(_vmsd), \
.flags = VMS_POINTER | VMS_VARRAY_INT32 | VMS_STRUCT, \
.offset = vmstate_offset_pointer(_state, _field, _type), \
}

#define VMSTATE_STRUCT_VARRAY_POINTER_UINT16(_field, _state, _field_num, _vmsd, _type) { \
.name = (stringify(_field)), \
.version_id = 0, \
Expand Down Expand Up @@ -380,14 +393,14 @@ extern const VMStateInfo vmstate_info_bitmap;
.offset = vmstate_offset_buffer(_state, _field) + _start, \
}

#define VMSTATE_BUFFER_MULTIPLY(_field, _state, _version, _test, _start, _field_size, _multiply) { \
#define VMSTATE_VBUFFER_MULTIPLY(_field, _state, _version, _test, _start, _field_size, _multiply) { \
.name = (stringify(_field)), \
.version_id = (_version), \
.field_exists = (_test), \
.size_offset = vmstate_offset_value(_state, _field_size, uint32_t),\
.size = (_multiply), \
.info = &vmstate_info_buffer, \
.flags = VMS_VBUFFER|VMS_MULTIPLY, \
.flags = VMS_VBUFFER|VMS_POINTER|VMS_MULTIPLY, \
.offset = offsetof(_state, _field), \
.start = (_start), \
}
Expand Down Expand Up @@ -518,8 +531,17 @@ extern const VMStateInfo vmstate_info_bitmap;
#define VMSTATE_INT32_EQUAL(_f, _s) \
VMSTATE_SINGLE(_f, _s, 0, vmstate_info_int32_equal, int32_t)

#define VMSTATE_UINT32_EQUAL(_f, _s) \
VMSTATE_SINGLE(_f, _s, 0, vmstate_info_uint32_equal, uint32_t)
#define VMSTATE_UINT32_EQUAL_V(_f, _s, _v) \
VMSTATE_SINGLE(_f, _s, _v, vmstate_info_uint32_equal, uint32_t)

#define VMSTATE_UINT32_EQUAL(_f, _s) \
VMSTATE_UINT32_EQUAL_V(_f, _s, 0)

#define VMSTATE_UINT64_EQUAL_V(_f, _s, _v) \
VMSTATE_SINGLE(_f, _s, _v, vmstate_info_uint64_equal, uint64_t)

#define VMSTATE_UINT64_EQUAL(_f, _s) \
VMSTATE_UINT64_EQUAL_V(_f, _s, 0)

#define VMSTATE_INT32_LE(_f, _s) \
VMSTATE_SINGLE(_f, _s, 0, vmstate_info_int32_le, int32_t)
Expand All @@ -533,6 +555,13 @@ extern const VMStateInfo vmstate_info_bitmap;
#define VMSTATE_UINT32_TEST(_f, _s, _t) \
VMSTATE_SINGLE_TEST(_f, _s, _t, 0, vmstate_info_uint32, uint32_t)


#define VMSTATE_FLOAT64_V(_f, _s, _v) \
VMSTATE_SINGLE(_f, _s, _v, vmstate_info_float64, float64)

#define VMSTATE_FLOAT64(_f, _s) \
VMSTATE_FLOAT64_V(_f, _s, 0)

#define VMSTATE_TIMER_TEST(_f, _s, _test) \
VMSTATE_POINTER_TEST(_f, _s, _test, vmstate_info_timer, QEMUTimer *)

Expand Down Expand Up @@ -599,6 +628,12 @@ extern const VMStateInfo vmstate_info_bitmap;
#define VMSTATE_INT64_ARRAY(_f, _s, _n) \
VMSTATE_INT64_ARRAY_V(_f, _s, _n, 0)

#define VMSTATE_FLOAT64_ARRAY_V(_f, _s, _n, _v) \
VMSTATE_ARRAY(_f, _s, _n, _v, vmstate_info_float64, float64)

#define VMSTATE_FLOAT64_ARRAY(_f, _s, _n) \
VMSTATE_FLOAT64_ARRAY_V(_f, _s, _n, 0)

#define VMSTATE_BUFFER_V(_f, _s, _v) \
VMSTATE_STATIC_BUFFER(_f, _s, _v, NULL, 0, sizeof(typeof_field(_s, _f)))

Expand Down
31 changes: 31 additions & 0 deletions include/qemu-common.h
Original file line number Diff line number Diff line change
Expand Up @@ -448,4 +448,35 @@ int uleb128_decode_small(const uint8_t *in, uint32_t *n);

void hexdump(const char *buf, FILE *fp, const char *prefix, size_t size);

/* vector definitions */
#ifdef __ALTIVEC__
#include <altivec.h>
#define VECTYPE vector unsigned char
#define SPLAT(p) vec_splat(vec_ld(0, p), 0)
#define ALL_EQ(v1, v2) vec_all_eq(v1, v2)
/* altivec.h may redefine the bool macro as vector type.
* Reset it to POSIX semantics. */
#undef bool
#define bool _Bool
#elif defined __SSE2__
#include <emmintrin.h>
#define VECTYPE __m128i
#define SPLAT(p) _mm_set1_epi8(*(p))
#define ALL_EQ(v1, v2) (_mm_movemask_epi8(_mm_cmpeq_epi8(v1, v2)) == 0xFFFF)
#else
#define VECTYPE unsigned long
#define SPLAT(p) (*(p) * (~0UL / 255))
#define ALL_EQ(v1, v2) ((v1) == (v2))
#endif

#define BUFFER_FIND_NONZERO_OFFSET_UNROLL_FACTOR 8
static inline bool
can_use_buffer_find_nonzero_offset(const void *buf, size_t len)
{
return (len % (BUFFER_FIND_NONZERO_OFFSET_UNROLL_FACTOR
* sizeof(VECTYPE)) == 0
&& ((uintptr_t) buf) % sizeof(VECTYPE) == 0);
}
size_t buffer_find_nonzero_offset(const void *buf, size_t len);

#endif
3 changes: 2 additions & 1 deletion migration.c
Original file line number Diff line number Diff line change
Expand Up @@ -197,11 +197,11 @@ MigrationInfo *qmp_query_migrate(Error **errp)
info->ram->remaining = ram_bytes_remaining();
info->ram->total = ram_bytes_total();
info->ram->duplicate = dup_mig_pages_transferred();
info->ram->skipped = skipped_mig_pages_transferred();
info->ram->normal = norm_mig_pages_transferred();
info->ram->normal_bytes = norm_mig_bytes_transferred();
info->ram->dirty_pages_rate = s->dirty_pages_rate;


if (blk_mig_active()) {
info->has_disk = true;
info->disk = g_malloc0(sizeof(*info->disk));
Expand All @@ -227,6 +227,7 @@ MigrationInfo *qmp_query_migrate(Error **errp)
info->ram->remaining = 0;
info->ram->total = ram_bytes_total();
info->ram->duplicate = dup_mig_pages_transferred();
info->ram->skipped = skipped_mig_pages_transferred();
info->ram->normal = norm_mig_pages_transferred();
info->ram->normal_bytes = norm_mig_bytes_transferred();
break;
Expand Down
8 changes: 5 additions & 3 deletions qapi-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -496,7 +496,9 @@
#
# @total: total amount of bytes involved in the migration process
#
# @duplicate: number of duplicate pages (since 1.2)
# @duplicate: number of duplicate (zero) pages (since 1.2)
#
# @skipped: number of skipped zero pages (since 1.5)
#
# @normal : number of normal pages (since 1.2)
#
Expand All @@ -509,8 +511,8 @@
##
{ 'type': 'MigrationStats',
'data': {'transferred': 'int', 'remaining': 'int', 'total': 'int' ,
'duplicate': 'int', 'normal': 'int', 'normal-bytes': 'int',
'dirty-pages-rate' : 'int' } }
'duplicate': 'int', 'skipped': 'int', 'normal': 'int',
'normal-bytes': 'int', 'dirty-pages-rate' : 'int' } }

##
# @XBZRLECacheStats
Expand Down
Loading

0 comments on commit 18501ae

Please sign in to comment.