[pve-devel] [PATCH] internal snasphot async port to qemu 1.7 v4
Stefan Priebe
s.priebe at profihost.ag
Sat Nov 30 13:47:37 CET 2013
This one works completely fine for me and fixes some bugs in the code
which does not work anymore with newer qemu versions.
1.) use brdv_unref (this implies brdv_delete) instead of bdrv_delete
2.) always mark a bs as in use or not in use before begin / stop work
3.) qemu_savevm_state_complete was called twice which resulted in a
segfault. Also the return code has no sense to detect an error.
Stefan
Am 30.11.2013 13:43, schrieb Stefan Priebe:
> From: Alexandre Derumier <aderumier at odiso.com>
>
> Signed-off-by: Stefan Priebe <s.priebe at profihost.ag>
> ---
> .../patches/internal-snapshot-async-qemu1.7.patch | 948 ++++++++++++++++++++
> debian/patches/internal-snapshot-async.patch | 572 +++++++-----
> debian/patches/series | 5 +-
> 3 files changed, 1314 insertions(+), 211 deletions(-)
> create mode 100644 debian/patches/internal-snapshot-async-qemu1.7.patch
>
> diff --git a/debian/patches/internal-snapshot-async-qemu1.7.patch b/debian/patches/internal-snapshot-async-qemu1.7.patch
> new file mode 100644
> index 0000000..7022538
> --- /dev/null
> +++ b/debian/patches/internal-snapshot-async-qemu1.7.patch
> @@ -0,0 +1,948 @@
> +From 46fd4bb673a91d40352c95e9d3f62f63b5021053 Mon Sep 17 00:00:00 2001
> +From: Stefan Priebe <s.priebe at profihost.ag>
> +Date: Fri, 29 Nov 2013 22:17:03 +0100
> +Subject: [PATCH] internal-snapshot-async-qemu1.7.patch
> +
> +---
> + Makefile.objs | 1 +
> + block.c | 2 +-
> + hmp-commands.hx | 34 ++++
> + hmp.c | 57 ++++++
> + hmp.h | 5 +
> + include/block/block.h | 1 +
> + include/sysemu/sysemu.h | 5 +-
> + monitor.c | 7 +
> + qapi-schema.json | 46 +++++
> + qemu-options.hx | 13 ++
> + qmp-commands.hx | 31 +++
> + savevm-async.c | 478 +++++++++++++++++++++++++++++++++++++++++++++++
> + savevm.c | 10 +-
> + vl.c | 9 +
> + 14 files changed, 692 insertions(+), 7 deletions(-)
> + create mode 100644 savevm-async.c
> +
> +diff --git a/Makefile.objs b/Makefile.objs
> +index 2b6c1fe..cce2562 100644
> +--- a/Makefile.objs
> ++++ b/Makefile.objs
> +@@ -55,6 +55,7 @@ common-obj-$(CONFIG_RDMA) += migration-rdma.o
> + common-obj-y += qemu-char.o #aio.o
> + common-obj-y += block-migration.o
> + common-obj-y += page_cache.o xbzrle.o
> ++common-obj-y += savevm-async.o
> +
> + common-obj-$(CONFIG_POSIX) += migration-exec.o migration-unix.o migration-fd.o
> +
> +diff --git a/block.c b/block.c
> +index 382ea71..249cb82 100644
> +--- a/block.c
> ++++ b/block.c
> +@@ -1710,7 +1710,7 @@ void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top)
> + bs_new->drv ? bs_new->drv->format_name : "");
> + }
> +
> +-static void bdrv_delete(BlockDriverState *bs)
> ++void bdrv_delete(BlockDriverState *bs)
> + {
> + assert(!bs->dev);
> + assert(!bs->job);
> +diff --git a/hmp-commands.hx b/hmp-commands.hx
> +index caae5ad..5dbe6d0 100644
> +--- a/hmp-commands.hx
> ++++ b/hmp-commands.hx
> +@@ -1698,6 +1698,8 @@ show migration status
> + show current migration capabilities
> + @item info migrate_cache_size
> + show current migration XBZRLE cache size
> ++ at item info savevm
> ++show savevm status
> + @item info balloon
> + show balloon information
> + @item info qtree
> +@@ -1719,3 +1721,35 @@ ETEXI
> + STEXI
> + @end table
> + ETEXI
> ++
> ++ {
> ++ .name = "savevm-start",
> ++ .args_type = "statefile:s?",
> ++ .params = "[statefile]",
> ++ .help = "Prepare for snapshot and halt VM. Save VM state to statefile.",
> ++ .mhandler.cmd = hmp_savevm_start,
> ++ },
> ++
> ++ {
> ++ .name = "snapshot-drive",
> ++ .args_type = "device:s,name:s",
> ++ .params = "device name",
> ++ .help = "Create internal snapshot.",
> ++ .mhandler.cmd = hmp_snapshot_drive,
> ++ },
> ++
> ++ {
> ++ .name = "delete-drive-snapshot",
> ++ .args_type = "device:s,name:s",
> ++ .params = "device name",
> ++ .help = "Delete internal snapshot.",
> ++ .mhandler.cmd = hmp_delete_drive_snapshot,
> ++ },
> ++
> ++ {
> ++ .name = "savevm-end",
> ++ .args_type = "",
> ++ .params = "",
> ++ .help = "Resume VM after snaphot.",
> ++ .mhandler.cmd = hmp_savevm_end,
> ++ },
> +diff --git a/hmp.c b/hmp.c
> +index c2efa8d..60d6a8e 100644
> +--- a/hmp.c
> ++++ b/hmp.c
> +@@ -1592,3 +1592,60 @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict)
> +
> + hmp_handle_error(mon, &err);
> + }
> ++
> ++void hmp_savevm_start(Monitor *mon, const QDict *qdict)
> ++{
> ++ Error *errp = NULL;
> ++ const char *statefile = qdict_get_try_str(qdict, "statefile");
> ++
> ++ qmp_savevm_start(statefile != NULL, statefile, &errp);
> ++ hmp_handle_error(mon, &errp);
> ++}
> ++
> ++void hmp_snapshot_drive(Monitor *mon, const QDict *qdict)
> ++{
> ++ Error *errp = NULL;
> ++ const char *name = qdict_get_str(qdict, "name");
> ++ const char *device = qdict_get_str(qdict, "device");
> ++
> ++ qmp_snapshot_drive(device, name, &errp);
> ++ hmp_handle_error(mon, &errp);
> ++}
> ++
> ++void hmp_delete_drive_snapshot(Monitor *mon, const QDict *qdict)
> ++{
> ++ Error *errp = NULL;
> ++ const char *name = qdict_get_str(qdict, "name");
> ++ const char *device = qdict_get_str(qdict, "device");
> ++
> ++ qmp_delete_drive_snapshot(device, name, &errp);
> ++ hmp_handle_error(mon, &errp);
> ++}
> ++
> ++void hmp_savevm_end(Monitor *mon, const QDict *qdict)
> ++{
> ++ Error *errp = NULL;
> ++
> ++ qmp_savevm_end(&errp);
> ++ hmp_handle_error(mon, &errp);
> ++}
> ++
> ++void hmp_info_savevm(Monitor *mon, const QDict *qdict)
> ++{
> ++ SaveVMInfo *info;
> ++ info = qmp_query_savevm(NULL);
> ++
> ++ if (info->has_status) {
> ++ monitor_printf(mon, "savevm status: %s\n", info->status);
> ++ monitor_printf(mon, "total time: %" PRIu64 " milliseconds\n",
> ++ info->total_time);
> ++ } else {
> ++ monitor_printf(mon, "savevm status: not running\n");
> ++ }
> ++ if (info->has_bytes) {
> ++ monitor_printf(mon, "Bytes saved: %"PRIu64"\n", info->bytes);
> ++ }
> ++ if (info->has_error) {
> ++ monitor_printf(mon, "Error: %s\n", info->error);
> ++ }
> ++}
> +diff --git a/hmp.h b/hmp.h
> +index 54cf71f..bcc1cb6 100644
> +--- a/hmp.h
> ++++ b/hmp.h
> +@@ -25,6 +25,7 @@ void hmp_info_status(Monitor *mon, const QDict *qdict);
> + void hmp_info_uuid(Monitor *mon, const QDict *qdict);
> + void hmp_info_chardev(Monitor *mon, const QDict *qdict);
> + void hmp_info_mice(Monitor *mon, const QDict *qdict);
> ++void hmp_info_savevm(Monitor *mon, const QDict *qdict);
> + void hmp_info_migrate(Monitor *mon, const QDict *qdict);
> + void hmp_info_migrate_capabilities(Monitor *mon, const QDict *qdict);
> + void hmp_info_migrate_cache_size(Monitor *mon, const QDict *qdict);
> +@@ -81,6 +82,10 @@ void hmp_netdev_add(Monitor *mon, const QDict *qdict);
> + void hmp_netdev_del(Monitor *mon, const QDict *qdict);
> + void hmp_getfd(Monitor *mon, const QDict *qdict);
> + void hmp_closefd(Monitor *mon, const QDict *qdict);
> ++void hmp_savevm_start(Monitor *mon, const QDict *qdict);
> ++void hmp_snapshot_drive(Monitor *mon, const QDict *qdict);
> ++void hmp_delete_drive_snapshot(Monitor *mon, const QDict *qdict);
> ++void hmp_savevm_end(Monitor *mon, const QDict *qdict);
> + void hmp_send_key(Monitor *mon, const QDict *qdict);
> + void hmp_screen_dump(Monitor *mon, const QDict *qdict);
> + void hmp_nbd_server_start(Monitor *mon, const QDict *qdict);
> +diff --git a/include/block/block.h b/include/block/block.h
> +index 3560deb..22c06bb 100644
> +--- a/include/block/block.h
> ++++ b/include/block/block.h
> +@@ -214,6 +214,7 @@ BlockDriverState *bdrv_find_backing_image(BlockDriverState *bs,
> + const char *backing_file);
> + int bdrv_get_backing_file_depth(BlockDriverState *bs);
> + int bdrv_truncate(BlockDriverState *bs, int64_t offset);
> ++void bdrv_delete(BlockDriverState *bs);
> + int64_t bdrv_getlength(BlockDriverState *bs);
> + int64_t bdrv_get_allocated_file_size(BlockDriverState *bs);
> + void bdrv_get_geometry(BlockDriverState *bs, uint64_t *nb_sectors_ptr);
> +diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h
> +index 495dae8..d2bb686 100644
> +--- a/include/sysemu/sysemu.h
> ++++ b/include/sysemu/sysemu.h
> +@@ -73,16 +73,17 @@ void qemu_add_machine_init_done_notifier(Notifier *notify);
> +
> + void do_savevm(Monitor *mon, const QDict *qdict);
> + int load_vmstate(const char *name);
> ++int load_state_from_blockdev(const char *filename);
> + void do_delvm(Monitor *mon, const QDict *qdict);
> + void do_info_snapshots(Monitor *mon, const QDict *qdict);
> +
> + void qemu_announce_self(void);
> +
> + bool qemu_savevm_state_blocked(Error **errp);
> +-void qemu_savevm_state_begin(QEMUFile *f,
> ++int qemu_savevm_state_begin(QEMUFile *f,
> + const MigrationParams *params);
> + int qemu_savevm_state_iterate(QEMUFile *f);
> +-void qemu_savevm_state_complete(QEMUFile *f);
> ++int qemu_savevm_state_complete(QEMUFile *f);
> + void qemu_savevm_state_cancel(void);
> + uint64_t qemu_savevm_state_pending(QEMUFile *f, uint64_t max_size);
> + int qemu_loadvm_state(QEMUFile *f);
> +diff --git a/monitor.c b/monitor.c
> +index 845f608..30cc965 100644
> +--- a/monitor.c
> ++++ b/monitor.c
> +@@ -2901,6 +2901,13 @@ static mon_cmd_t info_cmds[] = {
> + .mhandler.cmd = hmp_info_migrate_cache_size,
> + },
> + {
> ++ .name = "savevm",
> ++ .args_type = "",
> ++ .params = "",
> ++ .help = "show savevm status",
> ++ .mhandler.cmd = hmp_info_savevm,
> ++ },
> ++ {
> + .name = "balloon",
> + .args_type = "",
> + .params = "",
> +diff --git a/qapi-schema.json b/qapi-schema.json
> +index 7a36e99..1faf622 100644
> +--- a/qapi-schema.json
> ++++ b/qapi-schema.json
> +@@ -651,6 +651,42 @@
> + '*downtime': 'int',
> + '*setup-time': 'int'} }
> +
> ++
> ++# @SaveVMInfo
> ++#
> ++# Information about current migration process.
> ++#
> ++# @status: #optional string describing the current savevm status.
> ++# This can be 'active', 'completed', 'failed'.
> ++# If this field is not returned, no savevm process
> ++# has been initiated
> ++#
> ++# @error: #optional string containing error message is status is failed.
> ++#
> ++# @total-time: #optional total amount of milliseconds since savevm started.
> ++# If savevm has ended, it returns the total save time
> ++#
> ++# @bytes: #optional total amount of data transfered
> ++#
> ++# Since: 1.3
> ++##
> ++{ 'type': 'SaveVMInfo',
> ++ 'data': {'*status': 'str', '*error': 'str',
> ++ '*total-time': 'int', '*bytes': 'int'} }
> ++
> ++##
> ++# @query-savevm
> ++#
> ++# Returns information about current savevm process.
> ++#
> ++# Returns: @SaveVMInfo
> ++#
> ++# Since: 1.3
> ++##
> ++{ 'command': 'query-savevm', 'returns': 'SaveVMInfo' }
> ++
> ++##
> ++
> + ##
> + # @query-migrate
> + #
> +@@ -3347,8 +3383,18 @@
> + #
> + # Since: 1.2.0
> + ##
> ++
> + { 'command': 'query-target', 'returns': 'TargetInfo' }
> +
> ++{ 'command': 'savevm-start', 'data': { '*statefile': 'str' } }
> ++
> ++{ 'command': 'snapshot-drive', 'data': { 'device': 'str', 'name': 'str' } }
> ++
> ++{ 'command': 'delete-drive-snapshot', 'data': { 'device': 'str', 'name': 'str' } }
> ++
> ++{ 'command': 'savevm-end' }
> ++
> ++
> + ##
> + # @QKeyCode:
> + #
> +diff --git a/qemu-options.hx b/qemu-options.hx
> +index 51b1cd0..a2834da 100644
> +--- a/qemu-options.hx
> ++++ b/qemu-options.hx
> +@@ -2734,6 +2734,19 @@ STEXI
> + Start right away with a saved state (@code{loadvm} in monitor)
> + ETEXI
> +
> ++DEF("loadstate", HAS_ARG, QEMU_OPTION_loadstate, \
> ++ "-loadstate file\n" \
> ++ " start right away with a saved state\n",
> ++ QEMU_ARCH_ALL)
> ++STEXI
> ++ at item -loadstate @var{file}
> ++ at findex -loadstate
> ++Start right away with a saved state. This option does not rollback
> ++disk state like @code{loadvm}, so user must make sure that disk
> ++have correct state. @var{file} can be any valid device URL. See the section
> ++for "Device URL Syntax" for more information.
> ++ETEXI
> ++
> + #ifndef _WIN32
> + DEF("daemonize", 0, QEMU_OPTION_daemonize, \
> + "-daemonize daemonize QEMU after initializing\n", QEMU_ARCH_ALL)
> +diff --git a/qmp-commands.hx b/qmp-commands.hx
> +index 5584fe2..90a0d71 100644
> +--- a/qmp-commands.hx
> ++++ b/qmp-commands.hx
> +@@ -3308,3 +3308,34 @@ Example (2):
> + <- { "return": {} }
> +
> + EQMP
> ++
> ++
> ++ {
> ++ .name = "savevm-start",
> ++ .args_type = "statefile:s?",
> ++ .mhandler.cmd_new = qmp_marshal_input_savevm_start,
> ++ },
> ++
> ++ {
> ++ .name = "snapshot-drive",
> ++ .args_type = "device:s,name:s",
> ++ .mhandler.cmd_new = qmp_marshal_input_snapshot_drive,
> ++ },
> ++
> ++ {
> ++ .name = "delete-drive-snapshot",
> ++ .args_type = "device:s,name:s",
> ++ .mhandler.cmd_new = qmp_marshal_input_delete_drive_snapshot,
> ++ },
> ++
> ++ {
> ++ .name = "savevm-end",
> ++ .args_type = "",
> ++ .mhandler.cmd_new = qmp_marshal_input_savevm_end,
> ++ },
> ++
> ++ {
> ++ .name = "query-savevm",
> ++ .args_type = "",
> ++ .mhandler.cmd_new = qmp_marshal_input_query_savevm,
> ++ },
> +diff --git a/savevm-async.c b/savevm-async.c
> +new file mode 100644
> +index 0000000..64768b1
> +--- /dev/null
> ++++ b/savevm-async.c
> +@@ -0,0 +1,478 @@
> ++#include "qemu-common.h"
> ++#include "qapi/qmp/qerror.h"
> ++#include "sysemu/sysemu.h"
> ++#include "qmp-commands.h"
> ++#include "qemu-options.h"
> ++#include "migration/qemu-file.h"
> ++#include "qom/qom-qobject.h"
> ++#include "migration/migration.h"
> ++#include "block/snapshot.h"
> ++#include "block/qapi.h"
> ++#include "block/block.h"
> ++#include "qemu/timer.h"
> ++
> ++/* #define DEBUG_SAVEVM_STATE */
> ++
> ++#ifdef DEBUG_SAVEVM_STATE
> ++#define DPRINTF(fmt, ...) \
> ++ do { printf("savevm-async: " fmt, ## __VA_ARGS__); } while (0)
> ++#else
> ++#define DPRINTF(fmt, ...) \
> ++ do { } while (0)
> ++#endif
> ++
> ++enum {
> ++ SAVE_STATE_DONE,
> ++ SAVE_STATE_ERROR,
> ++ SAVE_STATE_ACTIVE,
> ++ SAVE_STATE_COMPLETED,
> ++};
> ++
> ++
> ++static struct SnapshotState {
> ++ BlockDriverState *bs;
> ++ size_t bs_pos;
> ++ int state;
> ++ Error *error;
> ++ int saved_vm_running;
> ++ QEMUFile *file;
> ++ int64_t total_time;
> ++} snap_state;
> ++
> ++SaveVMInfo *qmp_query_savevm(Error **errp)
> ++{
> ++ SaveVMInfo *info = g_malloc0(sizeof(*info));
> ++ struct SnapshotState *s = &snap_state;
> ++
> ++ if (s->state != SAVE_STATE_DONE) {
> ++ info->has_bytes = true;
> ++ info->bytes = s->bs_pos;
> ++ switch (s->state) {
> ++ case SAVE_STATE_ERROR:
> ++ info->has_status = true;
> ++ info->status = g_strdup("failed");
> ++ info->has_total_time = true;
> ++ info->total_time = s->total_time;
> ++ if (s->error) {
> ++ info->has_error = true;
> ++ info->error = g_strdup(error_get_pretty(s->error));
> ++ }
> ++ break;
> ++ case SAVE_STATE_ACTIVE:
> ++ info->has_status = true;
> ++ info->status = g_strdup("active");
> ++ info->has_total_time = true;
> ++ info->total_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME)
> ++ - s->total_time;
> ++ break;
> ++ case SAVE_STATE_COMPLETED:
> ++ info->has_status = true;
> ++ info->status = g_strdup("completed");
> ++ info->has_total_time = true;
> ++ info->total_time = s->total_time;
> ++ break;
> ++ }
> ++ }
> ++
> ++ return info;
> ++}
> ++
> ++static int save_snapshot_cleanup(void)
> ++{
> ++ int ret = 0;
> ++
> ++ DPRINTF("save_snapshot_cleanup\n");
> ++
> ++ snap_state.total_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) -
> ++ snap_state.total_time;
> ++
> ++ if (snap_state.file) {
> ++ ret = qemu_fclose(snap_state.file);
> ++ }
> ++
> ++ if (snap_state.bs) {
> ++ /* 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_set_in_use(snap_state.bs, 0);
> ++ bdrv_unref(snap_state.bs);
> ++ snap_state.bs = NULL;
> ++ }
> ++
> ++ return ret;
> ++}
> ++
> ++static void save_snapshot_error(const char *fmt, ...)
> ++{
> ++ va_list ap;
> ++ char *msg;
> ++
> ++ va_start(ap, fmt);
> ++ msg = g_strdup_vprintf(fmt, ap);
> ++ va_end(ap);
> ++
> ++ DPRINTF("save_snapshot_error: %s\n", msg);
> ++
> ++ if (!snap_state.error) {
> ++ error_set(&snap_state.error, ERROR_CLASS_GENERIC_ERROR, "%s", msg);
> ++ }
> ++
> ++ g_free (msg);
> ++
> ++ snap_state.state = SAVE_STATE_ERROR;
> ++
> ++ save_snapshot_cleanup();
> ++}
> ++
> ++static void save_snapshot_completed(void)
> ++{
> ++ DPRINTF("save_snapshot_completed\n");
> ++
> ++ if (save_snapshot_cleanup() < 0) {
> ++ snap_state.state = SAVE_STATE_ERROR;
> ++ } else {
> ++ snap_state.state = SAVE_STATE_COMPLETED;
> ++ }
> ++}
> ++
> ++static int block_state_close(void *opaque)
> ++{
> ++ snap_state.file = NULL;
> ++ return bdrv_flush(snap_state.bs);
> ++}
> ++
> ++static int block_state_put_buffer(void *opaque, const uint8_t *buf,
> ++ int64_t pos, int size)
> ++{
> ++ int ret;
> ++
> ++ assert(pos == snap_state.bs_pos);
> ++
> ++ if ((ret = bdrv_pwrite(snap_state.bs, snap_state.bs_pos, buf, size)) > 0) {
> ++ snap_state.bs_pos += ret;
> ++ }
> ++
> ++ return ret;
> ++}
> ++
> ++static void process_savevm_co(void *opaque)
> ++{
> ++ int ret;
> ++ int64_t maxlen;
> ++ MigrationParams params = {
> ++ .blk = 0,
> ++ .shared = 0
> ++ };
> ++
> ++ snap_state.state = SAVE_STATE_ACTIVE;
> ++
> ++ qemu_mutex_unlock_iothread();
> ++ ret = qemu_savevm_state_begin(snap_state.file, ¶ms);
> ++ qemu_mutex_lock_iothread();
> ++
> ++ if (ret < 0) {
> ++ save_snapshot_error("qemu_savevm_state_begin failed");
> ++ return;
> ++ }
> ++
> ++ while (snap_state.state == SAVE_STATE_ACTIVE) {
> ++ uint64_t pending_size;
> ++
> ++ pending_size = qemu_savevm_state_pending(snap_state.file, 0);
> ++
> ++ if (pending_size) {
> ++ ret = qemu_savevm_state_iterate(snap_state.file);
> ++ if (ret < 0) {
> ++ save_snapshot_error("qemu_savevm_state_iterate error %d", ret);
> ++ break;
> ++ }
> ++ DPRINTF("savevm inerate pending size %lu ret %d\n", pending_size, ret);
> ++ } else {
> ++ DPRINTF("done iterating\n");
> ++ if (runstate_is_running()) {
> ++ vm_stop(RUN_STATE_SAVE_VM);
> ++ }
> ++ DPRINTF("savevm inerate finished\n");
> ++ qemu_savevm_state_complete(snap_state.file);
> ++ DPRINTF("save complete\n");
> ++ save_snapshot_completed();
> ++ break;
> ++ }
> ++
> ++ /* 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;
> ++ if ((pending_size < 100000) ||
> ++ ((snap_state.bs_pos + pending_size) >= maxlen)) {
> ++ if (runstate_is_running()) {
> ++ vm_stop(RUN_STATE_SAVE_VM);
> ++ }
> ++ }
> ++ }
> ++}
> ++
> ++static const QEMUFileOps block_file_ops = {
> ++ .put_buffer = block_state_put_buffer,
> ++ .close = block_state_close,
> ++};
> ++
> ++
> ++void qmp_savevm_start(bool has_statefile, const char *statefile, Error **errp)
> ++{
> ++ BlockDriver *drv = NULL;
> ++ Error *local_err = NULL;
> ++
> ++ int bdrv_oflags = BDRV_O_CACHE_WB | BDRV_O_RDWR;
> ++ int ret;
> ++
> ++ if (snap_state.state != SAVE_STATE_DONE) {
> ++ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
> ++ "VM snapshot already started\n");
> ++ return;
> ++ }
> ++
> ++ /* initialize snapshot info */
> ++ snap_state.saved_vm_running = runstate_is_running();
> ++ snap_state.bs_pos = 0;
> ++ snap_state.total_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
> ++
> ++ if (snap_state.error) {
> ++ error_free(snap_state.error);
> ++ snap_state.error = NULL;
> ++ }
> ++
> ++ if (!has_statefile) {
> ++ vm_stop(RUN_STATE_SAVE_VM);
> ++ snap_state.state = SAVE_STATE_COMPLETED;
> ++ return;
> ++ }
> ++
> ++ if (qemu_savevm_state_blocked(errp)) {
> ++ return;
> ++ }
> ++
> ++ /* Open the image */
> ++ snap_state.bs = bdrv_new("vmstate");
> ++ ret = bdrv_open(snap_state.bs, statefile, NULL, bdrv_oflags, drv, &local_err);
> ++
> ++ if (ret < 0) {
> ++ error_set(errp, ERROR_CLASS_GENERIC_ERROR, "failed to open '%s'", statefile);
> ++ goto restart;
> ++ }
> ++
> ++ snap_state.file = qemu_fopen_ops(&snap_state, &block_file_ops);
> ++
> ++ if (!snap_state.file) {
> ++ error_set(errp, ERROR_CLASS_GENERIC_ERROR, "failed to open '%s'", statefile);
> ++ goto restart;
> ++ }
> ++
> ++
> ++ bdrv_set_in_use(snap_state.bs, 1);
> ++ bdrv_ref(snap_state.bs);
> ++
> ++ Coroutine *co = qemu_coroutine_create(process_savevm_co);
> ++ qemu_coroutine_enter(co, NULL);
> ++
> ++ return;
> ++
> ++restart:
> ++
> ++ save_snapshot_error("setup failed");
> ++
> ++ if (snap_state.saved_vm_running) {
> ++ vm_start();
> ++ }
> ++}
> ++
> ++void qmp_savevm_end(Error **errp)
> ++{
> ++ if (snap_state.state == SAVE_STATE_DONE) {
> ++ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
> ++ "VM snapshot not started\n");
> ++ return;
> ++ }
> ++
> ++ if (snap_state.saved_vm_running) {
> ++ vm_start();
> ++ }
> ++
> ++ snap_state.state = SAVE_STATE_DONE;
> ++}
> ++
> ++void qmp_snapshot_drive(const char *device, const char *name, Error **errp)
> ++{
> ++ BlockDriverState *bs;
> ++ QEMUSnapshotInfo sn1, *sn = &sn1;
> ++ int ret;
> ++#ifdef _WIN32
> ++ struct _timeb tb;
> ++#else
> ++ struct timeval tv;
> ++#endif
> ++
> ++ if (snap_state.state != SAVE_STATE_COMPLETED) {
> ++ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
> ++ "VM snapshot not ready/started\n");
> ++ return;
> ++ }
> ++
> ++ bs = bdrv_find(device);
> ++ if (!bs) {
> ++ error_set(errp, QERR_DEVICE_NOT_FOUND, device);
> ++ return;
> ++ }
> ++
> ++ if (!bdrv_is_inserted(bs)) {
> ++ error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device);
> ++ return;
> ++ }
> ++
> ++ if (bdrv_is_read_only(bs)) {
> ++ error_set(errp, QERR_DEVICE_IS_READ_ONLY, device);
> ++ return;
> ++ }
> ++
> ++ if (!bdrv_can_snapshot(bs)) {
> ++ error_set(errp, QERR_NOT_SUPPORTED);
> ++ return;
> ++ }
> ++
> ++ if (bdrv_snapshot_find(bs, sn, name) >= 0) {
> ++ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
> ++ "snapshot '%s' already exists", name);
> ++ return;
> ++ }
> ++
> ++ sn = &sn1;
> ++ memset(sn, 0, sizeof(*sn));
> ++
> ++#ifdef _WIN32
> ++ _ftime(&tb);
> ++ sn->date_sec = tb.time;
> ++ sn->date_nsec = tb.millitm * 1000000;
> ++#else
> ++ gettimeofday(&tv, NULL);
> ++ sn->date_sec = tv.tv_sec;
> ++ sn->date_nsec = tv.tv_usec * 1000;
> ++#endif
> ++ sn->vm_clock_nsec = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> ++
> ++ pstrcpy(sn->name, sizeof(sn->name), name);
> ++
> ++ sn->vm_state_size = 0; /* do not save state */
> ++
> ++ ret = bdrv_snapshot_create(bs, sn);
> ++ if (ret < 0) {
> ++ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
> ++ "Error while creating snapshot on '%s'\n", device);
> ++ return;
> ++ }
> ++}
> ++
> ++void qmp_delete_drive_snapshot(const char *device, const char *name,
> ++ Error **errp)
> ++{
> ++ BlockDriverState *bs;
> ++ QEMUSnapshotInfo sn1, *sn = &sn1;
> ++ Error *local_err = NULL;
> ++
> ++ int ret;
> ++
> ++ bs = bdrv_find(device);
> ++ if (!bs) {
> ++ error_set(errp, QERR_DEVICE_NOT_FOUND, device);
> ++ return;
> ++ }
> ++ if (bdrv_is_read_only(bs)) {
> ++ error_set(errp, QERR_DEVICE_IS_READ_ONLY, device);
> ++ return;
> ++ }
> ++
> ++ if (!bdrv_can_snapshot(bs)) {
> ++ error_set(errp, QERR_NOT_SUPPORTED);
> ++ return;
> ++ }
> ++
> ++ if (bdrv_snapshot_find(bs, sn, name) < 0) {
> ++ /* return success if snapshot does not exists */
> ++ return;
> ++ }
> ++
> ++ ret = bdrv_snapshot_delete(bs, NULL, name, &local_err);
> ++ if (ret < 0) {
> ++ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
> ++ "Error while deleting snapshot on '%s'\n", device);
> ++ return;
> ++ }
> ++}
> ++
> ++static int loadstate_get_buffer(void *opaque, uint8_t *buf, int64_t pos,
> ++ int size)
> ++{
> ++ BlockDriverState *bs = (BlockDriverState *)opaque;
> ++ int64_t maxlen = bdrv_getlength(bs);
> ++ if (pos > maxlen) {
> ++ return -EIO;
> ++ }
> ++ if ((pos + size) > maxlen) {
> ++ size = maxlen - pos - 1;
> ++ }
> ++ if (size == 0) {
> ++ return 0;
> ++ }
> ++ return bdrv_pread(bs, pos, buf, size);
> ++}
> ++
> ++static const QEMUFileOps loadstate_file_ops = {
> ++ .get_buffer = loadstate_get_buffer,
> ++};
> ++
> ++int load_state_from_blockdev(const char *filename)
> ++{
> ++ BlockDriverState *bs = NULL;
> ++ BlockDriver *drv = NULL;
> ++ Error *local_err = NULL;
> ++
> ++ QEMUFile *f;
> ++ int ret = -1;
> ++
> ++ bs = bdrv_new("vmstate");
> ++ ret = bdrv_open(bs, filename, NULL, BDRV_O_CACHE_WB, drv, &local_err);
> ++ bdrv_set_in_use(bs, 1);
> ++ bdrv_ref(bs);
> ++
> ++ if (ret < 0) {
> ++ error_report("Could not open VM state file");
> ++ goto the_end;
> ++ }
> ++
> ++ /* restore the VM state */
> ++ f = qemu_fopen_ops(bs, &loadstate_file_ops);
> ++ if (!f) {
> ++ error_report("Could not open VM state file");
> ++ ret = -EINVAL;
> ++ goto the_end;
> ++ }
> ++
> ++ qemu_system_reset(VMRESET_SILENT);
> ++ ret = qemu_loadvm_state(f);
> ++
> ++ qemu_fclose(f);
> ++ if (ret < 0) {
> ++ error_report("Error %d while loading VM state", ret);
> ++ goto the_end;
> ++ }
> ++
> ++ ret = 0;
> ++
> ++ the_end:
> ++ if (bs) {
> ++ bdrv_set_in_use(bs, 0);
> ++ bdrv_unref(bs);
> ++ }
> ++ return ret;
> ++}
> +diff --git a/savevm.c b/savevm.c
> +index 3f912dd..9ca3309 100644
> +--- a/savevm.c
> ++++ b/savevm.c
> +@@ -1845,11 +1845,11 @@ bool qemu_savevm_state_blocked(Error **errp)
> + return false;
> + }
> +
> +-void qemu_savevm_state_begin(QEMUFile *f,
> ++int qemu_savevm_state_begin(QEMUFile *f,
> + const MigrationParams *params)
> + {
> + SaveStateEntry *se;
> +- int ret;
> ++ int ret = 0;
> +
> + QTAILQ_FOREACH(se, &savevm_handlers, entry) {
> + if (!se->ops || !se->ops->set_params) {
> +@@ -1890,6 +1890,7 @@ void qemu_savevm_state_begin(QEMUFile *f,
> + break;
> + }
> + }
> ++ return ret;
> + }
> +
> + /*
> +@@ -1937,7 +1938,7 @@ int qemu_savevm_state_iterate(QEMUFile *f)
> + return ret;
> + }
> +
> +-void qemu_savevm_state_complete(QEMUFile *f)
> ++int qemu_savevm_state_complete(QEMUFile *f)
> + {
> + SaveStateEntry *se;
> + int ret;
> +@@ -1962,7 +1963,7 @@ void qemu_savevm_state_complete(QEMUFile *f)
> + trace_savevm_section_end(se->section_id);
> + if (ret < 0) {
> + qemu_file_set_error(f, ret);
> +- return;
> ++ return ret;
> + }
> + }
> +
> +@@ -1991,6 +1992,7 @@ void qemu_savevm_state_complete(QEMUFile *f)
> +
> + qemu_put_byte(f, QEMU_VM_EOF);
> + qemu_fflush(f);
> ++ return qemu_file_get_error(f);
> + }
> +
> + uint64_t qemu_savevm_state_pending(QEMUFile *f, uint64_t max_size)
> +diff --git a/vl.c b/vl.c
> +index 16540f6..1b39acf 100644
> +--- a/vl.c
> ++++ b/vl.c
> +@@ -1643,6 +1643,7 @@ MachineInfoList *qmp_query_machines(Error **errp)
> + info->name = g_strdup(m->name);
> + info->cpu_max = !m->max_cpus ? 1 : m->max_cpus;
> +
> ++
> + if (strcmp(m->name, current_machine->name) == 0) {
> + info->has_is_current = true;
> + info->is_current = true;
> +@@ -2842,6 +2843,7 @@ int main(int argc, char **argv, char **envp)
> + int optind;
> + const char *optarg;
> + const char *loadvm = NULL;
> ++ const char *loadstate = NULL;
> + QEMUMachine *machine;
> + const char *cpu_model;
> + const char *vga_model = "none";
> +@@ -3455,6 +3457,9 @@ int main(int argc, char **argv, char **envp)
> + case QEMU_OPTION_loadvm:
> + loadvm = optarg;
> + break;
> ++ case QEMU_OPTION_loadstate:
> ++ loadstate = optarg;
> ++ break;
> + case QEMU_OPTION_full_screen:
> + full_screen = 1;
> + break;
> +@@ -4406,6 +4411,10 @@ int main(int argc, char **argv, char **envp)
> + if (load_vmstate(loadvm) < 0) {
> + autostart = 0;
> + }
> ++ } else if (loadstate) {
> ++ if (load_state_from_blockdev(loadstate) < 0) {
> ++ autostart = 0;
> ++ }
> + }
> +
> + if (incoming) {
> +--
> +1.7.10.4
> +
> diff --git a/debian/patches/internal-snapshot-async.patch b/debian/patches/internal-snapshot-async.patch
> index c67a97e..04b501b 100644
> --- a/debian/patches/internal-snapshot-async.patch
> +++ b/debian/patches/internal-snapshot-async.patch
> @@ -1,109 +1,110 @@
> -Index: new/qapi-schema.json
> -===================================================================
> ---- new.orig/qapi-schema.json 2013-02-12 12:05:14.000000000 +0100
> -+++ new/qapi-schema.json 2013-02-12 12:07:05.000000000 +0100
> -@@ -528,6 +528,40 @@
> - '*downtime': 'int'} }
> +From 6e115473fc214204538163f79b1bc781019ad004 Mon Sep 17 00:00:00 2001
> +From: Alexandre Derumier <aderumier at odiso.com>
> +Date: Thu, 28 Nov 2013 17:41:03 +0100
> +Subject: [PATCH] internal async savevm
> +
> +
> +Signed-off-by: Alexandre Derumier <aderumier at odiso.com>
> +---
> + Makefile.objs | 1 +
> + block.c | 2 +-
> + hmp-commands.hx | 34 ++++
> + hmp.c | 57 ++++++
> + hmp.h | 5 +
> + include/block/block.h | 1 +
> + include/sysemu/sysemu.h | 5 +-
> + monitor.c | 7 +
> + qapi-schema.json | 46 +++++
> + qemu-options.hx | 13 ++
> + qmp-commands.hx | 31 +++
> + savevm-async.c | 482 +++++++++++++++++++++++++++++++++++++++++++++++
> + savevm.c | 10 +-
> + vl.c | 9 +
> + 16 files changed, 698 insertions(+), 9 deletions(-)
> + create mode 100644 savevm-async.c
> + mode change 100644 => 100755 scripts/hxtool
> +
> +diff --git a/Makefile.objs b/Makefile.objs
> +index 2b6c1fe..cce2562 100644
> +--- a/Makefile.objs
> ++++ b/Makefile.objs
> +@@ -55,6 +55,7 @@ common-obj-$(CONFIG_RDMA) += migration-rdma.o
> + common-obj-y += qemu-char.o #aio.o
> + common-obj-y += block-migration.o
> + common-obj-y += page_cache.o xbzrle.o
> ++common-obj-y += savevm-async.o
>
> - ##
> -+# @SaveVMInfo
> -+#
> -+# Information about current migration process.
> -+#
> -+# @status: #optional string describing the current savevm status.
> -+# This can be 'active', 'completed', 'failed'.
> -+# If this field is not returned, no savevm process
> -+# has been initiated
> -+#
> -+# @error: #optional string containing error message is status is failed.
> -+#
> -+# @total-time: #optional total amount of milliseconds since savevm started.
> -+# If savevm has ended, it returns the total save time
> -+#
> -+# @bytes: #optional total amount of data transfered
> -+#
> -+# Since: 1.3
> -+##
> -+{ 'type': 'SaveVMInfo',
> -+ 'data': {'*status': 'str', '*error': 'str',
> -+ '*total-time': 'int', '*bytes': 'int'} }
> -+
> -+##
> -+# @query-savevm
> -+#
> -+# Returns information about current savevm process.
> -+#
> -+# Returns: @SaveVMInfo
> -+#
> -+# Since: 1.3
> -+##
> -+{ 'command': 'query-savevm', 'returns': 'SaveVMInfo' }
> -+
> -+##
> - # @query-migrate
> - #
> - # Returns information about current migration process.
> -@@ -2965,6 +2999,14 @@
> - ##
> - { 'command': 'query-target', 'returns': 'TargetInfo' }
> + common-obj-$(CONFIG_POSIX) += migration-exec.o migration-unix.o migration-fd.o
>
> -+{ 'command': 'savevm-start' 'data': { '*statefile': 'str' } }
> -+
> -+{ 'command': 'snapshot-drive', 'data': { 'device': 'str', 'name': 'str' } }
> -+
> -+{ 'command': 'delete-drive-snapshot', 'data': { 'device': 'str', 'name': 'str' } }
> -+
> -+{ 'command': 'savevm-end' }
> -+
> - ##
> - # @QKeyCode:
> - #
> -Index: new/qmp-commands.hx
> -===================================================================
> ---- new.orig/qmp-commands.hx 2013-02-12 12:05:14.000000000 +0100
> -+++ new/qmp-commands.hx 2013-02-12 12:07:05.000000000 +0100
> -@@ -2775,3 +2775,34 @@
> - <- { "return": {} }
> +diff --git a/block.c b/block.c
> +index 382ea71..249cb82 100644
> +--- a/block.c
> ++++ b/block.c
> +@@ -1710,7 +1710,7 @@ void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top)
> + bs_new->drv ? bs_new->drv->format_name : "");
> + }
>
> - EQMP
> -+
> +-static void bdrv_delete(BlockDriverState *bs)
> ++void bdrv_delete(BlockDriverState *bs)
> + {
> + assert(!bs->dev);
> + assert(!bs->job);
> +diff --git a/hmp-commands.hx b/hmp-commands.hx
> +index caae5ad..5dbe6d0 100644
> +--- a/hmp-commands.hx
> ++++ b/hmp-commands.hx
> +@@ -1698,6 +1698,8 @@ show migration status
> + show current migration capabilities
> + @item info migrate_cache_size
> + show current migration XBZRLE cache size
> ++ at item info savevm
> ++show savevm status
> + @item info balloon
> + show balloon information
> + @item info qtree
> +@@ -1719,3 +1721,35 @@ ETEXI
> + STEXI
> + @end table
> + ETEXI
> +
> + {
> + .name = "savevm-start",
> + .args_type = "statefile:s?",
> -+ .mhandler.cmd_new = qmp_marshal_input_savevm_start,
> ++ .params = "[statefile]",
> ++ .help = "Prepare for snapshot and halt VM. Save VM state to statefile.",
> ++ .mhandler.cmd = hmp_savevm_start,
> + },
> +
> + {
> + .name = "snapshot-drive",
> + .args_type = "device:s,name:s",
> -+ .mhandler.cmd_new = qmp_marshal_input_snapshot_drive,
> ++ .params = "device name",
> ++ .help = "Create internal snapshot.",
> ++ .mhandler.cmd = hmp_snapshot_drive,
> + },
> +
> + {
> + .name = "delete-drive-snapshot",
> + .args_type = "device:s,name:s",
> -+ .mhandler.cmd_new = qmp_marshal_input_delete_drive_snapshot,
> ++ .params = "device name",
> ++ .help = "Delete internal snapshot.",
> ++ .mhandler.cmd = hmp_delete_drive_snapshot,
> + },
> +
> + {
> + .name = "savevm-end",
> + .args_type = "",
> -+ .mhandler.cmd_new = qmp_marshal_input_savevm_end,
> -+ },
> -+
> -+ {
> -+ .name = "query-savevm",
> -+ .args_type = "",
> -+ .mhandler.cmd_new = qmp_marshal_input_query_savevm,
> ++ .params = "",
> ++ .help = "Resume VM after snaphot.",
> ++ .mhandler.cmd = hmp_savevm_end,
> + },
> -Index: new/hmp.c
> -===================================================================
> ---- new.orig/hmp.c 2013-02-12 12:05:14.000000000 +0100
> -+++ new/hmp.c 2013-02-12 12:07:05.000000000 +0100
> -@@ -1379,3 +1379,60 @@
> - qmp_chardev_remove(qdict_get_str(qdict, "id"), &local_err);
> - hmp_handle_error(mon, &local_err);
> +diff --git a/hmp.c b/hmp.c
> +index c2efa8d..60d6a8e 100644
> +--- a/hmp.c
> ++++ b/hmp.c
> +@@ -1592,3 +1592,60 @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict)
> +
> + hmp_handle_error(mon, &err);
> }
> +
> +void hmp_savevm_start(Monitor *mon, const QDict *qdict)
> @@ -162,11 +163,11 @@ Index: new/hmp.c
> + monitor_printf(mon, "Error: %s\n", info->error);
> + }
> +}
> -Index: new/hmp.h
> -===================================================================
> ---- new.orig/hmp.h 2013-02-12 12:05:14.000000000 +0100
> -+++ new/hmp.h 2013-02-12 12:07:05.000000000 +0100
> -@@ -25,6 +25,7 @@
> +diff --git a/hmp.h b/hmp.h
> +index 54cf71f..bcc1cb6 100644
> +--- a/hmp.h
> ++++ b/hmp.h
> +@@ -25,6 +25,7 @@ void hmp_info_status(Monitor *mon, const QDict *qdict);
> void hmp_info_uuid(Monitor *mon, const QDict *qdict);
> void hmp_info_chardev(Monitor *mon, const QDict *qdict);
> void hmp_info_mice(Monitor *mon, const QDict *qdict);
> @@ -174,7 +175,7 @@ Index: new/hmp.h
> void hmp_info_migrate(Monitor *mon, const QDict *qdict);
> void hmp_info_migrate_capabilities(Monitor *mon, const QDict *qdict);
> void hmp_info_migrate_cache_size(Monitor *mon, const QDict *qdict);
> -@@ -77,6 +78,10 @@
> +@@ -81,6 +82,10 @@ void hmp_netdev_add(Monitor *mon, const QDict *qdict);
> void hmp_netdev_del(Monitor *mon, const QDict *qdict);
> void hmp_getfd(Monitor *mon, const QDict *qdict);
> void hmp_closefd(Monitor *mon, const QDict *qdict);
> @@ -185,68 +186,206 @@ Index: new/hmp.h
> void hmp_send_key(Monitor *mon, const QDict *qdict);
> void hmp_screen_dump(Monitor *mon, const QDict *qdict);
> void hmp_nbd_server_start(Monitor *mon, const QDict *qdict);
> -Index: new/hmp-commands.hx
> -===================================================================
> ---- new.orig/hmp-commands.hx 2013-02-12 12:05:14.000000000 +0100
> -+++ new/hmp-commands.hx 2013-02-12 12:07:05.000000000 +0100
> -@@ -1634,6 +1634,8 @@
> - show current migration capabilities
> - @item info migrate_cache_size
> - show current migration XBZRLE cache size
> -+ at item info savevm
> -+show savevm status
> - @item info balloon
> - show balloon information
> - @item info qtree
> -@@ -1653,3 +1655,35 @@
> - STEXI
> - @end table
> +diff --git a/include/block/block.h b/include/block/block.h
> +index 3560deb..22c06bb 100644
> +--- a/include/block/block.h
> ++++ b/include/block/block.h
> +@@ -214,6 +214,7 @@ BlockDriverState *bdrv_find_backing_image(BlockDriverState *bs,
> + const char *backing_file);
> + int bdrv_get_backing_file_depth(BlockDriverState *bs);
> + int bdrv_truncate(BlockDriverState *bs, int64_t offset);
> ++void bdrv_delete(BlockDriverState *bs);
> + int64_t bdrv_getlength(BlockDriverState *bs);
> + int64_t bdrv_get_allocated_file_size(BlockDriverState *bs);
> + void bdrv_get_geometry(BlockDriverState *bs, uint64_t *nb_sectors_ptr);
> +diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h
> +index 495dae8..d2bb686 100644
> +--- a/include/sysemu/sysemu.h
> ++++ b/include/sysemu/sysemu.h
> +@@ -73,16 +73,17 @@ void qemu_add_machine_init_done_notifier(Notifier *notify);
> +
> + void do_savevm(Monitor *mon, const QDict *qdict);
> + int load_vmstate(const char *name);
> ++int load_state_from_blockdev(const char *filename);
> + void do_delvm(Monitor *mon, const QDict *qdict);
> + void do_info_snapshots(Monitor *mon, const QDict *qdict);
> +
> + void qemu_announce_self(void);
> +
> + bool qemu_savevm_state_blocked(Error **errp);
> +-void qemu_savevm_state_begin(QEMUFile *f,
> ++int qemu_savevm_state_begin(QEMUFile *f,
> + const MigrationParams *params);
> + int qemu_savevm_state_iterate(QEMUFile *f);
> +-void qemu_savevm_state_complete(QEMUFile *f);
> ++int qemu_savevm_state_complete(QEMUFile *f);
> + void qemu_savevm_state_cancel(void);
> + uint64_t qemu_savevm_state_pending(QEMUFile *f, uint64_t max_size);
> + int qemu_loadvm_state(QEMUFile *f);
> +diff --git a/monitor.c b/monitor.c
> +index 845f608..30cc965 100644
> +--- a/monitor.c
> ++++ b/monitor.c
> +@@ -2901,6 +2901,13 @@ static mon_cmd_t info_cmds[] = {
> + .mhandler.cmd = hmp_info_migrate_cache_size,
> + },
> + {
> ++ .name = "savevm",
> ++ .args_type = "",
> ++ .params = "",
> ++ .help = "show savevm status",
> ++ .mhandler.cmd = hmp_info_savevm,
> ++ },
> ++ {
> + .name = "balloon",
> + .args_type = "",
> + .params = "",
> +diff --git a/qapi-schema.json b/qapi-schema.json
> +index 7a36e99..1faf622 100644
> +--- a/qapi-schema.json
> ++++ b/qapi-schema.json
> +@@ -651,6 +651,42 @@
> + '*downtime': 'int',
> + '*setup-time': 'int'} }
> +
> ++
> ++# @SaveVMInfo
> ++#
> ++# Information about current migration process.
> ++#
> ++# @status: #optional string describing the current savevm status.
> ++# This can be 'active', 'completed', 'failed'.
> ++# If this field is not returned, no savevm process
> ++# has been initiated
> ++#
> ++# @error: #optional string containing error message is status is failed.
> ++#
> ++# @total-time: #optional total amount of milliseconds since savevm started.
> ++# If savevm has ended, it returns the total save time
> ++#
> ++# @bytes: #optional total amount of data transfered
> ++#
> ++# Since: 1.3
> ++##
> ++{ 'type': 'SaveVMInfo',
> ++ 'data': {'*status': 'str', '*error': 'str',
> ++ '*total-time': 'int', '*bytes': 'int'} }
> ++
> ++##
> ++# @query-savevm
> ++#
> ++# Returns information about current savevm process.
> ++#
> ++# Returns: @SaveVMInfo
> ++#
> ++# Since: 1.3
> ++##
> ++{ 'command': 'query-savevm', 'returns': 'SaveVMInfo' }
> ++
> ++##
> ++
> + ##
> + # @query-migrate
> + #
> +@@ -3347,8 +3383,18 @@
> + #
> + # Since: 1.2.0
> + ##
> ++
> + { 'command': 'query-target', 'returns': 'TargetInfo' }
> +
> ++{ 'command': 'savevm-start', 'data': { '*statefile': 'str' } }
> ++
> ++{ 'command': 'snapshot-drive', 'data': { 'device': 'str', 'name': 'str' } }
> ++
> ++{ 'command': 'delete-drive-snapshot', 'data': { 'device': 'str', 'name': 'str' } }
> ++
> ++{ 'command': 'savevm-end' }
> ++
> ++
> + ##
> + # @QKeyCode:
> + #
> +diff --git a/qemu-options.hx b/qemu-options.hx
> +index 51b1cd0..a2834da 100644
> +--- a/qemu-options.hx
> ++++ b/qemu-options.hx
> +@@ -2734,6 +2734,19 @@ STEXI
> + Start right away with a saved state (@code{loadvm} in monitor)
> ETEXI
> +
> ++DEF("loadstate", HAS_ARG, QEMU_OPTION_loadstate, \
> ++ "-loadstate file\n" \
> ++ " start right away with a saved state\n",
> ++ QEMU_ARCH_ALL)
> ++STEXI
> ++ at item -loadstate @var{file}
> ++ at findex -loadstate
> ++Start right away with a saved state. This option does not rollback
> ++disk state like @code{loadvm}, so user must make sure that disk
> ++have correct state. @var{file} can be any valid device URL. See the section
> ++for "Device URL Syntax" for more information.
> ++ETEXI
> ++
> + #ifndef _WIN32
> + DEF("daemonize", 0, QEMU_OPTION_daemonize, \
> + "-daemonize daemonize QEMU after initializing\n", QEMU_ARCH_ALL)
> +diff --git a/qmp-commands.hx b/qmp-commands.hx
> +index 5584fe2..90a0d71 100644
> +--- a/qmp-commands.hx
> ++++ b/qmp-commands.hx
> +@@ -3308,3 +3308,34 @@ Example (2):
> + <- { "return": {} }
> +
> + EQMP
> ++
> +
> + {
> + .name = "savevm-start",
> + .args_type = "statefile:s?",
> -+ .params = "[statefile]",
> -+ .help = "Prepare for snapshot and halt VM. Save VM state to statefile.",
> -+ .mhandler.cmd = hmp_savevm_start,
> ++ .mhandler.cmd_new = qmp_marshal_input_savevm_start,
> + },
> +
> + {
> + .name = "snapshot-drive",
> + .args_type = "device:s,name:s",
> -+ .params = "device name",
> -+ .help = "Create internal snapshot.",
> -+ .mhandler.cmd = hmp_snapshot_drive,
> ++ .mhandler.cmd_new = qmp_marshal_input_snapshot_drive,
> + },
> +
> + {
> + .name = "delete-drive-snapshot",
> + .args_type = "device:s,name:s",
> -+ .params = "device name",
> -+ .help = "Delete internal snapshot.",
> -+ .mhandler.cmd = hmp_delete_drive_snapshot,
> ++ .mhandler.cmd_new = qmp_marshal_input_delete_drive_snapshot,
> + },
> +
> + {
> + .name = "savevm-end",
> + .args_type = "",
> -+ .params = "",
> -+ .help = "Resume VM after snaphot.",
> -+ .mhandler.cmd = hmp_savevm_end,
> ++ .mhandler.cmd_new = qmp_marshal_input_savevm_end,
> + },
> -Index: new/savevm-async.c
> -===================================================================
> ---- /dev/null 1970-01-01 00:00:00.000000000 +0000
> -+++ new/savevm-async.c 2013-02-12 12:07:05.000000000 +0100
> -@@ -0,0 +1,459 @@
> -+#include "include/qemu-common.h"
> -+#include "include/qapi/qmp/qerror.h"
> -+#include "include/sysemu/sysemu.h"
> ++
> ++ {
> ++ .name = "query-savevm",
> ++ .args_type = "",
> ++ .mhandler.cmd_new = qmp_marshal_input_query_savevm,
> ++ },
> +new file mode 100644
> +index 0000000..87c2650
> +--- /dev/null
> ++++ b/savevm-async.c
> +@@ -0,0 +1,482 @@
> ++#include "qemu-common.h"
> ++#include "qapi/qmp/qerror.h"
> ++#include "sysemu/sysemu.h"
> +#include "qmp-commands.h"
> -+#include "include/migration/qemu-file.h"
> -+#include "include/sysemu/blockdev.h"
> -+#include "include/qom/qom-qobject.h"
> -+#include "include/migration/migration.h"
> ++#include "qemu-options.h"
> ++#include "migration/qemu-file.h"
> ++#include "qom/qom-qobject.h"
> ++#include "migration/migration.h"
> ++#include "block/snapshot.h"
> ++#include "block/qapi.h"
> ++#include "block/block.h"
> ++#include "qemu/timer.h"
> +
> +/* #define DEBUG_SAVEVM_STATE */
> +
> @@ -265,6 +404,7 @@ Index: new/savevm-async.c
> + SAVE_STATE_COMPLETED,
> +};
> +
> ++
> +static struct SnapshotState {
> + BlockDriverState *bs;
> + size_t bs_pos;
> @@ -298,7 +438,7 @@ Index: new/savevm-async.c
> + info->has_status = true;
> + info->status = g_strdup("active");
> + info->has_total_time = true;
> -+ info->total_time = qemu_get_clock_ms(rt_clock)
> ++ info->total_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME)
> + - s->total_time;
> + break;
> + case SAVE_STATE_COMPLETED:
> @@ -319,7 +459,7 @@ Index: new/savevm-async.c
> +
> + DPRINTF("save_snapshot_cleanup\n");
> +
> -+ snap_state.total_time = qemu_get_clock_ms(rt_clock) -
> ++ snap_state.total_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) -
> + snap_state.total_time;
> +
> + if (snap_state.file) {
> @@ -332,6 +472,7 @@ Index: new/savevm-async.c
> + */
> + size_t size = (snap_state.bs_pos + BDRV_SECTOR_SIZE) & BDRV_SECTOR_MASK;
> + bdrv_truncate(snap_state.bs, size);
> ++ bdrv_ref(snap_state.bs);
> + bdrv_delete(snap_state.bs);
> + snap_state.bs = NULL;
> + }
> @@ -403,12 +544,17 @@ Index: new/savevm-async.c
> +
> + snap_state.state = SAVE_STATE_ACTIVE;
> +
> ++ qemu_mutex_unlock_iothread();
> + ret = qemu_savevm_state_begin(snap_state.file, ¶ms);
> ++ qemu_mutex_lock_iothread();
> ++
> + if (ret < 0) {
> + save_snapshot_error("qemu_savevm_state_begin failed");
> + return;
> + }
> +
> ++ qemu_savevm_state_begin(snap_state.file, ¶ms);
> ++
> + while (snap_state.state == SAVE_STATE_ACTIVE) {
> + uint64_t pending_size;
> +
> @@ -427,6 +573,9 @@ Index: new/savevm-async.c
> + vm_stop(RUN_STATE_SAVE_VM);
> + }
> + DPRINTF("savevm inerate finished\n");
> ++
> ++ qemu_savevm_state_complete(snap_state.file);
> ++
> + ret = qemu_savevm_state_complete(snap_state.file);
> + if (ret < 0) {
> + save_snapshot_error("qemu_savevm_state_complete error %d", ret);
> @@ -460,6 +609,8 @@ Index: new/savevm-async.c
> +void qmp_savevm_start(bool has_statefile, const char *statefile, Error **errp)
> +{
> + BlockDriver *drv = NULL;
> ++ Error *local_err = NULL;
> ++
> + int bdrv_oflags = BDRV_O_CACHE_WB | BDRV_O_RDWR;
> + int ret;
> +
> @@ -472,7 +623,7 @@ Index: new/savevm-async.c
> + /* initialize snapshot info */
> + snap_state.saved_vm_running = runstate_is_running();
> + snap_state.bs_pos = 0;
> -+ snap_state.total_time = qemu_get_clock_ms(rt_clock);
> ++ snap_state.total_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
> +
> + if (snap_state.error) {
> + error_free(snap_state.error);
> @@ -491,16 +642,17 @@ Index: new/savevm-async.c
> +
> + /* Open the image */
> + snap_state.bs = bdrv_new("vmstate");
> -+ ret = bdrv_open(snap_state.bs, statefile, bdrv_oflags, drv);
> ++ ret = bdrv_open(snap_state.bs, statefile, NULL, bdrv_oflags, drv, &local_err);
> ++
> + if (ret < 0) {
> -+ error_set(errp, QERR_OPEN_FILE_FAILED, statefile);
> ++ error_set(errp, ERROR_CLASS_GENERIC_ERROR, "failed to open '%s'", statefile);
> + goto restart;
> + }
> +
> + snap_state.file = qemu_fopen_ops(&snap_state, &block_file_ops);
> +
> + if (!snap_state.file) {
> -+ error_set(errp, QERR_OPEN_FILE_FAILED, statefile);
> ++ error_set(errp, ERROR_CLASS_GENERIC_ERROR, "failed to open '%s'", statefile);
> + goto restart;
> + }
> +
> @@ -589,7 +741,7 @@ Index: new/savevm-async.c
> + sn->date_sec = tv.tv_sec;
> + sn->date_nsec = tv.tv_usec * 1000;
> +#endif
> -+ sn->vm_clock_nsec = qemu_get_clock_ns(vm_clock);
> ++ sn->vm_clock_nsec = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> +
> + pstrcpy(sn->name, sizeof(sn->name), name);
> +
> @@ -608,6 +760,8 @@ Index: new/savevm-async.c
> +{
> + BlockDriverState *bs;
> + QEMUSnapshotInfo sn1, *sn = &sn1;
> ++ Error *local_err = NULL;
> ++
> + int ret;
> +
> + bs = bdrv_find(device);
> @@ -630,7 +784,7 @@ Index: new/savevm-async.c
> + return;
> + }
> +
> -+ ret = bdrv_snapshot_delete(bs, name);
> ++ ret = bdrv_snapshot_delete(bs, NULL, name, &local_err);
> + if (ret < 0) {
> + error_set(errp, ERROR_CLASS_GENERIC_ERROR,
> + "Error while deleting snapshot on '%s'\n", device);
> @@ -663,11 +817,14 @@ Index: new/savevm-async.c
> +{
> + BlockDriverState *bs = NULL;
> + BlockDriver *drv = NULL;
> ++ Error *local_err = NULL;
> ++
> + QEMUFile *f;
> + int ret = -1;
> +
> + bs = bdrv_new("vmstate");
> -+ ret = bdrv_open(bs, filename, BDRV_O_CACHE_WB, drv);
> ++ ret = bdrv_open(bs, filename, NULL, BDRV_O_CACHE_WB, drv, &local_err);
> ++
> + if (ret < 0) {
> + error_report("Could not open VM state file");
> + goto the_end;
> @@ -694,51 +851,79 @@ Index: new/savevm-async.c
> +
> + the_end:
> + if (bs) {
> ++ bdrv_ref(bs);
> + bdrv_delete(bs);
> + }
> + return ret;
> +}
> -Index: new/Makefile.objs
> -===================================================================
> ---- new.orig/Makefile.objs 2013-02-12 12:05:14.000000000 +0100
> -+++ new/Makefile.objs 2013-02-12 12:07:05.000000000 +0100
> -@@ -60,6 +60,7 @@
> - common-obj-y += qemu-char.o #aio.o
> - common-obj-y += block-migration.o
> - common-obj-y += page_cache.o xbzrle.o
> -+common-obj-y += savevm-async.o
> +diff --git a/savevm.c b/savevm.c
> +index 3f912dd..9ca3309 100644
> +--- a/savevm.c
> ++++ b/savevm.c
> +@@ -1845,11 +1845,11 @@ bool qemu_savevm_state_blocked(Error **errp)
> + return false;
> + }
>
> - common-obj-$(CONFIG_POSIX) += migration-exec.o migration-unix.o migration-fd.o
> +-void qemu_savevm_state_begin(QEMUFile *f,
> ++int qemu_savevm_state_begin(QEMUFile *f,
> + const MigrationParams *params)
> + {
> + SaveStateEntry *se;
> +- int ret;
> ++ int ret = 0;
>
> -Index: new/qemu-options.hx
> -===================================================================
> ---- new.orig/qemu-options.hx 2013-02-12 12:06:20.000000000 +0100
> -+++ new/qemu-options.hx 2013-02-12 12:07:05.000000000 +0100
> -@@ -2605,6 +2605,19 @@
> - Start right away with a saved state (@code{loadvm} in monitor)
> - ETEXI
> + QTAILQ_FOREACH(se, &savevm_handlers, entry) {
> + if (!se->ops || !se->ops->set_params) {
> +@@ -1890,6 +1890,7 @@ void qemu_savevm_state_begin(QEMUFile *f,
> + break;
> + }
> + }
> ++ return ret;
> + }
> +
> + /*
> +@@ -1937,7 +1938,7 @@ int qemu_savevm_state_iterate(QEMUFile *f)
> + return ret;
> + }
> +
> +-void qemu_savevm_state_complete(QEMUFile *f)
> ++int qemu_savevm_state_complete(QEMUFile *f)
> + {
> + SaveStateEntry *se;
> + int ret;
> +@@ -1962,7 +1963,7 @@ void qemu_savevm_state_complete(QEMUFile *f)
> + trace_savevm_section_end(se->section_id);
> + if (ret < 0) {
> + qemu_file_set_error(f, ret);
> +- return;
> ++ return ret;
> + }
> + }
> +
> +@@ -1991,6 +1992,7 @@ void qemu_savevm_state_complete(QEMUFile *f)
> +
> + qemu_put_byte(f, QEMU_VM_EOF);
> + qemu_fflush(f);
> ++ return qemu_file_get_error(f);
> + }
> +
> + uint64_t qemu_savevm_state_pending(QEMUFile *f, uint64_t max_size)
> +diff --git a/scripts/hxtool b/scripts/hxtool
> +old mode 100644
> +new mode 100755
> +diff --git a/vl.c b/vl.c
> +index 16540f6..1b39acf 100644
> +--- a/vl.c
> ++++ b/vl.c
> +@@ -1643,6 +1643,7 @@ MachineInfoList *qmp_query_machines(Error **errp)
> + info->name = g_strdup(m->name);
> + info->cpu_max = !m->max_cpus ? 1 : m->max_cpus;
>
> -+DEF("loadstate", HAS_ARG, QEMU_OPTION_loadstate, \
> -+ "-loadstate file\n" \
> -+ " start right away with a saved state\n",
> -+ QEMU_ARCH_ALL)
> -+STEXI
> -+ at item -loadstate @var{file}
> -+ at findex -loadstate
> -+Start right away with a saved state. This option does not rollback
> -+disk state like @code{loadvm}, so user must make sure that disk
> -+have correct state. @var{file} can be any valid device URL. See the section
> -+for "Device URL Syntax" for more information.
> -+ETEXI
> +
> - #ifndef _WIN32
> - DEF("daemonize", 0, QEMU_OPTION_daemonize, \
> - "-daemonize daemonize QEMU after initializing\n", QEMU_ARCH_ALL)
> -Index: new/vl.c
> -===================================================================
> ---- new.orig/vl.c 2013-02-12 12:06:40.000000000 +0100
> -+++ new/vl.c 2013-02-12 12:07:05.000000000 +0100
> -@@ -2816,6 +2816,7 @@
> + if (strcmp(m->name, current_machine->name) == 0) {
> + info->has_is_current = true;
> + info->is_current = true;
> +@@ -2842,6 +2843,7 @@ int main(int argc, char **argv, char **envp)
> int optind;
> const char *optarg;
> const char *loadvm = NULL;
> @@ -746,7 +931,7 @@ Index: new/vl.c
> QEMUMachine *machine;
> const char *cpu_model;
> const char *vga_model = "none";
> -@@ -3466,6 +3467,9 @@
> +@@ -3455,6 +3457,9 @@ int main(int argc, char **argv, char **envp)
> case QEMU_OPTION_loadvm:
> loadvm = optarg;
> break;
> @@ -756,7 +941,7 @@ Index: new/vl.c
> case QEMU_OPTION_full_screen:
> full_screen = 1;
> break;
> -@@ -4361,6 +4365,10 @@
> +@@ -4406,6 +4411,10 @@ int main(int argc, char **argv, char **envp)
> if (load_vmstate(loadvm) < 0) {
> autostart = 0;
> }
> @@ -767,33 +952,6 @@ Index: new/vl.c
> }
>
> if (incoming) {
> -Index: new/monitor.c
> -===================================================================
> ---- new.orig/monitor.c 2013-02-12 12:05:14.000000000 +0100
> -+++ new/monitor.c 2013-02-12 12:07:05.000000000 +0100
> -@@ -2687,6 +2687,13 @@
> - .mhandler.cmd = hmp_info_migrate_cache_size,
> - },
> - {
> -+ .name = "savevm",
> -+ .args_type = "",
> -+ .params = "",
> -+ .help = "show savevm status",
> -+ .mhandler.cmd = hmp_info_savevm,
> -+ },
> -+ {
> - .name = "balloon",
> - .args_type = "",
> - .params = "",
> -Index: new/include/sysemu/sysemu.h
> -===================================================================
> ---- new.orig/include/sysemu/sysemu.h 2013-02-12 12:05:14.000000000 +0100
> -+++ new/include/sysemu/sysemu.h 2013-02-12 12:07:05.000000000 +0100
> -@@ -67,6 +67,7 @@
> -
> - void do_savevm(Monitor *mon, const QDict *qdict);
> - int load_vmstate(const char *name);
> -+int load_state_from_blockdev(const char *filename);
> - void do_delvm(Monitor *mon, const QDict *qdict);
> - void do_info_snapshots(Monitor *mon, const QDict *qdict);
> -
> +--
> +1.7.10.4
> +
> diff --git a/debian/patches/series b/debian/patches/series
> index 5bfe773..71baf6d 100644
> --- a/debian/patches/series
> +++ b/debian/patches/series
> @@ -5,9 +5,6 @@ keymap.diff
> pve-auth.patch
> vencrypt-auth-plain.patch
> fix-qemu-img-snapshot-removal.patch
> -# fixme: update snapshot patches for 1.7
> -#move-bdrv-snapshot-find.patch
> -#internal-snapshot-async.patch
> enable-kvm-by-default.patch
> # fixme: include qemu backup series
> #0001-add-documenation-for-new-backup-framework.patch
> @@ -28,7 +25,7 @@ spice-use-pve-certs.patch
> backup-add-vma-binary.patch
> backup-add-vma-verify-command.patch
>
> -
> backup-vma-restore-tolerate-a-size-difference-up-to-4M.patch
> +internal-snapshot-async-qemu1.7.patch
>
> qemu_do_exit_on_failed_load_vmstate.patch
>
More information about the pve-devel
mailing list