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