*/
#include "qemu/osdep.h"
-#include "cpu.h"
#include "hw/boards.h"
-#include "hw/hw.h"
-#include "hw/qdev.h"
#include "hw/xen/xen.h"
#include "net/net.h"
-#include "sysemu/sysemu.h"
-#include "qemu/timer.h"
-#include "migration/migration.h"
+#include "migration.h"
+#include "migration/snapshot.h"
+#include "migration/misc.h"
+#include "migration/register.h"
+#include "migration/global_state.h"
+#include "ram.h"
+#include "qemu-file-channel.h"
+#include "qemu-file.h"
+#include "savevm.h"
#include "postcopy-ram.h"
#include "qapi/qmp/qerror.h"
#include "qemu/error-report.h"
-#include "qemu/queue.h"
#include "sysemu/cpus.h"
#include "exec/memory.h"
+#include "exec/target_page.h"
#include "qmp-commands.h"
#include "trace.h"
-#include "qemu/bitops.h"
#include "qemu/iov.h"
#include "block/snapshot.h"
#include "qemu/cutils.h"
static bool skip_section_footers;
+/* Subcommands for QEMU_VM_COMMAND */
+enum qemu_vm_cmd {
+ MIG_CMD_INVALID = 0, /* Must be 0 */
+ MIG_CMD_OPEN_RETURN_PATH, /* Tell the dest to open the Return path */
+ MIG_CMD_PING, /* Request a PONG on the RP */
+
+ MIG_CMD_POSTCOPY_ADVISE, /* Prior to any page transfers, just
+ warn we might want to do PC */
+ MIG_CMD_POSTCOPY_LISTEN, /* Start listening for incoming
+ pages as it's running. */
+ MIG_CMD_POSTCOPY_RUN, /* Start execution */
+
+ MIG_CMD_POSTCOPY_RAM_DISCARD, /* A list of pages to discard that
+ were previously sent during
+ precopy but are dirty. */
+ MIG_CMD_PACKAGED, /* Send a wrapped stream within this stream */
+ MIG_CMD_MAX
+};
+
+#define MAX_VM_CMD_PACKAGED_SIZE (1ul << 24)
static struct mig_cmd_args {
ssize_t len; /* -1 = variable */
const char *name;
int instance_id;
int alias_id;
int version_id;
+ /* version id read from the stream */
+ int load_version_id;
int section_id;
+ /* section id read from the stream */
+ int load_section_id;
SaveVMHandlers *ops;
const VMStateDescription *vmsd;
void *opaque;
state->len = strlen(current_name);
state->name = current_name;
- state->target_page_bits = TARGET_PAGE_BITS;
+ state->target_page_bits = qemu_target_page_bits();
}
static int configuration_pre_load(void *opaque)
* 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;
+ state->target_page_bits = qemu_target_page_bits_min();
return 0;
}
return -EINVAL;
}
- if (state->target_page_bits != TARGET_PAGE_BITS) {
+ 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, TARGET_PAGE_BITS);
+ state->target_page_bits, qemu_target_page_bits());
return -EINVAL;
}
*/
static bool vmstate_target_page_bits_needed(void *opaque)
{
- return TARGET_PAGE_BITS > TARGET_PAGE_BITS_MIN;
+ return qemu_target_page_bits()
+ > qemu_target_page_bits_min();
}
static const VMStateDescription vmstate_target_page_bits = {
return 0;
}
-int register_savevm(DeviceState *dev,
- const char *idstr,
- int instance_id,
- int version_id,
- SaveStateHandler *save_state,
- LoadStateHandler *load_state,
- void *opaque)
-{
- SaveVMHandlers *ops = g_new0(SaveVMHandlers, 1);
- ops->save_state = save_state;
- ops->load_state = load_state;
- return register_savevm_live(dev, idstr, instance_id, version_id,
- ops, opaque);
-}
-
void unregister_savevm(DeviceState *dev, const char *idstr, void *opaque)
{
SaveStateEntry *se, *new_se;
if (strcmp(se->idstr, id) == 0 && se->opaque == opaque) {
QTAILQ_REMOVE(&savevm_state.handlers, se, entry);
g_free(se->compat);
- g_free(se->ops);
g_free(se);
}
}
}
}
-static int vmstate_load(QEMUFile *f, SaveStateEntry *se, int version_id)
+static int vmstate_load(QEMUFile *f, SaveStateEntry *se)
{
trace_vmstate_load(se->idstr, se->vmsd ? se->vmsd->name : "(old)");
if (!se->vmsd) { /* Old style */
- return se->ops->load_state(f, se->opaque, version_id);
+ return se->ops->load_state(f, se->opaque, se->load_version_id);
}
- return vmstate_load_state(f, se->vmsd, se->opaque, version_id);
+ return vmstate_load_state(f, se->vmsd, se->opaque, se->load_version_id);
}
static void vmstate_save_old_style(QEMUFile *f, SaveStateEntry *se, QJSON *vmdesc)
* @len: Length of associated data
* @data: Data associated with command.
*/
-void qemu_savevm_command_send(QEMUFile *f,
- enum qemu_vm_cmd command,
- uint16_t len,
- uint8_t *data)
+static void qemu_savevm_command_send(QEMUFile *f,
+ enum qemu_vm_cmd command,
+ uint16_t len,
+ uint8_t *data)
{
trace_savevm_command_send(command, len);
qemu_put_byte(f, QEMU_VM_COMMAND);
}
-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;
}
vmdesc = qjson_new();
- json_prop_int(vmdesc, "page_size", TARGET_PAGE_SIZE);
+ json_prop_int(vmdesc, "page_size", qemu_target_page_size());
json_start_array(vmdesc, "devices");
QTAILQ_FOREACH(se, &savevm_state.handlers, entry) {
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) {
return 0;
}
-struct LoadStateEntry {
- QLIST_ENTRY(LoadStateEntry) entry;
- SaveStateEntry *se;
- int section_id;
- int version_id;
-};
-
/*
* Read a footer off the wire and check that it matches the expected section
*
* Returns: true if the footer was good
* false if there is a problem (and calls error_report to say why)
*/
-static bool check_section_footer(QEMUFile *f, LoadStateEntry *le)
+static bool check_section_footer(QEMUFile *f, SaveStateEntry *se)
{
uint8_t read_mark;
uint32_t read_section_id;
read_mark = qemu_get_byte(f);
if (read_mark != QEMU_VM_SECTION_FOOTER) {
- error_report("Missing section footer for %s", le->se->idstr);
+ error_report("Missing section footer for %s", se->idstr);
return false;
}
read_section_id = qemu_get_be32(f);
- if (read_section_id != le->section_id) {
+ if (read_section_id != se->load_section_id) {
error_report("Mismatched section id in footer for %s -"
" read 0x%x expected 0x%x",
- le->se->idstr, read_section_id, le->section_id);
+ se->idstr, read_section_id, se->load_section_id);
return false;
}
return true;
}
-void loadvm_free_handlers(MigrationIncomingState *mis)
-{
- LoadStateEntry *le, *new_le;
-
- QLIST_FOREACH_SAFE(le, &mis->loadvm_handlers, entry, new_le) {
- QLIST_REMOVE(le, entry);
- g_free(le);
- }
-}
-
static int
qemu_loadvm_section_start_full(QEMUFile *f, MigrationIncomingState *mis)
{
uint32_t instance_id, version_id, section_id;
SaveStateEntry *se;
- LoadStateEntry *le;
char idstr[256];
int ret;
version_id, idstr, se->version_id);
return -EINVAL;
}
+ se->load_version_id = version_id;
+ se->load_section_id = section_id;
/* Validate if it is a device's state */
if (xen_enabled() && se->is_ram) {
return -EINVAL;
}
- /* Add entry */
- le = g_malloc0(sizeof(*le));
-
- le->se = se;
- le->section_id = section_id;
- le->version_id = version_id;
- QLIST_INSERT_HEAD(&mis->loadvm_handlers, le, entry);
-
- ret = vmstate_load(f, le->se, le->version_id);
+ ret = vmstate_load(f, se);
if (ret < 0) {
error_report("error while loading state for instance 0x%x of"
" device '%s'", instance_id, idstr);
return ret;
}
- if (!check_section_footer(f, le)) {
+ if (!check_section_footer(f, se)) {
return -EINVAL;
}
qemu_loadvm_section_part_end(QEMUFile *f, MigrationIncomingState *mis)
{
uint32_t section_id;
- LoadStateEntry *le;
+ SaveStateEntry *se;
int ret;
section_id = qemu_get_be32(f);
trace_qemu_loadvm_state_section_partend(section_id);
- QLIST_FOREACH(le, &mis->loadvm_handlers, entry) {
- if (le->section_id == section_id) {
+ QTAILQ_FOREACH(se, &savevm_state.handlers, entry) {
+ if (se->load_section_id == section_id) {
break;
}
}
- if (le == NULL) {
+ if (se == NULL) {
error_report("Unknown savevm section %d", section_id);
return -EINVAL;
}
- ret = vmstate_load(f, le->se, le->version_id);
+ ret = vmstate_load(f, se);
if (ret < 0) {
error_report("error while loading state section id %d(%s)",
- section_id, le->se->idstr);
+ section_id, se->idstr);
return ret;
}
- if (!check_section_footer(f, le)) {
+ if (!check_section_footer(f, se)) {
return -EINVAL;
}
}
}
+ cpu_synchronize_all_pre_loadvm();
+
ret = qemu_loadvm_state_main(f, mis);
qemu_event_set(&mis->main_thread_load_event);
return ret;
}
-int save_vmstate(const char *name)
+int save_snapshot(const char *name, Error **errp)
{
BlockDriverState *bs, *bs1;
QEMUSnapshotInfo sn1, *sn = &sn1, old_sn1, *old_sn = &old_sn1;
uint64_t vm_state_size;
qemu_timeval tv;
struct tm tm;
- Error *local_err = NULL;
AioContext *aio_context;
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 ret;
}
/* Delete old snapshots of the same name */
if (name) {
- ret = bdrv_all_delete_snapshot(name, &bs1, &local_err);
+ ret = bdrv_all_delete_snapshot(name, &bs1, errp);
if (ret < 0) {
- error_reportf_err(local_err,
- "Error while deleting snapshot on device '%s': ",
- bdrv_get_device_name(bs1));
+ 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) {
- error_report("No block device can accept snapshots");
+ error_setg(errp, "No block device can accept snapshots");
return ret;
}
aio_context = bdrv_get_aio_context(bs);
ret = global_state_store();
if (ret) {
- error_report("Error saving global state");
+ 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) {
- error_report("Could not open VM state file");
+ 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) {
- error_report("Error while creating snapshot on '%s'",
- bdrv_get_device_name(bs));
+ error_setg(errp, "Error while creating snapshot on '%s'",
+ bdrv_get_device_name(bs));
goto the_end;
}
migration_incoming_state_destroy();
}
-int load_vmstate(const char *name)
+int load_snapshot(const char *name, Error **errp)
{
BlockDriverState *bs, *bs_vm_state;
QEMUSnapshotInfo sn;
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);
+ qemu_system_reset(SHUTDOWN_CAUSE_NONE);
mis->from_src_file = f;
aio_context_acquire(aio_context);
ret = qemu_loadvm_state(f);
- qemu_fclose(f);
aio_context_release(aio_context);
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;
}
{
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);
+}