From 27af7d6ea5015e5ef1f7985eab94a8a218267a2b Mon Sep 17 00:00:00 2001 From: ChenLiang Date: Mon, 24 Nov 2014 19:55:47 +0800 Subject: xbzrle: optimize XBZRLE to decrease the cache misses Avoid hot pages being replaced by others to remarkably decrease cache misses Sample results with the test program which quote from xbzrle.txt ran in vm:(migrate bandwidth:1GE and xbzrle cache size 8MB) the test program: include include int main() { char *buf = (char *) calloc(4096, 4096); while (1) { int i; for (i = 0; i < 4096 * 4; i++) { buf[i * 4096 / 4]++; } printf("."); } } before this patch: virsh qemu-monitor-command test_vm '{"execute": "query-migrate"}' {"return":{"expected-downtime":1020,"xbzrle-cache":{"bytes":1108284, "cache-size":8388608,"cache-miss-rate":0.987013,"pages":18297,"overflow":8, "cache-miss":1228737},"status":"active","setup-time":10,"total-time":52398, "ram":{"total":12466991104,"remaining":1695744,"mbps":935.559472, "transferred":5780760580,"dirty-sync-counter":271,"duplicate":2878530, "dirty-pages-rate":29130,"skipped":0,"normal-bytes":5748592640, "normal":1403465}},"id":"libvirt-706"} 18k pages sent compressed in 52 seconds. cache-miss-rate is 98.7%, totally miss. after optimizing: virsh qemu-monitor-command test_vm '{"execute": "query-migrate"}' {"return":{"expected-downtime":2054,"xbzrle-cache":{"bytes":5066763, "cache-size":8388608,"cache-miss-rate":0.485924,"pages":194823,"overflow":0, "cache-miss":210653},"status":"active","setup-time":11,"total-time":18729, "ram":{"total":12466991104,"remaining":3895296,"mbps":937.663549, "transferred":1615042219,"dirty-sync-counter":98,"duplicate":2869840, "dirty-pages-rate":58781,"skipped":0,"normal-bytes":1588404224, "normal":387794}},"id":"libvirt-266"} 194k pages sent compressed in 18 seconds. The value of cache-miss-rate decrease to 48.59%. Signed-off-by: ChenLiang Signed-off-by: Gonglei Reviewed-by: Eric Blake Signed-off-by: Amit Shah --- arch_init.c | 8 +++++--- docs/xbzrle.txt | 8 ++++++++ include/migration/page_cache.h | 10 +++++++--- page_cache.c | 23 +++++++++++++++++++---- 4 files changed, 39 insertions(+), 10 deletions(-) diff --git a/arch_init.c b/arch_init.c index cfedbf08af..89c8fa46bb 100644 --- a/arch_init.c +++ b/arch_init.c @@ -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 diff --git a/docs/xbzrle.txt b/docs/xbzrle.txt index cc3a26a91d..52c8511a4c 100644 --- a/docs/xbzrle.txt +++ b/docs/xbzrle.txt @@ -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. diff --git a/include/migration/page_cache.h b/include/migration/page_cache.h index 2d5ce2dd7a..10ed53274c 100644 --- a/include/migration/page_cache.h +++ b/include/migration/page_cache.h @@ -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 diff --git a/page_cache.c b/page_cache.c index 89bb1ec3a0..aa681923c6 100644 --- a/page_cache.c +++ b/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,7 +125,8 @@ static size_t cache_get_cache_pos(const PageCache *cache, return pos; } -bool cache_is_cached(const PageCache *cache, uint64_t addr) +bool cache_is_cached(const PageCache *cache, uint64_t addr, + uint64_t current_age) { size_t pos; @@ -131,7 +135,12 @@ bool cache_is_cached(const PageCache *cache, uint64_t addr) pos = cache_get_cache_pos(cache, addr); - return (cache->page_cache[pos].it_addr == addr); + if (cache->page_cache[pos].it_addr == addr) { + /* update the it_age when the cache hit */ + cache->page_cache[pos].it_age = current_age; + return true; + } + return false; } static CacheItem *cache_get_by_addr(const PageCache *cache, uint64_t addr) @@ -151,7 +160,8 @@ 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) +int cache_insert(PageCache *cache, uint64_t addr, const uint8_t *pdata, + uint64_t current_age) { CacheItem *it = NULL; @@ -162,6 +172,11 @@ int cache_insert(PageCache *cache, uint64_t addr, const uint8_t *pdata) /* 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 +189,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; -- cgit v1.2.3 From 1b826f277814dd9496fe3cc71cbe6ab7b203cadf Mon Sep 17 00:00:00 2001 From: ChenLiang Date: Mon, 24 Nov 2014 19:55:48 +0800 Subject: xbzrle: rebuild the cache_is_cached function Rebuild the cache_is_cached function by cache_get_by_addr. And drops the asserts because the caller is also asserting the same thing. Signed-off-by: ChenLiang Signed-off-by: Gonglei Reviewed-by: Dr. David Alan Gilbert Signed-off-by: Amit Shah --- page_cache.c | 38 ++++++++++++++++---------------------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/page_cache.c b/page_cache.c index aa681923c6..cf8878d1d7 100644 --- a/page_cache.c +++ b/page_cache.c @@ -125,24 +125,6 @@ static size_t cache_get_cache_pos(const PageCache *cache, return pos; } -bool cache_is_cached(const PageCache *cache, uint64_t addr, - uint64_t current_age) -{ - size_t pos; - - g_assert(cache); - g_assert(cache->page_cache); - - pos = cache_get_cache_pos(cache, addr); - - if (cache->page_cache[pos].it_addr == addr) { - /* update the it_age when the cache hit */ - cache->page_cache[pos].it_age = current_age; - return true; - } - return false; -} - static CacheItem *cache_get_by_addr(const PageCache *cache, uint64_t addr) { size_t pos; @@ -160,14 +142,26 @@ uint8_t *get_cached_data(const PageCache *cache, uint64_t addr) return cache_get_by_addr(cache, addr)->it_data; } +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); -- cgit v1.2.3 From f018d8cd2123f495300935d5019931abbee4e5d9 Mon Sep 17 00:00:00 2001 From: Yang Hongyang Date: Fri, 19 Dec 2014 11:38:05 +0800 Subject: QEMUSizedBuffer: only free qsb that qemu_bufopen allocated Only free qsb that qemu_bufopen allocated, and also allow qemu_bufopen accept qsb as input for write operation. It will make the API more logical: 1.If you create the QEMUSizedBuffer yourself, you need to free it by using qsb_free() but not depends on other API like qemu_fclose. 2.allow qemu_bufopen() accept QEMUSizedBuffer as input for write operation, otherwise, it will be a little strange for this API won't accept the second parameter. This brings API change, since there are only 3 users of this API currently, this change only impact the first one which will be fixed in patch 2 of this patchset, so I think it is safe to do this change. 1 70 tests/test-vmstate.c <> return qemu_bufopen("r", qsb); 2 404 tests/test-vmstate.c <> QEMUFile *fsave = qemu_bufopen("w", NULL); 3 424 tests/test-vmstate.c <> QEMUFile *fsave = qemu_bufopen("w", NULL); Signed-off-by: Yang Hongyang Cc: Dr. David Alan Gilbert Cc: Juan Quintela Cc: Amit Shah Reviewed-by: Dr. David Alan Gilbert Signed-off-by: Amit Shah --- migration/qemu-file-buf.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/migration/qemu-file-buf.c b/migration/qemu-file-buf.c index d33dd44747..e97e0bd655 100644 --- a/migration/qemu-file-buf.c +++ b/migration/qemu-file-buf.c @@ -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); -- cgit v1.2.3 From 8580b06498a5dffe554e7ac627726b1d7775c591 Mon Sep 17 00:00:00 2001 From: Yang Hongyang Date: Fri, 19 Dec 2014 11:38:06 +0800 Subject: Tests: QEMUSizedBuffer/QEMUBuffer Modify some of tests/test-vmstate.c due to qemu_bufopen() change. If you create a QEMUSizedBuffer yourself, you have to explicitly free it. Signed-off-by: Yang Hongyang Cc: Dr. David Alan Gilbert Cc: Juan Quintela Cc: Amit Shah Reviewed-by: Dr. David Alan Gilbert Signed-off-by: Amit Shah --- tests/test-vmstate.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/tests/test-vmstate.c b/tests/test-vmstate.c index 5e0fd13cc4..39b7b01734 100644 --- a/tests/test-vmstate.c +++ b/tests/test-vmstate.c @@ -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) -- cgit v1.2.3 From e1a8c9b67fc97d293211773edcae9e8e2f3367ab Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Thu, 8 Jan 2015 11:11:30 +0000 Subject: socket shutdown Add QEMUFile interface to allow a socket to be 'shut down' - i.e. any reads/writes will fail (and any blocking read/write will be woken). Signed-off-by: Dr. David Alan Gilbert Reviewed-by: Paolo Bonzini Reviewed-by: Amit Shah Signed-off-by: Amit Shah --- include/migration/qemu-file.h | 10 ++++++++++ include/qemu/sockets.h | 7 +++++++ migration/qemu-file-unix.c | 23 +++++++++++++++++++---- migration/qemu-file.c | 12 ++++++++++++ 4 files changed, 48 insertions(+), 4 deletions(-) diff --git a/include/migration/qemu-file.h b/include/migration/qemu-file.h index 401676bf4d..d843c0010c 100644 --- a/include/migration/qemu-file.h +++ b/include/migration/qemu-file.h @@ -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) diff --git a/include/qemu/sockets.h b/include/qemu/sockets.h index f47dae614a..7992ece72a 100644 --- a/include/qemu/sockets.h +++ b/include/qemu/sockets.h @@ -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 */ diff --git a/migration/qemu-file-unix.c b/migration/qemu-file-unix.c index 9682396d97..bfbc0861ab 100644 --- a/migration/qemu-file-unix.c +++ b/migration/qemu-file-unix.c @@ -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) diff --git a/migration/qemu-file.c b/migration/qemu-file.c index a7f2a34430..edc283073a 100644 --- a/migration/qemu-file.c +++ b/migration/qemu-file.c @@ -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 || -- cgit v1.2.3 From 131fe9b843f9a1e55fcbf2457c9cb25c3711b9d8 Mon Sep 17 00:00:00 2001 From: Cristian Klein Date: Thu, 8 Jan 2015 11:11:31 +0000 Subject: Handle bi-directional communication for fd migration libvirt prefers opening the TCP connection itself, for two reasons. First, connection failed errors can be detected easier, without having to parse qemu's error output. Second, libvirt might be asked to secure the transfer by tunnelling the communication through an TLS layer. Therefore, libvirt opens the TCP connection itself and passes an FD to qemu using QMP and a POSIX-specific mechanism. Hence, in order to make the reverse-path work in such cases, qemu needs to distinguish if the transmitted FD is a socket (reverse-path available) or not (reverse-path might not be available) and use the corresponding abstraction. Signed-off-by: Cristian Klein Reviewed-by: Paolo Bonzini Reviewed-by: Amit Shah Signed-off-by: Amit Shah --- migration/fd.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/migration/fd.c b/migration/fd.c index d2e523af74..129da9910b 100644 --- a/migration/fd.c +++ b/migration/fd.c @@ -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; -- cgit v1.2.3 From a26ba26e214911dc879a23e797d2c269cdb38577 Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Thu, 8 Jan 2015 11:11:32 +0000 Subject: migration_cancel: shutdown migration socket Force shutdown on migration socket on cancel to cause the cancel to complete even if the socket is blocked on a dead network. Signed-off-by: Dr. David Alan Gilbert Reviewed-by: Paolo Bonzini Reviewed-by: Amit Shah Signed-off-by: Amit Shah --- migration/migration.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/migration/migration.c b/migration/migration.c index c49a05a165..b3adbc653a 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -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) -- cgit v1.2.3 From ea987c2c21d4326bb58ee28f6888fdcf8fbda067 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 7 Jan 2015 15:12:13 +0100 Subject: vmstate: type-check sub-arrays While we cannot check against the type of the full array, we can check against the type of the fields. Signed-off-by: Paolo Bonzini Signed-off-by: Amit Shah --- include/migration/vmstate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h index e45fc49cb1..d712a651ca 100644 --- a/include/migration/vmstate.h +++ b/include/migration/vmstate.h @@ -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, \ -- cgit v1.2.3