--- /dev/null
+From 8ba1bc13200b72a287554224b5aeb8cbf1bba156 Mon Sep 17 00:00:00 2001
+From: Wolfgang Bumiller <w.bumiller@proxmox.com>
+Date: Fri, 9 Sep 2016 15:21:19 +0200
+Subject: [PATCH 41/41] savevm-async updates
+
+---
+ savevm-async.c | 99 +++++++++++++++++++++++++++++++++++++---------------------
+ 1 file changed, 63 insertions(+), 36 deletions(-)
+
+diff --git a/savevm-async.c b/savevm-async.c
+index 76cd8fa..fddb18c 100644
+--- a/savevm-async.c
++++ b/savevm-async.c
+@@ -20,6 +20,8 @@
+
+ /* #define DEBUG_SAVEVM_STATE */
+
++#define NOT_DONE 0x7fffffff /* used while emulated sync operation in progress */
++
+ #ifdef DEBUG_SAVEVM_STATE
+ #define DPRINTF(fmt, ...) \
+ do { printf("savevm-async: " fmt, ## __VA_ARGS__); } while (0)
+@@ -38,7 +40,7 @@ enum {
+
+
+ static struct SnapshotState {
+- BlockDriverState *bs;
++ BlockBackend *target;
+ size_t bs_pos;
+ int state;
+ Error *error;
+@@ -99,17 +101,17 @@ static int save_snapshot_cleanup(void)
+ ret = qemu_fclose(snap_state.file);
+ }
+
+- if (snap_state.bs) {
++ if (snap_state.target) {
+ /* try to truncate, but ignore errors (will fail on block devices).
+ * note: bdrv_read() need whole blocks, so we round up
+ */
+ size_t size = (snap_state.bs_pos + BDRV_SECTOR_SIZE) & BDRV_SECTOR_MASK;
+- bdrv_truncate(snap_state.bs, size);
+- bdrv_op_unblock_all(snap_state.bs, snap_state.blocker);
++ blk_truncate(snap_state.target, size);
++ blk_op_unblock_all(snap_state.target, snap_state.blocker);
+ error_free(snap_state.blocker);
+ snap_state.blocker = NULL;
+- bdrv_unref(snap_state.bs);
+- snap_state.bs = NULL;
++ blk_unref(snap_state.target);
++ snap_state.target = NULL;
+ }
+
+ return ret;
+@@ -151,21 +153,49 @@ static void save_snapshot_completed(void)
+ static int block_state_close(void *opaque)
+ {
+ snap_state.file = NULL;
+- return bdrv_flush(snap_state.bs);
++ return blk_flush(snap_state.target);
+ }
+
+-static ssize_t block_state_put_buffer(void *opaque, const uint8_t *buf,
+- int64_t pos, size_t size)
++typedef struct BlkRwCo {
++ int64_t offset;
++ QEMUIOVector *qiov;
++ int ret;
++} BlkRwCo;
++
++static void block_state_write_entry(void *opaque) {
++ BlkRwCo *rwco = opaque;
++ rwco->ret = blk_co_pwritev(snap_state.target, rwco->offset, rwco->qiov->size,
++ rwco->qiov, 0);
++}
++
++static ssize_t block_state_writev_buffer(void *opaque, struct iovec *iov,
++ int iovcnt, int64_t pos)
+ {
+- ssize_t ret;
++ AioContext *aio_context;
++ QEMUIOVector qiov;
++ Coroutine *co;
++ BlkRwCo rwco;
++
++ qemu_iovec_init_external(&qiov, iov, iovcnt);
++
++ rwco = (BlkRwCo) {
++ .offset = pos,
++ .qiov = &qiov,
++ .ret = NOT_DONE,
++ };
+
+- assert(pos == snap_state.bs_pos);
++ co = qemu_coroutine_create(&block_state_write_entry, &rwco);
++ qemu_coroutine_enter(co);
+
+- if ((ret = bdrv_pwrite(snap_state.bs, snap_state.bs_pos, buf, size)) > 0) {
+- snap_state.bs_pos += ret;
++ aio_context = blk_get_aio_context(snap_state.target);
++ while (rwco.ret == NOT_DONE) {
++ aio_poll(aio_context, true);
+ }
+
+- return ret;
++ if (rwco.ret < 0) {
++ return rwco.ret;
++ }
++ return qiov.size;
+ }
+
+ static int store_and_stop(void) {
+@@ -227,7 +257,7 @@ static void process_savevm_co(void *opaque)
+ /* stop the VM if we get to the end of available space,
+ * or if pending_size is just a few MB
+ */
+- maxlen = bdrv_getlength(snap_state.bs) - 30*1024*1024;
++ maxlen = blk_getlength(snap_state.target) - 30*1024*1024;
+ if ((pending_size < 100000) ||
+ ((snap_state.bs_pos + pending_size) >= maxlen)) {
+ if (store_and_stop())
+@@ -244,7 +274,7 @@ static void process_savevm_co(void *opaque)
+ }
+
+ static const QEMUFileOps block_file_ops = {
+- .put_buffer = block_state_put_buffer,
++ .writev_buffer = block_state_writev_buffer,
+ .close = block_state_close,
+ };
+
+@@ -254,7 +284,6 @@ void qmp_savevm_start(bool has_statefile, const char *statefile, Error **errp)
+ Error *local_err = NULL;
+
+ int bdrv_oflags = BDRV_O_RDWR | BDRV_O_NO_FLUSH;
+- int ret;
+
+ if (snap_state.state != SAVE_STATE_DONE) {
+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
+@@ -284,13 +313,11 @@ void qmp_savevm_start(bool has_statefile, const char *statefile, Error **errp)
+ }
+
+ /* Open the image */
+- snap_state.bs = bdrv_new();
+-
+ QDict *options = NULL;
+ options = qdict_new();
+ qdict_put(options, "driver", qstring_from_str("raw"));
+- ret = bdrv_open(&snap_state.bs, statefile, NULL, options, bdrv_oflags, &local_err);
+- if (ret < 0) {
++ snap_state.target = blk_new_open(statefile, NULL, options, bdrv_oflags, &local_err);
++ if (!snap_state.target) {
+ error_set(errp, ERROR_CLASS_GENERIC_ERROR, "failed to open '%s'", statefile);
+ goto restart;
+ }
+@@ -304,9 +331,9 @@ void qmp_savevm_start(bool has_statefile, const char *statefile, Error **errp)
+
+
+ error_setg(&snap_state.blocker, "block device is in use by savevm");
+- bdrv_op_block_all(snap_state.bs, snap_state.blocker);
++ blk_op_block_all(snap_state.target, snap_state.blocker);
+
+- Coroutine *co = qemu_coroutine_create(process_savevm_co);
++ Coroutine *co = qemu_coroutine_create(process_savevm_co, NULL);
+ qemu_coroutine_enter(co);
+
+ return;
+@@ -457,8 +484,8 @@ void qmp_delete_drive_snapshot(const char *device, const char *name,
+ static ssize_t loadstate_get_buffer(void *opaque, uint8_t *buf, int64_t pos,
+ size_t size)
+ {
+- BlockDriverState *bs = (BlockDriverState *)opaque;
+- int64_t maxlen = bdrv_getlength(bs);
++ BlockBackend *be = opaque;
++ int64_t maxlen = blk_getlength(be);
+ if (pos > maxlen) {
+ return -EIO;
+ }
+@@ -468,7 +495,7 @@ static ssize_t loadstate_get_buffer(void *opaque, uint8_t *buf, int64_t pos,
+ if (size == 0) {
+ return 0;
+ }
+- return bdrv_pread(bs, pos, buf, size);
++ return blk_pread(be, pos, buf, size);
+ }
+
+ static const QEMUFileOps loadstate_file_ops = {
+@@ -477,25 +504,25 @@ static const QEMUFileOps loadstate_file_ops = {
+
+ int load_state_from_blockdev(const char *filename)
+ {
+- BlockDriverState *bs = NULL;
++ BlockBackend *be;
+ Error *local_err = NULL;
+ Error *blocker = NULL;
+
+ QEMUFile *f;
+ int ret;
+
+- bs = bdrv_new();
+- ret = bdrv_open(&bs, filename, NULL, NULL, 0, &local_err);
+- error_setg(&blocker, "block device is in use by load state");
+- bdrv_op_block_all(bs, blocker);
++ be = blk_new_open(filename, NULL, NULL, 0, &local_err);
+
+- if (ret < 0) {
++ if (!be) {
+ error_report("Could not open VM state file");
+ goto the_end;
+ }
+
++ error_setg(&blocker, "block device is in use by load state");
++ blk_op_block_all(be, blocker);
++
+ /* restore the VM state */
+- f = qemu_fopen_ops(bs, &loadstate_file_ops);
++ f = qemu_fopen_ops(be, &loadstate_file_ops);
+ if (!f) {
+ error_report("Could not open VM state file");
+ ret = -EINVAL;
+@@ -516,10 +543,10 @@ int load_state_from_blockdev(const char *filename)
+ ret = 0;
+
+ the_end:
+- if (bs) {
+- bdrv_op_unblock_all(bs, blocker);
++ if (be) {
++ blk_op_unblock_all(be, blocker);
+ error_free(blocker);
+- bdrv_unref(bs);
++ blk_unref(be);
+ }
+ return ret;
+ }
+--
+2.1.4
+