X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=migration%2Fsavevm.c;h=8d95e261f60acda0a173b9c46d20f490cee0d7c4;hb=0197d89025d768f3a2505ea50816fbc6b161b992;hp=ef707b8c439a7dcb3e101873287bf00818a8e445;hpb=69e2d03843412b9c076515b3aa9a71db161b6a1a;p=mirror_qemu.git diff --git a/migration/savevm.c b/migration/savevm.c index ef707b8c43..8d95e261f6 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -32,6 +32,7 @@ #include "net/net.h" #include "migration.h" #include "migration/snapshot.h" +#include "migration/vmstate.h" #include "migration/misc.h" #include "migration/register.h" #include "migration/global_state.h" @@ -50,20 +51,18 @@ #include "exec/target_page.h" #include "trace.h" #include "qemu/iov.h" +#include "qemu/main-loop.h" #include "block/snapshot.h" #include "qemu/cutils.h" #include "io/channel-buffer.h" #include "io/channel-file.h" #include "sysemu/replay.h" +#include "sysemu/runstate.h" +#include "sysemu/sysemu.h" #include "qjson.h" #include "migration/colo.h" - -#ifndef ETH_P_RARP -#define ETH_P_RARP 0x8035 -#endif -#define ARP_HTYPE_ETH 0x0001 -#define ARP_PTYPE_IP 0x0800 -#define ARP_OP_REQUEST_REV 0x3 +#include "qemu/bitmap.h" +#include "net/announce.h" const unsigned int postcopy_ram_discard_version = 0; @@ -125,72 +124,11 @@ static struct mig_cmd_args { * generic extendable format with an exception for two old entities. */ -static int announce_self_create(uint8_t *buf, - uint8_t *mac_addr) -{ - /* Ethernet header. */ - memset(buf, 0xff, 6); /* destination MAC addr */ - memcpy(buf + 6, mac_addr, 6); /* source MAC addr */ - *(uint16_t *)(buf + 12) = htons(ETH_P_RARP); /* ethertype */ - - /* RARP header. */ - *(uint16_t *)(buf + 14) = htons(ARP_HTYPE_ETH); /* hardware addr space */ - *(uint16_t *)(buf + 16) = htons(ARP_PTYPE_IP); /* protocol addr space */ - *(buf + 18) = 6; /* hardware addr length (ethernet) */ - *(buf + 19) = 4; /* protocol addr length (IPv4) */ - *(uint16_t *)(buf + 20) = htons(ARP_OP_REQUEST_REV); /* opcode */ - memcpy(buf + 22, mac_addr, 6); /* source hw addr */ - memset(buf + 28, 0x00, 4); /* source protocol addr */ - memcpy(buf + 32, mac_addr, 6); /* target hw addr */ - memset(buf + 38, 0x00, 4); /* target protocol addr */ - - /* Padding to get up to 60 bytes (ethernet min packet size, minus FCS). */ - memset(buf + 42, 0x00, 18); - - return 60; /* len (FCS will be added by hardware) */ -} - -static void qemu_announce_self_iter(NICState *nic, void *opaque) -{ - uint8_t buf[60]; - int len; - - trace_qemu_announce_self_iter(qemu_ether_ntoa(&nic->conf->macaddr)); - len = announce_self_create(buf, nic->conf->macaddr.a); - - qemu_send_packet_raw(qemu_get_queue(nic), buf, len); -} - - -static void qemu_announce_self_once(void *opaque) -{ - static int count = SELF_ANNOUNCE_ROUNDS; - QEMUTimer *timer = *(QEMUTimer **)opaque; - - qemu_foreach_nic(qemu_announce_self_iter, NULL); - - if (--count) { - /* delay 50ms, 150ms, 250ms, ... */ - timer_mod(timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + - self_announce_delay(count)); - } else { - timer_del(timer); - timer_free(timer); - } -} - -void qemu_announce_self(void) -{ - static QEMUTimer *timer; - timer = timer_new_ms(QEMU_CLOCK_REALTIME, qemu_announce_self_once, &timer); - qemu_announce_self_once(&timer); -} - /***********************************************************/ /* savevm/loadvm support */ static ssize_t block_writev_buffer(void *opaque, struct iovec *iov, int iovcnt, - int64_t pos) + int64_t pos, Error **errp) { int ret; QEMUIOVector qiov; @@ -205,12 +143,12 @@ static ssize_t block_writev_buffer(void *opaque, struct iovec *iov, int iovcnt, } static ssize_t block_get_buffer(void *opaque, uint8_t *buf, int64_t pos, - size_t size) + size_t size, Error **errp) { return bdrv_load_vmstate(opaque, buf, pos, size); } -static int bdrv_fclose(void *opaque) +static int bdrv_fclose(void *opaque, Error **errp) { return bdrv_flush(opaque); } @@ -263,15 +201,16 @@ void timer_get(QEMUFile *f, QEMUTimer *ts) * 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, VMStateField *field) +static int get_timer(QEMUFile *f, void *pv, size_t size, + const VMStateField *field) { QEMUTimer *v = pv; timer_get(f, v); return 0; } -static int put_timer(QEMUFile *f, void *pv, size_t size, VMStateField *field, - QJSON *vmdesc) +static int put_timer(QEMUFile *f, void *pv, size_t size, + const VMStateField *field, QJSON *vmdesc) { QEMUTimer *v = pv; timer_put(f, v); @@ -302,7 +241,7 @@ typedef struct SaveStateEntry { int section_id; /* section id read from the stream */ int load_section_id; - SaveVMHandlers *ops; + const SaveVMHandlers *ops; const VMStateDescription *vmsd; void *opaque; CompatEntry *compat; @@ -315,6 +254,9 @@ typedef struct SaveState { uint32_t len; const char *name; uint32_t target_page_bits; + uint32_t caps_count; + MigrationCapability *capabilities; + QemuUUID uuid; } SaveState; static SaveState savevm_state = { @@ -322,15 +264,52 @@ static SaveState savevm_state = { .global_section_id = 0, }; +static bool should_validate_capability(int capability) +{ + assert(capability >= 0 && capability < MIGRATION_CAPABILITY__MAX); + /* Validate only new capabilities to keep compatibility. */ + switch (capability) { + case MIGRATION_CAPABILITY_X_IGNORE_SHARED: + return true; + default: + return false; + } +} + +static uint32_t get_validatable_capabilities_count(void) +{ + MigrationState *s = migrate_get_current(); + uint32_t result = 0; + int i; + for (i = 0; i < MIGRATION_CAPABILITY__MAX; i++) { + if (should_validate_capability(i) && s->enabled_capabilities[i]) { + result++; + } + } + return result; +} + static int configuration_pre_save(void *opaque) { SaveState *state = opaque; const char *current_name = MACHINE_GET_CLASS(current_machine)->name; + MigrationState *s = migrate_get_current(); + int i, j; state->len = strlen(current_name); state->name = current_name; state->target_page_bits = qemu_target_page_bits(); + state->caps_count = get_validatable_capabilities_count(); + state->capabilities = g_renew(MigrationCapability, state->capabilities, + state->caps_count); + for (i = j = 0; i < MIGRATION_CAPABILITY__MAX; i++) { + if (should_validate_capability(i) && s->enabled_capabilities[i]) { + state->capabilities[j++] = i; + } + } + state->uuid = qemu_uuid; + return 0; } @@ -346,6 +325,40 @@ static int configuration_pre_load(void *opaque) return 0; } +static bool configuration_validate_capabilities(SaveState *state) +{ + bool ret = true; + MigrationState *s = migrate_get_current(); + unsigned long *source_caps_bm; + int i; + + source_caps_bm = bitmap_new(MIGRATION_CAPABILITY__MAX); + for (i = 0; i < state->caps_count; i++) { + MigrationCapability capability = state->capabilities[i]; + set_bit(capability, source_caps_bm); + } + + for (i = 0; i < MIGRATION_CAPABILITY__MAX; i++) { + bool source_state, target_state; + if (!should_validate_capability(i)) { + continue; + } + source_state = test_bit(i, source_caps_bm); + target_state = s->enabled_capabilities[i]; + if (source_state != target_state) { + error_report("Capability %s is %s, but received capability is %s", + MigrationCapability_str(i), + target_state ? "on" : "off", + source_state ? "on" : "off"); + ret = false; + /* Don't break here to report all failed capabilities */ + } + } + + g_free(source_caps_bm); + return ret; +} + static int configuration_post_load(void *opaque, int version_id) { SaveState *state = opaque; @@ -363,9 +376,53 @@ static int configuration_post_load(void *opaque, int version_id) return -EINVAL; } + if (!configuration_validate_capabilities(state)) { + return -EINVAL; + } + + return 0; +} + +static int get_capability(QEMUFile *f, void *pv, size_t size, + const VMStateField *field) +{ + MigrationCapability *capability = pv; + char capability_str[UINT8_MAX + 1]; + uint8_t len; + int i; + + len = qemu_get_byte(f); + qemu_get_buffer(f, (uint8_t *)capability_str, len); + capability_str[len] = '\0'; + for (i = 0; i < MIGRATION_CAPABILITY__MAX; i++) { + if (!strcmp(MigrationCapability_str(i), capability_str)) { + *capability = i; + return 0; + } + } + error_report("Received unknown capability %s", capability_str); + return -EINVAL; +} + +static int put_capability(QEMUFile *f, void *pv, size_t size, + const VMStateField *field, QJSON *vmdesc) +{ + MigrationCapability *capability = pv; + const char *capability_str = MigrationCapability_str(*capability); + size_t len = strlen(capability_str); + assert(len <= UINT8_MAX); + + qemu_put_byte(f, len); + qemu_put_buffer(f, (uint8_t *)capability_str, len); return 0; } +static const VMStateInfo vmstate_info_capability = { + .name = "capability", + .get = get_capability, + .put = put_capability, +}; + /* 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). @@ -390,6 +447,67 @@ static const VMStateDescription vmstate_target_page_bits = { } }; +static bool vmstate_capabilites_needed(void *opaque) +{ + return get_validatable_capabilities_count() > 0; +} + +static const VMStateDescription vmstate_capabilites = { + .name = "configuration/capabilities", + .version_id = 1, + .minimum_version_id = 1, + .needed = vmstate_capabilites_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT32_V(caps_count, SaveState, 1), + VMSTATE_VARRAY_UINT32_ALLOC(capabilities, SaveState, caps_count, 1, + vmstate_info_capability, + MigrationCapability), + VMSTATE_END_OF_LIST() + } +}; + +static bool vmstate_uuid_needed(void *opaque) +{ + return qemu_uuid_set && migrate_validate_uuid(); +} + +static int vmstate_uuid_post_load(void *opaque, int version_id) +{ + SaveState *state = opaque; + char uuid_src[UUID_FMT_LEN + 1]; + char uuid_dst[UUID_FMT_LEN + 1]; + + if (!qemu_uuid_set) { + /* + * It's warning because user might not know UUID in some cases, + * e.g. load an old snapshot + */ + qemu_uuid_unparse(&state->uuid, uuid_src); + warn_report("UUID is received %s, but local uuid isn't set", + uuid_src); + return 0; + } + if (!qemu_uuid_is_equal(&state->uuid, &qemu_uuid)) { + qemu_uuid_unparse(&state->uuid, uuid_src); + qemu_uuid_unparse(&qemu_uuid, uuid_dst); + error_report("UUID received is %s and local is %s", uuid_src, uuid_dst); + return -EINVAL; + } + return 0; +} + +static const VMStateDescription vmstate_uuid = { + .name = "configuration/uuid", + .version_id = 1, + .minimum_version_id = 1, + .needed = vmstate_uuid_needed, + .post_load = vmstate_uuid_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT8_ARRAY_V(uuid.data, SaveState, sizeof(QemuUUID), 1), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_configuration = { .name = "configuration", .version_id = 1, @@ -403,6 +521,8 @@ static const VMStateDescription vmstate_configuration = { }, .subsections = (const VMStateDescription*[]) { &vmstate_target_page_bits, + &vmstate_capabilites, + &vmstate_uuid, NULL } }; @@ -609,11 +729,10 @@ static void savevm_state_handler_insert(SaveStateEntry *nse) of the system, so instance_id should be removed/replaced. Meanwhile pass -1 as instance_id if you do not already have a clearly distinguishing id for all instances of your device class. */ -int register_savevm_live(DeviceState *dev, - const char *idstr, +int register_savevm_live(const char *idstr, int instance_id, int version_id, - SaveVMHandlers *ops, + const SaveVMHandlers *ops, void *opaque) { SaveStateEntry *se; @@ -629,26 +748,6 @@ int register_savevm_live(DeviceState *dev, se->is_ram = 1; } - if (dev) { - char *id = qdev_get_dev_path(dev); - if (id) { - 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); - pstrcpy(se->compat->idstr, sizeof(se->compat->idstr), idstr); - se->compat->instance_id = instance_id == -1 ? - calculate_compat_instance_id(idstr) : instance_id; - instance_id = -1; - } - } pstrcat(se->idstr, sizeof(se->idstr), idstr); if (instance_id == -1) { @@ -1017,6 +1116,7 @@ void qemu_savevm_state_header(QEMUFile *f) void qemu_savevm_state_setup(QEMUFile *f) { SaveStateEntry *se; + Error *local_err = NULL; int ret; trace_savevm_state_setup(); @@ -1024,7 +1124,7 @@ void qemu_savevm_state_setup(QEMUFile *f) if (!se->ops || !se->ops->save_setup) { continue; } - if (se->ops && se->ops->is_active) { + if (se->ops->is_active) { if (!se->ops->is_active(se->opaque)) { continue; } @@ -1038,6 +1138,10 @@ void qemu_savevm_state_setup(QEMUFile *f) break; } } + + if (precopy_notify(PRECOPY_NOTIFY_SETUP, &local_err)) { + error_report_err(local_err); + } } int qemu_savevm_state_resume_prepare(MigrationState *s) @@ -1051,7 +1155,7 @@ int qemu_savevm_state_resume_prepare(MigrationState *s) if (!se->ops || !se->ops->resume_prepare) { continue; } - if (se->ops && se->ops->is_active) { + if (se->ops->is_active) { if (!se->ops->is_active(se->opaque)) { continue; } @@ -1081,15 +1185,13 @@ int qemu_savevm_state_iterate(QEMUFile *f, bool postcopy) if (!se->ops || !se->ops->save_live_iterate) { continue; } - if (se->ops && se->ops->is_active) { - if (!se->ops->is_active(se->opaque)) { - continue; - } + if (se->ops->is_active && + !se->ops->is_active(se->opaque)) { + continue; } - if (se->ops && se->ops->is_active_iterate) { - if (!se->ops->is_active_iterate(se->opaque)) { - continue; - } + if (se->ops->is_active_iterate && + !se->ops->is_active_iterate(se->opaque)) { + continue; } /* * In the postcopy phase, any device that doesn't know how to @@ -1113,6 +1215,8 @@ int qemu_savevm_state_iterate(QEMUFile *f, bool postcopy) save_section_footer(f, se); if (ret < 0) { + error_report("failed to save SaveStateEntry with id(name): %d(%s)", + se->section_id, se->idstr); qemu_file_set_error(f, ret); } if (ret <= 0) { @@ -1149,7 +1253,7 @@ void qemu_savevm_state_complete_postcopy(QEMUFile *f) if (!se->ops || !se->ops->save_live_complete_postcopy) { continue; } - if (se->ops && se->ops->is_active) { + if (se->ops->is_active) { if (!se->ops->is_active(se->opaque)) { continue; } @@ -1172,29 +1276,21 @@ void qemu_savevm_state_complete_postcopy(QEMUFile *f) qemu_fflush(f); } -int qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only, - bool inactivate_disks) +static +int qemu_savevm_state_complete_precopy_iterable(QEMUFile *f, bool in_postcopy) { - QJSON *vmdesc; - int vmdesc_len; SaveStateEntry *se; int ret; - bool in_postcopy = migration_in_postcopy(); - - trace_savevm_state_complete_precopy(); - - cpu_synchronize_all_states(); QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { if (!se->ops || (in_postcopy && se->ops->has_postcopy && se->ops->has_postcopy(se->opaque)) || - (in_postcopy && !iterable_only) || !se->ops->save_live_complete_precopy) { continue; } - if (se->ops && se->ops->is_active) { + if (se->ops->is_active) { if (!se->ops->is_active(se->opaque)) { continue; } @@ -1212,9 +1308,18 @@ int qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only, } } - if (iterable_only) { - return 0; - } + return 0; +} + +static +int qemu_savevm_state_complete_precopy_non_iterable(QEMUFile *f, + bool in_postcopy, + bool inactivate_disks) +{ + g_autoptr(QJSON) vmdesc = NULL; + int vmdesc_len; + SaveStateEntry *se; + int ret; vmdesc = qjson_new(); json_prop_int(vmdesc, "page_size", qemu_target_page_size()); @@ -1272,8 +1377,43 @@ int qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only, qemu_put_be32(f, vmdesc_len); qemu_put_buffer(f, (uint8_t *)qjson_get_str(vmdesc), vmdesc_len); } - qjson_destroy(vmdesc); + return 0; +} + +int qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only, + bool inactivate_disks) +{ + int ret; + Error *local_err = NULL; + bool in_postcopy = migration_in_postcopy(); + + if (precopy_notify(PRECOPY_NOTIFY_COMPLETE, &local_err)) { + error_report_err(local_err); + } + + trace_savevm_state_complete_precopy(); + + cpu_synchronize_all_states(); + + if (!in_postcopy || iterable_only) { + ret = qemu_savevm_state_complete_precopy_iterable(f, in_postcopy); + if (ret) { + return ret; + } + } + + if (iterable_only) { + goto flush; + } + + ret = qemu_savevm_state_complete_precopy_non_iterable(f, in_postcopy, + inactivate_disks); + if (ret) { + return ret; + } + +flush: qemu_fflush(f); return 0; } @@ -1298,7 +1438,7 @@ void qemu_savevm_state_pending(QEMUFile *f, uint64_t threshold_size, if (!se->ops || !se->ops->save_live_pending) { continue; } - if (se->ops && se->ops->is_active) { + if (se->ops->is_active) { if (!se->ops->is_active(se->opaque)) { continue; } @@ -1312,6 +1452,11 @@ void qemu_savevm_state_pending(QEMUFile *f, uint64_t threshold_size, void qemu_savevm_state_cleanup(void) { SaveStateEntry *se; + Error *local_err = NULL; + + if (precopy_notify(PRECOPY_NOTIFY_CLEANUP, &local_err)) { + error_report_err(local_err); + } trace_savevm_state_cleanup(); QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { @@ -1334,16 +1479,13 @@ static int qemu_savevm_state(QEMUFile *f, Error **errp) return -EINVAL; } - if (migration_is_blocked(errp)) { - return -EINVAL; - } - if (migrate_use_block()) { error_setg(errp, "Block migration and snapshots are incompatible"); return -EINVAL; } migrate_init(ms); + memset(&ram_counters, 0, sizeof(ram_counters)); ms->to_dst_file = f; qemu_mutex_unlock_iothread(); @@ -1536,8 +1678,6 @@ static int loadvm_postcopy_handle_advise(MigrationIncomingState *mis, return -1; } - postcopy_state_set(POSTCOPY_INCOMING_ADVISE); - return 0; } @@ -1697,6 +1837,8 @@ static void *postcopy_ram_listen_thread(void *opaque) rcu_unregister_thread(); mis->have_listen_thread = false; + postcopy_state_set(POSTCOPY_INCOMING_END); + return NULL; } @@ -1727,7 +1869,8 @@ static int loadvm_postcopy_handle_listen(MigrationIncomingState *mis) * shouldn't be doing anything yet so don't actually expect requests */ if (migrate_postcopy_ram()) { - if (postcopy_ram_enable_notify(mis)) { + if (postcopy_ram_incoming_setup(mis)) { + postcopy_ram_incoming_cleanup(mis); return -1; } } @@ -1737,11 +1880,6 @@ static int loadvm_postcopy_handle_listen(MigrationIncomingState *mis) return -1; } - if (mis->have_listen_thread) { - error_report("CMD_POSTCOPY_RAM_LISTEN already has a listen thread"); - return -1; - } - mis->have_listen_thread = true; /* Start up the listening thread and wait for it to signal ready */ qemu_sem_init(&mis->listen_thread_sem, 0); @@ -1754,22 +1892,17 @@ static int loadvm_postcopy_handle_listen(MigrationIncomingState *mis) return 0; } - -typedef struct { - QEMUBH *bh; -} HandleRunBhData; - static void loadvm_postcopy_handle_run_bh(void *opaque) { Error *local_err = NULL; - HandleRunBhData *data = opaque; + MigrationIncomingState *mis = opaque; /* TODO we should move all of this lot into postcopy_ram.c or a shared code * in migration.c */ cpu_synchronize_all_post_init(); - qemu_announce_self(); + qemu_announce_self(&mis->announce_timer, migrate_announce_params()); /* Make sure all file formats flush their mutable metadata. * If we get an error here, just don't restart the VM yet. */ @@ -1781,7 +1914,6 @@ static void loadvm_postcopy_handle_run_bh(void *opaque) } trace_loadvm_postcopy_handle_run_cpu_sync(); - cpu_synchronize_all_post_init(); trace_loadvm_postcopy_handle_run_vmstart(); @@ -1795,15 +1927,13 @@ static void loadvm_postcopy_handle_run_bh(void *opaque) runstate_set(RUN_STATE_PAUSED); } - qemu_bh_delete(data->bh); - g_free(data); + qemu_bh_delete(mis->bh); } /* After all discards we can start running and asking for pages */ static int loadvm_postcopy_handle_run(MigrationIncomingState *mis) { - PostcopyState ps = postcopy_state_set(POSTCOPY_INCOMING_RUNNING); - HandleRunBhData *data; + PostcopyState ps = postcopy_state_get(); trace_loadvm_postcopy_handle_run(); if (ps != POSTCOPY_INCOMING_LISTENING) { @@ -1811,9 +1941,9 @@ static int loadvm_postcopy_handle_run(MigrationIncomingState *mis) return -1; } - data = g_new(HandleRunBhData, 1); - data->bh = qemu_bh_new(loadvm_postcopy_handle_run_bh, data); - qemu_bh_schedule(data->bh); + postcopy_state_set(POSTCOPY_INCOMING_RUNNING); + mis->bh = qemu_bh_new(loadvm_postcopy_handle_run_bh, mis); + qemu_bh_schedule(mis->bh); /* We need to finish reading the stream from the package * and also stop reading anything more from the stream that loaded the @@ -2180,6 +2310,43 @@ qemu_loadvm_section_part_end(QEMUFile *f, MigrationIncomingState *mis) return 0; } +static int qemu_loadvm_state_header(QEMUFile *f) +{ + unsigned int v; + int ret; + + v = qemu_get_be32(f); + if (v != QEMU_VM_FILE_MAGIC) { + error_report("Not a migration stream"); + return -EINVAL; + } + + v = qemu_get_be32(f); + if (v == QEMU_VM_FILE_VERSION_COMPAT) { + error_report("SaveVM v2 format is obsolete and don't work anymore"); + return -ENOTSUP; + } + if (v != QEMU_VM_FILE_VERSION) { + error_report("Unsupported migration stream version"); + return -ENOTSUP; + } + + if (migrate_get_current()->send_configuration) { + if (qemu_get_byte(f) != QEMU_VM_CONFIGURATION) { + error_report("Configuration section missing"); + qemu_loadvm_state_cleanup(); + return -EINVAL; + } + ret = vmstate_load_state(f, &vmstate_configuration, &savevm_state, 0); + + if (ret) { + qemu_loadvm_state_cleanup(); + return ret; + } + } + return 0; +} + static int qemu_loadvm_state_setup(QEMUFile *f) { SaveStateEntry *se; @@ -2190,7 +2357,7 @@ static int qemu_loadvm_state_setup(QEMUFile *f) if (!se->ops || !se->ops->load_setup) { continue; } - if (se->ops && se->ops->is_active) { + if (se->ops->is_active) { if (!se->ops->is_active(se->opaque)) { continue; } @@ -2289,7 +2456,7 @@ retry: case QEMU_VM_COMMAND: ret = loadvm_process_command(f); trace_qemu_loadvm_state_section_command(ret); - if ((ret < 0) || (ret & LOADVM_QUIT)) { + if ((ret < 0) || (ret == LOADVM_QUIT)) { goto out; } break; @@ -2328,7 +2495,6 @@ int qemu_loadvm_state(QEMUFile *f) { MigrationIncomingState *mis = migration_incoming_get_current(); Error *local_err = NULL; - unsigned int v; int ret; if (qemu_savevm_state_blocked(&local_err)) { @@ -2336,40 +2502,15 @@ int qemu_loadvm_state(QEMUFile *f) return -EINVAL; } - v = qemu_get_be32(f); - if (v != QEMU_VM_FILE_MAGIC) { - error_report("Not a migration stream"); - return -EINVAL; - } - - v = qemu_get_be32(f); - if (v == QEMU_VM_FILE_VERSION_COMPAT) { - error_report("SaveVM v2 format is obsolete and don't work anymore"); - return -ENOTSUP; - } - if (v != QEMU_VM_FILE_VERSION) { - error_report("Unsupported migration stream version"); - return -ENOTSUP; + ret = qemu_loadvm_state_header(f); + if (ret) { + return ret; } 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"); - qemu_loadvm_state_cleanup(); - return -EINVAL; - } - ret = vmstate_load_state(f, &vmstate_configuration, &savevm_state, 0); - - if (ret) { - qemu_loadvm_state_cleanup(); - return ret; - } - } - cpu_synchronize_all_pre_loadvm(); ret = qemu_loadvm_state_main(f, mis); @@ -2455,6 +2596,10 @@ int save_snapshot(const char *name, Error **errp) struct tm tm; AioContext *aio_context; + if (migration_is_blocked(errp)) { + return ret; + } + if (!replay_can_snapshot()) { error_setg(errp, "Record/replay does not allow making snapshot " "right now. Try once more later."); @@ -2752,7 +2897,7 @@ void vmstate_register_ram_global(MemoryRegion *mr) bool vmstate_check_only_migratable(const VMStateDescription *vmsd) { /* check needed if --only-migratable is specified */ - if (!migrate_get_current()->only_migratable) { + if (!only_migratable) { return true; }