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