[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, &params);
> ++    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, &params);
> ++    qemu_mutex_lock_iothread();
> ++
>   +    if (ret < 0) {
>   +        save_snapshot_error("qemu_savevm_state_begin failed");
>   +        return;
>   +    }
>   +
> ++    qemu_savevm_state_begin(snap_state.file, &params);
> ++
>   +    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