]> git.proxmox.com Git - pve-qemu.git/blame - debian/patches/pve/0017-PVE-internal-snapshot-async.patch
import QEMU 5.0.0-rc2 and rebase patches
[pve-qemu.git] / debian / patches / pve / 0017-PVE-internal-snapshot-async.patch
CommitLineData
23102ed6 1From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
6402d961 2From: Dietmar Maurer <dietmar@proxmox.com>
83faa3fe
TL
3Date: Mon, 6 Apr 2020 12:16:46 +0200
4Subject: [PATCH] PVE: internal snapshot async
6402d961
TL
5
6Truncate at 1024 boundary (Fabian Ebner will send a patch for stable)
95259824 7
b855dce7 8Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
6402d961 9Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
95259824 10---
53e83913 11 Makefile.objs | 1 +
b855dce7 12 hmp-commands-info.hx | 13 +
6838f038 13 hmp-commands.hx | 32 +++
6838f038 14 include/migration/snapshot.h | 1 +
be901f66
SR
15 include/monitor/hmp.h | 5 +
16 monitor/hmp-cmds.c | 57 +++++
b855dce7 17 qapi/migration.json | 34 +++
53e83913 18 qapi/misc.json | 32 +++
83faa3fe 19 qemu-options.hx | 12 +
6402d961 20 savevm-async.c | 464 +++++++++++++++++++++++++++++++++++
83faa3fe
TL
21 softmmu/vl.c | 10 +
22 11 files changed, 661 insertions(+)
95259824
WB
23 create mode 100644 savevm-async.c
24
25diff --git a/Makefile.objs b/Makefile.objs
83faa3fe 26index a7c967633a..d0b4dde836 100644
95259824
WB
27--- a/Makefile.objs
28+++ b/Makefile.objs
83faa3fe 29@@ -47,6 +47,7 @@ common-obj-y += bootdevice.o iothread.o
be901f66 30 common-obj-y += dump/
53e83913 31 common-obj-y += job-qmp.o
be901f66 32 common-obj-y += monitor/
95259824 33+common-obj-y += savevm-async.o
6838f038 34 common-obj-y += net/
83faa3fe 35 common-obj-y += qdev-monitor.o
6838f038 36 common-obj-$(CONFIG_WIN32) += os-win32.o
95259824 37diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx
83faa3fe 38index ca5198438d..89fea71972 100644
95259824
WB
39--- a/hmp-commands-info.hx
40+++ b/hmp-commands-info.hx
83faa3fe
TL
41@@ -579,6 +579,19 @@ SRST
42 Show current migration xbzrle cache size.
43 ERST
44
b855dce7 45+ {
95259824
WB
46+ .name = "savevm",
47+ .args_type = "",
48+ .params = "",
49+ .help = "show savevm status",
a544966d 50+ .cmd = hmp_info_savevm,
95259824
WB
51+ },
52+
83faa3fe
TL
53+SRST
54+ ``info savevm``
55+ Show savevm status.
56+ERST
57+
b855dce7 58 {
83faa3fe
TL
59 .name = "balloon",
60 .args_type = "",
95259824 61diff --git a/hmp-commands.hx b/hmp-commands.hx
83faa3fe 62index 7f0f3974ad..81fe305d07 100644
95259824
WB
63--- a/hmp-commands.hx
64+++ b/hmp-commands.hx
83faa3fe
TL
65@@ -1814,3 +1814,35 @@ ERST
66 .flags = "p",
67 },
68
95259824
WB
69+
70+ {
71+ .name = "savevm-start",
72+ .args_type = "statefile:s?",
73+ .params = "[statefile]",
74+ .help = "Prepare for snapshot and halt VM. Save VM state to statefile.",
a544966d 75+ .cmd = hmp_savevm_start,
95259824
WB
76+ },
77+
78+ {
79+ .name = "snapshot-drive",
80+ .args_type = "device:s,name:s",
81+ .params = "device name",
82+ .help = "Create internal snapshot.",
a544966d 83+ .cmd = hmp_snapshot_drive,
95259824
WB
84+ },
85+
86+ {
87+ .name = "delete-drive-snapshot",
88+ .args_type = "device:s,name:s",
89+ .params = "device name",
90+ .help = "Delete internal snapshot.",
a544966d 91+ .cmd = hmp_delete_drive_snapshot,
95259824
WB
92+ },
93+
94+ {
95+ .name = "savevm-end",
96+ .args_type = "",
97+ .params = "",
98+ .help = "Resume VM after snaphot.",
a544966d 99+ .cmd = hmp_savevm_end,
95259824 100+ },
be901f66
SR
101diff --git a/include/migration/snapshot.h b/include/migration/snapshot.h
102index c85b6ec75b..4411b7121d 100644
103--- a/include/migration/snapshot.h
104+++ b/include/migration/snapshot.h
105@@ -17,5 +17,6 @@
106
107 int save_snapshot(const char *name, Error **errp);
108 int load_snapshot(const char *name, Error **errp);
109+int load_snapshot_from_blockdev(const char *filename, Error **errp);
110
111 #endif
112diff --git a/include/monitor/hmp.h b/include/monitor/hmp.h
83faa3fe 113index e33ca5a911..601827d43f 100644
be901f66
SR
114--- a/include/monitor/hmp.h
115+++ b/include/monitor/hmp.h
116@@ -25,6 +25,7 @@ void hmp_info_status(Monitor *mon, const QDict *qdict);
117 void hmp_info_uuid(Monitor *mon, const QDict *qdict);
118 void hmp_info_chardev(Monitor *mon, const QDict *qdict);
119 void hmp_info_mice(Monitor *mon, const QDict *qdict);
120+void hmp_info_savevm(Monitor *mon, const QDict *qdict);
121 void hmp_info_migrate(Monitor *mon, const QDict *qdict);
122 void hmp_info_migrate_capabilities(Monitor *mon, const QDict *qdict);
123 void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict);
83faa3fe 124@@ -83,6 +84,10 @@ void hmp_netdev_add(Monitor *mon, const QDict *qdict);
be901f66
SR
125 void hmp_netdev_del(Monitor *mon, const QDict *qdict);
126 void hmp_getfd(Monitor *mon, const QDict *qdict);
127 void hmp_closefd(Monitor *mon, const QDict *qdict);
128+void hmp_savevm_start(Monitor *mon, const QDict *qdict);
129+void hmp_snapshot_drive(Monitor *mon, const QDict *qdict);
130+void hmp_delete_drive_snapshot(Monitor *mon, const QDict *qdict);
131+void hmp_savevm_end(Monitor *mon, const QDict *qdict);
132 void hmp_sendkey(Monitor *mon, const QDict *qdict);
133 void hmp_screendump(Monitor *mon, const QDict *qdict);
83faa3fe 134 void hmp_chardev_add(Monitor *mon, const QDict *qdict);
be901f66 135diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c
83faa3fe 136index 0c6f6ff331..39c7474cea 100644
be901f66
SR
137--- a/monitor/hmp-cmds.c
138+++ b/monitor/hmp-cmds.c
83faa3fe
TL
139@@ -1876,6 +1876,63 @@ void hmp_info_memory_devices(Monitor *mon, const QDict *qdict)
140 hmp_handle_error(mon, err);
95259824
WB
141 }
142
143+void hmp_savevm_start(Monitor *mon, const QDict *qdict)
144+{
145+ Error *errp = NULL;
146+ const char *statefile = qdict_get_try_str(qdict, "statefile");
147+
148+ qmp_savevm_start(statefile != NULL, statefile, &errp);
83faa3fe 149+ hmp_handle_error(mon, errp);
95259824
WB
150+}
151+
152+void hmp_snapshot_drive(Monitor *mon, const QDict *qdict)
153+{
154+ Error *errp = NULL;
155+ const char *name = qdict_get_str(qdict, "name");
156+ const char *device = qdict_get_str(qdict, "device");
157+
158+ qmp_snapshot_drive(device, name, &errp);
83faa3fe 159+ hmp_handle_error(mon, errp);
95259824
WB
160+}
161+
162+void hmp_delete_drive_snapshot(Monitor *mon, const QDict *qdict)
163+{
164+ Error *errp = NULL;
165+ const char *name = qdict_get_str(qdict, "name");
166+ const char *device = qdict_get_str(qdict, "device");
167+
168+ qmp_delete_drive_snapshot(device, name, &errp);
83faa3fe 169+ hmp_handle_error(mon, errp);
95259824
WB
170+}
171+
172+void hmp_savevm_end(Monitor *mon, const QDict *qdict)
173+{
174+ Error *errp = NULL;
175+
176+ qmp_savevm_end(&errp);
83faa3fe 177+ hmp_handle_error(mon, errp);
95259824
WB
178+}
179+
180+void hmp_info_savevm(Monitor *mon, const QDict *qdict)
181+{
182+ SaveVMInfo *info;
183+ info = qmp_query_savevm(NULL);
184+
185+ if (info->has_status) {
186+ monitor_printf(mon, "savevm status: %s\n", info->status);
187+ monitor_printf(mon, "total time: %" PRIu64 " milliseconds\n",
188+ info->total_time);
189+ } else {
190+ monitor_printf(mon, "savevm status: not running\n");
191+ }
192+ if (info->has_bytes) {
193+ monitor_printf(mon, "Bytes saved: %"PRIu64"\n", info->bytes);
194+ }
195+ if (info->has_error) {
196+ monitor_printf(mon, "Error: %s\n", info->error);
197+ }
198+}
199+
200 void hmp_info_iothreads(Monitor *mon, const QDict *qdict)
201 {
202 IOThreadInfoList *info_list = qmp_query_iothreads(NULL);
6838f038 203diff --git a/qapi/migration.json b/qapi/migration.json
83faa3fe 204index eca2981d0a..081663d67a 100644
6838f038
WB
205--- a/qapi/migration.json
206+++ b/qapi/migration.json
6402d961 207@@ -222,6 +222,40 @@
b855dce7
TL
208 '*compression': 'CompressionStats',
209 '*socket-address': ['SocketAddress'] } }
95259824 210
b855dce7 211+##
a544966d 212+# @SaveVMInfo:
95259824
WB
213+#
214+# Information about current migration process.
215+#
a544966d 216+# @status: string describing the current savevm status.
95259824
WB
217+# This can be 'active', 'completed', 'failed'.
218+# If this field is not returned, no savevm process
219+# has been initiated
220+#
a544966d 221+# @error: string containing error message is status is failed.
95259824 222+#
a544966d 223+# @total-time: total amount of milliseconds since savevm started.
95259824
WB
224+# If savevm has ended, it returns the total save time
225+#
a544966d 226+# @bytes: total amount of data transfered
95259824
WB
227+#
228+# Since: 1.3
229+##
230+{ 'struct': 'SaveVMInfo',
231+ 'data': {'*status': 'str', '*error': 'str',
232+ '*total-time': 'int', '*bytes': 'int'} }
233+
234+##
a544966d 235+# @query-savevm:
95259824
WB
236+#
237+# Returns information about current savevm process.
238+#
239+# Returns: @SaveVMInfo
240+#
241+# Since: 1.3
242+##
243+{ 'command': 'query-savevm', 'returns': 'SaveVMInfo' }
244+
b855dce7 245 ##
a544966d 246 # @query-migrate:
95259824 247 #
53e83913 248diff --git a/qapi/misc.json b/qapi/misc.json
83faa3fe 249index e2a6678eae..0868de22b7 100644
53e83913
WB
250--- a/qapi/misc.json
251+++ b/qapi/misc.json
83faa3fe 252@@ -1165,6 +1165,38 @@
b855dce7 253 ##
6402d961 254 { 'command': 'query-fdsets', 'returns': ['FdsetInfo'] }
53e83913 255
b855dce7 256+##
53e83913
WB
257+# @savevm-start:
258+#
259+# Prepare for snapshot and halt VM. Save VM state to statefile.
260+#
261+##
262+{ 'command': 'savevm-start', 'data': { '*statefile': 'str' } }
263+
264+##
265+# @snapshot-drive:
266+#
267+# Create an internal drive snapshot.
268+#
269+##
270+{ 'command': 'snapshot-drive', 'data': { 'device': 'str', 'name': 'str' } }
271+
272+##
273+# @delete-drive-snapshot:
274+#
275+# Delete a drive snapshot.
276+#
277+##
278+{ 'command': 'delete-drive-snapshot', 'data': { 'device': 'str', 'name': 'str' } }
279+
280+##
281+# @savevm-end:
282+#
283+# Resume VM after a snapshot.
284+#
285+##
286+{ 'command': 'savevm-end' }
287+
b855dce7 288 ##
53e83913
WB
289 # @AcpiTableOptions:
290 #
95259824 291diff --git a/qemu-options.hx b/qemu-options.hx
83faa3fe 292index 16debd03cb..ab80cc46fc 100644
95259824
WB
293--- a/qemu-options.hx
294+++ b/qemu-options.hx
83faa3fe
TL
295@@ -3820,6 +3820,18 @@ SRST
296 Start right away with a saved state (``loadvm`` in monitor)
297 ERST
95259824
WB
298
299+DEF("loadstate", HAS_ARG, QEMU_OPTION_loadstate, \
300+ "-loadstate file\n" \
301+ " start right away with a saved state\n",
302+ QEMU_ARCH_ALL)
83faa3fe
TL
303+SRST
304+``-loadstate file``
305+ Start right away with a saved state. This option does not rollback
306+ disk state like @code{loadvm}, so user must make sure that disk
307+ have correct state. @var{file} can be any valid device URL. See the section
308+ for "Device URL Syntax" for more information.
309+ERST
95259824
WB
310+
311 #ifndef _WIN32
312 DEF("daemonize", 0, QEMU_OPTION_daemonize, \
313 "-daemonize daemonize QEMU after initializing\n", QEMU_ARCH_ALL)
95259824
WB
314diff --git a/savevm-async.c b/savevm-async.c
315new file mode 100644
6402d961 316index 0000000000..54ceeae26c
95259824
WB
317--- /dev/null
318+++ b/savevm-async.c
6402d961 319@@ -0,0 +1,464 @@
95259824 320+#include "qemu/osdep.h"
6838f038
WB
321+#include "migration/migration.h"
322+#include "migration/savevm.h"
323+#include "migration/snapshot.h"
324+#include "migration/global_state.h"
325+#include "migration/ram.h"
326+#include "migration/qemu-file.h"
95259824 327+#include "sysemu/sysemu.h"
6402d961 328+#include "sysemu/runstate.h"
95259824 329+#include "block/block.h"
95259824 330+#include "sysemu/block-backend.h"
53e83913
WB
331+#include "qapi/error.h"
332+#include "qapi/qmp/qerror.h"
333+#include "qapi/qmp/qdict.h"
334+#include "qapi/qapi-commands-migration.h"
335+#include "qapi/qapi-commands-misc.h"
0775f12b 336+#include "qapi/qapi-commands-block.h"
95259824 337+#include "qemu/cutils.h"
6402d961
TL
338+#include "qemu/main-loop.h"
339+#include "qemu/rcu.h"
95259824
WB
340+
341+/* #define DEBUG_SAVEVM_STATE */
342+
0775f12b
WB
343+/* used while emulated sync operation in progress */
344+#define NOT_DONE -EINPROGRESS
67af0fa4 345+
95259824
WB
346+#ifdef DEBUG_SAVEVM_STATE
347+#define DPRINTF(fmt, ...) \
348+ do { printf("savevm-async: " fmt, ## __VA_ARGS__); } while (0)
349+#else
350+#define DPRINTF(fmt, ...) \
351+ do { } while (0)
352+#endif
353+
354+enum {
355+ SAVE_STATE_DONE,
356+ SAVE_STATE_ERROR,
357+ SAVE_STATE_ACTIVE,
358+ SAVE_STATE_COMPLETED,
359+ SAVE_STATE_CANCELLED
360+};
361+
362+
363+static struct SnapshotState {
67af0fa4 364+ BlockBackend *target;
95259824
WB
365+ size_t bs_pos;
366+ int state;
367+ Error *error;
368+ Error *blocker;
369+ int saved_vm_running;
370+ QEMUFile *file;
371+ int64_t total_time;
0775f12b
WB
372+ QEMUBH *cleanup_bh;
373+ QemuThread thread;
95259824
WB
374+} snap_state;
375+
376+SaveVMInfo *qmp_query_savevm(Error **errp)
377+{
378+ SaveVMInfo *info = g_malloc0(sizeof(*info));
379+ struct SnapshotState *s = &snap_state;
380+
381+ if (s->state != SAVE_STATE_DONE) {
382+ info->has_bytes = true;
383+ info->bytes = s->bs_pos;
384+ switch (s->state) {
385+ case SAVE_STATE_ERROR:
386+ info->has_status = true;
387+ info->status = g_strdup("failed");
388+ info->has_total_time = true;
389+ info->total_time = s->total_time;
390+ if (s->error) {
391+ info->has_error = true;
392+ info->error = g_strdup(error_get_pretty(s->error));
393+ }
394+ break;
395+ case SAVE_STATE_ACTIVE:
396+ info->has_status = true;
397+ info->status = g_strdup("active");
398+ info->has_total_time = true;
399+ info->total_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME)
400+ - s->total_time;
401+ break;
402+ case SAVE_STATE_COMPLETED:
403+ info->has_status = true;
404+ info->status = g_strdup("completed");
405+ info->has_total_time = true;
406+ info->total_time = s->total_time;
407+ break;
408+ }
409+ }
410+
411+ return info;
412+}
413+
414+static int save_snapshot_cleanup(void)
415+{
416+ int ret = 0;
417+
418+ DPRINTF("save_snapshot_cleanup\n");
419+
420+ snap_state.total_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) -
421+ snap_state.total_time;
422+
423+ if (snap_state.file) {
424+ ret = qemu_fclose(snap_state.file);
425+ }
426+
67af0fa4 427+ if (snap_state.target) {
95259824 428+ /* try to truncate, but ignore errors (will fail on block devices).
6402d961
TL
429+ * note1: bdrv_read() need whole blocks, so we need to round up
430+ * note2: PVE requires 1024 (BDRV_SECTOR_SIZE*2) alignment
95259824 431+ */
6402d961
TL
432+ size_t size = QEMU_ALIGN_UP(snap_state.bs_pos, BDRV_SECTOR_SIZE*2);
433+ blk_truncate(snap_state.target, size, false, PREALLOC_MODE_OFF, NULL);
67af0fa4 434+ blk_op_unblock_all(snap_state.target, snap_state.blocker);
95259824
WB
435+ error_free(snap_state.blocker);
436+ snap_state.blocker = NULL;
67af0fa4
WB
437+ blk_unref(snap_state.target);
438+ snap_state.target = NULL;
95259824
WB
439+ }
440+
441+ return ret;
442+}
443+
444+static void save_snapshot_error(const char *fmt, ...)
445+{
446+ va_list ap;
447+ char *msg;
448+
449+ va_start(ap, fmt);
450+ msg = g_strdup_vprintf(fmt, ap);
451+ va_end(ap);
452+
453+ DPRINTF("save_snapshot_error: %s\n", msg);
454+
455+ if (!snap_state.error) {
456+ error_set(&snap_state.error, ERROR_CLASS_GENERIC_ERROR, "%s", msg);
457+ }
458+
459+ g_free (msg);
460+
461+ snap_state.state = SAVE_STATE_ERROR;
95259824
WB
462+}
463+
6402d961 464+static int block_state_close(void *opaque, Error **errp)
95259824
WB
465+{
466+ snap_state.file = NULL;
67af0fa4 467+ return blk_flush(snap_state.target);
95259824
WB
468+}
469+
0775f12b
WB
470+typedef struct BlkRwCo {
471+ int64_t offset;
472+ QEMUIOVector *qiov;
473+ ssize_t ret;
474+} BlkRwCo;
475+
476+static void coroutine_fn block_state_write_entry(void *opaque) {
477+ BlkRwCo *rwco = opaque;
478+ rwco->ret = blk_co_pwritev(snap_state.target, rwco->offset, rwco->qiov->size,
479+ rwco->qiov, 0);
480+}
481+
67af0fa4 482+static ssize_t block_state_writev_buffer(void *opaque, struct iovec *iov,
6402d961 483+ int iovcnt, int64_t pos, Error **errp)
95259824 484+{
67af0fa4 485+ QEMUIOVector qiov;
0775f12b
WB
486+ BlkRwCo rwco;
487+
488+ assert(pos == snap_state.bs_pos);
489+ rwco = (BlkRwCo) {
490+ .offset = pos,
491+ .qiov = &qiov,
492+ .ret = NOT_DONE,
493+ };
95259824 494+
67af0fa4 495+ qemu_iovec_init_external(&qiov, iov, iovcnt);
0775f12b
WB
496+
497+ if (qemu_in_coroutine()) {
498+ block_state_write_entry(&rwco);
499+ } else {
500+ Coroutine *co = qemu_coroutine_create(&block_state_write_entry, &rwco);
501+ bdrv_coroutine_enter(blk_bs(snap_state.target), co);
502+ BDRV_POLL_WHILE(blk_bs(snap_state.target), rwco.ret == NOT_DONE);
95259824 503+ }
0775f12b
WB
504+ if (rwco.ret < 0) {
505+ return rwco.ret;
506+ }
507+
67af0fa4
WB
508+ snap_state.bs_pos += qiov.size;
509+ return qiov.size;
95259824
WB
510+}
511+
0775f12b
WB
512+static const QEMUFileOps block_file_ops = {
513+ .writev_buffer = block_state_writev_buffer,
514+ .close = block_state_close,
515+};
516+
517+static void process_savevm_cleanup(void *opaque)
518+{
519+ int ret;
520+ qemu_bh_delete(snap_state.cleanup_bh);
521+ snap_state.cleanup_bh = NULL;
522+ qemu_mutex_unlock_iothread();
523+ qemu_thread_join(&snap_state.thread);
524+ qemu_mutex_lock_iothread();
525+ ret = save_snapshot_cleanup();
526+ if (ret < 0) {
527+ save_snapshot_error("save_snapshot_cleanup error %d", ret);
528+ } else if (snap_state.state == SAVE_STATE_ACTIVE) {
529+ snap_state.state = SAVE_STATE_COMPLETED;
530+ } else {
531+ save_snapshot_error("process_savevm_cleanup: invalid state: %d",
532+ snap_state.state);
95259824 533+ }
0775f12b
WB
534+ if (snap_state.saved_vm_running) {
535+ vm_start();
536+ snap_state.saved_vm_running = false;
95259824 537+ }
95259824
WB
538+}
539+
0775f12b 540+static void *process_savevm_thread(void *opaque)
95259824
WB
541+{
542+ int ret;
543+ int64_t maxlen;
95259824 544+
0775f12b 545+ rcu_register_thread();
95259824 546+
95259824 547+ qemu_savevm_state_header(snap_state.file);
6838f038
WB
548+ qemu_savevm_state_setup(snap_state.file);
549+ ret = qemu_file_get_error(snap_state.file);
95259824
WB
550+
551+ if (ret < 0) {
6838f038 552+ save_snapshot_error("qemu_savevm_state_setup failed");
0775f12b
WB
553+ rcu_unregister_thread();
554+ return NULL;
95259824
WB
555+ }
556+
557+ while (snap_state.state == SAVE_STATE_ACTIVE) {
0775f12b 558+ uint64_t pending_size, pend_precopy, pend_compatible, pend_postcopy;
95259824 559+
0775f12b
WB
560+ qemu_savevm_state_pending(snap_state.file, 0, &pend_precopy, &pend_compatible, &pend_postcopy);
561+ pending_size = pend_precopy + pend_compatible + pend_postcopy;
95259824 562+
0775f12b
WB
563+ maxlen = blk_getlength(snap_state.target) - 30*1024*1024;
564+
565+ if (pending_size > 400000 && snap_state.bs_pos + pending_size < maxlen) {
566+ qemu_mutex_lock_iothread();
567+ ret = qemu_savevm_state_iterate(snap_state.file, false);
568+ if (ret < 0) {
569+ save_snapshot_error("qemu_savevm_state_iterate error %d", ret);
570+ break;
571+ }
572+ qemu_mutex_unlock_iothread();
573+ DPRINTF("savevm inerate pending size %lu ret %d\n", pending_size, ret);
95259824 574+ } else {
0775f12b 575+ qemu_mutex_lock_iothread();
b855dce7 576+ qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL);
0775f12b
WB
577+ ret = global_state_store();
578+ if (ret) {
579+ save_snapshot_error("global_state_store error %d", ret);
95259824 580+ break;
0775f12b
WB
581+ }
582+ ret = vm_stop_force_state(RUN_STATE_FINISH_MIGRATE);
583+ if (ret < 0) {
584+ save_snapshot_error("vm_stop_force_state error %d", ret);
585+ break;
586+ }
95259824 587+ DPRINTF("savevm inerate finished\n");
6838f038
WB
588+ /* upstream made the return value here inconsistent
589+ * (-1 instead of 'ret' in one case and 0 after flush which can
590+ * still set a file error...)
591+ */
592+ (void)qemu_savevm_state_complete_precopy(snap_state.file, false, false);
593+ ret = qemu_file_get_error(snap_state.file);
594+ if (ret < 0) {
595+ save_snapshot_error("qemu_savevm_state_iterate error %d", ret);
596+ break;
597+ }
0775f12b 598+ qemu_savevm_state_cleanup();
95259824 599+ DPRINTF("save complete\n");
95259824
WB
600+ break;
601+ }
95259824
WB
602+ }
603+
0775f12b
WB
604+ qemu_bh_schedule(snap_state.cleanup_bh);
605+ qemu_mutex_unlock_iothread();
95259824 606+
0775f12b
WB
607+ rcu_unregister_thread();
608+ return NULL;
95259824
WB
609+}
610+
95259824
WB
611+void qmp_savevm_start(bool has_statefile, const char *statefile, Error **errp)
612+{
95259824
WB
613+ Error *local_err = NULL;
614+
67af0fa4 615+ int bdrv_oflags = BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_NO_FLUSH;
95259824
WB
616+
617+ if (snap_state.state != SAVE_STATE_DONE) {
618+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
619+ "VM snapshot already started\n");
620+ return;
621+ }
622+
623+ /* initialize snapshot info */
624+ snap_state.saved_vm_running = runstate_is_running();
625+ snap_state.bs_pos = 0;
626+ snap_state.total_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
627+ snap_state.blocker = NULL;
628+
629+ if (snap_state.error) {
630+ error_free(snap_state.error);
631+ snap_state.error = NULL;
632+ }
633+
634+ if (!has_statefile) {
635+ vm_stop(RUN_STATE_SAVE_VM);
636+ snap_state.state = SAVE_STATE_COMPLETED;
637+ return;
638+ }
639+
640+ if (qemu_savevm_state_blocked(errp)) {
641+ return;
642+ }
643+
644+ /* Open the image */
95259824
WB
645+ QDict *options = NULL;
646+ options = qdict_new();
53e83913 647+ qdict_put_str(options, "driver", "raw");
67af0fa4
WB
648+ snap_state.target = blk_new_open(statefile, NULL, options, bdrv_oflags, &local_err);
649+ if (!snap_state.target) {
95259824
WB
650+ error_set(errp, ERROR_CLASS_GENERIC_ERROR, "failed to open '%s'", statefile);
651+ goto restart;
652+ }
653+
654+ snap_state.file = qemu_fopen_ops(&snap_state, &block_file_ops);
655+
656+ if (!snap_state.file) {
657+ error_set(errp, ERROR_CLASS_GENERIC_ERROR, "failed to open '%s'", statefile);
658+ goto restart;
659+ }
660+
661+
662+ error_setg(&snap_state.blocker, "block device is in use by savevm");
67af0fa4 663+ blk_op_block_all(snap_state.target, snap_state.blocker);
95259824 664+
0775f12b
WB
665+ snap_state.state = SAVE_STATE_ACTIVE;
666+ snap_state.cleanup_bh = qemu_bh_new(process_savevm_cleanup, &snap_state);
667+ qemu_thread_create(&snap_state.thread, "savevm-async", process_savevm_thread,
668+ NULL, QEMU_THREAD_JOINABLE);
95259824
WB
669+
670+ return;
671+
672+restart:
673+
674+ save_snapshot_error("setup failed");
675+
676+ if (snap_state.saved_vm_running) {
677+ vm_start();
678+ }
679+}
680+
681+void qmp_savevm_end(Error **errp)
682+{
683+ if (snap_state.state == SAVE_STATE_DONE) {
684+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
685+ "VM snapshot not started\n");
686+ return;
687+ }
688+
689+ if (snap_state.state == SAVE_STATE_ACTIVE) {
690+ snap_state.state = SAVE_STATE_CANCELLED;
691+ return;
692+ }
693+
694+ if (snap_state.saved_vm_running) {
695+ vm_start();
696+ }
697+
698+ snap_state.state = SAVE_STATE_DONE;
699+}
700+
0775f12b 701+// FIXME: Deprecated
95259824
WB
702+void qmp_snapshot_drive(const char *device, const char *name, Error **errp)
703+{
0775f12b
WB
704+ // Compatibility to older qemu-server.
705+ qmp_blockdev_snapshot_internal_sync(device, name, errp);
95259824
WB
706+}
707+
0775f12b 708+// FIXME: Deprecated
95259824
WB
709+void qmp_delete_drive_snapshot(const char *device, const char *name,
710+ Error **errp)
711+{
0775f12b
WB
712+ // Compatibility to older qemu-server.
713+ (void)qmp_blockdev_snapshot_delete_internal_sync(device, false, NULL,
714+ true, name, errp);
95259824
WB
715+}
716+
67af0fa4 717+static ssize_t loadstate_get_buffer(void *opaque, uint8_t *buf, int64_t pos,
6402d961 718+ size_t size, Error **errp)
95259824 719+{
67af0fa4
WB
720+ BlockBackend *be = opaque;
721+ int64_t maxlen = blk_getlength(be);
95259824
WB
722+ if (pos > maxlen) {
723+ return -EIO;
724+ }
725+ if ((pos + size) > maxlen) {
726+ size = maxlen - pos - 1;
727+ }
728+ if (size == 0) {
729+ return 0;
730+ }
67af0fa4 731+ return blk_pread(be, pos, buf, size);
95259824
WB
732+}
733+
734+static const QEMUFileOps loadstate_file_ops = {
735+ .get_buffer = loadstate_get_buffer,
736+};
737+
6838f038 738+int load_snapshot_from_blockdev(const char *filename, Error **errp)
95259824 739+{
67af0fa4 740+ BlockBackend *be;
95259824
WB
741+ Error *local_err = NULL;
742+ Error *blocker = NULL;
743+
744+ QEMUFile *f;
67af0fa4 745+ int ret = -EINVAL;
95259824 746+
67af0fa4 747+ be = blk_new_open(filename, NULL, NULL, 0, &local_err);
95259824 748+
67af0fa4 749+ if (!be) {
6838f038 750+ error_setg(errp, "Could not open VM state file");
95259824
WB
751+ goto the_end;
752+ }
753+
67af0fa4
WB
754+ error_setg(&blocker, "block device is in use by load state");
755+ blk_op_block_all(be, blocker);
756+
95259824 757+ /* restore the VM state */
67af0fa4 758+ f = qemu_fopen_ops(be, &loadstate_file_ops);
95259824 759+ if (!f) {
6838f038 760+ error_setg(errp, "Could not open VM state file");
95259824
WB
761+ goto the_end;
762+ }
763+
6838f038 764+ qemu_system_reset(SHUTDOWN_CAUSE_NONE);
95259824
WB
765+ ret = qemu_loadvm_state(f);
766+
767+ qemu_fclose(f);
768+ migration_incoming_state_destroy();
769+ if (ret < 0) {
6838f038 770+ error_setg_errno(errp, -ret, "Error while loading VM state");
95259824
WB
771+ goto the_end;
772+ }
773+
774+ ret = 0;
775+
776+ the_end:
67af0fa4
WB
777+ if (be) {
778+ blk_op_unblock_all(be, blocker);
95259824 779+ error_free(blocker);
67af0fa4 780+ blk_unref(be);
95259824
WB
781+ }
782+ return ret;
783+}
83faa3fe
TL
784diff --git a/softmmu/vl.c b/softmmu/vl.c
785index 58a40bcfc1..312fa8ccda 100644
786--- a/softmmu/vl.c
787+++ b/softmmu/vl.c
788@@ -2827,6 +2827,7 @@ void qemu_init(int argc, char **argv, char **envp)
95259824
WB
789 int optind;
790 const char *optarg;
791 const char *loadvm = NULL;
792+ const char *loadstate = NULL;
793 MachineClass *machine_class;
be901f66 794 const char *cpu_option;
95259824 795 const char *vga_model = NULL;
83faa3fe 796@@ -3391,6 +3392,9 @@ void qemu_init(int argc, char **argv, char **envp)
95259824
WB
797 case QEMU_OPTION_loadvm:
798 loadvm = optarg;
799 break;
800+ case QEMU_OPTION_loadstate:
801+ loadstate = optarg;
802+ break;
803 case QEMU_OPTION_full_screen:
53e83913
WB
804 dpy.has_full_screen = true;
805 dpy.full_screen = true;
83faa3fe 806@@ -4442,6 +4446,12 @@ void qemu_init(int argc, char **argv, char **envp)
95259824 807 autostart = 0;
b855dce7 808 exit(1);
95259824
WB
809 }
810+ } else if (loadstate) {
6838f038
WB
811+ Error *local_err = NULL;
812+ if (load_snapshot_from_blockdev(loadstate, &local_err) < 0) {
813+ error_report_err(local_err);
95259824
WB
814+ autostart = 0;
815+ }
816 }
b855dce7
TL
817 if (replay_mode != REPLAY_MODE_NONE) {
818 replay_vmstate_init();