]> git.proxmox.com Git - pve-qemu-kvm.git/blame - debian/patches/internal-snapshot-async.patch
vma: add an easy way to extract a configuration file
[pve-qemu-kvm.git] / debian / patches / internal-snapshot-async.patch
CommitLineData
fb40cdcd
AD
1From 46fd4bb673a91d40352c95e9d3f62f63b5021053 Mon Sep 17 00:00:00 2001
2From: Stefan Priebe <s.priebe@profihost.ag>
3Date: Fri, 29 Nov 2013 22:17:03 +0100
4Subject: [PATCH] internal-snapshot-async-qemu1.7.patch
5
6---
7 Makefile.objs | 1 +
8 block.c | 2 +-
9 hmp-commands.hx | 34 ++++
10 hmp.c | 57 ++++++
11 hmp.h | 5 +
12 include/block/block.h | 1 +
13 include/sysemu/sysemu.h | 5 +-
14 monitor.c | 7 +
15 qapi-schema.json | 46 +++++
16 qemu-options.hx | 13 ++
17 qmp-commands.hx | 31 +++
18 savevm-async.c | 478 +++++++++++++++++++++++++++++++++++++++++++++++
19 savevm.c | 10 +-
20 vl.c | 9 +
21 14 files changed, 692 insertions(+), 7 deletions(-)
22 create mode 100644 savevm-async.c
23
24diff --git a/Makefile.objs b/Makefile.objs
25index 2b6c1fe..cce2562 100644
26--- a/Makefile.objs
27+++ b/Makefile.objs
28@@ -55,6 +55,7 @@ common-obj-$(CONFIG_RDMA) += migration-rdma.o
29 common-obj-y += qemu-char.o #aio.o
30 common-obj-y += block-migration.o
31 common-obj-y += page_cache.o xbzrle.o
32+common-obj-y += savevm-async.o
cd983153 33
fb40cdcd 34 common-obj-$(CONFIG_POSIX) += migration-exec.o migration-unix.o migration-fd.o
e96de165 35
fb40cdcd
AD
36diff --git a/block.c b/block.c
37index 382ea71..249cb82 100644
38--- a/block.c
39+++ b/block.c
40@@ -1710,7 +1710,7 @@ void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top)
41 bs_new->drv ? bs_new->drv->format_name : "");
42 }
92bf040c 43
fb40cdcd
AD
44-static void bdrv_delete(BlockDriverState *bs)
45+void bdrv_delete(BlockDriverState *bs)
46 {
47 assert(!bs->dev);
48 assert(!bs->job);
49diff --git a/hmp-commands.hx b/hmp-commands.hx
50index caae5ad..5dbe6d0 100644
51--- a/hmp-commands.hx
52+++ b/hmp-commands.hx
53@@ -1698,6 +1698,8 @@ show migration status
54 show current migration capabilities
55 @item info migrate_cache_size
56 show current migration XBZRLE cache size
57+@item info savevm
58+show savevm status
59 @item info balloon
60 show balloon information
61 @item info qtree
62@@ -1719,3 +1721,35 @@ ETEXI
63 STEXI
64 @end table
65 ETEXI
cd983153
DM
66+
67+ {
1f416f3a 68+ .name = "savevm-start",
cd983153 69+ .args_type = "statefile:s?",
fb40cdcd
AD
70+ .params = "[statefile]",
71+ .help = "Prepare for snapshot and halt VM. Save VM state to statefile.",
72+ .mhandler.cmd = hmp_savevm_start,
cd983153
DM
73+ },
74+
75+ {
76+ .name = "snapshot-drive",
77+ .args_type = "device:s,name:s",
fb40cdcd
AD
78+ .params = "device name",
79+ .help = "Create internal snapshot.",
80+ .mhandler.cmd = hmp_snapshot_drive,
cd983153
DM
81+ },
82+
83+ {
84+ .name = "delete-drive-snapshot",
85+ .args_type = "device:s,name:s",
fb40cdcd
AD
86+ .params = "device name",
87+ .help = "Delete internal snapshot.",
88+ .mhandler.cmd = hmp_delete_drive_snapshot,
cd983153
DM
89+ },
90+
91+ {
1f416f3a 92+ .name = "savevm-end",
cd983153 93+ .args_type = "",
fb40cdcd
AD
94+ .params = "",
95+ .help = "Resume VM after snaphot.",
96+ .mhandler.cmd = hmp_savevm_end,
629cf2f4 97+ },
fb40cdcd
AD
98diff --git a/hmp.c b/hmp.c
99index c2efa8d..60d6a8e 100644
100--- a/hmp.c
101+++ b/hmp.c
102@@ -1592,3 +1592,60 @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict)
103
104 hmp_handle_error(mon, &err);
cd983153
DM
105 }
106+
1f416f3a 107+void hmp_savevm_start(Monitor *mon, const QDict *qdict)
cd983153
DM
108+{
109+ Error *errp = NULL;
110+ const char *statefile = qdict_get_try_str(qdict, "statefile");
111+
1f416f3a 112+ qmp_savevm_start(statefile != NULL, statefile, &errp);
cd983153
DM
113+ hmp_handle_error(mon, &errp);
114+}
115+
116+void hmp_snapshot_drive(Monitor *mon, const QDict *qdict)
117+{
118+ Error *errp = NULL;
119+ const char *name = qdict_get_str(qdict, "name");
120+ const char *device = qdict_get_str(qdict, "device");
121+
122+ qmp_snapshot_drive(device, name, &errp);
123+ hmp_handle_error(mon, &errp);
124+}
125+
126+void hmp_delete_drive_snapshot(Monitor *mon, const QDict *qdict)
127+{
128+ Error *errp = NULL;
129+ const char *name = qdict_get_str(qdict, "name");
130+ const char *device = qdict_get_str(qdict, "device");
131+
132+ qmp_delete_drive_snapshot(device, name, &errp);
133+ hmp_handle_error(mon, &errp);
134+}
135+
1f416f3a 136+void hmp_savevm_end(Monitor *mon, const QDict *qdict)
cd983153
DM
137+{
138+ Error *errp = NULL;
139+
1f416f3a 140+ qmp_savevm_end(&errp);
cd983153
DM
141+ hmp_handle_error(mon, &errp);
142+}
143+
92bf040c 144+void hmp_info_savevm(Monitor *mon, const QDict *qdict)
cd983153
DM
145+{
146+ SaveVMInfo *info;
147+ info = qmp_query_savevm(NULL);
148+
149+ if (info->has_status) {
150+ monitor_printf(mon, "savevm status: %s\n", info->status);
151+ monitor_printf(mon, "total time: %" PRIu64 " milliseconds\n",
152+ info->total_time);
153+ } else {
154+ monitor_printf(mon, "savevm status: not running\n");
155+ }
156+ if (info->has_bytes) {
157+ monitor_printf(mon, "Bytes saved: %"PRIu64"\n", info->bytes);
158+ }
159+ if (info->has_error) {
160+ monitor_printf(mon, "Error: %s\n", info->error);
161+ }
162+}
fb40cdcd
AD
163diff --git a/hmp.h b/hmp.h
164index 54cf71f..bcc1cb6 100644
165--- a/hmp.h
166+++ b/hmp.h
167@@ -25,6 +25,7 @@ void hmp_info_status(Monitor *mon, const QDict *qdict);
92bf040c
DM
168 void hmp_info_uuid(Monitor *mon, const QDict *qdict);
169 void hmp_info_chardev(Monitor *mon, const QDict *qdict);
170 void hmp_info_mice(Monitor *mon, const QDict *qdict);
171+void hmp_info_savevm(Monitor *mon, const QDict *qdict);
172 void hmp_info_migrate(Monitor *mon, const QDict *qdict);
173 void hmp_info_migrate_capabilities(Monitor *mon, const QDict *qdict);
174 void hmp_info_migrate_cache_size(Monitor *mon, const QDict *qdict);
fb40cdcd 175@@ -81,6 +82,10 @@ void hmp_netdev_add(Monitor *mon, const QDict *qdict);
cd983153
DM
176 void hmp_netdev_del(Monitor *mon, const QDict *qdict);
177 void hmp_getfd(Monitor *mon, const QDict *qdict);
178 void hmp_closefd(Monitor *mon, const QDict *qdict);
1f416f3a 179+void hmp_savevm_start(Monitor *mon, const QDict *qdict);
cd983153
DM
180+void hmp_snapshot_drive(Monitor *mon, const QDict *qdict);
181+void hmp_delete_drive_snapshot(Monitor *mon, const QDict *qdict);
1f416f3a 182+void hmp_savevm_end(Monitor *mon, const QDict *qdict);
e96de165
DM
183 void hmp_send_key(Monitor *mon, const QDict *qdict);
184 void hmp_screen_dump(Monitor *mon, const QDict *qdict);
185 void hmp_nbd_server_start(Monitor *mon, const QDict *qdict);
fb40cdcd
AD
186diff --git a/include/block/block.h b/include/block/block.h
187index 3560deb..22c06bb 100644
188--- a/include/block/block.h
189+++ b/include/block/block.h
190@@ -214,6 +214,7 @@ BlockDriverState *bdrv_find_backing_image(BlockDriverState *bs,
191 const char *backing_file);
192 int bdrv_get_backing_file_depth(BlockDriverState *bs);
193 int bdrv_truncate(BlockDriverState *bs, int64_t offset);
194+void bdrv_delete(BlockDriverState *bs);
195 int64_t bdrv_getlength(BlockDriverState *bs);
196 int64_t bdrv_get_allocated_file_size(BlockDriverState *bs);
197 void bdrv_get_geometry(BlockDriverState *bs, uint64_t *nb_sectors_ptr);
198diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h
199index 495dae8..d2bb686 100644
200--- a/include/sysemu/sysemu.h
201+++ b/include/sysemu/sysemu.h
202@@ -73,16 +73,17 @@ void qemu_add_machine_init_done_notifier(Notifier *notify);
203
204 void do_savevm(Monitor *mon, const QDict *qdict);
205 int load_vmstate(const char *name);
206+int load_state_from_blockdev(const char *filename);
207 void do_delvm(Monitor *mon, const QDict *qdict);
208 void do_info_snapshots(Monitor *mon, const QDict *qdict);
209
210 void qemu_announce_self(void);
211
212 bool qemu_savevm_state_blocked(Error **errp);
213-void qemu_savevm_state_begin(QEMUFile *f,
214+int qemu_savevm_state_begin(QEMUFile *f,
215 const MigrationParams *params);
216 int qemu_savevm_state_iterate(QEMUFile *f);
217-void qemu_savevm_state_complete(QEMUFile *f);
218+int qemu_savevm_state_complete(QEMUFile *f);
219 void qemu_savevm_state_cancel(void);
220 uint64_t qemu_savevm_state_pending(QEMUFile *f, uint64_t max_size);
221 int qemu_loadvm_state(QEMUFile *f);
222diff --git a/monitor.c b/monitor.c
223index 845f608..30cc965 100644
224--- a/monitor.c
225+++ b/monitor.c
226@@ -2901,6 +2901,13 @@ static mon_cmd_t info_cmds[] = {
227 .mhandler.cmd = hmp_info_migrate_cache_size,
228 },
229 {
230+ .name = "savevm",
231+ .args_type = "",
232+ .params = "",
233+ .help = "show savevm status",
234+ .mhandler.cmd = hmp_info_savevm,
235+ },
236+ {
237 .name = "balloon",
238 .args_type = "",
239 .params = "",
240diff --git a/qapi-schema.json b/qapi-schema.json
241index 7a36e99..1faf622 100644
242--- a/qapi-schema.json
243+++ b/qapi-schema.json
244@@ -651,6 +651,42 @@
245 '*downtime': 'int',
246 '*setup-time': 'int'} }
247
248+
249+# @SaveVMInfo
250+#
251+# Information about current migration process.
252+#
253+# @status: #optional string describing the current savevm status.
254+# This can be 'active', 'completed', 'failed'.
255+# If this field is not returned, no savevm process
256+# has been initiated
257+#
258+# @error: #optional string containing error message is status is failed.
259+#
260+# @total-time: #optional total amount of milliseconds since savevm started.
261+# If savevm has ended, it returns the total save time
262+#
263+# @bytes: #optional total amount of data transfered
264+#
265+# Since: 1.3
266+##
267+{ 'type': 'SaveVMInfo',
268+ 'data': {'*status': 'str', '*error': 'str',
269+ '*total-time': 'int', '*bytes': 'int'} }
270+
271+##
272+# @query-savevm
273+#
274+# Returns information about current savevm process.
275+#
276+# Returns: @SaveVMInfo
277+#
278+# Since: 1.3
279+##
280+{ 'command': 'query-savevm', 'returns': 'SaveVMInfo' }
281+
282+##
283+
284 ##
285 # @query-migrate
286 #
287@@ -3347,8 +3383,18 @@
288 #
289 # Since: 1.2.0
290 ##
291+
292 { 'command': 'query-target', 'returns': 'TargetInfo' }
293
294+{ 'command': 'savevm-start', 'data': { '*statefile': 'str' } }
295+
296+{ 'command': 'snapshot-drive', 'data': { 'device': 'str', 'name': 'str' } }
297+
298+{ 'command': 'delete-drive-snapshot', 'data': { 'device': 'str', 'name': 'str' } }
299+
300+{ 'command': 'savevm-end' }
301+
302+
303 ##
304 # @QKeyCode:
305 #
306diff --git a/qemu-options.hx b/qemu-options.hx
307index 51b1cd0..a2834da 100644
308--- a/qemu-options.hx
309+++ b/qemu-options.hx
310@@ -2734,6 +2734,19 @@ STEXI
311 Start right away with a saved state (@code{loadvm} in monitor)
cd983153 312 ETEXI
fb40cdcd
AD
313
314+DEF("loadstate", HAS_ARG, QEMU_OPTION_loadstate, \
315+ "-loadstate file\n" \
316+ " start right away with a saved state\n",
317+ QEMU_ARCH_ALL)
318+STEXI
319+@item -loadstate @var{file}
320+@findex -loadstate
321+Start right away with a saved state. This option does not rollback
322+disk state like @code{loadvm}, so user must make sure that disk
323+have correct state. @var{file} can be any valid device URL. See the section
324+for "Device URL Syntax" for more information.
325+ETEXI
326+
327 #ifndef _WIN32
328 DEF("daemonize", 0, QEMU_OPTION_daemonize, \
329 "-daemonize daemonize QEMU after initializing\n", QEMU_ARCH_ALL)
330diff --git a/qmp-commands.hx b/qmp-commands.hx
331index 5584fe2..90a0d71 100644
332--- a/qmp-commands.hx
333+++ b/qmp-commands.hx
334@@ -3308,3 +3308,34 @@ Example (2):
335 <- { "return": {} }
336
337 EQMP
338+
cd983153
DM
339+
340+ {
1f416f3a 341+ .name = "savevm-start",
cd983153 342+ .args_type = "statefile:s?",
fb40cdcd 343+ .mhandler.cmd_new = qmp_marshal_input_savevm_start,
cd983153
DM
344+ },
345+
346+ {
347+ .name = "snapshot-drive",
348+ .args_type = "device:s,name:s",
fb40cdcd 349+ .mhandler.cmd_new = qmp_marshal_input_snapshot_drive,
cd983153
DM
350+ },
351+
352+ {
353+ .name = "delete-drive-snapshot",
354+ .args_type = "device:s,name:s",
fb40cdcd 355+ .mhandler.cmd_new = qmp_marshal_input_delete_drive_snapshot,
cd983153
DM
356+ },
357+
358+ {
1f416f3a 359+ .name = "savevm-end",
cd983153 360+ .args_type = "",
fb40cdcd
AD
361+ .mhandler.cmd_new = qmp_marshal_input_savevm_end,
362+ },
363+
364+ {
365+ .name = "query-savevm",
366+ .args_type = "",
367+ .mhandler.cmd_new = qmp_marshal_input_query_savevm,
cd983153 368+ },
fb40cdcd
AD
369diff --git a/savevm-async.c b/savevm-async.c
370new file mode 100644
371index 0000000..64768b1
372--- /dev/null
373+++ b/savevm-async.c
374@@ -0,0 +1,478 @@
375+#include "qemu-common.h"
376+#include "qapi/qmp/qerror.h"
377+#include "sysemu/sysemu.h"
cd983153 378+#include "qmp-commands.h"
fb40cdcd
AD
379+#include "qemu-options.h"
380+#include "migration/qemu-file.h"
381+#include "qom/qom-qobject.h"
382+#include "migration/migration.h"
383+#include "block/snapshot.h"
384+#include "block/qapi.h"
385+#include "block/block.h"
386+#include "qemu/timer.h"
cd983153 387+
b84aa9c4 388+/* #define DEBUG_SAVEVM_STATE */
cd983153
DM
389+
390+#ifdef DEBUG_SAVEVM_STATE
391+#define DPRINTF(fmt, ...) \
392+ do { printf("savevm-async: " fmt, ## __VA_ARGS__); } while (0)
393+#else
394+#define DPRINTF(fmt, ...) \
395+ do { } while (0)
396+#endif
397+
398+enum {
399+ SAVE_STATE_DONE,
400+ SAVE_STATE_ERROR,
401+ SAVE_STATE_ACTIVE,
402+ SAVE_STATE_COMPLETED,
403+};
404+
fb40cdcd 405+
cd983153
DM
406+static struct SnapshotState {
407+ BlockDriverState *bs;
408+ size_t bs_pos;
409+ int state;
410+ Error *error;
411+ int saved_vm_running;
412+ QEMUFile *file;
413+ int64_t total_time;
414+} snap_state;
415+
416+SaveVMInfo *qmp_query_savevm(Error **errp)
417+{
418+ SaveVMInfo *info = g_malloc0(sizeof(*info));
419+ struct SnapshotState *s = &snap_state;
420+
421+ if (s->state != SAVE_STATE_DONE) {
422+ info->has_bytes = true;
423+ info->bytes = s->bs_pos;
424+ switch (s->state) {
425+ case SAVE_STATE_ERROR:
426+ info->has_status = true;
427+ info->status = g_strdup("failed");
428+ info->has_total_time = true;
429+ info->total_time = s->total_time;
430+ if (s->error) {
431+ info->has_error = true;
432+ info->error = g_strdup(error_get_pretty(s->error));
433+ }
434+ break;
435+ case SAVE_STATE_ACTIVE:
436+ info->has_status = true;
437+ info->status = g_strdup("active");
438+ info->has_total_time = true;
fb40cdcd 439+ info->total_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME)
cd983153
DM
440+ - s->total_time;
441+ break;
442+ case SAVE_STATE_COMPLETED:
443+ info->has_status = true;
444+ info->status = g_strdup("completed");
445+ info->has_total_time = true;
446+ info->total_time = s->total_time;
447+ break;
448+ }
449+ }
450+
451+ return info;
452+}
453+
454+static int save_snapshot_cleanup(void)
455+{
456+ int ret = 0;
457+
458+ DPRINTF("save_snapshot_cleanup\n");
459+
fb40cdcd 460+ snap_state.total_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) -
cd983153
DM
461+ snap_state.total_time;
462+
463+ if (snap_state.file) {
464+ ret = qemu_fclose(snap_state.file);
465+ }
466+
467+ if (snap_state.bs) {
b84aa9c4
DM
468+ /* try to truncate, but ignore errors (will fail on block devices).
469+ * note: bdrv_read() need whole blocks, so we round up
470+ */
cd983153
DM
471+ size_t size = (snap_state.bs_pos + BDRV_SECTOR_SIZE) & BDRV_SECTOR_MASK;
472+ bdrv_truncate(snap_state.bs, size);
fb40cdcd
AD
473+ bdrv_set_in_use(snap_state.bs, 0);
474+ bdrv_unref(snap_state.bs);
cd983153
DM
475+ snap_state.bs = NULL;
476+ }
477+
478+ return ret;
479+}
480+
481+static void save_snapshot_error(const char *fmt, ...)
482+{
483+ va_list ap;
484+ char *msg;
485+
486+ va_start(ap, fmt);
487+ msg = g_strdup_vprintf(fmt, ap);
488+ va_end(ap);
489+
490+ DPRINTF("save_snapshot_error: %s\n", msg);
491+
492+ if (!snap_state.error) {
493+ error_set(&snap_state.error, ERROR_CLASS_GENERIC_ERROR, "%s", msg);
494+ }
495+
496+ g_free (msg);
497+
498+ snap_state.state = SAVE_STATE_ERROR;
499+
500+ save_snapshot_cleanup();
501+}
502+
503+static void save_snapshot_completed(void)
504+{
505+ DPRINTF("save_snapshot_completed\n");
506+
507+ if (save_snapshot_cleanup() < 0) {
508+ snap_state.state = SAVE_STATE_ERROR;
509+ } else {
510+ snap_state.state = SAVE_STATE_COMPLETED;
511+ }
512+}
513+
514+static int block_state_close(void *opaque)
515+{
516+ snap_state.file = NULL;
517+ return bdrv_flush(snap_state.bs);
518+}
519+
b84aa9c4
DM
520+static int block_state_put_buffer(void *opaque, const uint8_t *buf,
521+ int64_t pos, int size)
cd983153
DM
522+{
523+ int ret;
524+
b84aa9c4
DM
525+ assert(pos == snap_state.bs_pos);
526+
cd983153
DM
527+ if ((ret = bdrv_pwrite(snap_state.bs, snap_state.bs_pos, buf, size)) > 0) {
528+ snap_state.bs_pos += ret;
529+ }
530+
531+ return ret;
532+}
533+
b84aa9c4 534+static void process_savevm_co(void *opaque)
cd983153
DM
535+{
536+ int ret;
b84aa9c4
DM
537+ int64_t maxlen;
538+ MigrationParams params = {
539+ .blk = 0,
540+ .shared = 0
541+ };
cd983153 542+
b84aa9c4 543+ snap_state.state = SAVE_STATE_ACTIVE;
cd983153 544+
fb40cdcd 545+ qemu_mutex_unlock_iothread();
b84aa9c4 546+ ret = qemu_savevm_state_begin(snap_state.file, &params);
fb40cdcd
AD
547+ qemu_mutex_lock_iothread();
548+
b84aa9c4
DM
549+ if (ret < 0) {
550+ save_snapshot_error("qemu_savevm_state_begin failed");
cd983153
DM
551+ return;
552+ }
553+
b84aa9c4 554+ while (snap_state.state == SAVE_STATE_ACTIVE) {
92bf040c
DM
555+ uint64_t pending_size;
556+
557+ pending_size = qemu_savevm_state_pending(snap_state.file, 0);
558+
559+ if (pending_size) {
560+ ret = qemu_savevm_state_iterate(snap_state.file);
561+ if (ret < 0) {
562+ save_snapshot_error("qemu_savevm_state_iterate error %d", ret);
563+ break;
564+ }
565+ DPRINTF("savevm inerate pending size %lu ret %d\n", pending_size, ret);
566+ } else {
567+ DPRINTF("done iterating\n");
b84aa9c4
DM
568+ if (runstate_is_running()) {
569+ vm_stop(RUN_STATE_SAVE_VM);
570+ }
571+ DPRINTF("savevm inerate finished\n");
fb40cdcd
AD
572+ qemu_savevm_state_complete(snap_state.file);
573+ DPRINTF("save complete\n");
574+ save_snapshot_completed();
575+ break;
92bf040c
DM
576+ }
577+
578+ /* stop the VM if we get to the end of available space,
579+ * or if pending_size is just a few MB
580+ */
581+ maxlen = bdrv_getlength(snap_state.bs) - 30*1024*1024;
582+ if ((pending_size < 100000) ||
583+ ((snap_state.bs_pos + pending_size) >= maxlen)) {
584+ if (runstate_is_running()) {
585+ vm_stop(RUN_STATE_SAVE_VM);
b84aa9c4
DM
586+ }
587+ }
cd983153
DM
588+ }
589+}
590+
b84aa9c4
DM
591+static const QEMUFileOps block_file_ops = {
592+ .put_buffer = block_state_put_buffer,
593+ .close = block_state_close,
594+};
595+
cd983153 596+
1f416f3a 597+void qmp_savevm_start(bool has_statefile, const char *statefile, Error **errp)
cd983153
DM
598+{
599+ BlockDriver *drv = NULL;
fb40cdcd
AD
600+ Error *local_err = NULL;
601+
cd983153 602+ int bdrv_oflags = BDRV_O_CACHE_WB | BDRV_O_RDWR;
cd983153
DM
603+ int ret;
604+
605+ if (snap_state.state != SAVE_STATE_DONE) {
606+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
607+ "VM snapshot already started\n");
608+ return;
609+ }
610+
611+ /* initialize snapshot info */
612+ snap_state.saved_vm_running = runstate_is_running();
613+ snap_state.bs_pos = 0;
fb40cdcd 614+ snap_state.total_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
cd983153
DM
615+
616+ if (snap_state.error) {
617+ error_free(snap_state.error);
618+ snap_state.error = NULL;
619+ }
620+
cd983153 621+ if (!has_statefile) {
b84aa9c4 622+ vm_stop(RUN_STATE_SAVE_VM);
cd983153
DM
623+ snap_state.state = SAVE_STATE_COMPLETED;
624+ return;
625+ }
626+
627+ if (qemu_savevm_state_blocked(errp)) {
628+ return;
629+ }
630+
631+ /* Open the image */
632+ snap_state.bs = bdrv_new("vmstate");
fb40cdcd
AD
633+ ret = bdrv_open(snap_state.bs, statefile, NULL, bdrv_oflags, drv, &local_err);
634+
cd983153 635+ if (ret < 0) {
fb40cdcd 636+ error_set(errp, ERROR_CLASS_GENERIC_ERROR, "failed to open '%s'", statefile);
cd983153
DM
637+ goto restart;
638+ }
639+
b84aa9c4 640+ snap_state.file = qemu_fopen_ops(&snap_state, &block_file_ops);
cd983153
DM
641+
642+ if (!snap_state.file) {
fb40cdcd 643+ error_set(errp, ERROR_CLASS_GENERIC_ERROR, "failed to open '%s'", statefile);
cd983153
DM
644+ goto restart;
645+ }
646+
fb40cdcd
AD
647+
648+ bdrv_set_in_use(snap_state.bs, 1);
649+ bdrv_ref(snap_state.bs);
650+
b84aa9c4
DM
651+ Coroutine *co = qemu_coroutine_create(process_savevm_co);
652+ qemu_coroutine_enter(co, NULL);
cd983153
DM
653+
654+ return;
655+
656+restart:
657+
658+ save_snapshot_error("setup failed");
659+
660+ if (snap_state.saved_vm_running) {
661+ vm_start();
662+ }
663+}
664+
1f416f3a 665+void qmp_savevm_end(Error **errp)
cd983153
DM
666+{
667+ if (snap_state.state == SAVE_STATE_DONE) {
668+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
669+ "VM snapshot not started\n");
670+ return;
671+ }
672+
673+ if (snap_state.saved_vm_running) {
674+ vm_start();
675+ }
676+
677+ snap_state.state = SAVE_STATE_DONE;
678+}
679+
680+void qmp_snapshot_drive(const char *device, const char *name, Error **errp)
681+{
682+ BlockDriverState *bs;
683+ QEMUSnapshotInfo sn1, *sn = &sn1;
684+ int ret;
685+#ifdef _WIN32
686+ struct _timeb tb;
687+#else
688+ struct timeval tv;
689+#endif
690+
691+ if (snap_state.state != SAVE_STATE_COMPLETED) {
692+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
693+ "VM snapshot not ready/started\n");
694+ return;
695+ }
696+
697+ bs = bdrv_find(device);
698+ if (!bs) {
699+ error_set(errp, QERR_DEVICE_NOT_FOUND, device);
700+ return;
701+ }
702+
703+ if (!bdrv_is_inserted(bs)) {
704+ error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device);
705+ return;
706+ }
707+
708+ if (bdrv_is_read_only(bs)) {
709+ error_set(errp, QERR_DEVICE_IS_READ_ONLY, device);
710+ return;
711+ }
712+
713+ if (!bdrv_can_snapshot(bs)) {
714+ error_set(errp, QERR_NOT_SUPPORTED);
715+ return;
716+ }
717+
718+ if (bdrv_snapshot_find(bs, sn, name) >= 0) {
719+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
720+ "snapshot '%s' already exists", name);
721+ return;
722+ }
723+
724+ sn = &sn1;
725+ memset(sn, 0, sizeof(*sn));
726+
727+#ifdef _WIN32
728+ _ftime(&tb);
729+ sn->date_sec = tb.time;
730+ sn->date_nsec = tb.millitm * 1000000;
731+#else
732+ gettimeofday(&tv, NULL);
733+ sn->date_sec = tv.tv_sec;
734+ sn->date_nsec = tv.tv_usec * 1000;
735+#endif
fb40cdcd 736+ sn->vm_clock_nsec = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
cd983153
DM
737+
738+ pstrcpy(sn->name, sizeof(sn->name), name);
739+
740+ sn->vm_state_size = 0; /* do not save state */
741+
742+ ret = bdrv_snapshot_create(bs, sn);
743+ if (ret < 0) {
744+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
745+ "Error while creating snapshot on '%s'\n", device);
746+ return;
747+ }
748+}
749+
750+void qmp_delete_drive_snapshot(const char *device, const char *name,
751+ Error **errp)
752+{
753+ BlockDriverState *bs;
754+ QEMUSnapshotInfo sn1, *sn = &sn1;
fb40cdcd
AD
755+ Error *local_err = NULL;
756+
cd983153
DM
757+ int ret;
758+
759+ bs = bdrv_find(device);
760+ if (!bs) {
761+ error_set(errp, QERR_DEVICE_NOT_FOUND, device);
762+ return;
763+ }
764+ if (bdrv_is_read_only(bs)) {
765+ error_set(errp, QERR_DEVICE_IS_READ_ONLY, device);
766+ return;
767+ }
768+
769+ if (!bdrv_can_snapshot(bs)) {
770+ error_set(errp, QERR_NOT_SUPPORTED);
771+ return;
772+ }
773+
774+ if (bdrv_snapshot_find(bs, sn, name) < 0) {
775+ /* return success if snapshot does not exists */
776+ return;
777+ }
778+
fb40cdcd 779+ ret = bdrv_snapshot_delete(bs, NULL, name, &local_err);
cd983153
DM
780+ if (ret < 0) {
781+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
782+ "Error while deleting snapshot on '%s'\n", device);
783+ return;
784+ }
785+}
786+
b84aa9c4
DM
787+static int loadstate_get_buffer(void *opaque, uint8_t *buf, int64_t pos,
788+ int size)
cd983153
DM
789+{
790+ BlockDriverState *bs = (BlockDriverState *)opaque;
791+ int64_t maxlen = bdrv_getlength(bs);
792+ if (pos > maxlen) {
793+ return -EIO;
794+ }
795+ if ((pos + size) > maxlen) {
796+ size = maxlen - pos - 1;
797+ }
798+ if (size == 0) {
799+ return 0;
800+ }
801+ return bdrv_pread(bs, pos, buf, size);
802+}
803+
b84aa9c4
DM
804+static const QEMUFileOps loadstate_file_ops = {
805+ .get_buffer = loadstate_get_buffer,
806+};
807+
cd983153
DM
808+int load_state_from_blockdev(const char *filename)
809+{
810+ BlockDriverState *bs = NULL;
811+ BlockDriver *drv = NULL;
fb40cdcd
AD
812+ Error *local_err = NULL;
813+
cd983153
DM
814+ QEMUFile *f;
815+ int ret = -1;
816+
817+ bs = bdrv_new("vmstate");
fb40cdcd
AD
818+ ret = bdrv_open(bs, filename, NULL, BDRV_O_CACHE_WB, drv, &local_err);
819+ bdrv_set_in_use(bs, 1);
820+ bdrv_ref(bs);
821+
cd983153
DM
822+ if (ret < 0) {
823+ error_report("Could not open VM state file");
824+ goto the_end;
825+ }
826+
827+ /* restore the VM state */
b84aa9c4 828+ f = qemu_fopen_ops(bs, &loadstate_file_ops);
cd983153
DM
829+ if (!f) {
830+ error_report("Could not open VM state file");
831+ ret = -EINVAL;
832+ goto the_end;
833+ }
834+
835+ qemu_system_reset(VMRESET_SILENT);
836+ ret = qemu_loadvm_state(f);
837+
838+ qemu_fclose(f);
839+ if (ret < 0) {
840+ error_report("Error %d while loading VM state", ret);
841+ goto the_end;
842+ }
843+
844+ ret = 0;
845+
846+ the_end:
847+ if (bs) {
fb40cdcd
AD
848+ bdrv_set_in_use(bs, 0);
849+ bdrv_unref(bs);
cd983153
DM
850+ }
851+ return ret;
852+}
fb40cdcd
AD
853diff --git a/savevm.c b/savevm.c
854index 3f912dd..9ca3309 100644
855--- a/savevm.c
856+++ b/savevm.c
857@@ -1845,11 +1845,11 @@ bool qemu_savevm_state_blocked(Error **errp)
858 return false;
859 }
cd983153 860
fb40cdcd
AD
861-void qemu_savevm_state_begin(QEMUFile *f,
862+int qemu_savevm_state_begin(QEMUFile *f,
863 const MigrationParams *params)
864 {
865 SaveStateEntry *se;
866- int ret;
867+ int ret = 0;
cd983153 868
fb40cdcd
AD
869 QTAILQ_FOREACH(se, &savevm_handlers, entry) {
870 if (!se->ops || !se->ops->set_params) {
871@@ -1890,6 +1890,7 @@ void qemu_savevm_state_begin(QEMUFile *f,
872 break;
873 }
874 }
875+ return ret;
876 }
877
878 /*
879@@ -1937,7 +1938,7 @@ int qemu_savevm_state_iterate(QEMUFile *f)
880 return ret;
881 }
882
883-void qemu_savevm_state_complete(QEMUFile *f)
884+int qemu_savevm_state_complete(QEMUFile *f)
885 {
886 SaveStateEntry *se;
887 int ret;
888@@ -1962,7 +1963,7 @@ void qemu_savevm_state_complete(QEMUFile *f)
889 trace_savevm_section_end(se->section_id);
890 if (ret < 0) {
891 qemu_file_set_error(f, ret);
892- return;
893+ return ret;
894 }
895 }
896
897@@ -1991,6 +1992,7 @@ void qemu_savevm_state_complete(QEMUFile *f)
898
899 qemu_put_byte(f, QEMU_VM_EOF);
900 qemu_fflush(f);
901+ return qemu_file_get_error(f);
902 }
903
904 uint64_t qemu_savevm_state_pending(QEMUFile *f, uint64_t max_size)
905diff --git a/vl.c b/vl.c
906index 16540f6..1b39acf 100644
907--- a/vl.c
908+++ b/vl.c
909@@ -1643,6 +1643,7 @@ MachineInfoList *qmp_query_machines(Error **errp)
910 info->name = g_strdup(m->name);
911 info->cpu_max = !m->max_cpus ? 1 : m->max_cpus;
cd983153 912
cd983153 913+
fb40cdcd
AD
914 if (strcmp(m->name, current_machine->name) == 0) {
915 info->has_is_current = true;
916 info->is_current = true;
917@@ -2842,6 +2843,7 @@ int main(int argc, char **argv, char **envp)
cd983153
DM
918 int optind;
919 const char *optarg;
920 const char *loadvm = NULL;
921+ const char *loadstate = NULL;
922 QEMUMachine *machine;
923 const char *cpu_model;
924 const char *vga_model = "none";
fb40cdcd 925@@ -3455,6 +3457,9 @@ int main(int argc, char **argv, char **envp)
cd983153
DM
926 case QEMU_OPTION_loadvm:
927 loadvm = optarg;
928 break;
929+ case QEMU_OPTION_loadstate:
930+ loadstate = optarg;
931+ break;
932 case QEMU_OPTION_full_screen:
933 full_screen = 1;
934 break;
fb40cdcd 935@@ -4406,6 +4411,10 @@ int main(int argc, char **argv, char **envp)
cd983153
DM
936 if (load_vmstate(loadvm) < 0) {
937 autostart = 0;
938 }
939+ } else if (loadstate) {
940+ if (load_state_from_blockdev(loadstate) < 0) {
941+ autostart = 0;
942+ }
943 }
944
945 if (incoming) {
fb40cdcd
AD
946--
9471.7.10.4
948