A set of patches collected over the holidays. Mix of optimizations and

fixes.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABAgAGBQJUuMDIAAoJEB6aO1+FQIO2RSEP/0QQvvRz9gv20VfGMvT6+tUQ
 NYcnJ1AAsykLD4upQax/hX8SLIoQ7dnHgBmuJK5GAxCTyxqrkjtQ1i/9ISqw1ZmG
 m+64RxNnENb4XWRKRt03YS/4JntB4o9UTEbxjKQb+KCslITzM4e1kf6NwUJK1exE
 vNibzt4vfbTrJrNeDbDpZg4zX6Gi+WPnXUyV6Fg4vsaW2lkfk8uCg7u6fu1VI8lG
 uQtfB4K8+DtlqjF2oqMD+a3NmDIKgHC3b25PBl02dJroNb2vTCNGxoO00xsynStR
 JeHOzApY0koWQIWzYfoCj5VEz5Dm1TkKxqOpWnN+Xh9PZiovLsM9JWMqCnujJHaa
 OS5nx7SueMBaz3qSxeV+8G3VI0x9l2R3aHOUP3VWS6mDb23HBXgbUNy5FihL4qKl
 hRhvkdA7VD05CS/x+4/tz3lrfzPMqeMbRBhnfxUhV1arZPQgc9PzNBJKF42u/eA4
 H75xkPcn/HtQwZZY7tW9RN6o61OkDkWGtjAI4l5VXh9ARLUVVvR/AQJeXyiWklSQ
 ARqtqosXIUPUdMcAMjD8GA909cwZn+5yKci4VuK3+omStcuZioRrtarp2m0TfW5V
 F0f3BjTDMG1aXmhqBsPsMuAUHVrber1phrB6JjVK/adJ246v3MOqp1/woxaRzNrC
 mWJUBeOZwl1Ld+BMg4eX
 =xCqX
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/amit-migration/tags/mig-2.3-1' into staging

A set of patches collected over the holidays.  Mix of optimizations and
fixes.

# gpg: Signature made Fri 16 Jan 2015 07:42:00 GMT using RSA key ID 854083B6
# gpg: Good signature from "Amit Shah <amit@amitshah.net>"
# gpg:                 aka "Amit Shah <amit@kernel.org>"
# gpg:                 aka "Amit Shah <amitshah@gmx.net>"

* remotes/amit-migration/tags/mig-2.3-1:
  vmstate: type-check sub-arrays
  migration_cancel: shutdown migration socket
  Handle bi-directional communication for fd migration
  socket shutdown
  Tests: QEMUSizedBuffer/QEMUBuffer
  QEMUSizedBuffer: only free qsb that qemu_bufopen allocated
  xbzrle: rebuild the cache_is_cached function
  xbzrle: optimize XBZRLE to decrease the cache misses

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2015-01-16 10:16:14 +00:00
commit e68cba3636
13 changed files with 144 additions and 47 deletions

View File

