*/
#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"
const unsigned int postcopy_ram_discard_version = 0;
-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;
typedef struct SaveState {
QTAILQ_HEAD(, SaveStateEntry) handlers;
int global_section_id;
- bool skip_configuration;
uint32_t len;
const char *name;
uint32_t target_page_bits;
static SaveState savevm_state = {
.handlers = QTAILQ_HEAD_INITIALIZER(savevm_state.handlers),
.global_section_id = 0,
- .skip_configuration = false,
};
-void savevm_skip_configuration(void)
-{
- savevm_state.skip_configuration = true;
-}
-
-
static void configuration_pre_save(void *opaque)
{
SaveState *state = 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 = {
se->opaque = opaque;
se->vmsd = NULL;
/* if this is a live_savem then set is_ram */
- if (ops->save_live_setup != NULL) {
+ if (ops->save_setup != NULL) {
se->is_ram = 1;
}
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)
vmstate_save_state(f, se->vmsd, se->opaque, vmdesc);
}
-void savevm_skip_section_footers(void)
-{
- skip_section_footers = true;
-}
-
/*
* Write the header for device section (QEMU_VM_SECTION START/END/PART/FULL)
*/
*/
static void save_section_footer(QEMUFile *f, SaveStateEntry *se)
{
- if (!skip_section_footers) {
+ if (migrate_get_current()->send_section_footer) {
qemu_put_byte(f, QEMU_VM_SECTION_FOOTER);
qemu_put_be32(f, se->section_id);
}
* @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);
return false;
}
-static bool enforce_config_section(void)
-{
- MachineState *machine = MACHINE(qdev_get_machine());
- return machine->enforce_config_section;
-}
-
void qemu_savevm_state_header(QEMUFile *f)
{
trace_savevm_state_header();
qemu_put_be32(f, QEMU_VM_FILE_MAGIC);
qemu_put_be32(f, QEMU_VM_FILE_VERSION);
- if (!savevm_state.skip_configuration || enforce_config_section()) {
+ if (migrate_get_current()->send_configuration) {
qemu_put_byte(f, QEMU_VM_CONFIGURATION);
vmstate_save_state(f, &vmstate_configuration, &savevm_state, 0);
}
-
}
-void qemu_savevm_state_begin(QEMUFile *f)
+void qemu_savevm_state_setup(QEMUFile *f)
{
SaveStateEntry *se;
int ret;
- trace_savevm_state_begin();
+ trace_savevm_state_setup();
QTAILQ_FOREACH(se, &savevm_state.handlers, entry) {
- if (!se->ops || !se->ops->save_live_setup) {
+ if (!se->ops || !se->ops->save_setup) {
continue;
}
if (se->ops && se->ops->is_active) {
}
save_section_header(f, se, QEMU_VM_SECTION_START);
- ret = se->ops->save_live_setup(f, se->opaque);
+ ret = se->ops->save_setup(f, se->opaque);
save_section_footer(f, se);
if (ret < 0) {
qemu_file_set_error(f, ret);
* call that's already run, it might get confused if we call
* iterate afterwards.
*/
- if (postcopy && !se->ops->save_live_complete_postcopy) {
+ if (postcopy &&
+ !(se->ops->has_postcopy && se->ops->has_postcopy(se->opaque))) {
continue;
}
if (qemu_file_rate_limit(f)) {
qemu_fflush(f);
}
-void qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only)
+int qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only,
+ bool inactivate_disks)
{
QJSON *vmdesc;
int vmdesc_len;
QTAILQ_FOREACH(se, &savevm_state.handlers, entry) {
if (!se->ops ||
- (in_postcopy && se->ops->save_live_complete_postcopy) ||
+ (in_postcopy && se->ops->has_postcopy &&
+ se->ops->has_postcopy(se->opaque)) ||
(in_postcopy && !iterable_only) ||
!se->ops->save_live_complete_precopy) {
continue;
save_section_footer(f, se);
if (ret < 0) {
qemu_file_set_error(f, ret);
- return;
+ return -1;
}
}
if (iterable_only) {
- return;
+ return 0;
}
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) {
json_end_object(vmdesc);
}
+ if (inactivate_disks) {
+ /* Inactivate before sending QEMU_VM_EOF so that the
+ * bdrv_invalidate_cache_all() on the other end won't fail. */
+ ret = bdrv_inactivate_all();
+ if (ret) {
+ error_report("%s: bdrv_inactivate_all() failed (%d)",
+ __func__, ret);
+ qemu_file_set_error(f, ret);
+ return ret;
+ }
+ }
if (!in_postcopy) {
/* Postcopy stream will still be going */
qemu_put_byte(f, QEMU_VM_EOF);
qjson_destroy(vmdesc);
qemu_fflush(f);
+ return 0;
}
/* Give an estimate of the amount left to be transferred,
trace_savevm_state_cleanup();
QTAILQ_FOREACH(se, &savevm_state.handlers, entry) {
- if (se->ops && se->ops->cleanup) {
- se->ops->cleanup(se->opaque);
+ if (se->ops && se->ops->save_cleanup) {
+ se->ops->save_cleanup(se->opaque);
}
}
}
qemu_mutex_unlock_iothread();
qemu_savevm_state_header(f);
- qemu_savevm_state_begin(f);
+ qemu_savevm_state_setup(f);
qemu_mutex_lock_iothread();
while (qemu_file_get_error(f) == 0) {
ret = qemu_file_get_error(f);
if (ret == 0) {
- qemu_savevm_state_complete_precopy(f, false);
+ qemu_savevm_state_complete_precopy(f, false, false);
ret = qemu_file_get_error(f);
}
qemu_savevm_state_cleanup();
* got a bad migration state).
*/
migration_incoming_state_destroy();
-
+ qemu_loadvm_state_cleanup();
return NULL;
}
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;
- if (skip_section_footers) {
+ if (!migrate_get_current()->send_section_footer) {
/* No footer to check */
return true;
}
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;
}
return 0;
}
+static int qemu_loadvm_state_setup(QEMUFile *f)
+{
+ SaveStateEntry *se;
+ int ret;
+
+ trace_loadvm_state_setup();
+ QTAILQ_FOREACH(se, &savevm_state.handlers, entry) {
+ if (!se->ops || !se->ops->load_setup) {
+ continue;
+ }
+ if (se->ops && se->ops->is_active) {
+ if (!se->ops->is_active(se->opaque)) {
+ continue;
+ }
+ }
+
+ ret = se->ops->load_setup(f, se->opaque);
+ if (ret < 0) {
+ qemu_file_set_error(f, ret);
+ error_report("Load state of device %s failed", se->idstr);
+ return ret;
+ }
+ }
+ return 0;
+}
+
+void qemu_loadvm_state_cleanup(void)
+{
+ SaveStateEntry *se;
+
+ trace_loadvm_state_cleanup();
+ QTAILQ_FOREACH(se, &savevm_state.handlers, entry) {
+ if (se->ops && se->ops->load_cleanup) {
+ se->ops->load_cleanup(se->opaque);
+ }
+ }
+}
+
static int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis)
{
uint8_t section_type;
return -ENOTSUP;
}
- if (!savevm_state.skip_configuration || enforce_config_section()) {
+ if (qemu_loadvm_state_setup(f) != 0) {
+ return -EINVAL;
+ }
+
+ if (migrate_get_current()->send_configuration) {
if (qemu_get_byte(f) != QEMU_VM_CONFIGURATION) {
error_report("Configuration section missing");
return -EINVAL;
}
}
+ cpu_synchronize_all_pre_loadvm();
+
ret = qemu_loadvm_state_main(f, mis);
qemu_event_set(&mis->main_thread_load_event);
}
}
+ qemu_loadvm_state_cleanup();
cpu_synchronize_all_post_init();
return ret;
}
-int save_vmstate(const char *name, Error **errp)
+int save_snapshot(const char *name, Error **errp)
{
BlockDriverState *bs, *bs1;
QEMUSnapshotInfo sn1, *sn = &sn1, old_sn1, *old_sn = &old_sn1;
}
vm_stop(RUN_STATE_SAVE_VM);
+ bdrv_drain_all_begin();
+
aio_context_acquire(aio_context);
memset(sn, 0, sizeof(*sn));
goto the_end;
}
+ /* The bdrv_all_create_snapshot() call that follows acquires the AioContext
+ * for itself. BDRV_POLL_WHILE() does not support nested locking because
+ * it only releases the lock once. Therefore synchronous I/O will deadlock
+ * unless we release the AioContext before bdrv_all_create_snapshot().
+ */
+ aio_context_release(aio_context);
+ aio_context = NULL;
+
ret = bdrv_all_create_snapshot(sn, bs, vm_state_size, &bs);
if (ret < 0) {
error_setg(errp, "Error while creating snapshot on '%s'",
ret = 0;
the_end:
- aio_context_release(aio_context);
+ if (aio_context) {
+ aio_context_release(aio_context);
+ }
+
+ bdrv_drain_all_end();
+
if (saved_vm_running) {
vm_start();
}
migration_incoming_state_destroy();
}
-int load_vmstate(const char *name, Error **errp)
+int load_snapshot(const char *name, Error **errp)
{
BlockDriverState *bs, *bs_vm_state;
QEMUSnapshotInfo sn;
}
/* Flush all IO requests so they don't interfere with the new state. */
- bdrv_drain_all();
+ bdrv_drain_all_begin();
ret = bdrv_all_goto_snapshot(name, &bs);
if (ret < 0) {
error_setg(errp, "Error %d while activating snapshot '%s' on '%s'",
ret, name, bdrv_get_device_name(bs));
- return ret;
+ goto err_drain;
}
/* restore the VM state */
f = qemu_fopen_bdrv(bs_vm_state, 0);
if (!f) {
error_setg(errp, "Could not open VM state file");
- return -EINVAL;
+ ret = -EINVAL;
+ goto err_drain;
}
- 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);
+ migration_incoming_state_destroy();
aio_context_release(aio_context);
- migration_incoming_state_destroy();
+ bdrv_drain_all_end();
+
if (ret < 0) {
error_setg(errp, "Error %d while loading VM state", ret);
return ret;
}
return 0;
+
+err_drain:
+ bdrv_drain_all_end();
+ return ret;
}
void vmstate_register_ram(MemoryRegion *mr, DeviceState *dev)
bool vmstate_check_only_migratable(const VMStateDescription *vmsd)
{
/* check needed if --only-migratable is specified */
- if (!only_migratable) {
+ if (!migrate_get_current()->only_migratable) {
return true;
}