update to qemu-2.9.0-rc2
[pve-qemu-kvm.git] / debian / patches / pve / 0022-internal-snapshot-async.patch
1 From 15215ea4cb52cf95d68063289bb185dae7de5433 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/47] 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