From e128dc273bd31e7e056a01de4e8e41ddc8727fee Mon Sep 17 00:00:00 2001 From: Dietmar Maurer Date: Mon, 24 Sep 2012 08:00:20 +0200 Subject: [PATCH] remove rate limit --- new/savevm-async.c | 441 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 441 insertions(+) create mode 100644 new/savevm-async.c diff --git a/new/savevm-async.c b/new/savevm-async.c new file mode 100644 index 0000000..6e81fd7 --- /dev/null +++ b/new/savevm-async.c @@ -0,0 +1,441 @@ +#include "qemu-common.h" +#include "qerror.h" +#include "sysemu.h" +#include "qmp-commands.h" +#include "blockdev.h" +#include "qemu/qom-qobject.h" +#include "buffered_file.h" +#include "migration.h" + +//#define DEBUG_SAVEVM_STATE + +#ifdef DEBUG_SAVEVM_STATE +#define DPRINTF(fmt, ...) \ + do { printf("savevm-async: " fmt, ## __VA_ARGS__); } while (0) +#else +#define DPRINTF(fmt, ...) \ + do { } while (0) +#endif + +enum { + SAVE_STATE_DONE, + SAVE_STATE_ERROR, + SAVE_STATE_ACTIVE, + SAVE_STATE_COMPLETED, +}; + +static struct SnapshotState { + BlockDriverState *bs; + size_t bs_pos; + int state; + Error *error; + int saved_vm_running; + QEMUFile *file; + int64_t total_time; +} snap_state; + +SaveVMInfo *qmp_query_savevm(Error **errp) +{ + SaveVMInfo *info = g_malloc0(sizeof(*info)); + struct SnapshotState *s = &snap_state; + + if (s->state != SAVE_STATE_DONE) { + info->has_bytes = true; + info->bytes = s->bs_pos; + switch (s->state) { + case SAVE_STATE_ERROR: + info->has_status = true; + info->status = g_strdup("failed"); + info->has_total_time = true; + info->total_time = s->total_time; + if (s->error) { + info->has_error = true; + info->error = g_strdup(error_get_pretty(s->error)); + } + break; + case SAVE_STATE_ACTIVE: + info->has_status = true; + info->status = g_strdup("active"); + info->has_total_time = true; + info->total_time = qemu_get_clock_ms(rt_clock) + - s->total_time; + break; + case SAVE_STATE_COMPLETED: + info->has_status = true; + info->status = g_strdup("completed"); + info->has_total_time = true; + info->total_time = s->total_time; + break; + } + } + + return info; +} + +static int save_snapshot_cleanup(void) +{ + int ret = 0; + + DPRINTF("save_snapshot_cleanup\n"); + + snap_state.total_time = qemu_get_clock_ms(rt_clock) - + snap_state.total_time; + + if (snap_state.file) { + ret = qemu_fclose(snap_state.file); + } + + if (snap_state.bs) { + // try to truncate, but ignore errors (will fail on block devices). + // note: bdrv_read() need whole blocks, so we round up + size_t size = (snap_state.bs_pos + BDRV_SECTOR_SIZE) & BDRV_SECTOR_MASK; + bdrv_truncate(snap_state.bs, size); + + bdrv_delete(snap_state.bs); + snap_state.bs = NULL; + } + + return ret; +} + +static void save_snapshot_error(const char *fmt, ...) +{ + va_list ap; + char *msg; + + va_start(ap, fmt); + msg = g_strdup_vprintf(fmt, ap); + va_end(ap); + + DPRINTF("save_snapshot_error: %s\n", msg); + + if (!snap_state.error) { + error_set(&snap_state.error, ERROR_CLASS_GENERIC_ERROR, "%s", msg); + } + + g_free (msg); + + snap_state.state = SAVE_STATE_ERROR; + + save_snapshot_cleanup(); +} + +static void save_snapshot_completed(void) +{ + DPRINTF("save_snapshot_completed\n"); + + if (save_snapshot_cleanup() < 0) { + snap_state.state = SAVE_STATE_ERROR; + } else { + snap_state.state = SAVE_STATE_COMPLETED; + } +} + +static int block_state_close(void *opaque) +{ + snap_state.file = NULL; + return bdrv_flush(snap_state.bs); +} + +static ssize_t block_state_put_buffer(void *opaque, const void *buf, + size_t size) +{ + int ret; + + if ((ret = bdrv_pwrite(snap_state.bs, snap_state.bs_pos, buf, size)) > 0) { + snap_state.bs_pos += ret; + } + + return ret; +} + +static void block_state_put_ready(void *opaque) +{ + int ret; + + if (snap_state.state != SAVE_STATE_ACTIVE) { + save_snapshot_error("put_ready returning because of non-active state"); + return; + } + + if (!runstate_check(RUN_STATE_SAVE_VM)) { + save_snapshot_error("put_ready returning because of wrong run state"); + return; + } + + ret = qemu_savevm_state_iterate(snap_state.file); + if (ret < 0) { + save_snapshot_error("qemu_savevm_state_iterate error %d", ret); + return; + } else if (ret == 1) { + DPRINTF("savevm inerate finished\n"); + if ((ret = qemu_savevm_state_complete(snap_state.file)) < 0) { + save_snapshot_error("qemu_savevm_state_complete error %d", ret); + return; + } else { + DPRINTF("save complete\n"); + save_snapshot_completed(); + return; + } + } +} + +static void block_state_wait_for_unfreeze(void *opaque) +{ + /* do nothing here - should not be called */ +} + +void qmp_savevm_start(bool has_statefile, const char *statefile, Error **errp) +{ + BlockDriver *drv = NULL; + int bdrv_oflags = BDRV_O_CACHE_WB | BDRV_O_RDWR; + MigrationParams params = { + .blk = 0, + .shared = 0 + }; + int ret; + + if (snap_state.state != SAVE_STATE_DONE) { + error_set(errp, ERROR_CLASS_GENERIC_ERROR, + "VM snapshot already started\n"); + return; + } + + /* initialize snapshot info */ + snap_state.saved_vm_running = runstate_is_running(); + snap_state.bs_pos = 0; + snap_state.total_time = qemu_get_clock_ms(rt_clock); + + if (snap_state.error) { + error_free(snap_state.error); + snap_state.error = NULL; + } + + /* stop the VM */ + vm_stop(RUN_STATE_SAVE_VM); + + if (!has_statefile) { + snap_state.state = SAVE_STATE_COMPLETED; + return; + } + + if (qemu_savevm_state_blocked(errp)) { + return; + } + + /* Open the image */ + snap_state.bs = bdrv_new("vmstate"); + ret = bdrv_open(snap_state.bs, statefile, bdrv_oflags, drv); + if (ret < 0) { + error_set(errp, QERR_OPEN_FILE_FAILED, statefile); + goto restart; + } + + snap_state.file = qemu_fopen_ops_buffered(&snap_state, 1000000000, + block_state_put_buffer, + block_state_put_ready, + block_state_wait_for_unfreeze, + block_state_close); + + if (!snap_state.file) { + error_set(errp, QERR_OPEN_FILE_FAILED, statefile); + goto restart; + } + + snap_state.state = SAVE_STATE_ACTIVE; + + ret = qemu_savevm_state_begin(snap_state.file, ¶ms); + if (ret < 0) { + error_set(errp, ERROR_CLASS_GENERIC_ERROR, + "qemu_savevm_state_begin failed\n"); + goto restart; + } + + block_state_put_ready(&snap_state); + + return; + +restart: + + save_snapshot_error("setup failed"); + + if (snap_state.saved_vm_running) { + vm_start(); + } +} + +void qmp_savevm_end(Error **errp) +{ + if (snap_state.state == SAVE_STATE_DONE) { + error_set(errp, ERROR_CLASS_GENERIC_ERROR, + "VM snapshot not started\n"); + return; + } + + if (snap_state.saved_vm_running) { + vm_start(); + } + + snap_state.state = SAVE_STATE_DONE; +} + +void qmp_snapshot_drive(const char *device, const char *name, Error **errp) +{ + BlockDriverState *bs; + QEMUSnapshotInfo sn1, *sn = &sn1; + int ret; +#ifdef _WIN32 + struct _timeb tb; +#else + struct timeval tv; +#endif + + if (snap_state.state != SAVE_STATE_COMPLETED) { + error_set(errp, ERROR_CLASS_GENERIC_ERROR, + "VM snapshot not ready/started\n"); + return; + } + + bs = bdrv_find(device); + if (!bs) { + error_set(errp, QERR_DEVICE_NOT_FOUND, device); + return; + } + + if (!bdrv_is_inserted(bs)) { + error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device); + return; + } + + if (bdrv_is_read_only(bs)) { + error_set(errp, QERR_DEVICE_IS_READ_ONLY, device); + return; + } + + if (!bdrv_can_snapshot(bs)) { + error_set(errp, QERR_NOT_SUPPORTED); + return; + } + + if (bdrv_snapshot_find(bs, sn, name) >= 0) { + error_set(errp, ERROR_CLASS_GENERIC_ERROR, + "snapshot '%s' already exists", name); + return; + } + + sn = &sn1; + memset(sn, 0, sizeof(*sn)); + +#ifdef _WIN32 + _ftime(&tb); + sn->date_sec = tb.time; + sn->date_nsec = tb.millitm * 1000000; +#else + gettimeofday(&tv, NULL); + sn->date_sec = tv.tv_sec; + sn->date_nsec = tv.tv_usec * 1000; +#endif + sn->vm_clock_nsec = qemu_get_clock_ns(vm_clock); + + pstrcpy(sn->name, sizeof(sn->name), name); + + sn->vm_state_size = 0; /* do not save state */ + + ret = bdrv_snapshot_create(bs, sn); + if (ret < 0) { + error_set(errp, ERROR_CLASS_GENERIC_ERROR, + "Error while creating snapshot on '%s'\n", device); + return; + } +} + +void qmp_delete_drive_snapshot(const char *device, const char *name, + Error **errp) +{ + BlockDriverState *bs; + QEMUSnapshotInfo sn1, *sn = &sn1; + int ret; + + bs = bdrv_find(device); + if (!bs) { + error_set(errp, QERR_DEVICE_NOT_FOUND, device); + return; + } + if (bdrv_is_read_only(bs)) { + error_set(errp, QERR_DEVICE_IS_READ_ONLY, device); + return; + } + + if (!bdrv_can_snapshot(bs)) { + error_set(errp, QERR_NOT_SUPPORTED); + return; + } + + if (bdrv_snapshot_find(bs, sn, name) < 0) { + /* return success if snapshot does not exists */ + return; + } + + ret = bdrv_snapshot_delete(bs, name); + if (ret < 0) { + error_set(errp, ERROR_CLASS_GENERIC_ERROR, + "Error while deleting snapshot on '%s'\n", device); + return; + } +} + +static int loadstate_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size) +{ + BlockDriverState *bs = (BlockDriverState *)opaque; + int64_t maxlen = bdrv_getlength(bs); + if (pos > maxlen) { + return -EIO; + } + if ((pos + size) > maxlen) { + size = maxlen - pos - 1; + } + if (size == 0) { + return 0; + } + return bdrv_pread(bs, pos, buf, size); +} + +int load_state_from_blockdev(const char *filename) +{ + BlockDriverState *bs = NULL; + BlockDriver *drv = NULL; + QEMUFile *f; + int ret = -1; + + bs = bdrv_new("vmstate"); + ret = bdrv_open(bs, filename, BDRV_O_CACHE_WB, drv); + if (ret < 0) { + error_report("Could not open VM state file"); + goto the_end; + } + + /* restore the VM state */ + f = qemu_fopen_ops(bs, NULL, loadstate_get_buffer, NULL, NULL, NULL, NULL); + if (!f) { + error_report("Could not open VM state file"); + ret = -EINVAL; + goto the_end; + } + + qemu_system_reset(VMRESET_SILENT); + ret = qemu_loadvm_state(f); + + qemu_fclose(f); + if (ret < 0) { + error_report("Error %d while loading VM state", ret); + goto the_end; + } + + ret = 0; + + the_end: + if (bs) { + bdrv_delete(bs); + } + return ret; +} -- 2.39.2