@ -346,7 +346,8 @@ static void xbzrle_cache_zero_page(ram_addr_t current_addr)
/* We don't care if this fails to allocate a new cache page /* We don't care if this fails to allocate a new cache page
* as long as it updated an old one */ * as long as it updated an old one */
cache_insert(XBZRLE.cache, current_addr, ZERO_TARGET_PAGE); cache_insert(XBZRLE.cache, current_addr, ZERO_TARGET_PAGE,
bitmap_sync_count);
} }
#define ENCODING_FLAG_XBZRLE 0x1 #define ENCODING_FLAG_XBZRLE 0x1
@ -358,10 +359,11 @@ static int save_xbzrle_page(QEMUFile *f, uint8_t **current_data,
int encoded_len = 0, bytes_sent = -1; int encoded_len = 0, bytes_sent = -1;
uint8_t *prev_cached_page; uint8_t *prev_cached_page;
if (!cache_is_cached(XBZRLE.cache, current_addr)) { if (!cache_is_cached(XBZRLE.cache, current_addr, bitmap_sync_count)) {
acct_info.xbzrle_cache_miss++; acct_info.xbzrle_cache_miss++;
if (!last_stage) { if (!last_stage) {
if (cache_insert(XBZRLE.cache, current_addr, *current_data) == -1) { if (cache_insert(XBZRLE.cache, current_addr, *current_data,
bitmap_sync_count) == -1) {
return -1; return -1;
} else { } else {
/* update *current_data when the page has been /* update *current_data when the page has been

View File

@ -71,6 +71,14 @@ encoded buffer:
encoded length 24 encoded length 24
e9 07 0f 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 03 01 67 01 01 69 e9 07 0f 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 03 01 67 01 01 69
Cache update strategy
=====================
Keeping the hot pages in the cache is effective for decreased cache
misses. XBZRLE uses a counter as the age of each page. The counter will
increase after each ram dirty bitmap sync. When a cache conflict is
detected, XBZRLE will only evict pages in the cache that are older than
a threshold.
Usage Usage
====================== ======================
1. Verify the destination QEMU version is able to decode the new format. 1. Verify the destination QEMU version is able to decode the new format.

View File

@ -43,8 +43,10 @@ void cache_fini(PageCache *cache);
* *
* @cache pointer to the PageCache struct * @cache pointer to the PageCache struct
* @addr: page addr * @addr: page addr
* @current_age: current bitmap generation
*/ */
bool cache_is_cached(const PageCache *cache, uint64_t addr); bool cache_is_cached(const PageCache *cache, uint64_t addr,
uint64_t current_age);
/** /**
* get_cached_data: Get the data cached for an addr * get_cached_data: Get the data cached for an addr
@ -60,13 +62,15 @@ uint8_t *get_cached_data(const PageCache *cache, uint64_t addr);
* cache_insert: insert the page into the cache. the page cache * cache_insert: insert the page into the cache. the page cache
* will dup the data on insert. the previous value will be overwritten * will dup the data on insert. the previous value will be overwritten
* *
* Returns -1 on error * Returns -1 when the page isn't inserted into cache
* *
* @cache pointer to the PageCache struct * @cache pointer to the PageCache struct
* @addr: page address * @addr: page address
* @pdata: pointer to the page * @pdata: pointer to the page
* @current_age: current bitmap generation
*/ */
int cache_insert(PageCache *cache, uint64_t addr, const uint8_t *pdata); int cache_insert(PageCache *cache, uint64_t addr, const uint8_t *pdata,
uint64_t current_age);
/** /**
* cache_resize: resize the page cache. In case of size reduction the extra * cache_resize: resize the page cache. In case of size reduction the extra

View File

@ -84,6 +84,14 @@ typedef size_t (QEMURamSaveFunc)(QEMUFile *f, void *opaque,
size_t size, size_t size,
int *bytes_sent); int *bytes_sent);
/*
* Stop any read or write (depending on flags) on the underlying
* transport on the QEMUFile.
* Existing blocking reads/writes must be woken
* Returns 0 on success, -err on error
*/
typedef int (QEMUFileShutdownFunc)(void *opaque, bool rd, bool wr);
typedef struct QEMUFileOps { typedef struct QEMUFileOps {
QEMUFilePutBufferFunc *put_buffer; QEMUFilePutBufferFunc *put_buffer;
QEMUFileGetBufferFunc *get_buffer; QEMUFileGetBufferFunc *get_buffer;
@ -94,6 +102,7 @@ typedef struct QEMUFileOps {
QEMURamHookFunc *after_ram_iterate; QEMURamHookFunc *after_ram_iterate;
QEMURamHookFunc *hook_ram_load; QEMURamHookFunc *hook_ram_load;
QEMURamSaveFunc *save_page; QEMURamSaveFunc *save_page;
QEMUFileShutdownFunc *shut_down;
} QEMUFileOps; } QEMUFileOps;
struct QEMUSizedBuffer { struct QEMUSizedBuffer {
@ -177,6 +186,7 @@ void qemu_file_set_rate_limit(QEMUFile *f, int64_t new_rate);
int64_t qemu_file_get_rate_limit(QEMUFile *f); int64_t qemu_file_get_rate_limit(QEMUFile *f);
int qemu_file_get_error(QEMUFile *f); int qemu_file_get_error(QEMUFile *f);
void qemu_file_set_error(QEMUFile *f, int ret); void qemu_file_set_error(QEMUFile *f, int ret);
int qemu_file_shutdown(QEMUFile *f);
void qemu_fflush(QEMUFile *f); void qemu_fflush(QEMUFile *f);
static inline void qemu_put_be64s(QEMUFile *f, const uint64_t *pv) static inline void qemu_put_be64s(QEMUFile *f, const uint64_t *pv)

View File

@ -189,7 +189,7 @@ extern const VMStateInfo vmstate_info_bitmap;
type_check_2darray(_type, typeof_field(_state, _field), _n1, _n2)) type_check_2darray(_type, typeof_field(_state, _field), _n1, _n2))
#define vmstate_offset_sub_array(_state, _field, _type, _start) \ #define vmstate_offset_sub_array(_state, _field, _type, _start) \
(offsetof(_state, _field[_start])) vmstate_offset_value(_state, _field[_start], _type)
#define vmstate_offset_buffer(_state, _field) \ #define vmstate_offset_buffer(_state, _field) \
vmstate_offset_array(_state, _field, uint8_t, \ vmstate_offset_array(_state, _field, uint8_t, \

View File

@ -44,6 +44,13 @@ int socket_set_fast_reuse(int fd);
int send_all(int fd, const void *buf, int len1); int send_all(int fd, const void *buf, int len1);
int recv_all(int fd, void *buf, int len1, bool single_read); int recv_all(int fd, void *buf, int len1, bool single_read);
#ifdef WIN32
/* Windows has different names for the same constants with the same values */
#define SHUT_RD 0
#define SHUT_WR 1
#define SHUT_RDWR 2
#endif
/* callback function for nonblocking connect /* callback function for nonblocking connect
* valid fd on success, negative error code on failure * valid fd on success, negative error code on failure
*/ */

View File

@ -31,13 +31,29 @@
do { } while (0) do { } while (0)
#endif #endif
static bool fd_is_socket(int fd)
{
struct stat stat;
int ret = fstat(fd, &stat);
if (ret == -1) {
/* When in doubt say no */
return false;
}
return S_ISSOCK(stat.st_mode);
}
void fd_start_outgoing_migration(MigrationState *s, const char *fdname, Error **errp) void fd_start_outgoing_migration(MigrationState *s, const char *fdname, Error **errp)
{ {
int fd = monitor_get_fd(cur_mon, fdname, errp); int fd = monitor_get_fd(cur_mon, fdname, errp);
if (fd == -1) { if (fd == -1) {
return; return;
} }
s->file = qemu_fdopen(fd, "wb");
if (fd_is_socket(fd)) {
s->file = qemu_fopen_socket(fd, "wb");
} else {
s->file = qemu_fdopen(fd, "wb");
}
migrate_fd_connect(s); migrate_fd_connect(s);
} }
@ -58,7 +74,11 @@ void fd_start_incoming_migration(const char *infd, Error **errp)
DPRINTF("Attempting to start an incoming migration via fd\n"); DPRINTF("Attempting to start an incoming migration via fd\n");
fd = strtol(infd, NULL, 0); fd = strtol(infd, NULL, 0);
f = qemu_fdopen(fd, "rb"); if (fd_is_socket(fd)) {
f = qemu_fopen_socket(fd, "rb");
} else {
f = qemu_fdopen(fd, "rb");
}
if(f == NULL) { if(f == NULL) {
error_setg_errno(errp, errno, "failed to open the source descriptor"); error_setg_errno(errp, errno, "failed to open the source descriptor");
return; return;

View File

@ -330,6 +330,7 @@ void migrate_fd_error(MigrationState *s)
static void migrate_fd_cancel(MigrationState *s) static void migrate_fd_cancel(MigrationState *s)
{ {
int old_state ; int old_state ;
QEMUFile *f = migrate_get_current()->file;
trace_migrate_fd_cancel(); trace_migrate_fd_cancel();
do { do {
@ -339,6 +340,17 @@ static void migrate_fd_cancel(MigrationState *s)
} }
migrate_set_state(s, old_state, MIG_STATE_CANCELLING); migrate_set_state(s, old_state, MIG_STATE_CANCELLING);
} while (s->state != MIG_STATE_CANCELLING); } while (s->state != MIG_STATE_CANCELLING);
/*
* If we're unlucky the migration code might be stuck somewhere in a
* send/write while the network has failed and is waiting to timeout;
* if we've got shutdown(2) available then we can force it to quit.
* The outgoing qemu file gets closed in migrate_fd_cleanup that is
* called in a bh, so there is no race against this cancel.
*/
if (s->state == MIG_STATE_CANCELLING && f) {
qemu_file_shutdown(f);
}
} }
void add_migration_state_change_notifier(Notifier *notify) void add_migration_state_change_notifier(Notifier *notify)

View File

@ -395,6 +395,7 @@ QEMUSizedBuffer *qsb_clone(const QEMUSizedBuffer *qsb)
typedef struct QEMUBuffer { typedef struct QEMUBuffer {
QEMUSizedBuffer *qsb; QEMUSizedBuffer *qsb;
QEMUFile *file; QEMUFile *file;
bool qsb_allocated;
} QEMUBuffer; } QEMUBuffer;
static int buf_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size) static int buf_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
@ -424,7 +425,9 @@ static int buf_close(void *opaque)
{ {
QEMUBuffer *s = opaque; QEMUBuffer *s = opaque;
qsb_free(s->qsb); if (s->qsb_allocated) {
qsb_free(s->qsb);
}
g_free(s); g_free(s);
@ -463,12 +466,11 @@ QEMUFile *qemu_bufopen(const char *mode, QEMUSizedBuffer *input)
} }
s = g_malloc0(sizeof(QEMUBuffer)); s = g_malloc0(sizeof(QEMUBuffer));
if (mode[0] == 'r') { s->qsb = input;
s->qsb = input;
}
if (s->qsb == NULL) { if (s->qsb == NULL) {
s->qsb = qsb_create(NULL, 0); s->qsb = qsb_create(NULL, 0);
s->qsb_allocated = true;
} }
if (!s->qsb) { if (!s->qsb) {
g_free(s); g_free(s);

View File

@ -26,6 +26,7 @@
#include "qemu/sockets.h" #include "qemu/sockets.h"
#include "block/coroutine.h" #include "block/coroutine.h"
#include "migration/qemu-file.h" #include "migration/qemu-file.h"
#include "migration/qemu-file-internal.h"
typedef struct QEMUFileSocket { typedef struct QEMUFileSocket {
int fd; int fd;
@ -84,6 +85,17 @@ static int socket_close(void *opaque)
return 0; return 0;
} }
static int socket_shutdown(void *opaque, bool rd, bool wr)
{
QEMUFileSocket *s = opaque;
if (shutdown(s->fd, rd ? (wr ? SHUT_RDWR : SHUT_RD) : SHUT_WR)) {
return -errno;
} else {
return 0;
}
}
static ssize_t unix_writev_buffer(void *opaque, struct iovec *iov, int iovcnt, static ssize_t unix_writev_buffer(void *opaque, struct iovec *iov, int iovcnt,
int64_t pos) int64_t pos)
{ {
@ -192,15 +204,18 @@ QEMUFile *qemu_fdopen(int fd, const char *mode)
} }
static const QEMUFileOps socket_read_ops = { static const QEMUFileOps socket_read_ops = {
.get_fd = socket_get_fd, .get_fd = socket_get_fd,
.get_buffer = socket_get_buffer, .get_buffer = socket_get_buffer,
.close = socket_close .close = socket_close,
.shut_down = socket_shutdown
}; };
static const QEMUFileOps socket_write_ops = { static const QEMUFileOps socket_write_ops = {
.get_fd = socket_get_fd, .get_fd = socket_get_fd,
.writev_buffer = socket_writev_buffer, .writev_buffer = socket_writev_buffer,
.close = socket_close .close = socket_close,
.shut_down = socket_shutdown
}; };
QEMUFile *qemu_fopen_socket(int fd, const char *mode) QEMUFile *qemu_fopen_socket(int fd, const char *mode)

View File

@ -30,6 +30,18 @@
#include "migration/qemu-file-internal.h" #include "migration/qemu-file-internal.h"
#include "trace.h" #include "trace.h"
/*
* Stop a file from being read/written - not all backing files can do this
* typically only sockets can.
*/
int qemu_file_shutdown(QEMUFile *f)
{
if (!f->ops->shut_down) {
return -ENOSYS;
}
return f->ops->shut_down(f->opaque, true, true);
}
bool qemu_file_mode_is_not_valid(const char *mode) bool qemu_file_mode_is_not_valid(const char *mode)
{ {
if (mode == NULL || if (mode == NULL ||

View File

@ -33,6 +33,9 @@
do { } while (0) do { } while (0)
#endif #endif
/* the page in cache will not be replaced in two cycles */
#define CACHED_PAGE_LIFETIME 2
typedef struct CacheItem CacheItem; typedef struct CacheItem CacheItem;
struct CacheItem { struct CacheItem {
@ -122,18 +125,6 @@ static size_t cache_get_cache_pos(const PageCache *cache,
return pos; return pos;
} }
bool cache_is_cached(const PageCache *cache, uint64_t addr)
{
size_t pos;
g_assert(cache);
g_assert(cache->page_cache);
pos = cache_get_cache_pos(cache, addr);
return (cache->page_cache[pos].it_addr == addr);
}
static CacheItem *cache_get_by_addr(const PageCache *cache, uint64_t addr) static CacheItem *cache_get_by_addr(const PageCache *cache, uint64_t addr)
{ {
size_t pos; size_t pos;
@ -151,17 +142,35 @@ uint8_t *get_cached_data(const PageCache *cache, uint64_t addr)
return cache_get_by_addr(cache, addr)->it_data; return cache_get_by_addr(cache, addr)->it_data;
} }
int cache_insert(PageCache *cache, uint64_t addr, const uint8_t *pdata) bool cache_is_cached(const PageCache *cache, uint64_t addr,
uint64_t current_age)
{
CacheItem *it;
it = cache_get_by_addr(cache, addr);
if (it->it_addr == addr) {
/* update the it_age when the cache hit */
it->it_age = current_age;
return true;
}
return false;
}
int cache_insert(PageCache *cache, uint64_t addr, const uint8_t *pdata,
uint64_t current_age)
{ {
CacheItem *it = NULL; CacheItem *it;
g_assert(cache);
g_assert(cache->page_cache);
/* actual update of entry */ /* actual update of entry */
it = cache_get_by_addr(cache, addr); it = cache_get_by_addr(cache, addr);
if (it->it_data && it->it_addr != addr &&
it->it_age + CACHED_PAGE_LIFETIME > current_age) {
/* the cache page is fresh, don't replace it */
return -1;
}
/* allocate page */ /* allocate page */
if (!it->it_data) { if (!it->it_data) {
it->it_data = g_try_malloc(cache->page_size); it->it_data = g_try_malloc(cache->page_size);
@ -174,7 +183,7 @@ int cache_insert(PageCache *cache, uint64_t addr, const uint8_t *pdata)
memcpy(it->it_data, pdata, cache->page_size); memcpy(it->it_data, pdata, cache->page_size);
it->it_age = ++cache->max_item_age; it->it_age = current_age;
it->it_addr = addr; it->it_addr = addr;
return 0; return 0;

View File

@ -60,16 +60,6 @@ static QEMUFile *open_test_file(bool write)
return qemu_fdopen(fd, write ? "wb" : "rb"); return qemu_fdopen(fd, write ? "wb" : "rb");
} }
/* Open a read-only qemu-file from an existing memory block */
static QEMUFile *open_mem_file_read(const void *data, size_t len)
{
/* The qsb gets freed by qemu_fclose */
QEMUSizedBuffer *qsb = qsb_create(data, len);
g_assert(qsb);
return qemu_bufopen("r", qsb);
}
/* /*
* Check that the contents of the memory-buffered file f match * Check that the contents of the memory-buffered file f match
* the given size/data. * the given size/data.
@ -450,7 +440,9 @@ static void test_load_noskip(void)
QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */ QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */
}; };
QEMUFile *loading = open_mem_file_read(buf, sizeof(buf)); QEMUSizedBuffer *qsb = qsb_create(buf, sizeof(buf));
g_assert(qsb);
QEMUFile *loading = qemu_bufopen("r", qsb);
TestStruct obj = { .skip_c_e = false }; TestStruct obj = { .skip_c_e = false };
vmstate_load_state(loading, &vmstate_skipping, &obj, 2); vmstate_load_state(loading, &vmstate_skipping, &obj, 2);
g_assert(!qemu_file_get_error(loading)); g_assert(!qemu_file_get_error(loading));
@ -461,6 +453,7 @@ static void test_load_noskip(void)
g_assert_cmpint(obj.e, ==, 50); g_assert_cmpint(obj.e, ==, 50);
g_assert_cmpint(obj.f, ==, 60); g_assert_cmpint(obj.f, ==, 60);
qemu_fclose(loading); qemu_fclose(loading);
qsb_free(qsb);
} }
static void test_load_skip(void) static void test_load_skip(void)
@ -473,7 +466,9 @@ static void test_load_skip(void)
QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */ QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */
}; };
QEMUFile *loading = open_mem_file_read(buf, sizeof(buf)); QEMUSizedBuffer *qsb = qsb_create(buf, sizeof(buf));
g_assert(qsb);
QEMUFile *loading = qemu_bufopen("r", qsb);
TestStruct obj = { .skip_c_e = true, .c = 300, .e = 500 }; TestStruct obj = { .skip_c_e = true, .c = 300, .e = 500 };
vmstate_load_state(loading, &vmstate_skipping, &obj, 2); vmstate_load_state(loading, &vmstate_skipping, &obj, 2);
g_assert(!qemu_file_get_error(loading)); g_assert(!qemu_file_get_error(loading));
@ -484,6 +479,7 @@ static void test_load_skip(void)
g_assert_cmpint(obj.e, ==, 500); g_assert_cmpint(obj.e, ==, 500);
g_assert_cmpint(obj.f, ==, 60); g_assert_cmpint(obj.f, ==, 60);
qemu_fclose(loading); qemu_fclose(loading);
qsb_free(qsb);
} }
int main(int argc, char **argv) int main(int argc, char **argv)