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:
commit
e68cba3636
@ -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
|
||||
* 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
|
||||
@ -358,10 +359,11 @@ static int save_xbzrle_page(QEMUFile *f, uint8_t **current_data,
|
||||
int encoded_len = 0, bytes_sent = -1;
|
||||
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++;
|
||||
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;
|
||||
} else {
|
||||
/* update *current_data when the page has been
|
||||
|
@ -71,6 +71,14 @@ encoded buffer:
|
||||
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
|
||||
|
||||
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
|
||||
======================
|
||||
1. Verify the destination QEMU version is able to decode the new format.
|
||||
|
@ -43,8 +43,10 @@ void cache_fini(PageCache *cache);
|
||||
*
|
||||
* @cache pointer to the PageCache struct
|
||||
* @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
|
||||
@ -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
|
||||
* 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
|
||||
* @addr: page address
|
||||
* @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
|
||||
|
@ -84,6 +84,14 @@ typedef size_t (QEMURamSaveFunc)(QEMUFile *f, void *opaque,
|
||||
size_t size,
|
||||
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 {
|
||||
QEMUFilePutBufferFunc *put_buffer;
|
||||
QEMUFileGetBufferFunc *get_buffer;
|
||||
@ -94,6 +102,7 @@ typedef struct QEMUFileOps {
|
||||
QEMURamHookFunc *after_ram_iterate;
|
||||
QEMURamHookFunc *hook_ram_load;
|
||||
QEMURamSaveFunc *save_page;
|
||||
QEMUFileShutdownFunc *shut_down;
|
||||
} QEMUFileOps;
|
||||
|
||||
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);
|
||||
int qemu_file_get_error(QEMUFile *f);
|
||||
void qemu_file_set_error(QEMUFile *f, int ret);
|
||||
int qemu_file_shutdown(QEMUFile *f);
|
||||
void qemu_fflush(QEMUFile *f);
|
||||
|
||||
static inline void qemu_put_be64s(QEMUFile *f, const uint64_t *pv)
|
||||
|
@ -189,7 +189,7 @@ extern const VMStateInfo vmstate_info_bitmap;
|
||||
type_check_2darray(_type, typeof_field(_state, _field), _n1, _n2))
|
||||
|
||||
#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) \
|
||||
vmstate_offset_array(_state, _field, uint8_t, \
|
||||
|
@ -44,6 +44,13 @@ int socket_set_fast_reuse(int fd);
|
||||
int send_all(int fd, const void *buf, int len1);
|
||||
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
|
||||
* valid fd on success, negative error code on failure
|
||||
*/
|
||||
|
@ -31,13 +31,29 @@
|
||||
do { } while (0)
|
||||
#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)
|
||||
{
|
||||
int fd = monitor_get_fd(cur_mon, fdname, errp);
|
||||
if (fd == -1) {
|
||||
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);
|
||||
}
|
||||
@ -58,7 +74,11 @@ void fd_start_incoming_migration(const char *infd, Error **errp)
|
||||
DPRINTF("Attempting to start an incoming migration via fd\n");
|
||||
|
||||
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) {
|
||||
error_setg_errno(errp, errno, "failed to open the source descriptor");
|
||||
return;
|
||||
|
@ -330,6 +330,7 @@ void migrate_fd_error(MigrationState *s)
|
||||
static void migrate_fd_cancel(MigrationState *s)
|
||||
{
|
||||
int old_state ;
|
||||
QEMUFile *f = migrate_get_current()->file;
|
||||
trace_migrate_fd_cancel();
|
||||
|
||||
do {
|
||||
@ -339,6 +340,17 @@ static void migrate_fd_cancel(MigrationState *s)
|
||||
}
|
||||
migrate_set_state(s, old_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)
|
||||
|
@ -395,6 +395,7 @@ QEMUSizedBuffer *qsb_clone(const QEMUSizedBuffer *qsb)
|
||||
typedef struct QEMUBuffer {
|
||||
QEMUSizedBuffer *qsb;
|
||||
QEMUFile *file;
|
||||
bool qsb_allocated;
|
||||
} QEMUBuffer;
|
||||
|
||||
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;
|
||||
|
||||
qsb_free(s->qsb);
|
||||
if (s->qsb_allocated) {
|
||||
qsb_free(s->qsb);
|
||||
}
|
||||
|
||||
g_free(s);
|
||||
|
||||
@ -463,12 +466,11 @@ QEMUFile *qemu_bufopen(const char *mode, QEMUSizedBuffer *input)
|
||||
}
|
||||
|
||||
s = g_malloc0(sizeof(QEMUBuffer));
|
||||
if (mode[0] == 'r') {
|
||||
s->qsb = input;
|
||||
}
|
||||
s->qsb = input;
|
||||
|
||||
if (s->qsb == NULL) {
|
||||
s->qsb = qsb_create(NULL, 0);
|
||||
s->qsb_allocated = true;
|
||||
}
|
||||
if (!s->qsb) {
|
||||
g_free(s);
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "qemu/sockets.h"
|
||||
#include "block/coroutine.h"
|
||||
#include "migration/qemu-file.h"
|
||||
#include "migration/qemu-file-internal.h"
|
||||
|
||||
typedef struct QEMUFileSocket {
|
||||
int fd;
|
||||
@ -84,6 +85,17 @@ static int socket_close(void *opaque)
|
||||
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,
|
||||
int64_t pos)
|
||||
{
|
||||
@ -192,15 +204,18 @@ QEMUFile *qemu_fdopen(int fd, const char *mode)
|
||||
}
|
||||
|
||||
static const QEMUFileOps socket_read_ops = {
|
||||
.get_fd = socket_get_fd,
|
||||
.get_fd = socket_get_fd,
|
||||
.get_buffer = socket_get_buffer,
|
||||
.close = socket_close
|
||||
.close = socket_close,
|
||||
.shut_down = socket_shutdown
|
||||
|
||||
};
|
||||
|
||||
static const QEMUFileOps socket_write_ops = {
|
||||
.get_fd = socket_get_fd,
|
||||
.get_fd = socket_get_fd,
|
||||
.writev_buffer = socket_writev_buffer,
|
||||
.close = socket_close
|
||||
.close = socket_close,
|
||||
.shut_down = socket_shutdown
|
||||
};
|
||||
|
||||
QEMUFile *qemu_fopen_socket(int fd, const char *mode)
|
||||
|
@ -30,6 +30,18 @@
|
||||
#include "migration/qemu-file-internal.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)
|
||||
{
|
||||
if (mode == NULL ||
|
||||
|
45
page_cache.c
45
page_cache.c
@ -33,6 +33,9 @@
|
||||
do { } while (0)
|
||||
#endif
|
||||
|
||||
/* the page in cache will not be replaced in two cycles */
|
||||
#define CACHED_PAGE_LIFETIME 2
|
||||
|
||||
typedef struct CacheItem CacheItem;
|
||||
|
||||
struct CacheItem {
|
||||
@ -122,18 +125,6 @@ static size_t cache_get_cache_pos(const PageCache *cache,
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
g_assert(cache);
|
||||
g_assert(cache->page_cache);
|
||||
CacheItem *it;
|
||||
|
||||
/* actual update of entry */
|
||||
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 */
|
||||
if (!it->it_data) {
|
||||
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);
|
||||
|
||||
it->it_age = ++cache->max_item_age;
|
||||
it->it_age = current_age;
|
||||
it->it_addr = addr;
|
||||
|
||||
return 0;
|
||||
|
@ -60,16 +60,6 @@ static QEMUFile *open_test_file(bool write)
|
||||
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
|
||||
* 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 */
|
||||
};
|
||||
|
||||
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 };
|
||||
vmstate_load_state(loading, &vmstate_skipping, &obj, 2);
|
||||
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.f, ==, 60);
|
||||
qemu_fclose(loading);
|
||||
qsb_free(qsb);
|
||||
}
|
||||
|
||||
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 */
|
||||
};
|
||||
|
||||
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 };
|
||||
vmstate_load_state(loading, &vmstate_skipping, &obj, 2);
|
||||
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.f, ==, 60);
|
||||
qemu_fclose(loading);
|
||||
qsb_free(qsb);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
|
Loading…
Reference in New Issue
Block a user