]> git.proxmox.com Git - pve-qemu.git/blame - debian/patches/pve/0010-internal-snapshot-async.patch
bump version to 2.11.1-1
[pve-qemu.git] / debian / patches / pve / 0010-internal-snapshot-async.patch
CommitLineData
23102ed6 1From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
95259824
WB
2From: Wolfgang Bumiller <w.bumiller@proxmox.com>
3Date: Wed, 9 Dec 2015 16:04:32 +0100
23102ed6 4Subject: [PATCH] internal snapshot async
95259824
WB
5
6---
6838f038
WB
7 Makefile.objs | 2 +-
8 hmp-commands-info.hx | 13 ++
9 hmp-commands.hx | 32 +++
10 hmp.c | 57 +++++
11 hmp.h | 5 +
12 include/migration/snapshot.h | 1 +
13 qapi-schema.json | 32 +++
14 qapi/migration.json | 34 +++
15 qemu-options.hx | 13 ++
16 savevm-async.c | 524 +++++++++++++++++++++++++++++++++++++++++++
17 vl.c | 10 +
18 11 files changed, 722 insertions(+), 1 deletion(-)
95259824
WB
19 create mode 100644 savevm-async.c
20
21diff --git a/Makefile.objs b/Makefile.objs
6838f038 22index 285c6f3c15..686247b556 100644
95259824
WB
23--- a/Makefile.objs
24+++ b/Makefile.objs
6838f038
WB
25@@ -41,6 +41,7 @@ io-obj-y = io/
26 ifeq ($(CONFIG_SOFTMMU),y)
27 common-obj-y = blockdev.o blockdev-nbd.o block/
28 common-obj-y += bootdevice.o iothread.o
95259824 29+common-obj-y += savevm-async.o
6838f038
WB
30 common-obj-y += net/
31 common-obj-y += qdev-monitor.o device-hotplug.o
32 common-obj-$(CONFIG_WIN32) += os-win32.o
33@@ -49,7 +50,6 @@ common-obj-$(CONFIG_POSIX) += os-posix.o
34 common-obj-$(CONFIG_LINUX) += fsdev/
95259824 35
6838f038
WB
36 common-obj-y += migration/
37-
38 common-obj-y += audio/
39 common-obj-y += hw/
95259824 40
95259824 41diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx
6838f038 42index 54c3e5eac6..3bf69a193c 100644
95259824
WB
43--- a/hmp-commands-info.hx
44+++ b/hmp-commands-info.hx
6838f038 45@@ -566,6 +566,19 @@ Show current migration xbzrle cache size.
95259824
WB
46 ETEXI
47
48 {
49+ .name = "savevm",
50+ .args_type = "",
51+ .params = "",
52+ .help = "show savevm status",
a544966d 53+ .cmd = hmp_info_savevm,
95259824
WB
54+ },
55+
56+STEXI
57+@item info savevm
58+show savevm status
59+ETEXI
60+
61+ {
62 .name = "balloon",
63 .args_type = "",
64 .params = "",
65diff --git a/hmp-commands.hx b/hmp-commands.hx
6838f038 66index 4afd57cf5f..b35bc6ab6c 100644
95259824
WB
67--- a/hmp-commands.hx
68+++ b/hmp-commands.hx
6838f038 69@@ -1873,3 +1873,35 @@ ETEXI
95259824
WB
70 STEXI
71 @end table
72 ETEXI
73+
74+ {
75+ .name = "savevm-start",
76+ .args_type = "statefile:s?",
77+ .params = "[statefile]",
78+ .help = "Prepare for snapshot and halt VM. Save VM state to statefile.",
a544966d 79+ .cmd = hmp_savevm_start,
95259824
WB
80+ },
81+
82+ {
83+ .name = "snapshot-drive",
84+ .args_type = "device:s,name:s",
85+ .params = "device name",
86+ .help = "Create internal snapshot.",
a544966d 87+ .cmd = hmp_snapshot_drive,
95259824
WB
88+ },
89+
90+ {
91+ .name = "delete-drive-snapshot",
92+ .args_type = "device:s,name:s",
93+ .params = "device name",
94+ .help = "Delete internal snapshot.",
a544966d 95+ .cmd = hmp_delete_drive_snapshot,
95259824
WB
96+ },
97+
98+ {
99+ .name = "savevm-end",
100+ .args_type = "",
101+ .params = "",
102+ .help = "Resume VM after snaphot.",
a544966d 103+ .cmd = hmp_savevm_end,
95259824
WB
104+ },
105diff --git a/hmp.c b/hmp.c
6838f038 106index 4e1d571003..b9ade681f0 100644
95259824
WB
107--- a/hmp.c
108+++ b/hmp.c
6838f038
WB
109@@ -2486,6 +2486,63 @@ void hmp_info_memory_devices(Monitor *mon, const QDict *qdict)
110 hmp_handle_error(mon, &err);
95259824
WB
111 }
112
113+void hmp_savevm_start(Monitor *mon, const QDict *qdict)
114+{
115+ Error *errp = NULL;
116+ const char *statefile = qdict_get_try_str(qdict, "statefile");
117+
118+ qmp_savevm_start(statefile != NULL, statefile, &errp);
119+ hmp_handle_error(mon, &errp);
120+}
121+
122+void hmp_snapshot_drive(Monitor *mon, const QDict *qdict)
123+{
124+ Error *errp = NULL;
125+ const char *name = qdict_get_str(qdict, "name");
126+ const char *device = qdict_get_str(qdict, "device");
127+
128+ qmp_snapshot_drive(device, name, &errp);
129+ hmp_handle_error(mon, &errp);
130+}
131+
132+void hmp_delete_drive_snapshot(Monitor *mon, const QDict *qdict)
133+{
134+ Error *errp = NULL;
135+ const char *name = qdict_get_str(qdict, "name");
136+ const char *device = qdict_get_str(qdict, "device");
137+
138+ qmp_delete_drive_snapshot(device, name, &errp);
139+ hmp_handle_error(mon, &errp);
140+}
141+
142+void hmp_savevm_end(Monitor *mon, const QDict *qdict)
143+{
144+ Error *errp = NULL;
145+
146+ qmp_savevm_end(&errp);
147+ hmp_handle_error(mon, &errp);
148+}
149+
150+void hmp_info_savevm(Monitor *mon, const QDict *qdict)
151+{
152+ SaveVMInfo *info;
153+ info = qmp_query_savevm(NULL);
154+
155+ if (info->has_status) {
156+ monitor_printf(mon, "savevm status: %s\n", info->status);
157+ monitor_printf(mon, "total time: %" PRIu64 " milliseconds\n",
158+ info->total_time);
159+ } else {
160+ monitor_printf(mon, "savevm status: not running\n");
161+ }
162+ if (info->has_bytes) {
163+ monitor_printf(mon, "Bytes saved: %"PRIu64"\n", info->bytes);
164+ }
165+ if (info->has_error) {
166+ monitor_printf(mon, "Error: %s\n", info->error);
167+ }
168+}
169+
170 void hmp_info_iothreads(Monitor *mon, const QDict *qdict)
171 {
172 IOThreadInfoList *info_list = qmp_query_iothreads(NULL);
173diff --git a/hmp.h b/hmp.h
6838f038 174index a6f56b1f29..45ada581b6 100644
95259824
WB
175--- a/hmp.h
176+++ b/hmp.h
177@@ -26,6 +26,7 @@ void hmp_info_status(Monitor *mon, const QDict *qdict);
178 void hmp_info_uuid(Monitor *mon, const QDict *qdict);
179 void hmp_info_chardev(Monitor *mon, const QDict *qdict);
180 void hmp_info_mice(Monitor *mon, const QDict *qdict);
181+void hmp_info_savevm(Monitor *mon, const QDict *qdict);
182 void hmp_info_migrate(Monitor *mon, const QDict *qdict);
183 void hmp_info_migrate_capabilities(Monitor *mon, const QDict *qdict);
184 void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict);
6838f038 185@@ -97,6 +98,10 @@ void hmp_netdev_add(Monitor *mon, const QDict *qdict);
95259824
WB
186 void hmp_netdev_del(Monitor *mon, const QDict *qdict);
187 void hmp_getfd(Monitor *mon, const QDict *qdict);
188 void hmp_closefd(Monitor *mon, const QDict *qdict);
189+void hmp_savevm_start(Monitor *mon, const QDict *qdict);
190+void hmp_snapshot_drive(Monitor *mon, const QDict *qdict);
191+void hmp_delete_drive_snapshot(Monitor *mon, const QDict *qdict);
192+void hmp_savevm_end(Monitor *mon, const QDict *qdict);
193 void hmp_sendkey(Monitor *mon, const QDict *qdict);
194 void hmp_screendump(Monitor *mon, const QDict *qdict);
195 void hmp_nbd_server_start(Monitor *mon, const QDict *qdict);
6838f038
WB
196diff --git a/include/migration/snapshot.h b/include/migration/snapshot.h
197index c85b6ec75b..4411b7121d 100644
198--- a/include/migration/snapshot.h
199+++ b/include/migration/snapshot.h
200@@ -17,5 +17,6 @@
95259824 201
6838f038
WB
202 int save_snapshot(const char *name, Error **errp);
203 int load_snapshot(const char *name, Error **errp);
204+int load_snapshot_from_blockdev(const char *filename, Error **errp);
95259824 205
6838f038 206 #endif
95259824 207diff --git a/qapi-schema.json b/qapi-schema.json
6838f038 208index 8f436ba1f3..348b527681 100644
95259824
WB
209--- a/qapi-schema.json
210+++ b/qapi-schema.json
6838f038
WB
211@@ -2439,6 +2439,38 @@
212 { 'command': 'query-target', 'returns': 'TargetInfo' }
213
214 ##
215+# @savevm-start:
216+#
217+# Prepare for snapshot and halt VM. Save VM state to statefile.
218+#
219+##
220+{ 'command': 'savevm-start', 'data': { '*statefile': 'str' } }
221+
222+##
223+# @snapshot-drive:
224+#
225+# Create an internal drive snapshot.
226+#
227+##
228+{ 'command': 'snapshot-drive', 'data': { 'device': 'str', 'name': 'str' } }
229+
230+##
231+# @delete-drive-snapshot:
232+#
233+# Delete a drive snapshot.
234+#
235+##
236+{ 'command': 'delete-drive-snapshot', 'data': { 'device': 'str', 'name': 'str' } }
237+
238+##
239+# @savevm-end:
240+#
241+# Resume VM after a snapshot.
242+#
243+##
244+{ 'command': 'savevm-end' }
245+
246+##
247 # @AcpiTableOptions:
248 #
249 # Specify an ACPI table on the command line to load.
250diff --git a/qapi/migration.json b/qapi/migration.json
251index 03f57c9616..9ae55b81a2 100644
252--- a/qapi/migration.json
253+++ b/qapi/migration.json
254@@ -170,6 +170,40 @@
95259824
WB
255 '*error-desc': 'str'} }
256
a544966d
WB
257 ##
258+# @SaveVMInfo:
95259824
WB
259+#
260+# Information about current migration process.
261+#
a544966d 262+# @status: string describing the current savevm status.
95259824
WB
263+# This can be 'active', 'completed', 'failed'.
264+# If this field is not returned, no savevm process
265+# has been initiated
266+#
a544966d 267+# @error: string containing error message is status is failed.
95259824 268+#
a544966d 269+# @total-time: total amount of milliseconds since savevm started.
95259824
WB
270+# If savevm has ended, it returns the total save time
271+#
a544966d 272+# @bytes: total amount of data transfered
95259824
WB
273+#
274+# Since: 1.3
275+##
276+{ 'struct': 'SaveVMInfo',
277+ 'data': {'*status': 'str', '*error': 'str',
278+ '*total-time': 'int', '*bytes': 'int'} }
279+
280+##
a544966d 281+# @query-savevm:
95259824
WB
282+#
283+# Returns information about current savevm process.
284+#
285+# Returns: @SaveVMInfo
286+#
287+# Since: 1.3
288+##
289+{ 'command': 'query-savevm', 'returns': 'SaveVMInfo' }
290+
291+##
a544966d 292 # @query-migrate:
95259824 293 #
a544966d 294 # Returns information about current migration process. If migration
95259824 295diff --git a/qemu-options.hx b/qemu-options.hx
6838f038 296index 57f2c6a75f..7c054af8f9 100644
95259824
WB
297--- a/qemu-options.hx
298+++ b/qemu-options.hx
6838f038 299@@ -3698,6 +3698,19 @@ STEXI
95259824
WB
300 Start right away with a saved state (@code{loadvm} in monitor)
301 ETEXI
302
303+DEF("loadstate", HAS_ARG, QEMU_OPTION_loadstate, \
304+ "-loadstate file\n" \
305+ " start right away with a saved state\n",
306+ QEMU_ARCH_ALL)
307+STEXI
308+@item -loadstate @var{file}
309+@findex -loadstate
310+Start right away with a saved state. This option does not rollback
311+disk state like @code{loadvm}, so user must make sure that disk
312+have correct state. @var{file} can be any valid device URL. See the section
313+for "Device URL Syntax" for more information.
314+ETEXI
315+
316 #ifndef _WIN32
317 DEF("daemonize", 0, QEMU_OPTION_daemonize, \
318 "-daemonize daemonize QEMU after initializing\n", QEMU_ARCH_ALL)
95259824
WB
319diff --git a/savevm-async.c b/savevm-async.c
320new file mode 100644
6838f038 321index 0000000000..897134ab5a
95259824
WB
322--- /dev/null
323+++ b/savevm-async.c
6838f038 324@@ -0,0 +1,524 @@
95259824 325+#include "qemu/osdep.h"
6838f038
WB
326+#include "migration/migration.h"
327+#include "migration/savevm.h"
328+#include "migration/snapshot.h"
329+#include "migration/global_state.h"
330+#include "migration/ram.h"
331+#include "migration/qemu-file.h"
95259824 332+#include "qapi/qmp/qerror.h"
95259824
WB
333+#include "sysemu/sysemu.h"
334+#include "qmp-commands.h"
95259824 335+#include "block/block.h"
95259824
WB
336+#include "sysemu/block-backend.h"
337+#include "qapi/qmp/qstring.h"
95259824
WB
338+#include "qemu/cutils.h"
339+
340+/* #define DEBUG_SAVEVM_STATE */
341+
67af0fa4
WB
342+#define NOT_DONE 0x7fffffff /* used while emulated sync operation in progress */
343+
95259824
WB
344+#ifdef DEBUG_SAVEVM_STATE
345+#define DPRINTF(fmt, ...) \
346+ do { printf("savevm-async: " fmt, ## __VA_ARGS__); } while (0)
347+#else
348+#define DPRINTF(fmt, ...) \
349+ do { } while (0)
350+#endif
351+
352+enum {
353+ SAVE_STATE_DONE,
354+ SAVE_STATE_ERROR,
355+ SAVE_STATE_ACTIVE,
356+ SAVE_STATE_COMPLETED,
357+ SAVE_STATE_CANCELLED
358+};
359+
360+
361+static struct SnapshotState {
67af0fa4 362+ BlockBackend *target;
95259824
WB
363+ size_t bs_pos;
364+ int state;
365+ Error *error;
366+ Error *blocker;
367+ int saved_vm_running;
368+ QEMUFile *file;
369+ int64_t total_time;
370+} snap_state;
371+
372+SaveVMInfo *qmp_query_savevm(Error **errp)
373+{
374+ SaveVMInfo *info = g_malloc0(sizeof(*info));
375+ struct SnapshotState *s = &snap_state;
376+
377+ if (s->state != SAVE_STATE_DONE) {
378+ info->has_bytes = true;
379+ info->bytes = s->bs_pos;
380+ switch (s->state) {
381+ case SAVE_STATE_ERROR:
382+ info->has_status = true;
383+ info->status = g_strdup("failed");
384+ info->has_total_time = true;
385+ info->total_time = s->total_time;
386+ if (s->error) {
387+ info->has_error = true;
388+ info->error = g_strdup(error_get_pretty(s->error));
389+ }
390+ break;
391+ case SAVE_STATE_ACTIVE:
392+ info->has_status = true;
393+ info->status = g_strdup("active");
394+ info->has_total_time = true;
395+ info->total_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME)
396+ - s->total_time;
397+ break;
398+ case SAVE_STATE_COMPLETED:
399+ info->has_status = true;
400+ info->status = g_strdup("completed");
401+ info->has_total_time = true;
402+ info->total_time = s->total_time;
403+ break;
404+ }
405+ }
406+
407+ return info;
408+}
409+
410+static int save_snapshot_cleanup(void)
411+{
412+ int ret = 0;
413+
414+ DPRINTF("save_snapshot_cleanup\n");
415+
416+ snap_state.total_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) -
417+ snap_state.total_time;
418+
419+ if (snap_state.file) {
420+ ret = qemu_fclose(snap_state.file);
421+ }
422+
67af0fa4 423+ if (snap_state.target) {
95259824
WB
424+ /* try to truncate, but ignore errors (will fail on block devices).
425+ * note: bdrv_read() need whole blocks, so we round up
426+ */
427+ size_t size = (snap_state.bs_pos + BDRV_SECTOR_SIZE) & BDRV_SECTOR_MASK;
6838f038 428+ blk_truncate(snap_state.target, size, PREALLOC_MODE_OFF, NULL);
67af0fa4 429+ blk_op_unblock_all(snap_state.target, snap_state.blocker);
95259824
WB
430+ error_free(snap_state.blocker);
431+ snap_state.blocker = NULL;
67af0fa4
WB
432+ blk_unref(snap_state.target);
433+ snap_state.target = NULL;
95259824
WB
434+ }
435+
436+ return ret;
437+}
438+
439+static void save_snapshot_error(const char *fmt, ...)
440+{
441+ va_list ap;
442+ char *msg;
443+
444+ va_start(ap, fmt);
445+ msg = g_strdup_vprintf(fmt, ap);
446+ va_end(ap);
447+
448+ DPRINTF("save_snapshot_error: %s\n", msg);
449+
450+ if (!snap_state.error) {
451+ error_set(&snap_state.error, ERROR_CLASS_GENERIC_ERROR, "%s", msg);
452+ }
453+
454+ g_free (msg);
455+
456+ snap_state.state = SAVE_STATE_ERROR;
457+
458+ save_snapshot_cleanup();
459+}
460+
461+static void save_snapshot_completed(void)
462+{
463+ DPRINTF("save_snapshot_completed\n");
464+
465+ if (save_snapshot_cleanup() < 0) {
466+ snap_state.state = SAVE_STATE_ERROR;
467+ } else {
468+ snap_state.state = SAVE_STATE_COMPLETED;
469+ }
470+}
471+
472+static int block_state_close(void *opaque)
473+{
474+ snap_state.file = NULL;
67af0fa4 475+ return blk_flush(snap_state.target);
95259824
WB
476+}
477+
67af0fa4
WB
478+static ssize_t block_state_writev_buffer(void *opaque, struct iovec *iov,
479+ int iovcnt, int64_t pos)
95259824
WB
480+{
481+ int ret;
67af0fa4 482+ QEMUIOVector qiov;
95259824 483+
67af0fa4
WB
484+ qemu_iovec_init_external(&qiov, iov, iovcnt);
485+ ret = blk_co_pwritev(snap_state.target, pos, qiov.size, &qiov, 0);
486+ if (ret < 0) {
487+ return ret;
95259824 488+ }
67af0fa4
WB
489+ snap_state.bs_pos += qiov.size;
490+ return qiov.size;
95259824
WB
491+}
492+
493+static int store_and_stop(void) {
494+ if (global_state_store()) {
495+ save_snapshot_error("Error saving global state");
496+ return 1;
497+ }
498+ if (runstate_is_running()) {
499+ vm_stop(RUN_STATE_SAVE_VM);
500+ }
501+ return 0;
502+}
503+
504+static void process_savevm_co(void *opaque)
505+{
506+ int ret;
507+ int64_t maxlen;
95259824
WB
508+
509+ snap_state.state = SAVE_STATE_ACTIVE;
510+
511+ qemu_mutex_unlock_iothread();
512+ qemu_savevm_state_header(snap_state.file);
6838f038
WB
513+ qemu_savevm_state_setup(snap_state.file);
514+ ret = qemu_file_get_error(snap_state.file);
95259824
WB
515+ qemu_mutex_lock_iothread();
516+
517+ if (ret < 0) {
6838f038 518+ save_snapshot_error("qemu_savevm_state_setup failed");
95259824
WB
519+ return;
520+ }
521+
522+ while (snap_state.state == SAVE_STATE_ACTIVE) {
67af0fa4 523+ uint64_t pending_size, pend_post, pend_nonpost;
95259824 524+
67af0fa4
WB
525+ qemu_savevm_state_pending(snap_state.file, 0, &pend_nonpost, &pend_post);
526+ pending_size = pend_post + pend_nonpost;
95259824
WB
527+
528+ if (pending_size) {
67af0fa4 529+ ret = qemu_savevm_state_iterate(snap_state.file, false);
95259824
WB
530+ if (ret < 0) {
531+ save_snapshot_error("qemu_savevm_state_iterate error %d", ret);
532+ break;
533+ }
534+ DPRINTF("savevm inerate pending size %lu ret %d\n", pending_size, ret);
535+ } else {
536+ DPRINTF("done iterating\n");
537+ if (store_and_stop())
538+ break;
539+ DPRINTF("savevm inerate finished\n");
6838f038
WB
540+ /* upstream made the return value here inconsistent
541+ * (-1 instead of 'ret' in one case and 0 after flush which can
542+ * still set a file error...)
543+ */
544+ (void)qemu_savevm_state_complete_precopy(snap_state.file, false, false);
545+ ret = qemu_file_get_error(snap_state.file);
546+ if (ret < 0) {
547+ save_snapshot_error("qemu_savevm_state_iterate error %d", ret);
548+ break;
549+ }
95259824
WB
550+ DPRINTF("save complete\n");
551+ save_snapshot_completed();
552+ break;
553+ }
554+
555+ /* stop the VM if we get to the end of available space,
556+ * or if pending_size is just a few MB
557+ */
67af0fa4 558+ maxlen = blk_getlength(snap_state.target) - 30*1024*1024;
95259824
WB
559+ if ((pending_size < 100000) ||
560+ ((snap_state.bs_pos + pending_size) >= maxlen)) {
561+ if (store_and_stop())
562+ break;
563+ }
564+ }
565+
566+ if(snap_state.state == SAVE_STATE_CANCELLED) {
567+ save_snapshot_completed();
568+ Error *errp = NULL;
569+ qmp_savevm_end(&errp);
570+ }
571+
572+}
573+
574+static const QEMUFileOps block_file_ops = {
67af0fa4 575+ .writev_buffer = block_state_writev_buffer,
95259824
WB
576+ .close = block_state_close,
577+};
578+
579+
580+void qmp_savevm_start(bool has_statefile, const char *statefile, Error **errp)
581+{
95259824
WB
582+ Error *local_err = NULL;
583+
67af0fa4 584+ int bdrv_oflags = BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_NO_FLUSH;
95259824
WB
585+
586+ if (snap_state.state != SAVE_STATE_DONE) {
587+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
588+ "VM snapshot already started\n");
589+ return;
590+ }
591+
592+ /* initialize snapshot info */
593+ snap_state.saved_vm_running = runstate_is_running();
594+ snap_state.bs_pos = 0;
595+ snap_state.total_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
596+ snap_state.blocker = NULL;
597+
598+ if (snap_state.error) {
599+ error_free(snap_state.error);
600+ snap_state.error = NULL;
601+ }
602+
603+ if (!has_statefile) {
604+ vm_stop(RUN_STATE_SAVE_VM);
605+ snap_state.state = SAVE_STATE_COMPLETED;
606+ return;
607+ }
608+
609+ if (qemu_savevm_state_blocked(errp)) {
610+ return;
611+ }
612+
613+ /* Open the image */
95259824
WB
614+ QDict *options = NULL;
615+ options = qdict_new();
616+ qdict_put(options, "driver", qstring_from_str("raw"));
67af0fa4
WB
617+ snap_state.target = blk_new_open(statefile, NULL, options, bdrv_oflags, &local_err);
618+ if (!snap_state.target) {
95259824
WB
619+ error_set(errp, ERROR_CLASS_GENERIC_ERROR, "failed to open '%s'", statefile);
620+ goto restart;
621+ }
622+
623+ snap_state.file = qemu_fopen_ops(&snap_state, &block_file_ops);
624+
625+ if (!snap_state.file) {
626+ error_set(errp, ERROR_CLASS_GENERIC_ERROR, "failed to open '%s'", statefile);
627+ goto restart;
628+ }
629+
630+
631+ error_setg(&snap_state.blocker, "block device is in use by savevm");
67af0fa4 632+ blk_op_block_all(snap_state.target, snap_state.blocker);
95259824 633+
67af0fa4 634+ Coroutine *co = qemu_coroutine_create(process_savevm_co, NULL);
95259824
WB
635+ qemu_coroutine_enter(co);
636+
637+ return;
638+
639+restart:
640+
641+ save_snapshot_error("setup failed");
642+
643+ if (snap_state.saved_vm_running) {
644+ vm_start();
645+ }
646+}
647+
648+void qmp_savevm_end(Error **errp)
649+{
650+ if (snap_state.state == SAVE_STATE_DONE) {
651+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
652+ "VM snapshot not started\n");
653+ return;
654+ }
655+
656+ if (snap_state.state == SAVE_STATE_ACTIVE) {
657+ snap_state.state = SAVE_STATE_CANCELLED;
658+ return;
659+ }
660+
661+ if (snap_state.saved_vm_running) {
662+ vm_start();
663+ }
664+
665+ snap_state.state = SAVE_STATE_DONE;
666+}
667+
668+void qmp_snapshot_drive(const char *device, const char *name, Error **errp)
669+{
670+ BlockBackend *blk;
671+ BlockDriverState *bs;
672+ QEMUSnapshotInfo sn1, *sn = &sn1;
673+ int ret;
674+#ifdef _WIN32
675+ struct _timeb tb;
676+#else
677+ struct timeval tv;
678+#endif
679+
680+ if (snap_state.state != SAVE_STATE_COMPLETED) {
681+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
682+ "VM snapshot not ready/started\n");
683+ return;
684+ }
685+
686+ blk = blk_by_name(device);
687+ if (!blk) {
688+ error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
689+ "Device '%s' not found", device);
690+ return;
691+ }
692+
693+ bs = blk_bs(blk);
694+ if (!bdrv_is_inserted(bs)) {
695+ error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, device);
696+ return;
697+ }
698+
699+ if (bdrv_is_read_only(bs)) {
700+ error_setg(errp, "Node '%s' is read only", device);
701+ return;
702+ }
703+
704+ if (!bdrv_can_snapshot(bs)) {
705+ error_setg(errp, QERR_UNSUPPORTED);
706+ return;
707+ }
708+
709+ if (bdrv_snapshot_find(bs, sn, name) >= 0) {
710+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
711+ "snapshot '%s' already exists", name);
712+ return;
713+ }
714+
715+ sn = &sn1;
716+ memset(sn, 0, sizeof(*sn));
717+
718+#ifdef _WIN32
719+ _ftime(&tb);
720+ sn->date_sec = tb.time;
721+ sn->date_nsec = tb.millitm * 1000000;
722+#else
723+ gettimeofday(&tv, NULL);
724+ sn->date_sec = tv.tv_sec;
725+ sn->date_nsec = tv.tv_usec * 1000;
726+#endif
727+ sn->vm_clock_nsec = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
728+
729+ pstrcpy(sn->name, sizeof(sn->name), name);
730+
731+ sn->vm_state_size = 0; /* do not save state */
732+
733+ ret = bdrv_snapshot_create(bs, sn);
734+ if (ret < 0) {
735+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
736+ "Error while creating snapshot on '%s'\n", device);
737+ return;
738+ }
739+}
740+
741+void qmp_delete_drive_snapshot(const char *device, const char *name,
742+ Error **errp)
743+{
744+ BlockBackend *blk;
745+ BlockDriverState *bs;
746+ QEMUSnapshotInfo sn1, *sn = &sn1;
747+ Error *local_err = NULL;
748+
749+ int ret;
750+
751+ blk = blk_by_name(device);
752+ if (!blk) {
753+ error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
754+ "Device '%s' not found", device);
755+ return;
756+ }
757+
758+ bs = blk_bs(blk);
759+ if (bdrv_is_read_only(bs)) {
760+ error_setg(errp, "Node '%s' is read only", device);
761+ return;
762+ }
763+
764+ if (!bdrv_can_snapshot(bs)) {
765+ error_setg(errp, QERR_UNSUPPORTED);
766+ return;
767+ }
768+
769+ if (bdrv_snapshot_find(bs, sn, name) < 0) {
770+ /* return success if snapshot does not exists */
771+ return;
772+ }
773+
774+ ret = bdrv_snapshot_delete(bs, NULL, name, &local_err);
775+ if (ret < 0) {
776+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
777+ "Error while deleting snapshot on '%s'\n", device);
778+ return;
779+ }
780+}
781+
67af0fa4
WB
782+static ssize_t loadstate_get_buffer(void *opaque, uint8_t *buf, int64_t pos,
783+ size_t size)
95259824 784+{
67af0fa4
WB
785+ BlockBackend *be = opaque;
786+ int64_t maxlen = blk_getlength(be);
95259824
WB
787+ if (pos > maxlen) {
788+ return -EIO;
789+ }
790+ if ((pos + size) > maxlen) {
791+ size = maxlen - pos - 1;
792+ }
793+ if (size == 0) {
794+ return 0;
795+ }
67af0fa4 796+ return blk_pread(be, pos, buf, size);
95259824
WB
797+}
798+
799+static const QEMUFileOps loadstate_file_ops = {
800+ .get_buffer = loadstate_get_buffer,
801+};
802+
6838f038 803+int load_snapshot_from_blockdev(const char *filename, Error **errp)
95259824 804+{
67af0fa4 805+ BlockBackend *be;
95259824
WB
806+ Error *local_err = NULL;
807+ Error *blocker = NULL;
808+
809+ QEMUFile *f;
67af0fa4 810+ int ret = -EINVAL;
95259824 811+
67af0fa4 812+ be = blk_new_open(filename, NULL, NULL, 0, &local_err);
95259824 813+
67af0fa4 814+ if (!be) {
6838f038 815+ error_setg(errp, "Could not open VM state file");
95259824
WB
816+ goto the_end;
817+ }
818+
67af0fa4
WB
819+ error_setg(&blocker, "block device is in use by load state");
820+ blk_op_block_all(be, blocker);
821+
95259824 822+ /* restore the VM state */
67af0fa4 823+ f = qemu_fopen_ops(be, &loadstate_file_ops);
95259824 824+ if (!f) {
6838f038 825+ error_setg(errp, "Could not open VM state file");
95259824
WB
826+ goto the_end;
827+ }
828+
6838f038 829+ qemu_system_reset(SHUTDOWN_CAUSE_NONE);
95259824
WB
830+ ret = qemu_loadvm_state(f);
831+
832+ qemu_fclose(f);
833+ migration_incoming_state_destroy();
834+ if (ret < 0) {
6838f038 835+ error_setg_errno(errp, -ret, "Error while loading VM state");
95259824
WB
836+ goto the_end;
837+ }
838+
839+ ret = 0;
840+
841+ the_end:
67af0fa4
WB
842+ if (be) {
843+ blk_op_unblock_all(be, blocker);
95259824 844+ error_free(blocker);
67af0fa4 845+ blk_unref(be);
95259824
WB
846+ }
847+ return ret;
848+}
849diff --git a/vl.c b/vl.c
6838f038 850index 2e0fe15978..1bfbe95b22 100644
95259824
WB
851--- a/vl.c
852+++ b/vl.c
6838f038 853@@ -3109,6 +3109,7 @@ int main(int argc, char **argv, char **envp)
95259824
WB
854 int optind;
855 const char *optarg;
856 const char *loadvm = NULL;
857+ const char *loadstate = NULL;
858 MachineClass *machine_class;
859 const char *cpu_model;
860 const char *vga_model = NULL;
6838f038 861@@ -3785,6 +3786,9 @@ int main(int argc, char **argv, char **envp)
95259824
WB
862 case QEMU_OPTION_loadvm:
863 loadvm = optarg;
864 break;
865+ case QEMU_OPTION_loadstate:
866+ loadstate = optarg;
867+ break;
868 case QEMU_OPTION_full_screen:
869 full_screen = 1;
870 break;
6838f038
WB
871@@ -4891,6 +4895,12 @@ int main(int argc, char **argv, char **envp)
872 error_report_err(local_err);
95259824
WB
873 autostart = 0;
874 }
875+ } else if (loadstate) {
6838f038
WB
876+ Error *local_err = NULL;
877+ if (load_snapshot_from_blockdev(loadstate, &local_err) < 0) {
878+ error_report_err(local_err);
95259824
WB
879+ autostart = 0;
880+ }
881 }
882
883 qdev_prop_check_globals();
884--
45169293 8852.11.0
95259824 886