]> git.proxmox.com Git - pve-qemu.git/blobdiff - debian/patches/pve/0018-PVE-internal-snapshot-async.patch
bump version to 3.0.1-1
[pve-qemu.git] / debian / patches / pve / 0018-PVE-internal-snapshot-async.patch
index 987ee2575aa2b08790dbaffe367a2172a12a42c2..7347a8b2aa600b3a25b9477d340b86aa3b54b471 100644 (file)
@@ -7,15 +7,15 @@ Subject: [PATCH] PVE: internal snapshot async
  Makefile.objs                |   1 +
  hmp-commands-info.hx         |  13 ++
  hmp-commands.hx              |  32 +++
- hmp.c                        |  57 +++++
+ hmp.c                        |  57 ++++++
  hmp.h                        |   5 +
  include/migration/snapshot.h |   1 +
- qapi/migration.json          |  34 +++
+ qapi/migration.json          |  34 ++++
  qapi/misc.json               |  32 +++
  qemu-options.hx              |  13 ++
- savevm-async.c               | 528 +++++++++++++++++++++++++++++++++++++++++++
+ savevm-async.c               | 460 +++++++++++++++++++++++++++++++++++++++++++
  vl.c                         |  10 +
- 11 files changed, 726 insertions(+)
+ 11 files changed, 658 insertions(+)
  create mode 100644 savevm-async.c
 
 diff --git a/Makefile.objs b/Makefile.objs
@@ -310,10 +310,10 @@ index b1bf0f485f..31329e26e2 100644
      "-daemonize      daemonize QEMU after initializing\n", QEMU_ARCH_ALL)
 diff --git a/savevm-async.c b/savevm-async.c
 new file mode 100644
-index 0000000000..0bf830c906
+index 0000000000..73b7fe75ed
 --- /dev/null
 +++ b/savevm-async.c
-@@ -0,0 +1,528 @@
+@@ -0,0 +1,460 @@
 +#include "qemu/osdep.h"
 +#include "migration/migration.h"
 +#include "migration/savevm.h"
@@ -321,9 +321,7 @@ index 0000000000..0bf830c906
 +#include "migration/global_state.h"
 +#include "migration/ram.h"
 +#include "migration/qemu-file.h"
-+#include "qapi/qmp/qerror.h"
 +#include "sysemu/sysemu.h"
-+#include "qmp-commands.h"
 +#include "block/block.h"
 +#include "sysemu/block-backend.h"
 +#include "qapi/error.h"
@@ -331,11 +329,13 @@ index 0000000000..0bf830c906
 +#include "qapi/qmp/qdict.h"
 +#include "qapi/qapi-commands-migration.h"
 +#include "qapi/qapi-commands-misc.h"
++#include "qapi/qapi-commands-block.h"
 +#include "qemu/cutils.h"
 +
 +/* #define DEBUG_SAVEVM_STATE */
 +
-+#define NOT_DONE 0x7fffffff /* used while emulated sync operation in progress */
++/* used while emulated sync operation in progress */
++#define NOT_DONE -EINPROGRESS
 +
 +#ifdef DEBUG_SAVEVM_STATE
 +#define DPRINTF(fmt, ...) \
@@ -363,6 +363,8 @@ index 0000000000..0bf830c906
 +    int saved_vm_running;
 +    QEMUFile *file;
 +    int64_t total_time;
++    QEMUBH *cleanup_bh;
++    QemuThread thread;
 +} snap_state;
 +
 +SaveVMInfo *qmp_query_savevm(Error **errp)
@@ -450,19 +452,6 @@ index 0000000000..0bf830c906
 +    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)
@@ -471,67 +460,123 @@ index 0000000000..0bf830c906
 +    return blk_flush(snap_state.target);
 +}
 +
