1 Index: new/qapi-schema.json
2 ===================================================================
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
11 +# Information about current migration process.
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
18 +# @error: #optional string containing error message is status is failed.
20 +# @total-time: #optional total amount of milliseconds since savevm started.
21 +# If savevm has ended, it returns the total save time
23 +# @bytes: #optional total amount of data transfered
27 +{ 'type': 'SaveVMInfo',
28 + 'data': {'*status': 'str', '*error': 'str',
29 + '*total-time': 'int', '*bytes': 'int'} }
34 +# Returns information about current savevm process.
36 +# Returns: @SaveVMInfo
40 +{ 'command': 'query-savevm', 'returns': 'SaveVMInfo' }
45 # Returns information about current migration process.
46 @@ -2965,6 +2999,14 @@
48 { 'command': 'query-target', 'returns': 'TargetInfo' }
50 +{ 'command': 'savevm-start' 'data': { '*statefile': 'str' } }
52 +{ 'command': 'snapshot-drive', 'data': { 'device': 'str', 'name': 'str' } }
54 +{ 'command': 'delete-drive-snapshot', 'data': { 'device': 'str', 'name': 'str' } }
56 +{ 'command': 'savevm-end' }
61 Index: new/qmp-commands.hx
62 ===================================================================
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 @@
72 + .name = "savevm-start",
73 + .args_type = "statefile:s?",
74 + .mhandler.cmd_new = qmp_marshal_input_savevm_start,
78 + .name = "snapshot-drive",
79 + .args_type = "device:s,name:s",
80 + .mhandler.cmd_new = qmp_marshal_input_snapshot_drive,
84 + .name = "delete-drive-snapshot",
85 + .args_type = "device:s,name:s",
86 + .mhandler.cmd_new = qmp_marshal_input_delete_drive_snapshot,
90 + .name = "savevm-end",
92 + .mhandler.cmd_new = qmp_marshal_input_savevm_end,
96 + .name = "query-savevm",
98 + .mhandler.cmd_new = qmp_marshal_input_query_savevm,
101 ===================================================================
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);
109 +void hmp_savevm_start(Monitor *mon, const QDict *qdict)
111 + Error *errp = NULL;
112 + const char *statefile = qdict_get_try_str(qdict, "statefile");
114 + qmp_savevm_start(statefile != NULL, statefile, &errp);
115 + hmp_handle_error(mon, &errp);
118 +void hmp_snapshot_drive(Monitor *mon, const QDict *qdict)
120 + Error *errp = NULL;
121 + const char *name = qdict_get_str(qdict, "name");
122 + const char *device = qdict_get_str(qdict, "device");
124 + qmp_snapshot_drive(device, name, &errp);
125 + hmp_handle_error(mon, &errp);
128 +void hmp_delete_drive_snapshot(Monitor *mon, const QDict *qdict)
130 + Error *errp = NULL;
131 + const char *name = qdict_get_str(qdict, "name");
132 + const char *device = qdict_get_str(qdict, "device");
134 + qmp_delete_drive_snapshot(device, name, &errp);
135 + hmp_handle_error(mon, &errp);
138 +void hmp_savevm_end(Monitor *mon, const QDict *qdict)
140 + Error *errp = NULL;
142 + qmp_savevm_end(&errp);
143 + hmp_handle_error(mon, &errp);
146 +void hmp_info_savevm(Monitor *mon, const QDict *qdict)
149 + info = qmp_query_savevm(NULL);
151 + if (info->has_status) {
152 + monitor_printf(mon, "savevm status: %s\n", info->status);
153 + monitor_printf(mon, "total time: %" PRIu64 " milliseconds\n",
156 + monitor_printf(mon, "savevm status: not running\n");
158 + if (info->has_bytes) {
159 + monitor_printf(mon, "Bytes saved: %"PRIu64"\n", info->bytes);
161 + if (info->has_error) {
162 + monitor_printf(mon, "Error: %s\n", info->error);
166 ===================================================================
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
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);
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);
181 +void hmp_savevm_start(Monitor *mon, const QDict *qdict);
182 +void hmp_snapshot_drive(Monitor *mon, const QDict *qdict);
183 +void hmp_delete_drive_snapshot(Monitor *mon, const QDict *qdict);
184 +void hmp_savevm_end(Monitor *mon, const QDict *qdict);
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);
188 Index: new/hmp-commands.hx
189 ===================================================================
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 @@
193 show current migration capabilities
194 @item info migrate_cache_size
195 show current migration XBZRLE cache size
199 show balloon information
201 @@ -1653,3 +1655,35 @@
207 + .name = "savevm-start",
208 + .args_type = "statefile:s?",
209 + .params = "[statefile]",
210 + .help = "Prepare for snapshot and halt VM. Save VM state to statefile.",
211 + .mhandler.cmd = hmp_savevm_start,
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,
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,
231 + .name = "savevm-end",
234 + .help = "Resume VM after snaphot.",
235 + .mhandler.cmd = hmp_savevm_end,
237 Index: new/savevm-async.c
238 ===================================================================
239 --- /dev/null 1970-01-01 00:00:00.000000000 +0000
240 +++ new/savevm-async.c 2013-02-12 12:07:05.000000000 +0100
242 +#include "include/qemu-common.h"
243 +#include "include/qapi/qmp/qerror.h"
244 +#include "include/sysemu/sysemu.h"
245 +#include "qmp-commands.h"
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"
251 +/* #define DEBUG_SAVEVM_STATE */
253 +#ifdef DEBUG_SAVEVM_STATE
254 +#define DPRINTF(fmt, ...) \
255 + do { printf("savevm-async: " fmt, ## __VA_ARGS__); } while (0)
257 +#define DPRINTF(fmt, ...) \
265 + SAVE_STATE_COMPLETED,
268 +static struct SnapshotState {
269 + BlockDriverState *bs;
273 + int saved_vm_running;
275 + int64_t total_time;
278 +SaveVMInfo *qmp_query_savevm(Error **errp)
280 + SaveVMInfo *info = g_malloc0(sizeof(*info));
281 + struct SnapshotState *s = &snap_state;
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;
293 + info->has_error = true;
294 + info->error = g_strdup(error_get_pretty(s->error));
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)
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;
316 +static int save_snapshot_cleanup(void)
320 + DPRINTF("save_snapshot_cleanup\n");
322 + snap_state.total_time = qemu_get_clock_ms(rt_clock) -
323 + snap_state.total_time;
325 + if (snap_state.file) {
326 + ret = qemu_fclose(snap_state.file);
329 + if (snap_state.bs) {
330 + /* try to truncate, but ignore errors (will fail on block devices).
331 + * note: bdrv_read() need whole blocks, so we round up
333 + size_t size = (snap_state.bs_pos + BDRV_SECTOR_SIZE) & BDRV_SECTOR_MASK;
334 + bdrv_truncate(snap_state.bs, size);
335 + bdrv_delete(snap_state.bs);
336 + snap_state.bs = NULL;
342 +static void save_snapshot_error(const char *fmt, ...)
348 + msg = g_strdup_vprintf(fmt, ap);
351 + DPRINTF("save_snapshot_error: %s\n", msg);
353 + if (!snap_state.error) {
354 + error_set(&snap_state.error, ERROR_CLASS_GENERIC_ERROR, "%s", msg);
359 + snap_state.state = SAVE_STATE_ERROR;
361 + save_snapshot_cleanup();
364 +static void save_snapshot_completed(void)
366 + DPRINTF("save_snapshot_completed\n");
368 + if (save_snapshot_cleanup() < 0) {
369 + snap_state.state = SAVE_STATE_ERROR;
371 + snap_state.state = SAVE_STATE_COMPLETED;
375 +static int block_state_close(void *opaque)
377 + snap_state.file = NULL;
378 + return bdrv_flush(snap_state.bs);
381 +static int block_state_put_buffer(void *opaque, const uint8_t *buf,
382 + int64_t pos, int size)
386 + assert(pos == snap_state.bs_pos);
388 + if ((ret = bdrv_pwrite(snap_state.bs, snap_state.bs_pos, buf, size)) > 0) {
389 + snap_state.bs_pos += ret;
395 +static void process_savevm_co(void *opaque)
399 + MigrationParams params = {
404 + snap_state.state = SAVE_STATE_ACTIVE;
406 + ret = qemu_savevm_state_begin(snap_state.file, ¶ms);
408 + save_snapshot_error("qemu_savevm_state_begin failed");
412 + while (snap_state.state == SAVE_STATE_ACTIVE) {
413 + uint64_t pending_size;
415 + pending_size = qemu_savevm_state_pending(snap_state.file, 0);
417 + if (pending_size) {
418 + ret = qemu_savevm_state_iterate(snap_state.file);
420 + save_snapshot_error("qemu_savevm_state_iterate error %d", ret);
423 + DPRINTF("savevm inerate pending size %lu ret %d\n", pending_size, ret);
425 + DPRINTF("done iterating\n");
426 + if (runstate_is_running()) {
427 + vm_stop(RUN_STATE_SAVE_VM);
429 + DPRINTF("savevm inerate finished\n");
430 + ret = qemu_savevm_state_complete(snap_state.file);
432 + save_snapshot_error("qemu_savevm_state_complete error %d", ret);
435 + DPRINTF("save complete\n");
436 + save_snapshot_completed();
441 + /* stop the VM if we get to the end of available space,
442 + * or if pending_size is just a few MB
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);
454 +static const QEMUFileOps block_file_ops = {
455 + .put_buffer = block_state_put_buffer,
456 + .close = block_state_close,
460 +void qmp_savevm_start(bool has_statefile, const char *statefile, Error **errp)
462 + BlockDriver *drv = NULL;
463 + int bdrv_oflags = BDRV_O_CACHE_WB | BDRV_O_RDWR;
466 + if (snap_state.state != SAVE_STATE_DONE) {
467 + error_set(errp, ERROR_CLASS_GENERIC_ERROR,
468 + "VM snapshot already started\n");
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);
477 + if (snap_state.error) {
478 + error_free(snap_state.error);
479 + snap_state.error = NULL;
482 + if (!has_statefile) {
483 + vm_stop(RUN_STATE_SAVE_VM);
484 + snap_state.state = SAVE_STATE_COMPLETED;
488 + if (qemu_savevm_state_blocked(errp)) {
492 + /* Open the image */
493 + snap_state.bs = bdrv_new("vmstate");
494 + ret = bdrv_open(snap_state.bs, statefile, bdrv_oflags, drv);
496 + error_set(errp, QERR_OPEN_FILE_FAILED, statefile);
500 + snap_state.file = qemu_fopen_ops(&snap_state, &block_file_ops);
502 + if (!snap_state.file) {
503 + error_set(errp, QERR_OPEN_FILE_FAILED, statefile);
507 + Coroutine *co = qemu_coroutine_create(process_savevm_co);
508 + qemu_coroutine_enter(co, NULL);
514 + save_snapshot_error("setup failed");
516 + if (snap_state.saved_vm_running) {
521 +void qmp_savevm_end(Error **errp)
523 + if (snap_state.state == SAVE_STATE_DONE) {
524 + error_set(errp, ERROR_CLASS_GENERIC_ERROR,
525 + "VM snapshot not started\n");
529 + if (snap_state.saved_vm_running) {
533 + snap_state.state = SAVE_STATE_DONE;
536 +void qmp_snapshot_drive(const char *device, const char *name, Error **errp)
538 + BlockDriverState *bs;
539 + QEMUSnapshotInfo sn1, *sn = &sn1;
547 + if (snap_state.state != SAVE_STATE_COMPLETED) {
548 + error_set(errp, ERROR_CLASS_GENERIC_ERROR,
549 + "VM snapshot not ready/started\n");
553 + bs = bdrv_find(device);
555 + error_set(errp, QERR_DEVICE_NOT_FOUND, device);
559 + if (!bdrv_is_inserted(bs)) {
560 + error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device);
564 + if (bdrv_is_read_only(bs)) {
565 + error_set(errp, QERR_DEVICE_IS_READ_ONLY, device);
569 + if (!bdrv_can_snapshot(bs)) {
570 + error_set(errp, QERR_NOT_SUPPORTED);
574 + if (bdrv_snapshot_find(bs, sn, name) >= 0) {
575 + error_set(errp, ERROR_CLASS_GENERIC_ERROR,
576 + "snapshot '%s' already exists", name);
581 + memset(sn, 0, sizeof(*sn));
585 + sn->date_sec = tb.time;
586 + sn->date_nsec = tb.millitm * 1000000;
588 + gettimeofday(&tv, NULL);
589 + sn->date_sec = tv.tv_sec;
590 + sn->date_nsec = tv.tv_usec * 1000;
592 + sn->vm_clock_nsec = qemu_get_clock_ns(vm_clock);
594 + pstrcpy(sn->name, sizeof(sn->name), name);
596 + sn->vm_state_size = 0; /* do not save state */
598 + ret = bdrv_snapshot_create(bs, sn);
600 + error_set(errp, ERROR_CLASS_GENERIC_ERROR,
601 + "Error while creating snapshot on '%s'\n", device);
606 +void qmp_delete_drive_snapshot(const char *device, const char *name,
609 + BlockDriverState *bs;
610 + QEMUSnapshotInfo sn1, *sn = &sn1;
613 + bs = bdrv_find(device);
615 + error_set(errp, QERR_DEVICE_NOT_FOUND, device);
618 + if (bdrv_is_read_only(bs)) {
619 + error_set(errp, QERR_DEVICE_IS_READ_ONLY, device);
623 + if (!bdrv_can_snapshot(bs)) {
624 + error_set(errp, QERR_NOT_SUPPORTED);
628 + if (bdrv_snapshot_find(bs, sn, name) < 0) {
629 + /* return success if snapshot does not exists */
633 + ret = bdrv_snapshot_delete(bs, name);
635 + error_set(errp, ERROR_CLASS_GENERIC_ERROR,
636 + "Error while deleting snapshot on '%s'\n", device);
641 +static int loadstate_get_buffer(void *opaque, uint8_t *buf, int64_t pos,
644 + BlockDriverState *bs = (BlockDriverState *)opaque;
645 + int64_t maxlen = bdrv_getlength(bs);
646 + if (pos > maxlen) {
649 + if ((pos + size) > maxlen) {
650 + size = maxlen - pos - 1;
655 + return bdrv_pread(bs, pos, buf, size);
658 +static const QEMUFileOps loadstate_file_ops = {
659 + .get_buffer = loadstate_get_buffer,
662 +int load_state_from_blockdev(const char *filename)
664 + BlockDriverState *bs = NULL;
665 + BlockDriver *drv = NULL;
669 + bs = bdrv_new("vmstate");
670 + ret = bdrv_open(bs, filename, BDRV_O_CACHE_WB, drv);
672 + error_report("Could not open VM state file");
676 + /* restore the VM state */
677 + f = qemu_fopen_ops(bs, &loadstate_file_ops);
679 + error_report("Could not open VM state file");
684 + qemu_system_reset(VMRESET_SILENT);
685 + ret = qemu_loadvm_state(f);
689 + error_report("Error %d while loading VM state", ret);
701 Index: new/Makefile.objs
702 ===================================================================
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
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
709 +common-obj-y += savevm-async.o
711 common-obj-$(CONFIG_POSIX) += migration-exec.o migration-unix.o migration-fd.o
713 Index: new/qemu-options.hx
714 ===================================================================
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 @@
718 Start right away with a saved state (@code{loadvm} in monitor)
721 +DEF("loadstate", HAS_ARG, QEMU_OPTION_loadstate, \
722 + "-loadstate file\n" \
723 + " start right away with a saved state\n",
726 +@item -loadstate @var{file}
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.
735 DEF("daemonize", 0, QEMU_OPTION_daemonize, \
736 "-daemonize daemonize QEMU after initializing\n", QEMU_ARCH_ALL)
738 ===================================================================
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 @@
744 const char *loadvm = NULL;
745 + const char *loadstate = NULL;
746 QEMUMachine *machine;
747 const char *cpu_model;
748 const char *vga_model = "none";
749 @@ -3466,6 +3467,9 @@
750 case QEMU_OPTION_loadvm:
753 + case QEMU_OPTION_loadstate:
754 + loadstate = optarg;
756 case QEMU_OPTION_full_screen:
759 @@ -4361,6 +4365,10 @@
760 if (load_vmstate(loadvm) < 0) {
763 + } else if (loadstate) {
764 + if (load_state_from_blockdev(loadstate) < 0) {
771 ===================================================================
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,
781 + .help = "show savevm status",
782 + .mhandler.cmd = hmp_info_savevm,
788 Index: 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
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);