#include "hw/qdev.h"
#include "hw/xen/xen.h"
#include "net/net.h"
-#include "monitor/monitor.h"
#include "sysemu/sysemu.h"
#include "qemu/timer.h"
-#include "audio/audio.h"
#include "migration/migration.h"
-#include "migration/postcopy-ram.h"
+#include "postcopy-ram.h"
#include "qapi/qmp/qerror.h"
#include "qemu/error-report.h"
-#include "qemu/sockets.h"
#include "qemu/queue.h"
#include "sysemu/cpus.h"
#include "exec/memory.h"
#include "qemu/bitops.h"
#include "qemu/iov.h"
#include "block/snapshot.h"
-#include "block/qapi.h"
#include "qemu/cutils.h"
#include "io/channel-buffer.h"
#include "io/channel-file.h"
* Not in vmstate.c to not add qemu-timer.c as dependency to vmstate.c
*/
-static int get_timer(QEMUFile *f, void *pv, size_t size)
+static int get_timer(QEMUFile *f, void *pv, size_t size, VMStateField *field)
{
QEMUTimer *v = pv;
timer_get(f, v);
return 0;
}
-static void put_timer(QEMUFile *f, void *pv, size_t size)
+static int put_timer(QEMUFile *f, void *pv, size_t size, VMStateField *field,
+ QJSON *vmdesc)
{
QEMUTimer *v = pv;
timer_put(f, v);
+
+ return 0;
}
const VMStateInfo vmstate_info_timer = {
bool skip_configuration;
uint32_t len;
const char *name;
+ uint32_t target_page_bits;
} SaveState;
static SaveState savevm_state = {
state->len = strlen(current_name);
state->name = current_name;
+ state->target_page_bits = TARGET_PAGE_BITS;
+}
+
+static int configuration_pre_load(void *opaque)
+{
+ SaveState *state = opaque;
+
+ /* If there is no target-page-bits subsection it means the source
+ * predates the variable-target-page-bits support and is using the
+ * minimum possible value for this CPU.
+ */
+ state->target_page_bits = TARGET_PAGE_BITS_MIN;
+ return 0;
}
static int configuration_post_load(void *opaque, int version_id)
(int) state->len, state->name, current_name);
return -EINVAL;
}
+
+ if (state->target_page_bits != TARGET_PAGE_BITS) {
+ error_report("Received TARGET_PAGE_BITS is %d but local is %d",
+ state->target_page_bits, TARGET_PAGE_BITS);
+ return -EINVAL;
+ }
+
return 0;
}
+/* The target-page-bits subsection is present only if the
+ * target page size is not the same as the default (ie the
+ * minimum page size for a variable-page-size guest CPU).
+ * If it is present then it contains the actual target page
+ * bits for the machine, and migration will fail if the
+ * two ends don't agree about it.
+ */
+static bool vmstate_target_page_bits_needed(void *opaque)
+{
+ return TARGET_PAGE_BITS > TARGET_PAGE_BITS_MIN;
+}
+
+static const VMStateDescription vmstate_target_page_bits = {
+ .name = "configuration/target-page-bits",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = vmstate_target_page_bits_needed,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(target_page_bits, SaveState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static const VMStateDescription vmstate_configuration = {
.name = "configuration",
.version_id = 1,
+ .pre_load = configuration_pre_load,
.post_load = configuration_post_load,
.pre_save = configuration_pre_save,
.fields = (VMStateField[]) {
VMSTATE_UINT32(len, SaveState),
- VMSTATE_VBUFFER_ALLOC_UINT32(name, SaveState, 0, NULL, 0, len),
+ VMSTATE_VBUFFER_ALLOC_UINT32(name, SaveState, 0, NULL, len),
VMSTATE_END_OF_LIST()
},
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_target_page_bits,
+ NULL
+ }
};
static void dump_vmstate_vmsd(FILE *out_file,
return instance_id;
}
+static inline MigrationPriority save_state_priority(SaveStateEntry *se)
+{
+ if (se->vmsd) {
+ return se->vmsd->priority;
+ }
+ return MIG_PRI_DEFAULT;
+}
+
+static void savevm_state_handler_insert(SaveStateEntry *nse)
+{
+ MigrationPriority priority = save_state_priority(nse);
+ SaveStateEntry *se;
+
+ assert(priority <= MIG_PRI_MAX);
+
+ QTAILQ_FOREACH(se, &savevm_state.handlers, entry) {
+ if (save_state_priority(se) < priority) {
+ break;
+ }
+ }
+
+ if (se) {
+ QTAILQ_INSERT_BEFORE(se, nse, entry);
+ } else {
+ QTAILQ_INSERT_TAIL(&savevm_state.handlers, nse, entry);
+ }
+}
+
/* TODO: Individual devices generally have very little idea about the rest
of the system, so instance_id should be removed/replaced.
Meanwhile pass -1 as instance_id if you do not already have a clearly
if (dev) {
char *id = qdev_get_dev_path(dev);
if (id) {
- pstrcpy(se->idstr, sizeof(se->idstr), id);
- pstrcat(se->idstr, sizeof(se->idstr), "/");
+ if (snprintf(se->idstr, sizeof(se->idstr), "%s/", id) >=
+ sizeof(se->idstr)) {
+ error_report("Path too long for VMState (%s)", id);
+ g_free(id);
+ g_free(se);
+
+ return -1;
+ }
g_free(id);
se->compat = g_new0(CompatEntry, 1);
se->instance_id = instance_id;
}
assert(!se->compat || se->instance_id == 0);
- /* add at the end of list */
- QTAILQ_INSERT_TAIL(&savevm_state.handlers, se, entry);
+ savevm_state_handler_insert(se);
return 0;
}
int vmstate_register_with_alias_id(DeviceState *dev, int instance_id,
const VMStateDescription *vmsd,
void *opaque, int alias_id,
- int required_for_version)
+ int required_for_version,
+ Error **errp)
{
SaveStateEntry *se;
if (dev) {
char *id = qdev_get_dev_path(dev);
if (id) {
- pstrcpy(se->idstr, sizeof(se->idstr), id);
- pstrcat(se->idstr, sizeof(se->idstr), "/");
+ if (snprintf(se->idstr, sizeof(se->idstr), "%s/", id) >=
+ sizeof(se->idstr)) {
+ error_setg(errp, "Path too long for VMState (%s)", id);
+ g_free(id);
+ g_free(se);
+
+ return -1;
+ }
g_free(id);
se->compat = g_new0(CompatEntry, 1);
se->instance_id = instance_id;
}
assert(!se->compat || se->instance_id == 0);
- /* add at the end of list */
- QTAILQ_INSERT_TAIL(&savevm_state.handlers, se, entry);
+ savevm_state_handler_insert(se);
return 0;
}
void qemu_savevm_send_postcopy_advise(QEMUFile *f)
{
uint64_t tmp[2];
- tmp[0] = cpu_to_be64(getpagesize());
- tmp[1] = cpu_to_be64(1ul << qemu_target_page_bits());
+ tmp[0] = cpu_to_be64(ram_pagesize_summary());
+ tmp[1] = cpu_to_be64(qemu_target_page_size());
trace_qemu_savevm_send_postcopy_advise();
qemu_savevm_command_send(f, MIG_CMD_POSTCOPY_ADVISE, 16, (uint8_t *)tmp);
}
-void qemu_savevm_state_begin(QEMUFile *f,
- const MigrationParams *params)
+void qemu_savevm_state_begin(QEMUFile *f)
{
SaveStateEntry *se;
int ret;
trace_savevm_state_begin();
- QTAILQ_FOREACH(se, &savevm_state.handlers, entry) {
- if (!se->ops || !se->ops->set_params) {
- continue;
- }
- se->ops->set_params(params, se->opaque);
- }
-
QTAILQ_FOREACH(se, &savevm_state.handlers, entry) {
if (!se->ops || !se->ops->save_live_setup) {
continue;
static bool should_send_vmdesc(void)
{
MachineState *machine = MACHINE(qdev_get_machine());
- bool in_postcopy = migration_in_postcopy(migrate_get_current());
+ bool in_postcopy = migration_in_postcopy();
return !machine->suppress_vmdesc && !in_postcopy;
}
int vmdesc_len;
SaveStateEntry *se;
int ret;
- bool in_postcopy = migration_in_postcopy(migrate_get_current());
+ bool in_postcopy = migration_in_postcopy();
trace_savevm_state_complete_precopy();
* the result is split into the amount for units that can and
* for units that can't do postcopy.
*/
-void qemu_savevm_state_pending(QEMUFile *f, uint64_t max_size,
+void qemu_savevm_state_pending(QEMUFile *f, uint64_t threshold_size,
uint64_t *res_non_postcopiable,
uint64_t *res_postcopiable)
{
continue;
}
}
- se->ops->save_live_pending(f, se->opaque, max_size,
+ se->ops->save_live_pending(f, se->opaque, threshold_size,
res_non_postcopiable, res_postcopiable);
}
}
static int qemu_savevm_state(QEMUFile *f, Error **errp)
{
int ret;
- MigrationParams params = {
- .blk = 0,
- .shared = 0
- };
- MigrationState *ms = migrate_init(¶ms);
+ MigrationState *ms = migrate_init();
MigrationStatus status;
ms->to_dst_file = f;
goto done;
}
+ if (migrate_use_block()) {
+ error_setg(errp, "Block migration and snapshots are incompatible");
+ ret = -EINVAL;
+ goto done;
+ }
+
qemu_mutex_unlock_iothread();
qemu_savevm_state_header(f);
- qemu_savevm_state_begin(f, ¶ms);
+ qemu_savevm_state_begin(f);
qemu_mutex_lock_iothread();
while (qemu_file_get_error(f) == 0) {
status = MIGRATION_STATUS_COMPLETED;
}
migrate_set_state(&ms->state, MIGRATION_STATUS_SETUP, status);
+
+ /* f is outer parameter, it should not stay in global migration state after
+ * this function finished */
+ ms->to_dst_file = NULL;
+
return ret;
}
static int loadvm_postcopy_handle_advise(MigrationIncomingState *mis)
{
PostcopyState ps = postcopy_state_set(POSTCOPY_INCOMING_ADVISE);
- uint64_t remote_hps, remote_tps;
+ uint64_t remote_pagesize_summary, local_pagesize_summary, remote_tps;
trace_loadvm_postcopy_handle_advise();
if (ps != POSTCOPY_INCOMING_NONE) {
}
if (!postcopy_ram_supported_by_host()) {
+ postcopy_state_set(POSTCOPY_INCOMING_NONE);
return -1;
}
- remote_hps = qemu_get_be64(mis->from_src_file);
- if (remote_hps != getpagesize()) {
+ remote_pagesize_summary = qemu_get_be64(mis->from_src_file);
+ local_pagesize_summary = ram_pagesize_summary();
+
+ if (remote_pagesize_summary != local_pagesize_summary) {
/*
- * Some combinations of mismatch are probably possible but it gets
- * a bit more complicated. In particular we need to place whole
- * host pages on the dest at once, and we need to ensure that we
- * handle dirtying to make sure we never end up sending part of
- * a hostpage on it's own.
+ * This detects two potential causes of mismatch:
+ * a) A mismatch in host page sizes
+ * Some combinations of mismatch are probably possible but it gets
+ * a bit more complicated. In particular we need to place whole
+ * host pages on the dest at once, and we need to ensure that we
+ * handle dirtying to make sure we never end up sending part of
+ * a hostpage on it's own.
+ * b) The use of different huge page sizes on source/destination
+ * a more fine grain test is performed during RAM block migration
+ * but this test here causes a nice early clear failure, and
+ * also fails when passed to an older qemu that doesn't
+ * do huge pages.
*/
- error_report("Postcopy needs matching host page sizes (s=%d d=%d)",
- (int)remote_hps, getpagesize());
+ error_report("Postcopy needs matching RAM page sizes (s=%" PRIx64
+ " d=%" PRIx64 ")",
+ remote_pagesize_summary, local_pagesize_summary);
return -1;
}
remote_tps = qemu_get_be64(mis->from_src_file);
- if (remote_tps != (1ul << qemu_target_page_bits())) {
+ if (remote_tps != qemu_target_page_size()) {
/*
* Again, some differences could be dealt with, but for now keep it
* simple.
*/
- error_report("Postcopy needs matching target page sizes (s=%d d=%d)",
- (int)remote_tps, 1 << qemu_target_page_bits());
+ error_report("Postcopy needs matching target page sizes (s=%d d=%zd)",
+ (int)remote_tps, qemu_target_page_size());
return -1;
}
block_length = qemu_get_be64(mis->from_src_file);
len -= 16;
- int ret = ram_discard_range(mis, ramid, start_addr,
- block_length);
+ int ret = ram_discard_range(ramid, start_addr, block_length);
if (ret) {
return ret;
}
qemu_announce_self();
- /* Make sure all file formats flush their mutable metadata */
+ /* Make sure all file formats flush their mutable metadata.
+ * If we get an error here, just don't restart the VM yet. */
bdrv_invalidate_cache_all(&local_err);
if (local_err) {
error_report_err(local_err);
+ local_err = NULL;
+ autostart = false;
}
trace_loadvm_postcopy_handle_run_cpu_sync();
}
bioc = qio_channel_buffer_new(length);
+ qio_channel_set_name(QIO_CHANNEL(bioc), "migration-loadvm-buffer");
ret = qemu_get_buffer(mis->from_src_file,
bioc->data,
length);
return ret;
}
-void hmp_savevm(Monitor *mon, const QDict *qdict)
+int save_vmstate(const char *name, Error **errp)
{
BlockDriverState *bs, *bs1;
QEMUSnapshotInfo sn1, *sn = &sn1, old_sn1, *old_sn = &old_sn1;
- int ret;
+ int ret = -1;
QEMUFile *f;
int saved_vm_running;
uint64_t vm_state_size;
qemu_timeval tv;
struct tm tm;
- const char *name = qdict_get_try_str(qdict, "name");
- Error *local_err = NULL;
AioContext *aio_context;
if (!bdrv_all_can_snapshot(&bs)) {
- monitor_printf(mon, "Device '%s' is writable but does not "
- "support snapshots.\n", bdrv_get_device_name(bs));
- return;
+ error_setg(errp, "Device '%s' is writable but does not support "
+ "snapshots", bdrv_get_device_name(bs));
+ return ret;
}
/* Delete old snapshots of the same name */
- if (name && bdrv_all_delete_snapshot(name, &bs1, &local_err) < 0) {
- error_reportf_err(local_err,
- "Error while deleting snapshot on device '%s': ",
- bdrv_get_device_name(bs1));
- return;
+ 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_name(bs1));
+ return ret;
+ }
}
bs = bdrv_all_find_vmstate_bs();
if (bs == NULL) {
- monitor_printf(mon, "No block device can accept snapshots\n");
- return;
+ error_setg(errp, "No block device can accept snapshots");
+ return ret;
}
aio_context = bdrv_get_aio_context(bs);
ret = global_state_store();
if (ret) {
- monitor_printf(mon, "Error saving global state\n");
- return;
+ error_setg(errp, "Error saving global state");
+ return ret;
}
vm_stop(RUN_STATE_SAVE_VM);
/* save the VM state */
f = qemu_fopen_bdrv(bs, 1);
if (!f) {
- monitor_printf(mon, "Could not open VM state file\n");
+ error_setg(errp, "Could not open VM state file");
goto the_end;
}
- ret = qemu_savevm_state(f, &local_err);
+ ret = qemu_savevm_state(f, errp);
vm_state_size = qemu_ftell(f);
qemu_fclose(f);
if (ret < 0) {
- error_report_err(local_err);
goto the_end;
}
ret = bdrv_all_create_snapshot(sn, bs, vm_state_size, &bs);
if (ret < 0) {
- monitor_printf(mon, "Error while creating snapshot on '%s'\n",
- bdrv_get_device_name(bs));
+ error_setg(errp, "Error while creating snapshot on '%s'",
+ bdrv_get_device_name(bs));
+ goto the_end;
}
+ ret = 0;
+
the_end:
aio_context_release(aio_context);
if (saved_vm_running) {
vm_start();
}
+ return ret;
}
void qmp_xen_save_devices_state(const char *filename, Error **errp)
if (!ioc) {
goto the_end;
}
+ qio_channel_set_name(QIO_CHANNEL(ioc), "migration-xen-save-state");
f = qemu_fopen_channel_output(QIO_CHANNEL(ioc));
ret = qemu_save_device_state(f);
qemu_fclose(f);
if (!ioc) {
return;
}
+ qio_channel_set_name(QIO_CHANNEL(ioc), "migration-xen-load-state");
f = qemu_fopen_channel_input(QIO_CHANNEL(ioc));
- migration_incoming_state_new(f);
ret = qemu_loadvm_state(f);
qemu_fclose(f);
if (ret < 0) {
migration_incoming_state_destroy();
}
-int load_vmstate(const char *name)
+int load_vmstate(const char *name, Error **errp)
{
BlockDriverState *bs, *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_report("Device '%s' is writable but does not support snapshots.",
- bdrv_get_device_name(bs));
+ error_setg(errp,
+ "Device '%s' is writable but does not support snapshots",
+ bdrv_get_device_name(bs));
return -ENOTSUP;
}
ret = bdrv_all_find_snapshot(name, &bs);
if (ret < 0) {
- error_report("Device '%s' does not have the requested snapshot '%s'",
- bdrv_get_device_name(bs), name);
+ error_setg(errp,
+ "Device '%s' does not have the requested snapshot '%s'",
+ bdrv_get_device_name(bs), name);
return ret;
}
bs_vm_state = bdrv_all_find_vmstate_bs();
if (!bs_vm_state) {
- error_report("No block device supports snapshots");
+ error_setg(errp, "No block device supports snapshots");
return -ENOTSUP;
}
aio_context = bdrv_get_aio_context(bs_vm_state);
if (ret < 0) {
return ret;
} else if (sn.vm_state_size == 0) {
- error_report("This is a disk-only snapshot. Revert to it offline "
- "using qemu-img.");
+ error_setg(errp, "This is a disk-only snapshot. Revert to it "
+ " offline using qemu-img");
return -EINVAL;
}
ret = bdrv_all_goto_snapshot(name, &bs);
if (ret < 0) {
- error_report("Error %d while activating snapshot '%s' on '%s'",
+ error_setg(errp, "Error %d while activating snapshot '%s' on '%s'",
ret, name, bdrv_get_device_name(bs));
return ret;
}
/* restore the VM state */
f = qemu_fopen_bdrv(bs_vm_state, 0);
if (!f) {
- error_report("Could not open VM state file");
+ error_setg(errp, "Could not open VM state file");
return -EINVAL;
}
qemu_system_reset(VMRESET_SILENT);
- migration_incoming_state_new(f);
+ mis->from_src_file = f;
aio_context_acquire(aio_context);
ret = qemu_loadvm_state(f);
migration_incoming_state_destroy();
if (ret < 0) {
- error_report("Error %d while loading VM state", ret);
+ error_setg(errp, "Error %d while loading VM state", ret);
return ret;
}
return 0;
}
-void hmp_delvm(Monitor *mon, const QDict *qdict)
-{
- BlockDriverState *bs;
- Error *err;
- const char *name = qdict_get_str(qdict, "name");
-
- if (bdrv_all_delete_snapshot(name, &bs, &err) < 0) {
- error_reportf_err(err,
- "Error while deleting snapshot on device '%s': ",
- bdrv_get_device_name(bs));
- }
-}
-
-void hmp_info_snapshots(Monitor *mon, const QDict *qdict)
-{
- BlockDriverState *bs, *bs1;
- BdrvNextIterator it1;
- QEMUSnapshotInfo *sn_tab, *sn;
- bool no_snapshot = true;
- int nb_sns, i;
- int total;
- int *global_snapshots;
- AioContext *aio_context;
-
- typedef struct SnapshotEntry {
- QEMUSnapshotInfo sn;
- QTAILQ_ENTRY(SnapshotEntry) next;
- } SnapshotEntry;
-
- typedef struct ImageEntry {
- const char *imagename;
- QTAILQ_ENTRY(ImageEntry) next;
- QTAILQ_HEAD(, SnapshotEntry) snapshots;
- } ImageEntry;
-
- QTAILQ_HEAD(, ImageEntry) image_list =
- QTAILQ_HEAD_INITIALIZER(image_list);
-
- ImageEntry *image_entry, *next_ie;
- SnapshotEntry *snapshot_entry;
-
- bs = bdrv_all_find_vmstate_bs();
- if (!bs) {
- monitor_printf(mon, "No available block device supports snapshots\n");
- return;
- }
- aio_context = bdrv_get_aio_context(bs);
-
- aio_context_acquire(aio_context);
- nb_sns = bdrv_snapshot_list(bs, &sn_tab);
- aio_context_release(aio_context);
-
- if (nb_sns < 0) {
- monitor_printf(mon, "bdrv_snapshot_list: error %d\n", nb_sns);
- return;
- }
-
- for (bs1 = bdrv_first(&it1); bs1; bs1 = bdrv_next(&it1)) {
- int bs1_nb_sns = 0;
- ImageEntry *ie;
- SnapshotEntry *se;
- AioContext *ctx = bdrv_get_aio_context(bs1);
-
- aio_context_acquire(ctx);
- if (bdrv_can_snapshot(bs1)) {
- sn = NULL;
- bs1_nb_sns = bdrv_snapshot_list(bs1, &sn);
- if (bs1_nb_sns > 0) {
- no_snapshot = false;
- ie = g_new0(ImageEntry, 1);
- ie->imagename = bdrv_get_device_name(bs1);
- QTAILQ_INIT(&ie->snapshots);
- QTAILQ_INSERT_TAIL(&image_list, ie, next);
- for (i = 0; i < bs1_nb_sns; i++) {
- se = g_new0(SnapshotEntry, 1);
- se->sn = sn[i];
- QTAILQ_INSERT_TAIL(&ie->snapshots, se, next);
- }
- }
- g_free(sn);
- }
- aio_context_release(ctx);
- }
-
- if (no_snapshot) {
- monitor_printf(mon, "There is no snapshot available.\n");
- return;
- }
-
- global_snapshots = g_new0(int, nb_sns);
- total = 0;
- for (i = 0; i < nb_sns; i++) {
- SnapshotEntry *next_sn;
- if (bdrv_all_find_snapshot(sn_tab[i].name, &bs1) == 0) {
- global_snapshots[total] = i;
- total++;
- QTAILQ_FOREACH(image_entry, &image_list, next) {
- QTAILQ_FOREACH_SAFE(snapshot_entry, &image_entry->snapshots,
- next, next_sn) {
- if (!strcmp(sn_tab[i].name, snapshot_entry->sn.name)) {
- QTAILQ_REMOVE(&image_entry->snapshots, snapshot_entry,
- next);
- g_free(snapshot_entry);
- }
- }
- }
- }
- }
-
- monitor_printf(mon, "List of snapshots present on all disks:\n");
-
- if (total > 0) {
- bdrv_snapshot_dump((fprintf_function)monitor_printf, mon, NULL);
- monitor_printf(mon, "\n");
- for (i = 0; i < total; i++) {
- sn = &sn_tab[global_snapshots[i]];
- /* The ID is not guaranteed to be the same on all images, so
- * overwrite it.
- */
- pstrcpy(sn->id_str, sizeof(sn->id_str), "--");
- bdrv_snapshot_dump((fprintf_function)monitor_printf, mon, sn);
- monitor_printf(mon, "\n");
- }
- } else {
- monitor_printf(mon, "None\n");
- }
-
- QTAILQ_FOREACH(image_entry, &image_list, next) {
- if (QTAILQ_EMPTY(&image_entry->snapshots)) {
- continue;
- }
- monitor_printf(mon,
- "\nList of partial (non-loadable) snapshots on '%s':\n",
- image_entry->imagename);
- bdrv_snapshot_dump((fprintf_function)monitor_printf, mon, NULL);
- monitor_printf(mon, "\n");
- QTAILQ_FOREACH(snapshot_entry, &image_entry->snapshots, next) {
- bdrv_snapshot_dump((fprintf_function)monitor_printf, mon,
- &snapshot_entry->sn);
- monitor_printf(mon, "\n");
- }
- }
-
- QTAILQ_FOREACH_SAFE(image_entry, &image_list, next, next_ie) {
- SnapshotEntry *next_sn;
- QTAILQ_FOREACH_SAFE(snapshot_entry, &image_entry->snapshots, next,
- next_sn) {
- g_free(snapshot_entry);
- }
- g_free(image_entry);
- }
- g_free(sn_tab);
- g_free(global_snapshots);
-
-}
-
void vmstate_register_ram(MemoryRegion *mr, DeviceState *dev)
{
qemu_ram_set_idstr(mr->ram_block,
{
vmstate_register_ram(mr, NULL);
}
+
+bool vmstate_check_only_migratable(const VMStateDescription *vmsd)
+{
+ /* check needed if --only-migratable is specified */
+ if (!only_migratable) {
+ return true;
+ }
+
+ return !(vmsd && vmsd->unmigratable);
+}