aboutsummaryrefslogtreecommitdiff
path: root/migration/savevm.c
diff options
context:
space:
mode:
Diffstat (limited to 'migration/savevm.c')
-rw-r--r--migration/savevm.c341
1 files changed, 290 insertions, 51 deletions
diff --git a/migration/savevm.c b/migration/savevm.c
index 4f3b69ecfc..52e2d72e4b 100644
--- a/migration/savevm.c
+++ b/migration/savevm.c
@@ -43,6 +43,8 @@
#include "qapi/error.h"
#include "qapi/qapi-commands-migration.h"
#include "qapi/qmp/json-writer.h"
+#include "qapi/clone-visitor.h"
+#include "qapi/qapi-builtin-visit.h"
#include "qapi/qmp/qerror.h"
#include "qemu/error-report.h"
#include "sysemu/cpus.h"
@@ -315,6 +317,16 @@ static int configuration_pre_save(void *opaque)
return 0;
}
+static int configuration_post_save(void *opaque)
+{
+ SaveState *state = opaque;
+
+ g_free(state->capabilities);
+ state->capabilities = NULL;
+ state->caps_count = 0;
+ return 0;
+}
+
static int configuration_pre_load(void *opaque)
{
SaveState *state = opaque;
@@ -365,24 +377,36 @@ static int configuration_post_load(void *opaque, int version_id)
{
SaveState *state = opaque;
const char *current_name = MACHINE_GET_CLASS(current_machine)->name;
+ int ret = 0;
if (strncmp(state->name, current_name, state->len) != 0) {
error_report("Machine type received is '%.*s' and local is '%s'",
(int) state->len, state->name, current_name);
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
if (state->target_page_bits != qemu_target_page_bits()) {
error_report("Received TARGET_PAGE_BITS is %d but local is %d",
state->target_page_bits, qemu_target_page_bits());
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
if (!configuration_validate_capabilities(state)) {
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
- return 0;
+out:
+ g_free((void *)state->name);
+ state->name = NULL;
+ state->len = 0;
+ g_free(state->capabilities);
+ state->capabilities = NULL;
+ state->caps_count = 0;
+
+ return ret;
}
static int get_capability(QEMUFile *f, void *pv, size_t size,
@@ -516,6 +540,7 @@ static const VMStateDescription vmstate_configuration = {
.pre_load = configuration_pre_load,
.post_load = configuration_post_load,
.pre_save = configuration_pre_save,
+ .post_save = configuration_post_save,
.fields = (VMStateField[]) {
VMSTATE_UINT32(len, SaveState),
VMSTATE_VBUFFER_ALLOC_UINT32(name, SaveState, 0, NULL, len),
@@ -1131,6 +1156,19 @@ bool qemu_savevm_state_blocked(Error **errp)
return false;
}
+void qemu_savevm_non_migratable_list(strList **reasons)
+{
+ SaveStateEntry *se;
+
+ QTAILQ_FOREACH(se, &savevm_state.handlers, entry) {
+ if (se->vmsd && se->vmsd->unmigratable) {
+ QAPI_LIST_PREPEND(*reasons,
+ g_strdup_printf("non-migratable device: %s",
+ se->idstr));
+ }
+ }
+}
+
void qemu_savevm_state_header(QEMUFile *f)
{
trace_savevm_state_header();
@@ -1355,7 +1393,6 @@ int qemu_savevm_state_complete_precopy_iterable(QEMUFile *f, bool in_postcopy)
return 0;
}
-static
int qemu_savevm_state_complete_precopy_non_iterable(QEMUFile *f,
bool in_postcopy,
bool inactivate_disks)
@@ -2729,9 +2766,10 @@ int qemu_load_device_state(QEMUFile *f)
return 0;
}
-int save_snapshot(const char *name, Error **errp)
+bool save_snapshot(const char *name, bool overwrite, const char *vmstate,
+ bool has_devices, strList *devices, Error **errp)
{
- BlockDriverState *bs, *bs1;
+ BlockDriverState *bs;
QEMUSnapshotInfo sn1, *sn = &sn1;
int ret = -1, ret2;
QEMUFile *f;
@@ -2742,35 +2780,43 @@ int save_snapshot(const char *name, Error **errp)
AioContext *aio_context;
if (migration_is_blocked(errp)) {
- return ret;
+ return false;
}
if (!replay_can_snapshot()) {
error_setg(errp, "Record/replay does not allow making snapshot "
"right now. Try once more later.");
- return ret;
+ return false;
}
- if (!bdrv_all_can_snapshot(&bs)) {
- error_setg(errp, "Device '%s' is writable but does not support "
- "snapshots", bdrv_get_device_or_node_name(bs));
- return ret;
+ if (!bdrv_all_can_snapshot(has_devices, devices, errp)) {
+ return false;
}
/* Delete old snapshots of the same name */
if (name) {
- ret = bdrv_all_delete_snapshot(name, &bs1, errp);
- if (ret < 0) {
- error_prepend(errp, "Error while deleting snapshot on device "
- "'%s': ", bdrv_get_device_or_node_name(bs1));
- return ret;
+ if (overwrite) {
+ if (bdrv_all_delete_snapshot(name, has_devices,
+ devices, errp) < 0) {
+ return false;
+ }
+ } else {
+ ret2 = bdrv_all_has_snapshot(name, has_devices, devices, errp);
+ if (ret2 < 0) {
+ return false;
+ }
+ if (ret2 == 1) {
+ error_setg(errp,
+ "Snapshot '%s' already exists in one or more devices",
+ name);
+ return false;
+ }
}
}
- bs = bdrv_all_find_vmstate_bs();
+ bs = bdrv_all_find_vmstate_bs(vmstate, has_devices, devices, errp);
if (bs == NULL) {
- error_setg(errp, "No block device can accept snapshots");
- return ret;
+ return false;
}
aio_context = bdrv_get_aio_context(bs);
@@ -2779,7 +2825,7 @@ int save_snapshot(const char *name, Error **errp)
ret = global_state_store();
if (ret) {
error_setg(errp, "Error saving global state");
- return ret;
+ return false;
}
vm_stop(RUN_STATE_SAVE_VM);
@@ -2833,11 +2879,10 @@ int save_snapshot(const char *name, Error **errp)
aio_context_release(aio_context);
aio_context = NULL;
- ret = bdrv_all_create_snapshot(sn, bs, vm_state_size, &bs);
+ ret = bdrv_all_create_snapshot(sn, bs, vm_state_size,
+ has_devices, devices, errp);
if (ret < 0) {
- error_setg(errp, "Error while creating snapshot on '%s'",
- bdrv_get_device_or_node_name(bs));
- bdrv_all_delete_snapshot(sn->name, &bs, NULL);
+ bdrv_all_delete_snapshot(sn->name, has_devices, devices, NULL);
goto the_end;
}
@@ -2853,7 +2898,7 @@ int save_snapshot(const char *name, Error **errp)
if (saved_vm_running) {
vm_start();
}
- return ret;
+ return ret == 0;
}
void qmp_xen_save_devices_state(const char *filename, bool has_live, bool live,
@@ -2938,33 +2983,32 @@ void qmp_xen_load_devices_state(const char *filename, Error **errp)
migration_incoming_state_destroy();
}
-int load_snapshot(const char *name, Error **errp)
+bool load_snapshot(const char *name, const char *vmstate,
+ bool has_devices, strList *devices, Error **errp)
{
- BlockDriverState *bs, *bs_vm_state;
+ BlockDriverState *bs_vm_state;
QEMUSnapshotInfo sn;
QEMUFile *f;
int ret;
AioContext *aio_context;
MigrationIncomingState *mis = migration_incoming_get_current();
- if (!bdrv_all_can_snapshot(&bs)) {
- error_setg(errp,
- "Device '%s' is writable but does not support snapshots",
- bdrv_get_device_or_node_name(bs));
- return -ENOTSUP;
+ if (!bdrv_all_can_snapshot(has_devices, devices, errp)) {
+ return false;
}
- ret = bdrv_all_find_snapshot(name, &bs);
+ ret = bdrv_all_has_snapshot(name, has_devices, devices, errp);
if (ret < 0) {
- error_setg(errp,
- "Device '%s' does not have the requested snapshot '%s'",
- bdrv_get_device_or_node_name(bs), name);
- return ret;
+ return false;
+ }
+ if (ret == 0) {
+ error_setg(errp, "Snapshot '%s' does not exist in one or more devices",
+ name);
+ return false;
}
- bs_vm_state = bdrv_all_find_vmstate_bs();
+ bs_vm_state = bdrv_all_find_vmstate_bs(vmstate, has_devices, devices, errp);
if (!bs_vm_state) {
- error_setg(errp, "No block device supports snapshots");
- return -ENOTSUP;
+ return false;
}
aio_context = bdrv_get_aio_context(bs_vm_state);
@@ -2973,11 +3017,11 @@ int load_snapshot(const char *name, Error **errp)
ret = bdrv_snapshot_find(bs_vm_state, &sn, name);
aio_context_release(aio_context);
if (ret < 0) {
- return ret;
+ return false;
} else if (sn.vm_state_size == 0) {
error_setg(errp, "This is a disk-only snapshot. Revert to it "
" offline using qemu-img");
- return -EINVAL;
+ return false;
}
/*
@@ -2989,10 +3033,8 @@ int load_snapshot(const char *name, Error **errp)
/* Flush all IO requests so they don't interfere with the new state. */
bdrv_drain_all_begin();
- ret = bdrv_all_goto_snapshot(name, &bs, errp);
+ ret = bdrv_all_goto_snapshot(name, has_devices, devices, errp);
if (ret < 0) {
- error_prepend(errp, "Could not load snapshot '%s' on '%s': ",
- name, bdrv_get_device_or_node_name(bs));
goto err_drain;
}
@@ -3000,7 +3042,6 @@ int load_snapshot(const char *name, Error **errp)
f = qemu_fopen_bdrv(bs_vm_state, 0);
if (!f) {
error_setg(errp, "Could not open VM state file");
- ret = -EINVAL;
goto err_drain;
}
@@ -3020,14 +3061,28 @@ int load_snapshot(const char *name, Error **errp)
if (ret < 0) {
error_setg(errp, "Error %d while loading VM state", ret);
- return ret;
+ return false;
}
- return 0;
+ return true;
err_drain:
bdrv_drain_all_end();
- return ret;
+ return false;
+}
+
+bool delete_snapshot(const char *name, bool has_devices,
+ strList *devices, Error **errp)
+{
+ if (!bdrv_all_can_snapshot(has_devices, devices, errp)) {
+ return false;
+ }
+
+ if (bdrv_all_delete_snapshot(name, has_devices, devices, errp) < 0) {
+ return false;
+ }
+
+ return true;
}
void vmstate_register_ram(MemoryRegion *mr, DeviceState *dev)
@@ -3057,3 +3112,187 @@ bool vmstate_check_only_migratable(const VMStateDescription *vmsd)
return !(vmsd && vmsd->unmigratable);
}
+
+typedef struct SnapshotJob {
+ Job common;
+ char *tag;
+ char *vmstate;
+ strList *devices;
+ Coroutine *co;
+ Error **errp;
+ bool ret;
+} SnapshotJob;
+
+static void qmp_snapshot_job_free(SnapshotJob *s)
+{
+ g_free(s->tag);
+ g_free(s->vmstate);
+ qapi_free_strList(s->devices);
+}
+
+
+static void snapshot_load_job_bh(void *opaque)
+{
+ Job *job = opaque;
+ SnapshotJob *s = container_of(job, SnapshotJob, common);
+ int orig_vm_running;
+
+ job_progress_set_remaining(&s->common, 1);
+
+ orig_vm_running = runstate_is_running();
+ vm_stop(RUN_STATE_RESTORE_VM);
+
+ s->ret = load_snapshot(s->tag, s->vmstate, true, s->devices, s->errp);
+ if (s->ret && orig_vm_running) {
+ vm_start();
+ }
+
+ job_progress_update(&s->common, 1);
+
+ qmp_snapshot_job_free(s);
+ aio_co_wake(s->co);
+}
+
+static void snapshot_save_job_bh(void *opaque)
+{
+ Job *job = opaque;
+ SnapshotJob *s = container_of(job, SnapshotJob, common);
+
+ job_progress_set_remaining(&s->common, 1);
+ s->ret = save_snapshot(s->tag, false, s->vmstate,
+ true, s->devices, s->errp);
+ job_progress_update(&s->common, 1);
+
+ qmp_snapshot_job_free(s);
+ aio_co_wake(s->co);
+}
+
+static void snapshot_delete_job_bh(void *opaque)
+{
+ Job *job = opaque;
+ SnapshotJob *s = container_of(job, SnapshotJob, common);
+
+ job_progress_set_remaining(&s->common, 1);
+ s->ret = delete_snapshot(s->tag, true, s->devices, s->errp);
+ job_progress_update(&s->common, 1);
+
+ qmp_snapshot_job_free(s);
+ aio_co_wake(s->co);
+}
+
+static int coroutine_fn snapshot_save_job_run(Job *job, Error **errp)
+{
+ SnapshotJob *s = container_of(job, SnapshotJob, common);
+ s->errp = errp;
+ s->co = qemu_coroutine_self();
+ aio_bh_schedule_oneshot(qemu_get_aio_context(),
+ snapshot_save_job_bh, job);
+ qemu_coroutine_yield();
+ return s->ret ? 0 : -1;
+}
+
+static int coroutine_fn snapshot_load_job_run(Job *job, Error **errp)
+{
+ SnapshotJob *s = container_of(job, SnapshotJob, common);
+ s->errp = errp;
+ s->co = qemu_coroutine_self();
+ aio_bh_schedule_oneshot(qemu_get_aio_context(),
+ snapshot_load_job_bh, job);
+ qemu_coroutine_yield();
+ return s->ret ? 0 : -1;
+}
+
+static int coroutine_fn snapshot_delete_job_run(Job *job, Error **errp)
+{
+ SnapshotJob *s = container_of(job, SnapshotJob, common);
+ s->errp = errp;
+ s->co = qemu_coroutine_self();
+ aio_bh_schedule_oneshot(qemu_get_aio_context(),
+ snapshot_delete_job_bh, job);
+ qemu_coroutine_yield();
+ return s->ret ? 0 : -1;
+}
+
+
+static const JobDriver snapshot_load_job_driver = {
+ .instance_size = sizeof(SnapshotJob),
+ .job_type = JOB_TYPE_SNAPSHOT_LOAD,
+ .run = snapshot_load_job_run,
+};
+
+static const JobDriver snapshot_save_job_driver = {
+ .instance_size = sizeof(SnapshotJob),
+ .job_type = JOB_TYPE_SNAPSHOT_SAVE,
+ .run = snapshot_save_job_run,
+};
+
+static const JobDriver snapshot_delete_job_driver = {
+ .instance_size = sizeof(SnapshotJob),
+ .job_type = JOB_TYPE_SNAPSHOT_DELETE,
+ .run = snapshot_delete_job_run,
+};
+
+
+void qmp_snapshot_save(const char *job_id,
+ const char *tag,
+ const char *vmstate,
+ strList *devices,
+ Error **errp)
+{
+ SnapshotJob *s;
+
+ s = job_create(job_id, &snapshot_save_job_driver, NULL,
+ qemu_get_aio_context(), JOB_MANUAL_DISMISS,
+ NULL, NULL, errp);
+ if (!s) {
+ return;
+ }
+
+ s->tag = g_strdup(tag);
+ s->vmstate = g_strdup(vmstate);
+ s->devices = QAPI_CLONE(strList, devices);
+
+ job_start(&s->common);
+}
+
+void qmp_snapshot_load(const char *job_id,
+ const char *tag,
+ const char *vmstate,
+ strList *devices,
+ Error **errp)
+{
+ SnapshotJob *s;
+
+ s = job_create(job_id, &snapshot_load_job_driver, NULL,
+ qemu_get_aio_context(), JOB_MANUAL_DISMISS,
+ NULL, NULL, errp);
+ if (!s) {
+ return;
+ }
+
+ s->tag = g_strdup(tag);
+ s->vmstate = g_strdup(vmstate);
+ s->devices = QAPI_CLONE(strList, devices);
+
+ job_start(&s->common);
+}
+
+void qmp_snapshot_delete(const char *job_id,
+ const char *tag,
+ strList *devices,
+ Error **errp)
+{
+ SnapshotJob *s;
+
+ s = job_create(job_id, &snapshot_delete_job_driver, NULL,
+ qemu_get_aio_context(), JOB_MANUAL_DISMISS,
+ NULL, NULL, errp);
+ if (!s) {
+ return;
+ }
+
+ s->tag = g_strdup(tag);
+ s->devices = QAPI_CLONE(strList, devices);
+
+ job_start(&s->common);
+}