]> git.proxmox.com Git - pve-qemu-kvm.git/blame - debian/patches/internal-snapshot-async.patch
updates for qemu 1.4.0
[pve-qemu-kvm.git] / debian / patches / internal-snapshot-async.patch
CommitLineData
cd983153
DM
1Index: new/qapi-schema.json
2===================================================================
92bf040c
DM
3--- new.orig/qapi-schema.json 2013-02-12 12:05:14.000000000 +0100
4+++ new/qapi-schema.json 2013-02-12 12:07:05.000000000 +0100
5@@ -528,6 +528,40 @@
e96de165 6 '*downtime': 'int'} }
cd983153
DM
7
8 ##
9+# @SaveVMInfo
10+#
11+# Information about current migration process.
12+#
13+# @status: #optional string describing the current savevm status.
14+# This can be 'active', 'completed', 'failed'.
15+# If this field is not returned, no savevm process
16+# has been initiated
17+#
18+# @error: #optional string containing error message is status is failed.
19+#
20+# @total-time: #optional total amount of milliseconds since savevm started.
21+# If savevm has ended, it returns the total save time
22+#
23+# @bytes: #optional total amount of data transfered
24+#
25+# Since: 1.3
26+##
27+{ 'type': 'SaveVMInfo',
28+ 'data': {'*status': 'str', '*error': 'str',
29+ '*total-time': 'int', '*bytes': 'int'} }
30+
31+##
32+# @query-savevm
33+#
34+# Returns information about current savevm process.
35+#
36+# Returns: @SaveVMInfo
37+#
38+# Since: 1.3
39+##
40+{ 'command': 'query-savevm', 'returns': 'SaveVMInfo' }
41+
42+##
43 # @query-migrate
44 #
45 # Returns information about current migration process.
92bf040c 46@@ -2965,6 +2999,14 @@
cd983153
DM
47 ##
48 { 'command': 'query-target', 'returns': 'TargetInfo' }
e96de165 49
1f416f3a 50+{ 'command': 'savevm-start' 'data': { '*statefile': 'str' } }
cd983153
DM
51+
52+{ 'command': 'snapshot-drive', 'data': { 'device': 'str', 'name': 'str' } }
53+
54+{ 'command': 'delete-drive-snapshot', 'data': { 'device': 'str', 'name': 'str' } }
55+
1f416f3a 56+{ 'command': 'savevm-end' }
e96de165
DM
57+
58 ##
59 # @QKeyCode:
60 #
cd983153
DM
61Index: new/qmp-commands.hx
62===================================================================
92bf040c
DM
63--- new.orig/qmp-commands.hx 2013-02-12 12:05:14.000000000 +0100
64+++ new/qmp-commands.hx 2013-02-12 12:07:05.000000000 +0100
65@@ -2775,3 +2775,34 @@
66 <- { "return": {} }
67
68 EQMP
69+
cd983153
DM
70+
71+ {
1f416f3a 72+ .name = "savevm-start",
cd983153 73+ .args_type = "statefile:s?",
1f416f3a 74+ .mhandler.cmd_new = qmp_marshal_input_savevm_start,
cd983153
DM
75+ },
76+
77+ {
78+ .name = "snapshot-drive",
79+ .args_type = "device:s,name:s",
80+ .mhandler.cmd_new = qmp_marshal_input_snapshot_drive,
81+ },
82+
83+ {
84+ .name = "delete-drive-snapshot",
85+ .args_type = "device:s,name:s",
86+ .mhandler.cmd_new = qmp_marshal_input_delete_drive_snapshot,
87+ },
88+
89+ {
1f416f3a 90+ .name = "savevm-end",
cd983153 91+ .args_type = "",
1f416f3a 92+ .mhandler.cmd_new = qmp_marshal_input_savevm_end,
cd983153 93+ },
629cf2f4
DM
94+
95+ {
96+ .name = "query-savevm",
97+ .args_type = "",
98+ .mhandler.cmd_new = qmp_marshal_input_query_savevm,
99+ },
cd983153
DM
100Index: new/hmp.c
101===================================================================
92bf040c
DM
102--- new.orig/hmp.c 2013-02-12 12:05:14.000000000 +0100
103+++ new/hmp.c 2013-02-12 12:07:05.000000000 +0100
104@@ -1379,3 +1379,60 @@
105 qmp_chardev_remove(qdict_get_str(qdict, "id"), &local_err);
106 hmp_handle_error(mon, &local_err);
cd983153
DM
107 }
108+
1f416f3a 109+void hmp_savevm_start(Monitor *mon, const QDict *qdict)
cd983153
DM
110+{
111+ Error *errp = NULL;
112+ const char *statefile = qdict_get_try_str(qdict, "statefile");
113+
1f416f3a 114+ qmp_savevm_start(statefile != NULL, statefile, &errp);
cd983153
DM
115+ hmp_handle_error(mon, &errp);
116+}
117+
118+void hmp_snapshot_drive(Monitor *mon, const QDict *qdict)
119+{
120+ Error *errp = NULL;
121+ const char *name = qdict_get_str(qdict, "name");
122+ const char *device = qdict_get_str(qdict, "device");
123+
124+ qmp_snapshot_drive(device, name, &errp);
125+ hmp_handle_error(mon, &errp);
126+}
127+
128+void hmp_delete_drive_snapshot(Monitor *mon, const QDict *qdict)
129+{
130+ Error *errp = NULL;
131+ const char *name = qdict_get_str(qdict, "name");
132+ const char *device = qdict_get_str(qdict, "device");
133+
134+ qmp_delete_drive_snapshot(device, name, &errp);
135+ hmp_handle_error(mon, &errp);
136+}
137+
1f416f3a 138+void hmp_savevm_end(Monitor *mon, const QDict *qdict)
cd983153
DM
139+{
140+ Error *errp = NULL;
141+
1f416f3a 142+ qmp_savevm_end(&errp);
cd983153
DM
143+ hmp_handle_error(mon, &errp);
144+}
145+
92bf040c 146+void hmp_info_savevm(Monitor *mon, const QDict *qdict)
cd983153
DM
147+{
148+ SaveVMInfo *info;
149+ info = qmp_query_savevm(NULL);
150+
151+ if (info->has_status) {
152+ monitor_printf(mon, "savevm status: %s\n", info->status);
153+ monitor_printf(mon, "total time: %" PRIu64 " milliseconds\n",
154+ info->total_time);
155+ } else {
156+ monitor_printf(mon, "savevm status: not running\n");
157+ }
158+ if (info->has_bytes) {
159+ monitor_printf(mon, "Bytes saved: %"PRIu64"\n", info->bytes);
160+ }
161+ if (info->has_error) {
162+ monitor_printf(mon, "Error: %s\n", info->error);
163+ }
164+}
165Index: new/hmp.h
166===================================================================
92bf040c
DM
167--- new.orig/hmp.h 2013-02-12 12:05:14.000000000 +0100
168+++ new/hmp.h 2013-02-12 12:07:05.000000000 +0100
cd983153 169@@ -25,6 +25,7 @@
92bf040c
DM
170 void hmp_info_uuid(Monitor *mon, const QDict *qdict);
171 void hmp_info_chardev(Monitor *mon, const QDict *qdict);
172 void hmp_info_mice(Monitor *mon, const QDict *qdict);
173+void hmp_info_savevm(Monitor *mon, const QDict *qdict);
174 void hmp_info_migrate(Monitor *mon, const QDict *qdict);
175 void hmp_info_migrate_capabilities(Monitor *mon, const QDict *qdict);
176 void hmp_info_migrate_cache_size(Monitor *mon, const QDict *qdict);
177@@ -77,6 +78,10 @@
cd983153
DM
178 void hmp_netdev_del(Monitor *mon, const QDict *qdict);
179 void hmp_getfd(Monitor *mon, const QDict *qdict);
180 void hmp_closefd(Monitor *mon, const QDict *qdict);
1f416f3a 181+void hmp_savevm_start(Monitor *mon, const QDict *qdict);
cd983153
DM
182+void hmp_snapshot_drive(Monitor *mon, const QDict *qdict);
183+void hmp_delete_drive_snapshot(Monitor *mon, const QDict *qdict);
1f416f3a 184+void hmp_savevm_end(Monitor *mon, const QDict *qdict);
e96de165
DM
185 void hmp_send_key(Monitor *mon, const QDict *qdict);
186 void hmp_screen_dump(Monitor *mon, const QDict *qdict);
187 void hmp_nbd_server_start(Monitor *mon, const QDict *qdict);
cd983153
DM
188Index: new/hmp-commands.hx
189===================================================================
92bf040c
DM
190--- new.orig/hmp-commands.hx 2013-02-12 12:05:14.000000000 +0100
191+++ new/hmp-commands.hx 2013-02-12 12:07:05.000000000 +0100
192@@ -1634,6 +1634,8 @@
cd983153
DM
193 show current migration capabilities
194 @item info migrate_cache_size
195 show current migration XBZRLE cache size
196+@item info savevm
197+show savevm status
198 @item info balloon
199 show balloon information
200 @item info qtree
92bf040c 201@@ -1653,3 +1655,35 @@
cd983153
DM
202 STEXI
203 @end table
204 ETEXI
205+
206+ {
1f416f3a 207+ .name = "savevm-start",
cd983153
DM
208+ .args_type = "statefile:s?",
209+ .params = "[statefile]",
210+ .help = "Prepare for snapshot and halt VM. Save VM state to statefile.",
1f416f3a 211+ .mhandler.cmd = hmp_savevm_start,
cd983153
DM
212+ },
213+
214+ {
215+ .name = "snapshot-drive",
216+ .args_type = "device:s,name:s",
217+ .params = "device name",
218+ .help = "Create internal snapshot.",
219+ .mhandler.cmd = hmp_snapshot_drive,
220+ },
221+
222+ {
223+ .name = "delete-drive-snapshot",
224+ .args_type = "device:s,name:s",
225+ .params = "device name",
226+ .help = "Delete internal snapshot.",
227+ .mhandler.cmd = hmp_delete_drive_snapshot,
228+ },
229+
230+ {
1f416f3a 231+ .name = "savevm-end",
cd983153
DM
232+ .args_type = "",
233+ .params = "",
234+ .help = "Resume VM after snaphot.",
1f416f3a 235+ .mhandler.cmd = hmp_savevm_end,
cd983153
DM
236+ },
237Index: new/savevm-async.c
238===================================================================
239--- /dev/null 1970-01-01 00:00:00.000000000 +0000
92bf040c
DM
240+++ new/savevm-async.c 2013-02-12 12:07:05.000000000 +0100
241@@ -0,0 +1,459 @@
242+#include "include/qemu-common.h"
243+#include "include/qapi/qmp/qerror.h"
244+#include "include/sysemu/sysemu.h"
cd983153 245+#include "qmp-commands.h"
92bf040c
DM
246+#include "include/migration/qemu-file.h"
247+#include "include/sysemu/blockdev.h"
248+#include "include/qom/qom-qobject.h"
249+#include "include/migration/migration.h"
cd983153 250+
b84aa9c4 251+/* #define DEBUG_SAVEVM_STATE */
cd983153
DM
252+
253+#ifdef DEBUG_SAVEVM_STATE
254+#define DPRINTF(fmt, ...) \
255+ do { printf("savevm-async: " fmt, ## __VA_ARGS__); } while (0)
256+#else
257+#define DPRINTF(fmt, ...) \
258+ do { } while (0)
259+#endif
260+
261+enum {
262+ SAVE_STATE_DONE,
263+ SAVE_STATE_ERROR,
264+ SAVE_STATE_ACTIVE,
265+ SAVE_STATE_COMPLETED,
266+};
267+
268+static struct SnapshotState {
269+ BlockDriverState *bs;
270+ size_t bs_pos;
271+ int state;
272+ Error *error;
273+ int saved_vm_running;
274+ QEMUFile *file;
275+ int64_t total_time;
276+} snap_state;
277+
278+SaveVMInfo *qmp_query_savevm(Error **errp)
279+{
280+ SaveVMInfo *info = g_malloc0(sizeof(*info));
281+ struct SnapshotState *s = &snap_state;
282+
283+ if (s->state != SAVE_STATE_DONE) {
284+ info->has_bytes = true;
285+ info->bytes = s->bs_pos;
286+ switch (s->state) {
287+ case SAVE_STATE_ERROR:
288+ info->has_status = true;
289+ info->status = g_strdup("failed");
290+ info->has_total_time = true;
291+ info->total_time = s->total_time;
292+ if (s->error) {
293+ info->has_error = true;
294+ info->error = g_strdup(error_get_pretty(s->error));
295+ }
296+ break;
297+ case SAVE_STATE_ACTIVE:
298+ info->has_status = true;
299+ info->status = g_strdup("active");
300+ info->has_total_time = true;
301+ info->total_time = qemu_get_clock_ms(rt_clock)
302+ - s->total_time;
303+ break;
304+ case SAVE_STATE_COMPLETED:
305+ info->has_status = true;
306+ info->status = g_strdup("completed");
307+ info->has_total_time = true;
308+ info->total_time = s->total_time;
309+ break;
310+ }
311+ }
312+
313+ return info;
314+}
315+
316+static int save_snapshot_cleanup(void)
317+{
318+ int ret = 0;
319+
320+ DPRINTF("save_snapshot_cleanup\n");
321+
322+ snap_state.total_time = qemu_get_clock_ms(rt_clock) -
323+ snap_state.total_time;
324+
325+ if (snap_state.file) {
326+ ret = qemu_fclose(snap_state.file);
327+ }
328+
329+ if (snap_state.bs) {
b84aa9c4
DM
330+ /* try to truncate, but ignore errors (will fail on block devices).
331+ * note: bdrv_read() need whole blocks, so we round up
332+ */
cd983153
DM
333+ size_t size = (snap_state.bs_pos + BDRV_SECTOR_SIZE) & BDRV_SECTOR_MASK;
334+ bdrv_truncate(snap_state.bs, size);
cd983153
DM
335+ bdrv_delete(snap_state.bs);
336+ snap_state.bs = NULL;
337+ }
338+
339+ return ret;
340+}
341+
342+static void save_snapshot_error(const char *fmt, ...)
343+{
344+ va_list ap;
345+ char *msg;
346+
347+ va_start(ap, fmt);
348+ msg = g_strdup_vprintf(fmt, ap);
349+ va_end(ap);
350+
351+ DPRINTF("save_snapshot_error: %s\n", msg);
352+
353+ if (!snap_state.error) {
354+ error_set(&snap_state.error, ERROR_CLASS_GENERIC_ERROR, "%s", msg);
355+ }
356+
357+ g_free (msg);
358+
359+ snap_state.state = SAVE_STATE_ERROR;
360+
361+ save_snapshot_cleanup();
362+}
363+
364+static void save_snapshot_completed(void)
365+{
366+ DPRINTF("save_snapshot_completed\n");
367+
368+ if (save_snapshot_cleanup() < 0) {
369+ snap_state.state = SAVE_STATE_ERROR;
370+ } else {
371+ snap_state.state = SAVE_STATE_COMPLETED;
372+ }
373+}
374+
375+static int block_state_close(void *opaque)
376+{
377+ snap_state.file = NULL;
378+ return bdrv_flush(snap_state.bs);
379+}
380+
b84aa9c4
DM
381+static int block_state_put_buffer(void *opaque, const uint8_t *buf,
382+ int64_t pos, int size)
cd983153
DM
383+{
384+ int ret;
385+
b84aa9c4
DM
386+ assert(pos == snap_state.bs_pos);
387+
cd983153
DM
388+ if ((ret = bdrv_pwrite(snap_state.bs, snap_state.bs_pos, buf, size)) > 0) {
389+ snap_state.bs_pos += ret;
390+ }
391+
392+ return ret;
393+}
394+
b84aa9c4 395+static void process_savevm_co(void *opaque)
cd983153
DM
396+{
397+ int ret;
b84aa9c4
DM
398+ int64_t maxlen;
399+ MigrationParams params = {
400+ .blk = 0,
401+ .shared = 0
402+ };
cd983153 403+
b84aa9c4 404+ snap_state.state = SAVE_STATE_ACTIVE;
cd983153 405+
b84aa9c4
DM
406+ ret = qemu_savevm_state_begin(snap_state.file, &params);
407+ if (ret < 0) {
408+ save_snapshot_error("qemu_savevm_state_begin failed");
cd983153
DM
409+ return;
410+ }
411+
b84aa9c4 412+ while (snap_state.state == SAVE_STATE_ACTIVE) {
92bf040c
DM
413+ uint64_t pending_size;
414+
415+ pending_size = qemu_savevm_state_pending(snap_state.file, 0);
416+
417+ if (pending_size) {
418+ ret = qemu_savevm_state_iterate(snap_state.file);
419+ if (ret < 0) {
420+ save_snapshot_error("qemu_savevm_state_iterate error %d", ret);
421+ break;
422+ }
423+ DPRINTF("savevm inerate pending size %lu ret %d\n", pending_size, ret);
424+ } else {
425+ DPRINTF("done iterating\n");
b84aa9c4
DM
426+ if (runstate_is_running()) {
427+ vm_stop(RUN_STATE_SAVE_VM);
428+ }
429+ DPRINTF("savevm inerate finished\n");
92bf040c
DM
430+ ret = qemu_savevm_state_complete(snap_state.file);
431+ if (ret < 0) {
b84aa9c4 432+ save_snapshot_error("qemu_savevm_state_complete error %d", ret);
92bf040c 433+ break;
b84aa9c4
DM
434+ } else {
435+ DPRINTF("save complete\n");
436+ save_snapshot_completed();
92bf040c
DM
437+ break;
438+ }
439+ }
440+
441+ /* stop the VM if we get to the end of available space,
442+ * or if pending_size is just a few MB
443+ */
444+ maxlen = bdrv_getlength(snap_state.bs) - 30*1024*1024;
445+ if ((pending_size < 100000) ||
446+ ((snap_state.bs_pos + pending_size) >= maxlen)) {
447+ if (runstate_is_running()) {
448+ vm_stop(RUN_STATE_SAVE_VM);
b84aa9c4
DM
449+ }
450+ }
cd983153
DM
451+ }
452+}
453+
b84aa9c4
DM
454+static const QEMUFileOps block_file_ops = {
455+ .put_buffer = block_state_put_buffer,
456+ .close = block_state_close,
457+};
458+
cd983153 459+
1f416f3a 460+void qmp_savevm_start(bool has_statefile, const char *statefile, Error **errp)
cd983153
DM
461+{
462+ BlockDriver *drv = NULL;
463+ int bdrv_oflags = BDRV_O_CACHE_WB | BDRV_O_RDWR;
cd983153
DM
464+ int ret;
465+
466+ if (snap_state.state != SAVE_STATE_DONE) {
467+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
468+ "VM snapshot already started\n");
469+ return;
470+ }
471+
472+ /* initialize snapshot info */
473+ snap_state.saved_vm_running = runstate_is_running();
474+ snap_state.bs_pos = 0;
475+ snap_state.total_time = qemu_get_clock_ms(rt_clock);
476+
477+ if (snap_state.error) {
478+ error_free(snap_state.error);
479+ snap_state.error = NULL;
480+ }
481+
cd983153 482+ if (!has_statefile) {
b84aa9c4 483+ vm_stop(RUN_STATE_SAVE_VM);
cd983153
DM
484+ snap_state.state = SAVE_STATE_COMPLETED;
485+ return;
486+ }
487+
488+ if (qemu_savevm_state_blocked(errp)) {
489+ return;
490+ }
491+
492+ /* Open the image */
493+ snap_state.bs = bdrv_new("vmstate");
494+ ret = bdrv_open(snap_state.bs, statefile, bdrv_oflags, drv);
495+ if (ret < 0) {
496+ error_set(errp, QERR_OPEN_FILE_FAILED, statefile);
497+ goto restart;
498+ }
499+
b84aa9c4 500+ snap_state.file = qemu_fopen_ops(&snap_state, &block_file_ops);
cd983153
DM
501+
502+ if (!snap_state.file) {
503+ error_set(errp, QERR_OPEN_FILE_FAILED, statefile);
504+ goto restart;
505+ }
506+
b84aa9c4
DM
507+ Coroutine *co = qemu_coroutine_create(process_savevm_co);
508+ qemu_coroutine_enter(co, NULL);
cd983153
DM
509+
510+ return;
511+
512+restart:
513+
514+ save_snapshot_error("setup failed");
515+
516+ if (snap_state.saved_vm_running) {
517+ vm_start();
518+ }
519+}
520+
1f416f3a 521+void qmp_savevm_end(Error **errp)
cd983153
DM
522+{
523+ if (snap_state.state == SAVE_STATE_DONE) {
524+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
525+ "VM snapshot not started\n");
526+ return;
527+ }
528+
529+ if (snap_state.saved_vm_running) {
530+ vm_start();
531+ }
532+
533+ snap_state.state = SAVE_STATE_DONE;
534+}
535+
536+void qmp_snapshot_drive(const char *device, const char *name, Error **errp)
537+{
538+ BlockDriverState *bs;
539+ QEMUSnapshotInfo sn1, *sn = &sn1;
540+ int ret;
541+#ifdef _WIN32
542+ struct _timeb tb;
543+#else
544+ struct timeval tv;
545+#endif
546+
547+ if (snap_state.state != SAVE_STATE_COMPLETED) {
548+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
549+ "VM snapshot not ready/started\n");
550+ return;
551+ }
552+
553+ bs = bdrv_find(device);
554+ if (!bs) {
555+ error_set(errp, QERR_DEVICE_NOT_FOUND, device);
556+ return;
557+ }
558+
559+ if (!bdrv_is_inserted(bs)) {
560+ error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device);
561+ return;
562+ }
563+
564+ if (bdrv_is_read_only(bs)) {
565+ error_set(errp, QERR_DEVICE_IS_READ_ONLY, device);
566+ return;
567+ }
568+
569+ if (!bdrv_can_snapshot(bs)) {
570+ error_set(errp, QERR_NOT_SUPPORTED);
571+ return;
572+ }
573+
574+ if (bdrv_snapshot_find(bs, sn, name) >= 0) {
575+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
576+ "snapshot '%s' already exists", name);
577+ return;
578+ }
579+
580+ sn = &sn1;
581+ memset(sn, 0, sizeof(*sn));
582+
583+#ifdef _WIN32
584+ _ftime(&tb);
585+ sn->date_sec = tb.time;
586+ sn->date_nsec = tb.millitm * 1000000;
587+#else
588+ gettimeofday(&tv, NULL);
589+ sn->date_sec = tv.tv_sec;
590+ sn->date_nsec = tv.tv_usec * 1000;
591+#endif
592+ sn->vm_clock_nsec = qemu_get_clock_ns(vm_clock);
593+
594+ pstrcpy(sn->name, sizeof(sn->name), name);
595+
596+ sn->vm_state_size = 0; /* do not save state */
597+
598+ ret = bdrv_snapshot_create(bs, sn);
599+ if (ret < 0) {
600+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
601+ "Error while creating snapshot on '%s'\n", device);
602+ return;
603+ }
604+}
605+
606+void qmp_delete_drive_snapshot(const char *device, const char *name,
607+ Error **errp)
608+{
609+ BlockDriverState *bs;
610+ QEMUSnapshotInfo sn1, *sn = &sn1;
611+ int ret;
612+
613+ bs = bdrv_find(device);
614+ if (!bs) {
615+ error_set(errp, QERR_DEVICE_NOT_FOUND, device);
616+ return;
617+ }
618+ if (bdrv_is_read_only(bs)) {
619+ error_set(errp, QERR_DEVICE_IS_READ_ONLY, device);
620+ return;
621+ }
622+
623+ if (!bdrv_can_snapshot(bs)) {
624+ error_set(errp, QERR_NOT_SUPPORTED);
625+ return;
626+ }
627+
628+ if (bdrv_snapshot_find(bs, sn, name) < 0) {
629+ /* return success if snapshot does not exists */
630+ return;
631+ }
632+
633+ ret = bdrv_snapshot_delete(bs, name);
634+ if (ret < 0) {
635+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
636+ "Error while deleting snapshot on '%s'\n", device);
637+ return;
638+ }
639+}
640+
b84aa9c4
DM
641+static int loadstate_get_buffer(void *opaque, uint8_t *buf, int64_t pos,
642+ int size)
cd983153
DM
643+{
644+ BlockDriverState *bs = (BlockDriverState *)opaque;
645+ int64_t maxlen = bdrv_getlength(bs);
646+ if (pos > maxlen) {
647+ return -EIO;
648+ }
649+ if ((pos + size) > maxlen) {
650+ size = maxlen - pos - 1;
651+ }
652+ if (size == 0) {
653+ return 0;
654+ }
655+ return bdrv_pread(bs, pos, buf, size);
656+}
657+
b84aa9c4
DM
658+static const QEMUFileOps loadstate_file_ops = {
659+ .get_buffer = loadstate_get_buffer,
660+};
661+
cd983153
DM
662+int load_state_from_blockdev(const char *filename)
663+{
664+ BlockDriverState *bs = NULL;
665+ BlockDriver *drv = NULL;
666+ QEMUFile *f;
667+ int ret = -1;
668+
669+ bs = bdrv_new("vmstate");
670+ ret = bdrv_open(bs, filename, BDRV_O_CACHE_WB, drv);
671+ if (ret < 0) {
672+ error_report("Could not open VM state file");
673+ goto the_end;
674+ }
675+
676+ /* restore the VM state */
b84aa9c4 677+ f = qemu_fopen_ops(bs, &loadstate_file_ops);
cd983153
DM
678+ if (!f) {
679+ error_report("Could not open VM state file");
680+ ret = -EINVAL;
681+ goto the_end;
682+ }
683+
684+ qemu_system_reset(VMRESET_SILENT);
685+ ret = qemu_loadvm_state(f);
686+
687+ qemu_fclose(f);
688+ if (ret < 0) {
689+ error_report("Error %d while loading VM state", ret);
690+ goto the_end;
691+ }
692+
693+ ret = 0;
694+
695+ the_end:
696+ if (bs) {
697+ bdrv_delete(bs);
698+ }
699+ return ret;
700+}
701Index: new/Makefile.objs
702===================================================================
92bf040c
DM
703--- new.orig/Makefile.objs 2013-02-12 12:05:14.000000000 +0100
704+++ new/Makefile.objs 2013-02-12 12:07:05.000000000 +0100
705@@ -60,6 +60,7 @@
706 common-obj-y += qemu-char.o #aio.o
707 common-obj-y += block-migration.o
708 common-obj-y += page_cache.o xbzrle.o
cd983153
DM
709+common-obj-y += savevm-async.o
710
711 common-obj-$(CONFIG_POSIX) += migration-exec.o migration-unix.o migration-fd.o
cd983153
DM
712
713Index: new/qemu-options.hx
714===================================================================
92bf040c
DM
715--- new.orig/qemu-options.hx 2013-02-12 12:06:20.000000000 +0100
716+++ new/qemu-options.hx 2013-02-12 12:07:05.000000000 +0100
717@@ -2605,6 +2605,19 @@
cd983153
DM
718 Start right away with a saved state (@code{loadvm} in monitor)
719 ETEXI
720
721+DEF("loadstate", HAS_ARG, QEMU_OPTION_loadstate, \
722+ "-loadstate file\n" \
723+ " start right away with a saved state\n",
724+ QEMU_ARCH_ALL)
725+STEXI
726+@item -loadstate @var{file}
727+@findex -loadstate
728+Start right away with a saved state. This option does not rollback
729+disk state like @code{loadvm}, so user must make sure that disk
730+have correct state. @var{file} can be any valid device URL. See the section
731+for "Device URL Syntax" for more information.
732+ETEXI
733+
734 #ifndef _WIN32
735 DEF("daemonize", 0, QEMU_OPTION_daemonize, \
736 "-daemonize daemonize QEMU after initializing\n", QEMU_ARCH_ALL)
737Index: new/vl.c
738===================================================================
92bf040c
DM
739--- new.orig/vl.c 2013-02-12 12:06:40.000000000 +0100
740+++ new/vl.c 2013-02-12 12:07:05.000000000 +0100
741@@ -2816,6 +2816,7 @@
cd983153
DM
742 int optind;
743 const char *optarg;
744 const char *loadvm = NULL;
745+ const char *loadstate = NULL;
746 QEMUMachine *machine;
747 const char *cpu_model;
748 const char *vga_model = "none";
92bf040c 749@@ -3466,6 +3467,9 @@
cd983153
DM
750 case QEMU_OPTION_loadvm:
751 loadvm = optarg;
752 break;
753+ case QEMU_OPTION_loadstate:
754+ loadstate = optarg;
755+ break;
756 case QEMU_OPTION_full_screen:
757 full_screen = 1;
758 break;
92bf040c 759@@ -4361,6 +4365,10 @@
cd983153
DM
760 if (load_vmstate(loadvm) < 0) {
761 autostart = 0;
762 }
763+ } else if (loadstate) {
764+ if (load_state_from_blockdev(loadstate) < 0) {
765+ autostart = 0;
766+ }
767 }
768
769 if (incoming) {
629cf2f4
DM
770Index: new/monitor.c
771===================================================================
92bf040c
DM
772--- new.orig/monitor.c 2013-02-12 12:05:14.000000000 +0100
773+++ new/monitor.c 2013-02-12 12:07:05.000000000 +0100
774@@ -2687,6 +2687,13 @@
775 .mhandler.cmd = hmp_info_migrate_cache_size,
629cf2f4
DM
776 },
777 {
778+ .name = "savevm",
779+ .args_type = "",
780+ .params = "",
781+ .help = "show savevm status",
92bf040c 782+ .mhandler.cmd = hmp_info_savevm,
629cf2f4
DM
783+ },
784+ {
785 .name = "balloon",
786 .args_type = "",
787 .params = "",
92bf040c
DM
788Index: new/include/sysemu/sysemu.h
789===================================================================
790--- new.orig/include/sysemu/sysemu.h 2013-02-12 12:05:14.000000000 +0100
791+++ new/include/sysemu/sysemu.h 2013-02-12 12:07:05.000000000 +0100
792@@ -67,6 +67,7 @@
793
794 void do_savevm(Monitor *mon, const QDict *qdict);
795 int load_vmstate(const char *name);
796+int load_state_from_blockdev(const char *filename);
797 void do_delvm(Monitor *mon, const QDict *qdict);
798 void do_info_snapshots(Monitor *mon, const QDict *qdict);
799