]> git.proxmox.com Git - pve-qemu-kvm.git/blame - debian/patches/pve/0023-internal-snapshot-async.patch
adding 2.5 pve patches and left-over extra fixes
[pve-qemu-kvm.git] / debian / patches / pve / 0023-internal-snapshot-async.patch
CommitLineData
ca0fe5f5
WB
1From de27ffeb5f55764a51b435533311f1d627d26177 Mon Sep 17 00:00:00 2001
2From: Wolfgang Bumiller <w.bumiller@proxmox.com>
3Date: Wed, 9 Dec 2015 16:04:32 +0100
4Subject: [PATCH 23/41] internal snapshot async
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 +++
19 savevm-async.c | 522 ++++++++++++++++++++++++++++++++++++++++++++++++
20 vl.c | 8 +
21 14 files changed, 739 insertions(+), 8 deletions(-)
22 create mode 100644 savevm-async.c
23
24diff --git a/Makefile.objs b/Makefile.objs
25index 97d2bcc..7da098b 100644
26--- a/Makefile.objs
27+++ b/Makefile.objs
28@@ -48,6 +48,7 @@ common-obj-y += migration/
29 common-obj-y += qemu-char.o #aio.o
30 common-obj-y += page_cache.o
31 common-obj-y += qjson.o
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
37index 3a7324b..5505afb 100644
38--- a/block.c
39+++ b/block.c
40@@ -2117,7 +2117,7 @@ void bdrv_replace_in_backing_chain(BlockDriverState *old, BlockDriverState *new)
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
50index 355289a..1de0d77 100644
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
74index f6cc0e5..1e1eb5d 100644
75--- a/hmp-commands.hx
76+++ b/hmp-commands.hx
77@@ -1789,3 +1789,35 @@ ETEXI
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
114index acc4aea..724b5d3 100644
115--- a/hmp.c
116+++ b/hmp.c
117@@ -2107,6 +2107,63 @@ void hmp_info_memory_devices(Monitor *mon, const QDict *qdict)
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
182index 0b39c25..86d2672 100644
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
205index 3477328..030599e 100644
206--- a/include/block/block.h
207+++ b/include/block/block.h
208@@ -265,6 +265,7 @@ BlockDriverState *bdrv_find_backing_image(BlockDriverState *bs,
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
217index 3bb8897..cc47e29 100644
218--- a/include/sysemu/sysemu.h
219+++ b/include/sysemu/sysemu.h
220@@ -79,6 +79,7 @@ void qemu_add_machine_init_done_notifier(Notifier *notify);
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
228@@ -106,13 +107,13 @@ enum qemu_vm_cmd {
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
245index 0ad1b93..a1cf4b5 100644
246--- a/migration/savevm.c
247+++ b/migration/savevm.c
248@@ -891,11 +891,11 @@ void qemu_savevm_state_header(QEMUFile *f)
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) {
262@@ -923,6 +923,7 @@ void qemu_savevm_state_begin(QEMUFile *f,
263 break;
264 }
265 }
266+ return ret;
267 }
268
269 /*
270@@ -1026,7 +1027,7 @@ void qemu_savevm_state_complete_postcopy(QEMUFile *f)
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;
279@@ -1060,12 +1061,12 @@ void qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only)
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();
294@@ -1113,6 +1114,7 @@ void qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only)
295 object_unref(OBJECT(vmdesc));
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
303index 84978bd..be51fa2 100644
304--- a/qapi-schema.json
305+++ b/qapi-schema.json
306@@ -587,6 +587,42 @@
307 '*setup-time': 'int',
308 '*x-cpu-throttle-percentage': 'int'} }
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 #
349@@ -3046,8 +3082,18 @@
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
369index 0eea4ee..cd465d5 100644
370--- a/qemu-options.hx
371+++ b/qemu-options.hx
372@@ -3099,6 +3099,19 @@ STEXI
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
393index 3dea794..5ef8540 100644
394--- a/qmp-commands.hx
395+++ b/qmp-commands.hx
396@@ -4675,6 +4675,36 @@ Example:
397 EQMP
398
399 {
400+ .name = "savevm-start",
401+ .args_type = "statefile:s?",
402+ .mhandler.cmd_new = qmp_marshal_input_savevm_start,
403+ },
404+
405+ {
406+ .name = "snapshot-drive",
407+ .args_type = "device:s,name:s",
408+ .mhandler.cmd_new = qmp_marshal_input_snapshot_drive,
409+ },
410+
411+ {
412+ .name = "delete-drive-snapshot",
413+ .args_type = "device:s,name:s",
414+ .mhandler.cmd_new = qmp_marshal_input_delete_drive_snapshot,
415+ },
416+
417+ {
418+ .name = "savevm-end",
419+ .args_type = "",
420+ .mhandler.cmd_new = qmp_marshal_input_savevm_end,
421+ },
422+
423+ {
424+ .name = "query-savevm",
425+ .args_type = "",
426+ .mhandler.cmd_new = qmp_marshal_input_query_savevm,
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
435index 0000000..8117443
436--- /dev/null
437+++ b/savevm-async.c
438@@ -0,0 +1,522 @@
439+#include "qemu-common.h"
440+#include "qapi/qmp/qerror.h"
441+#include "qemu/error-report.h"
442+#include "sysemu/sysemu.h"
443+#include "qmp-commands.h"
444+#include "qemu-options.h"
445+#include "migration/qemu-file.h"
446+#include "qom/qom-qobject.h"
447+#include "migration/migration.h"
448+#include "block/snapshot.h"
449+#include "block/qapi.h"
450+#include "block/block.h"
451+#include "qemu/timer.h"
452+#include "sysemu/block-backend.h"
453+#include "qapi/qmp/qstring.h"
454+
455+/* #define DEBUG_SAVEVM_STATE */
456+
457+#ifdef DEBUG_SAVEVM_STATE
458+#define DPRINTF(fmt, ...) \
459+ do { printf("savevm-async: " fmt, ## __VA_ARGS__); } while (0)
460+#else
461+#define DPRINTF(fmt, ...) \
462+ do { } while (0)
463+#endif
464+
465+enum {
466+ SAVE_STATE_DONE,
467+ SAVE_STATE_ERROR,
468+ SAVE_STATE_ACTIVE,
469+ SAVE_STATE_COMPLETED,
470+ SAVE_STATE_CANCELLED
471+};
472+
473+
474+static struct SnapshotState {
475+ BlockDriverState *bs;
476+ size_t bs_pos;
477+ int state;
478+ Error *error;
479+ Error *blocker;
480+ int saved_vm_running;
481+ QEMUFile *file;
482+ int64_t total_time;
483+} snap_state;
484+
485+SaveVMInfo *qmp_query_savevm(Error **errp)
486+{
487+ SaveVMInfo *info = g_malloc0(sizeof(*info));
488+ struct SnapshotState *s = &snap_state;
489+
490+ if (s->state != SAVE_STATE_DONE) {
491+ info->has_bytes = true;
492+ info->bytes = s->bs_pos;
493+ switch (s->state) {
494+ case SAVE_STATE_ERROR:
495+ info->has_status = true;
496+ info->status = g_strdup("failed");
497+ info->has_total_time = true;
498+ info->total_time = s->total_time;
499+ if (s->error) {
500+ info->has_error = true;
501+ info->error = g_strdup(error_get_pretty(s->error));
502+ }
503+ break;
504+ case SAVE_STATE_ACTIVE:
505+ info->has_status = true;
506+ info->status = g_strdup("active");
507+ info->has_total_time = true;
508+ info->total_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME)
509+ - s->total_time;
510+ break;
511+ case SAVE_STATE_COMPLETED:
512+ info->has_status = true;
513+ info->status = g_strdup("completed");
514+ info->has_total_time = true;
515+ info->total_time = s->total_time;
516+ break;
517+ }
518+ }
519+
520+ return info;
521+}
522+
523+static int save_snapshot_cleanup(void)
524+{
525+ int ret = 0;
526+
527+ DPRINTF("save_snapshot_cleanup\n");
528+
529+ snap_state.total_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) -
530+ snap_state.total_time;
531+
532+ if (snap_state.file) {
533+ ret = qemu_fclose(snap_state.file);
534+ }
535+
536+ if (snap_state.bs) {
537+ /* try to truncate, but ignore errors (will fail on block devices).
538+ * note: bdrv_read() need whole blocks, so we round up
539+ */
540+ size_t size = (snap_state.bs_pos + BDRV_SECTOR_SIZE) & BDRV_SECTOR_MASK;
541+ bdrv_truncate(snap_state.bs, size);
542+ bdrv_op_unblock_all(snap_state.bs, snap_state.blocker);
543+ error_free(snap_state.blocker);
544+ snap_state.blocker = NULL;
545+ bdrv_unref(snap_state.bs);
546+ snap_state.bs = NULL;
547+ }
548+
549+ return ret;
550+}
551+
552+static void save_snapshot_error(const char *fmt, ...)
553+{
554+ va_list ap;
555+ char *msg;
556+
557+ va_start(ap, fmt);
558+ msg = g_strdup_vprintf(fmt, ap);
559+ va_end(ap);
560+
561+ DPRINTF("save_snapshot_error: %s\n", msg);
562+
563+ if (!snap_state.error) {
564+ error_set(&snap_state.error, ERROR_CLASS_GENERIC_ERROR, "%s", msg);
565+ }
566+
567+ g_free (msg);
568+
569+ snap_state.state = SAVE_STATE_ERROR;
570+
571+ save_snapshot_cleanup();
572+}
573+
574+static void save_snapshot_completed(void)
575+{
576+ DPRINTF("save_snapshot_completed\n");
577+
578+ if (save_snapshot_cleanup() < 0) {
579+ snap_state.state = SAVE_STATE_ERROR;
580+ } else {
581+ snap_state.state = SAVE_STATE_COMPLETED;
582+ }
583+}
584+
585+static int block_state_close(void *opaque)
586+{
587+ snap_state.file = NULL;
588+ return bdrv_flush(snap_state.bs);
589+}
590+
591+static int block_state_put_buffer(void *opaque, const uint8_t *buf,
592+ int64_t pos, int size)
593+{
594+ int ret;
595+
596+ assert(pos == snap_state.bs_pos);
597+
598+ if ((ret = bdrv_pwrite(snap_state.bs, snap_state.bs_pos, buf, size)) > 0) {
599+ snap_state.bs_pos += ret;
600+ }
601+
602+ return ret;
603+}
604+
605+static int store_and_stop(void) {
606+ if (global_state_store()) {
607+ save_snapshot_error("Error saving global state");
608+ return 1;
609+ }
610+ if (runstate_is_running()) {
611+ vm_stop(RUN_STATE_SAVE_VM);
612+ }
613+ return 0;
614+}
615+
616+static void process_savevm_co(void *opaque)
617+{
618+ int ret;
619+ int64_t maxlen;
620+ MigrationParams params = {
621+ .blk = 0,
622+ .shared = 0
623+ };
624+
625+ snap_state.state = SAVE_STATE_ACTIVE;
626+
627+ qemu_mutex_unlock_iothread();
628+ qemu_savevm_state_header(snap_state.file);
629+ ret = qemu_savevm_state_begin(snap_state.file, &params);
630+ qemu_mutex_lock_iothread();
631+
632+ if (ret < 0) {
633+ save_snapshot_error("qemu_savevm_state_begin failed");
634+ return;
635+ }
636+
637+ while (snap_state.state == SAVE_STATE_ACTIVE) {
638+ uint64_t pending_size;
639+
640+ pending_size = qemu_savevm_state_pending(snap_state.file, 0);
641+
642+ if (pending_size) {
643+ ret = qemu_savevm_state_iterate(snap_state.file);
644+ if (ret < 0) {
645+ save_snapshot_error("qemu_savevm_state_iterate error %d", ret);
646+ break;
647+ }
648+ DPRINTF("savevm inerate pending size %lu ret %d\n", pending_size, ret);
649+ } else {
650+ DPRINTF("done iterating\n");
651+ if (store_and_stop())
652+ break;
653+ DPRINTF("savevm inerate finished\n");
654+ qemu_savevm_state_complete_precopy(snap_state.file);
655+ DPRINTF("save complete\n");
656+ save_snapshot_completed();
657+ break;
658+ }
659+
660+ /* stop the VM if we get to the end of available space,
661+ * or if pending_size is just a few MB
662+ */
663+ maxlen = bdrv_getlength(snap_state.bs) - 30*1024*1024;
664+ if ((pending_size < 100000) ||
665+ ((snap_state.bs_pos + pending_size) >= maxlen)) {
666+ if (store_and_stop())
667+ break;
668+ }
669+ }
670+
671+ if(snap_state.state == SAVE_STATE_CANCELLED) {
672+ save_snapshot_completed();
673+ Error *errp = NULL;
674+ qmp_savevm_end(&errp);
675+ }
676+
677+}
678+
679+static const QEMUFileOps block_file_ops = {
680+ .put_buffer = block_state_put_buffer,
681+ .close = block_state_close,
682+};
683+
684+
685+void qmp_savevm_start(bool has_statefile, const char *statefile, Error **errp)
686+{
687+ BlockDriver *drv = NULL;
688+ Error *local_err = NULL;
689+
690+ int bdrv_oflags = BDRV_O_CACHE_WB | BDRV_O_RDWR;
691+ int ret;
692+
693+ if (snap_state.state != SAVE_STATE_DONE) {
694+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
695+ "VM snapshot already started\n");
696+ return;
697+ }
698+
699+ /* initialize snapshot info */
700+ snap_state.saved_vm_running = runstate_is_running();
701+ snap_state.bs_pos = 0;
702+ snap_state.total_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
703+ snap_state.blocker = NULL;
704+
705+ if (snap_state.error) {
706+ error_free(snap_state.error);
707+ snap_state.error = NULL;
708+ }
709+
710+ if (!has_statefile) {
711+ vm_stop(RUN_STATE_SAVE_VM);
712+ snap_state.state = SAVE_STATE_COMPLETED;
713+ return;
714+ }
715+
716+ if (qemu_savevm_state_blocked(errp)) {
717+ return;
718+ }
719+
720+ /* Open the image */
721+ snap_state.bs = bdrv_new();
722+
723+ QDict *options = NULL;
724+ options = qdict_new();
725+ qdict_put(options, "driver", qstring_from_str("raw"));
726+ ret = bdrv_open(&snap_state.bs, statefile, NULL, options, bdrv_oflags, drv, &local_err);
727+ if (ret < 0) {
728+ error_set(errp, ERROR_CLASS_GENERIC_ERROR, "failed to open '%s'", statefile);
729+ goto restart;
730+ }
731+
732+ snap_state.file = qemu_fopen_ops(&snap_state, &block_file_ops);
733+
734+ if (!snap_state.file) {
735+ error_set(errp, ERROR_CLASS_GENERIC_ERROR, "failed to open '%s'", statefile);
736+ goto restart;
737+ }
738+
739+
740+ error_setg(&snap_state.blocker, "block device is in use by savevm");
741+ bdrv_op_block_all(snap_state.bs, snap_state.blocker);
742+
743+ Coroutine *co = qemu_coroutine_create(process_savevm_co);
744+ qemu_coroutine_enter(co, NULL);
745+
746+ return;
747+
748+restart:
749+
750+ save_snapshot_error("setup failed");
751+
752+ if (snap_state.saved_vm_running) {
753+ vm_start();
754+ }
755+}
756+
757+void qmp_savevm_end(Error **errp)
758+{
759+ if (snap_state.state == SAVE_STATE_DONE) {
760+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
761+ "VM snapshot not started\n");
762+ return;
763+ }
764+
765+ if (snap_state.state == SAVE_STATE_ACTIVE) {
766+ snap_state.state = SAVE_STATE_CANCELLED;
767+ return;
768+ }
769+
770+ if (snap_state.saved_vm_running) {
771+ vm_start();
772+ }
773+
774+ snap_state.state = SAVE_STATE_DONE;
775+}
776+
777+void qmp_snapshot_drive(const char *device, const char *name, Error **errp)
778+{
779+ BlockBackend *blk;
780+ BlockDriverState *bs;
781+ QEMUSnapshotInfo sn1, *sn = &sn1;
782+ int ret;
783+#ifdef _WIN32
784+ struct _timeb tb;
785+#else
786+ struct timeval tv;
787+#endif
788+
789+ if (snap_state.state != SAVE_STATE_COMPLETED) {
790+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
791+ "VM snapshot not ready/started\n");
792+ return;
793+ }
794+
795+ blk = blk_by_name(device);
796+ if (!blk) {
797+ error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
798+ "Device '%s' not found", device);
799+ return;
800+ }
801+
802+ bs = blk_bs(blk);
803+ if (!bdrv_is_inserted(bs)) {
804+ error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, device);
805+ return;
806+ }
807+
808+ if (bdrv_is_read_only(bs)) {
809+ error_setg(errp, "Node '%s' is read only", device);
810+ return;
811+ }
812+
813+ if (!bdrv_can_snapshot(bs)) {
814+ error_setg(errp, QERR_UNSUPPORTED);
815+ return;
816+ }
817+
818+ if (bdrv_snapshot_find(bs, sn, name) >= 0) {
819+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
820+ "snapshot '%s' already exists", name);
821+ return;
822+ }
823+
824+ sn = &sn1;
825+ memset(sn, 0, sizeof(*sn));
826+
827+#ifdef _WIN32
828+ _ftime(&tb);
829+ sn->date_sec = tb.time;
830+ sn->date_nsec = tb.millitm * 1000000;
831+#else
832+ gettimeofday(&tv, NULL);
833+ sn->date_sec = tv.tv_sec;
834+ sn->date_nsec = tv.tv_usec * 1000;
835+#endif
836+ sn->vm_clock_nsec = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
837+
838+ pstrcpy(sn->name, sizeof(sn->name), name);
839+
840+ sn->vm_state_size = 0; /* do not save state */
841+
842+ ret = bdrv_snapshot_create(bs, sn);
843+ if (ret < 0) {
844+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
845+ "Error while creating snapshot on '%s'\n", device);
846+ return;
847+ }
848+}
849+
850+void qmp_delete_drive_snapshot(const char *device, const char *name,
851+ Error **errp)
852+{
853+ BlockBackend *blk;
854+ BlockDriverState *bs;
855+ QEMUSnapshotInfo sn1, *sn = &sn1;
856+ Error *local_err = NULL;
857+
858+ int ret;
859+
860+ blk = blk_by_name(device);
861+ if (!blk) {
862+ error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
863+ "Device '%s' not found", device);
864+ return;
865+ }
866+
867+ bs = blk_bs(blk);
868+ if (bdrv_is_read_only(bs)) {
869+ error_setg(errp, "Node '%s' is read only", device);
870+ return;
871+ }
872+
873+ if (!bdrv_can_snapshot(bs)) {
874+ error_setg(errp, QERR_UNSUPPORTED);
875+ return;
876+ }
877+
878+ if (bdrv_snapshot_find(bs, sn, name) < 0) {
879+ /* return success if snapshot does not exists */
880+ return;
881+ }
882+
883+ ret = bdrv_snapshot_delete(bs, NULL, name, &local_err);
884+ if (ret < 0) {
885+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
886+ "Error while deleting snapshot on '%s'\n", device);
887+ return;
888+ }
889+}
890+
891+static int loadstate_get_buffer(void *opaque, uint8_t *buf, int64_t pos,
892+ int size)
893+{
894+ BlockDriverState *bs = (BlockDriverState *)opaque;
895+ int64_t maxlen = bdrv_getlength(bs);
896+ if (pos > maxlen) {
897+ return -EIO;
898+ }
899+ if ((pos + size) > maxlen) {
900+ size = maxlen - pos - 1;
901+ }
902+ if (size == 0) {
903+ return 0;
904+ }
905+ return bdrv_pread(bs, pos, buf, size);
906+}
907+
908+static const QEMUFileOps loadstate_file_ops = {
909+ .get_buffer = loadstate_get_buffer,
910+};
911+
912+int load_state_from_blockdev(const char *filename)
913+{
914+ BlockDriverState *bs = NULL;
915+ BlockDriver *drv = NULL;
916+ Error *local_err = NULL;
917+ Error *blocker = NULL;
918+
919+ QEMUFile *f;
920+ int ret;
921+
922+ bs = bdrv_new();
923+ ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_CACHE_WB, drv, &local_err);
924+ error_setg(&blocker, "block device is in use by load state");
925+ bdrv_op_block_all(bs, blocker);
926+
927+ if (ret < 0) {
928+ error_report("Could not open VM state file");
929+ goto the_end;
930+ }
931+
932+ /* restore the VM state */
933+ f = qemu_fopen_ops(bs, &loadstate_file_ops);
934+ if (!f) {
935+ error_report("Could not open VM state file");
936+ ret = -EINVAL;
937+ goto the_end;
938+ }
939+
940+ qemu_system_reset(VMRESET_SILENT);
941+ migration_incoming_state_new(f);
942+ ret = qemu_loadvm_state(f);
943+
944+ qemu_fclose(f);
945+ migration_incoming_state_destroy();
946+ if (ret < 0) {
947+ error_report("Error %d while loading VM state", ret);
948+ goto the_end;
949+ }
950+
951+ ret = 0;
952+
953+ the_end:
954+ if (bs) {
955+ bdrv_op_unblock_all(bs, blocker);
956+ error_free(blocker);
957+ bdrv_unref(bs);
958+ }
959+ return ret;
960+}
961diff --git a/vl.c b/vl.c
962index 77c734e..5379941 100644
963--- a/vl.c
964+++ b/vl.c
965@@ -2985,6 +2985,7 @@ int main(int argc, char **argv, char **envp)
966 int optind;
967 const char *optarg;
968 const char *loadvm = NULL;
969+ const char *loadstate = NULL;
970 MachineClass *machine_class;
971 const char *cpu_model;
972 const char *vga_model = NULL;
973@@ -3611,6 +3612,9 @@ int main(int argc, char **argv, char **envp)
974 case QEMU_OPTION_loadvm:
975 loadvm = optarg;
976 break;
977+ case QEMU_OPTION_loadstate:
978+ loadstate = optarg;
979+ break;
980 case QEMU_OPTION_full_screen:
981 full_screen = 1;
982 break;
983@@ -4656,6 +4660,10 @@ int main(int argc, char **argv, char **envp)
984 if (load_vmstate(loadvm) < 0) {
985 autostart = 0;
986 }
987+ } else if (loadstate) {
988+ if (load_state_from_blockdev(loadstate) < 0) {
989+ autostart = 0;
990+ }
991 }
992
993 qdev_prop_check_globals();
994--
9952.1.4
996