++typedef struct BlkRwCo {
++    int64_t offset;
++    QEMUIOVector *qiov;
++    ssize_t ret;
++} BlkRwCo;
++
++static void coroutine_fn block_state_write_entry(void *opaque) {
++    BlkRwCo *rwco = opaque;
++    rwco->ret = blk_co_pwritev(snap_state.target, rwco->offset, rwco->qiov->size,
++                               rwco->qiov, 0);
++}
++
 +static ssize_t block_state_writev_buffer(void *opaque, struct iovec *iov,
 +                                         int iovcnt, int64_t pos)
 +{
-+    int ret;
 +    QEMUIOVector qiov;
++    BlkRwCo rwco;
++
++    assert(pos == snap_state.bs_pos);
++    rwco = (BlkRwCo) {
++        .offset = pos,
++        .qiov = &qiov,
++        .ret = NOT_DONE,
++    };
 +
 +    qemu_iovec_init_external(&qiov, iov, iovcnt);
-+    ret = blk_co_pwritev(snap_state.target, pos, qiov.size, &qiov, 0);
-+    if (ret < 0) {
-+        return ret;
++
++    if (qemu_in_coroutine()) {
++        block_state_write_entry(&rwco);
++    } else {
++        Coroutine *co = qemu_coroutine_create(&block_state_write_entry, &rwco);
++        bdrv_coroutine_enter(blk_bs(snap_state.target), co);
++        BDRV_POLL_WHILE(blk_bs(snap_state.target), rwco.ret == NOT_DONE);
 +    }
++    if (rwco.ret < 0) {
++        return rwco.ret;
++    }
++
 +    snap_state.bs_pos += qiov.size;
 +    return qiov.size;
 +}
 +
-+static int store_and_stop(void) {
-+    if (global_state_store()) {
-+        save_snapshot_error("Error saving global state");
-+        return 1;
++static const QEMUFileOps block_file_ops = {
++    .writev_buffer =  block_state_writev_buffer,
++    .close =          block_state_close,
++};
++
++static void process_savevm_cleanup(void *opaque)
++{
++    int ret;
++    qemu_bh_delete(snap_state.cleanup_bh);
++    snap_state.cleanup_bh = NULL;
++    qemu_mutex_unlock_iothread();
++    qemu_thread_join(&snap_state.thread);
++    qemu_mutex_lock_iothread();
++    ret = save_snapshot_cleanup();
++    if (ret < 0) {
++        save_snapshot_error("save_snapshot_cleanup error %d", ret);
++    } else if (snap_state.state == SAVE_STATE_ACTIVE) {
++        snap_state.state = SAVE_STATE_COMPLETED;
++    } else {
++        save_snapshot_error("process_savevm_cleanup: invalid state: %d",
++                            snap_state.state);
 +    }
-+    if (runstate_is_running()) {
-+        vm_stop(RUN_STATE_SAVE_VM);
++    if (snap_state.saved_vm_running) {
++        vm_start();
++        snap_state.saved_vm_running = false;
 +    }
-+    return 0;
 +}
 +
-+static void process_savevm_co(void *opaque)
++static void *process_savevm_thread(void *opaque)
 +{
 +    int ret;
 +    int64_t maxlen;
 +
-+    snap_state.state = SAVE_STATE_ACTIVE;
++    rcu_register_thread();
 +
-+    qemu_mutex_unlock_iothread();
 +    qemu_savevm_state_header(snap_state.file);
 +    qemu_savevm_state_setup(snap_state.file);
 +    ret = qemu_file_get_error(snap_state.file);
-+    qemu_mutex_lock_iothread();
 +
 +    if (ret < 0) {
 +        save_snapshot_error("qemu_savevm_state_setup failed");
-+        return;
++        rcu_unregister_thread();
++        return NULL;
 +    }
 +
 +    while (snap_state.state == SAVE_STATE_ACTIVE) {
-+        uint64_t pending_size, pend_post, pend_nonpost;
++        uint64_t pending_size, pend_precopy, pend_compatible, pend_postcopy;
 +
-+        qemu_savevm_state_pending(snap_state.file, 0, &pend_nonpost, &pend_post);
-+        pending_size = pend_post + pend_nonpost;
++        qemu_savevm_state_pending(snap_state.file, 0, &pend_precopy, &pend_compatible, &pend_postcopy);
++        pending_size = pend_precopy + pend_compatible + pend_postcopy;
 +
-+        if (pending_size) {
-+                ret = qemu_savevm_state_iterate(snap_state.file, false);
-+                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);
++        maxlen = blk_getlength(snap_state.target) - 30*1024*1024;
++
++        if (pending_size > 400000 && snap_state.bs_pos + pending_size < maxlen) {
++            qemu_mutex_lock_iothread();
++            ret = qemu_savevm_state_iterate(snap_state.file, false);
++            if (ret < 0) {
++                save_snapshot_error("qemu_savevm_state_iterate error %d", ret);
++                break;
++            }
++            qemu_mutex_unlock_iothread();
++            DPRINTF("savevm inerate pending size %lu ret %d\n", pending_size, ret);
 +        } else {
-+            DPRINTF("done iterating\n");
-+            if (store_and_stop())
++            qemu_mutex_lock_iothread();
++            qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER);
++            ret = global_state_store();
++            if (ret) {
++                save_snapshot_error("global_state_store error %d", ret);
 +                break;
++            }
++            ret = vm_stop_force_state(RUN_STATE_FINISH_MIGRATE);
++            if (ret < 0) {
++                save_snapshot_error("vm_stop_force_state error %d", ret);
++                break;
++            }
 +            DPRINTF("savevm inerate finished\n");
 +            /* upstream made the return value here inconsistent
 +             * (-1 instead of 'ret' in one case and 0 after flush which can
@@ -543,36 +588,19 @@ index 0000000000..0bf830c906
 +                    save_snapshot_error("qemu_savevm_state_iterate error %d", ret);
 +                    break;
 +            }
++            qemu_savevm_state_cleanup();
 +            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 = blk_getlength(snap_state.target) - 30*1024*1024;
-+        if ((pending_size < 100000) ||
-+            ((snap_state.bs_pos + pending_size) >= maxlen)) {
-+            if (store_and_stop())
-+                break;
-+        }
 +    }
 +
-+    if(snap_state.state == SAVE_STATE_CANCELLED) {
-+        save_snapshot_completed();
-+        Error *errp = NULL;
-+        qmp_savevm_end(&errp);
-+    }
++    qemu_bh_schedule(snap_state.cleanup_bh);
++    qemu_mutex_unlock_iothread();
 +
++    rcu_unregister_thread();
++    return NULL;
 +}
 +
-+static const QEMUFileOps block_file_ops = {
-+    .writev_buffer =  block_state_writev_buffer,
-+    .close =          block_state_close,
-+};
-+
-+
 +void qmp_savevm_start(bool has_statefile, const char *statefile, Error **errp)
 +{
 +    Error *local_err = NULL;
@@ -627,8 +655,10 @@ index 0000000000..0bf830c906
 +    error_setg(&snap_state.blocker, "block device is in use by savevm");
 +    blk_op_block_all(snap_state.target, snap_state.blocker);
 +
-+    Coroutine *co = qemu_coroutine_create(process_savevm_co, NULL);
-+    qemu_coroutine_enter(co);
++    snap_state.state = SAVE_STATE_ACTIVE;
++    snap_state.cleanup_bh = qemu_bh_new(process_savevm_cleanup, &snap_state);
++    qemu_thread_create(&snap_state.thread, "savevm-async", process_savevm_thread,
++                       NULL, QEMU_THREAD_JOINABLE);
 +
 +    return;
 +
@@ -661,118 +691,20 @@ index 0000000000..0bf830c906
 +    snap_state.state = SAVE_STATE_DONE;
 +}
 +
++// FIXME: Deprecated
 +void qmp_snapshot_drive(const char *device, const char *name, Error **errp)
 +{
-+    BlockBackend *blk;
-+    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;
-+    }
-+
-+    blk = blk_by_name(device);
-+    if (!blk) {
-+        error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
-+                  "Device '%s' not found", device);
-+        return;
-+    }
-+
-+    bs = blk_bs(blk);
-+    if (!bdrv_is_inserted(bs)) {
-+        error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, device);
-+        return;
-+    }
-+
-+    if (bdrv_is_read_only(bs)) {
-+        error_setg(errp, "Node '%s' is read only", device);
-+        return;
-+    }
-+
-+    if (!bdrv_can_snapshot(bs)) {
-+        error_setg(errp, QERR_UNSUPPORTED);
-+        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;
-+    }
++    // Compatibility to older qemu-server.
++    qmp_blockdev_snapshot_internal_sync(device, name, errp);
 +}
 +
++// FIXME: Deprecated
 +void qmp_delete_drive_snapshot(const char *device, const char *name,
 +                               Error **errp)
 +{
-+    BlockBackend *blk;
-+    BlockDriverState *bs;
-+    QEMUSnapshotInfo sn1, *sn = &sn1;
-+    Error *local_err = NULL;
-+
-+    int ret;
-+
-+    blk = blk_by_name(device);
-+    if (!blk) {
-+        error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
-+                  "Device '%s' not found", device);
-+        return;
-+    }
-+
-+    bs = blk_bs(blk);
-+    if (bdrv_is_read_only(bs)) {
-+        error_setg(errp, "Node '%s' is read only", device);
-+        return;
-+    }
-+
-+    if (!bdrv_can_snapshot(bs)) {
-+        error_setg(errp, QERR_UNSUPPORTED);
-+        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;
-+    }
++    // Compatibility to older qemu-server.
++    (void)qmp_blockdev_snapshot_delete_internal_sync(device, false, NULL,
++                                                     true, name, errp);
 +}
 +
 +static ssize_t loadstate_get_buffer(void *opaque, uint8_t *buf, int64_t pos,
@@ -843,7 +775,7 @@ index 0000000000..0bf830c906
 +    return ret;
 +}
 diff --git a/vl.c b/vl.c
-index c750b7c18e..b2e3e23724 100644
+index 9c3a41bfe2..63107d82a3 100644
 --- a/vl.c
 +++ b/vl.c
 @@ -2927,6 +2927,7 @@ int main(int argc, char **argv, char **envp)
@@ -854,7 +786,7 @@ index c750b7c18e..b2e3e23724 100644
      MachineClass *machine_class;
      const char *cpu_model;
      const char *vga_model = NULL;
-@@ -3528,6 +3529,9 @@ int main(int argc, char **argv, char **envp)
+@@ -3529,6 +3530,9 @@ int main(int argc, char **argv, char **envp)
              case QEMU_OPTION_loadvm:
                  loadvm = optarg;
                  break;
@@ -864,7 +796,7 @@ index c750b7c18e..b2e3e23724 100644
              case QEMU_OPTION_full_screen:
                  dpy.has_full_screen = true;
                  dpy.full_screen = true;
-@@ -4623,6 +4627,12 @@ int main(int argc, char **argv, char **envp)
+@@ -4624,6 +4628,12 @@ int main(int argc, char **argv, char **envp)
              error_report_err(local_err);
              autostart = 0;
          }