X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=savevm.c;h=1c5abe247697d9a8cb7a61ba3e7b7381eda9a624;hb=4ab0ba9e26d52a272cadd5635437a341a4e7ff36;hp=99e49499a9fc98aca739e38336f8df893fb76d18;hpb=2431296806bc7a40c29b7775e16f36dc1cda4d06;p=qemu.git diff --git a/savevm.c b/savevm.c index 99e49499a..1c5abe247 100644 --- a/savevm.c +++ b/savevm.c @@ -23,7 +23,6 @@ */ #include #include -#include #include #include #include @@ -78,11 +77,11 @@ #include "sysemu.h" #include "qemu-timer.h" #include "qemu-char.h" -#include "blockdev.h" #include "audio/audio.h" #include "migration.h" #include "qemu_socket.h" #include "qemu-queue.h" +#include "cpus.h" #define SELF_ANNOUNCE_ROUNDS 5 @@ -138,7 +137,7 @@ static void qemu_announce_self_once(void *opaque) if (--count) { /* delay 50ms, 150ms, 250ms, ... */ - qemu_mod_timer(timer, qemu_get_clock(rt_clock) + + qemu_mod_timer(timer, qemu_get_clock_ms(rt_clock) + 50 + (SELF_ANNOUNCE_ROUNDS - count - 1) * 100); } else { qemu_del_timer(timer); @@ -149,7 +148,7 @@ static void qemu_announce_self_once(void *opaque) void qemu_announce_self(void) { static QEMUTimer *timer; - timer = qemu_new_timer(rt_clock, qemu_announce_self_once, &timer); + timer = qemu_new_timer_ms(rt_clock, qemu_announce_self_once, &timer); qemu_announce_self_once(&timer); } @@ -611,7 +610,7 @@ int qemu_file_rate_limit(QEMUFile *f) return 0; } -size_t qemu_file_get_rate_limit(QEMUFile *f) +int64_t qemu_file_get_rate_limit(QEMUFile *f) { if (f->get_rate_limit) return f->get_rate_limit(f->opaque); @@ -619,7 +618,7 @@ size_t qemu_file_get_rate_limit(QEMUFile *f) return 0; } -size_t qemu_file_set_rate_limit(QEMUFile *f, size_t new_rate) +int64_t qemu_file_set_rate_limit(QEMUFile *f, int64_t new_rate) { /* any failed or completed migration keeps its state to allow probing of * migration data, but has no associated file anymore */ @@ -675,6 +674,27 @@ uint64_t qemu_get_be64(QEMUFile *f) return v; } +/* bool */ + +static int get_bool(QEMUFile *f, void *pv, size_t size) +{ + bool *v = pv; + *v = qemu_get_byte(f); + return 0; +} + +static void put_bool(QEMUFile *f, void *pv, size_t size) +{ + bool *v = pv; + qemu_put_byte(f, *v); +} + +const VMStateInfo vmstate_info_bool = { + .name = "bool", + .get = get_bool, + .put = put_bool, +}; + /* 8 bit int */ static int get_int8(QEMUFile *f, void *pv, size_t size) @@ -862,6 +882,27 @@ const VMStateInfo vmstate_info_uint32 = { .put = put_uint32, }; +/* 32 bit uint. See that the received value is the same than the one + in the field */ + +static int get_uint32_equal(QEMUFile *f, void *pv, size_t size) +{ + uint32_t *v = pv; + uint32_t v2; + qemu_get_be32s(f, &v2); + + if (*v == v2) { + return 0; + } + return -EINVAL; +} + +const VMStateInfo vmstate_info_uint32_equal = { + .name = "uint32 equal", + .get = get_uint32_equal, + .put = put_uint32, +}; + /* 64 bit unsigned int */ static int get_uint64(QEMUFile *f, void *pv, size_t size) @@ -966,7 +1007,7 @@ const VMStateInfo vmstate_info_buffer = { }; /* unused buffers: space that was used for some fields that are - not usefull anymore */ + not useful anymore */ static int get_unused_buffer(QEMUFile *f, void *pv, size_t size) { @@ -1193,6 +1234,7 @@ int vmstate_register_with_alias_id(DeviceState *dev, int instance_id, se->opaque = opaque; se->vmsd = vmsd; se->alias_id = alias_id; + se->no_migrate = vmsd->unmigratable; if (dev && dev->parent_bus && dev->parent_bus->info->get_dev_path) { char *id = dev->parent_bus->info->get_dev_path(dev); @@ -1288,8 +1330,12 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, n_elems = field->num; } else if (field->flags & VMS_VARRAY_INT32) { n_elems = *(int32_t *)(opaque+field->num_offset); + } else if (field->flags & VMS_VARRAY_UINT32) { + n_elems = *(uint32_t *)(opaque+field->num_offset); } else if (field->flags & VMS_VARRAY_UINT16) { n_elems = *(uint16_t *)(opaque+field->num_offset); + } else if (field->flags & VMS_VARRAY_UINT8) { + n_elems = *(uint8_t *)(opaque+field->num_offset); } if (field->flags & VMS_POINTER) { base_addr = *(void **)base_addr + field->start; @@ -1350,6 +1396,8 @@ void vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd, n_elems = *(int32_t *)(opaque+field->num_offset); } else if (field->flags & VMS_VARRAY_UINT16) { n_elems = *(uint16_t *)(opaque+field->num_offset); + } else if (field->flags & VMS_VARRAY_UINT8) { + n_elems = *(uint8_t *)(opaque+field->num_offset); } if (field->flags & VMS_POINTER) { base_addr = *(void **)base_addr + field->start; @@ -1380,19 +1428,13 @@ static int vmstate_load(QEMUFile *f, SaveStateEntry *se, int version_id) return vmstate_load_state(f, se->vmsd, se->opaque, version_id); } -static int vmstate_save(QEMUFile *f, SaveStateEntry *se) +static void vmstate_save(QEMUFile *f, SaveStateEntry *se) { - if (se->no_migrate) { - return -1; - } - if (!se->vmsd) { /* Old style */ se->save_state(f, se->opaque); - return 0; + return; } vmstate_save_state(f,se->vmsd, se->opaque); - - return 0; } #define QEMU_VM_FILE_MAGIC 0x5145564d @@ -1406,6 +1448,20 @@ static int vmstate_save(QEMUFile *f, SaveStateEntry *se) #define QEMU_VM_SECTION_FULL 0x04 #define QEMU_VM_SUBSECTION 0x05 +bool qemu_savevm_state_blocked(Monitor *mon) +{ + SaveStateEntry *se; + + QTAILQ_FOREACH(se, &savevm_handlers, entry) { + if (se->no_migrate) { + monitor_printf(mon, "state blocked by non-migratable device '%s'\n", + se->idstr); + return true; + } + } + return false; +} + int qemu_savevm_state_begin(Monitor *mon, QEMUFile *f, int blk_enable, int shared) { @@ -1487,7 +1543,6 @@ int qemu_savevm_state_iterate(Monitor *mon, QEMUFile *f) int qemu_savevm_state_complete(Monitor *mon, QEMUFile *f) { SaveStateEntry *se; - int r; cpu_synchronize_all_states(); @@ -1520,11 +1575,7 @@ int qemu_savevm_state_complete(Monitor *mon, QEMUFile *f) qemu_put_be32(f, se->instance_id); qemu_put_be32(f, se->version_id); - r = vmstate_save(f, se); - if (r < 0) { - monitor_printf(mon, "cannot migrate with device '%s'\n", se->idstr); - return r; - } + vmstate_save(f, se); } qemu_put_byte(f, QEMU_VM_EOF); @@ -1552,9 +1603,12 @@ static int qemu_savevm_state(Monitor *mon, QEMUFile *f) int ret; saved_vm_running = vm_running; - vm_stop(0); + vm_stop(VMSTOP_SAVEVM); - bdrv_flush_all(); + if (qemu_savevm_state_blocked(mon)) { + ret = -EINVAL; + goto out; + } ret = qemu_savevm_state_begin(mon, f, 0, 0); if (ret < 0) @@ -1612,22 +1666,29 @@ static const VMStateDescription *vmstate_get_subsection(const VMStateSubsection static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd, void *opaque) { + const VMStateSubsection *sub = vmsd->subsections; + + if (!sub || !sub->needed) { + return 0; + } + while (qemu_peek_byte(f) == QEMU_VM_SUBSECTION) { char idstr[256]; int ret; - uint8_t version_id, subsection, len; + uint8_t version_id, len; const VMStateDescription *sub_vmsd; - subsection = qemu_get_byte(f); + qemu_get_byte(f); /* subsection */ len = qemu_get_byte(f); qemu_get_buffer(f, (uint8_t *)idstr, len); idstr[len] = 0; version_id = qemu_get_be32(f); - sub_vmsd = vmstate_get_subsection(vmsd->subsections, idstr); + sub_vmsd = vmstate_get_subsection(sub, idstr); if (sub_vmsd == NULL) { return -ENOENT; } + assert(!sub_vmsd->subsections); ret = vmstate_load_state(f, sub_vmsd, opaque, version_id); if (ret) { return ret; @@ -1651,6 +1712,7 @@ static void vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd, qemu_put_byte(f, len); qemu_put_buffer(f, (uint8_t *)vmsd->name, len); qemu_put_be32(f, vmsd->version_id); + assert(!vmsd->subsections); vmstate_save_state(f, vmsd, opaque); } sub++; @@ -1673,6 +1735,10 @@ int qemu_loadvm_state(QEMUFile *f) unsigned int v; int ret; + if (qemu_savevm_state_blocked(default_mon)) { + return -EINVAL; + } + v = qemu_get_be32(f); if (v != QEMU_VM_FILE_MAGIC) return -EINVAL; @@ -1837,8 +1903,10 @@ void do_savevm(Monitor *mon, const QDict *qdict) uint32_t vm_state_size; #ifdef _WIN32 struct _timeb tb; + struct tm *ptm; #else struct timeval tv; + struct tm tm; #endif const char *name = qdict_get_try_str(qdict, "name"); @@ -1862,22 +1930,11 @@ void do_savevm(Monitor *mon, const QDict *qdict) monitor_printf(mon, "No block device can accept snapshots\n"); return; } - /* ??? Should this occur after vm_stop? */ - qemu_aio_flush(); saved_vm_running = vm_running; - vm_stop(0); + vm_stop(VMSTOP_SAVEVM); memset(sn, 0, sizeof(*sn)); - if (name) { - ret = bdrv_snapshot_find(bs, old_sn, name); - if (ret >= 0) { - pstrcpy(sn->name, sizeof(sn->name), old_sn->name); - pstrcpy(sn->id_str, sizeof(sn->id_str), old_sn->id_str); - } else { - pstrcpy(sn->name, sizeof(sn->name), name); - } - } /* fill auxiliary fields */ #ifdef _WIN32 @@ -1889,7 +1946,26 @@ void do_savevm(Monitor *mon, const QDict *qdict) sn->date_sec = tv.tv_sec; sn->date_nsec = tv.tv_usec * 1000; #endif - sn->vm_clock_nsec = qemu_get_clock(vm_clock); + sn->vm_clock_nsec = qemu_get_clock_ns(vm_clock); + + if (name) { + ret = bdrv_snapshot_find(bs, old_sn, name); + if (ret >= 0) { + pstrcpy(sn->name, sizeof(sn->name), old_sn->name); + pstrcpy(sn->id_str, sizeof(sn->id_str), old_sn->id_str); + } else { + pstrcpy(sn->name, sizeof(sn->name), name); + } + } else { +#ifdef _WIN32 + ptm = localtime(&tb.time); + strftime(sn->name, sizeof(sn->name), "vm-%Y%m%d%H%M%S", ptm); +#else + /* cast below needed for OpenBSD where tv_sec is still 'long' */ + localtime_r((const time_t *)&tv.tv_sec, &tm); + strftime(sn->name, sizeof(sn->name), "vm-%Y%m%d%H%M%S", &tm); +#endif + } /* Delete old snapshots of the same name */ if (name && del_existing_snapshots(mon, name) < 0) { @@ -1948,6 +2024,8 @@ int load_vmstate(const char *name) 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."); return -EINVAL; } @@ -1996,6 +2074,7 @@ int load_vmstate(const char *name) return -EINVAL; } + qemu_system_reset(VMRESET_SILENT); ret = qemu_loadvm_state(f); qemu_fclose(f); @@ -2039,8 +2118,10 @@ void do_delvm(Monitor *mon, const QDict *qdict) void do_info_snapshots(Monitor *mon) { BlockDriverState *bs, *bs1; - QEMUSnapshotInfo *sn_tab, *sn; - int nb_sns, i; + QEMUSnapshotInfo *sn_tab, *sn, s, *sn_info = &s; + int nb_sns, i, ret, available; + int total; + int *available_snapshots; char buf[256]; bs = bdrv_snapshots(); @@ -2048,27 +2129,52 @@ void do_info_snapshots(Monitor *mon) monitor_printf(mon, "No available block device supports snapshots\n"); return; } - monitor_printf(mon, "Snapshot devices:"); - bs1 = NULL; - while ((bs1 = bdrv_next(bs1))) { - if (bdrv_can_snapshot(bs1)) { - if (bs == bs1) - monitor_printf(mon, " %s", bdrv_get_device_name(bs1)); - } - } - monitor_printf(mon, "\n"); nb_sns = bdrv_snapshot_list(bs, &sn_tab); if (nb_sns < 0) { monitor_printf(mon, "bdrv_snapshot_list: error %d\n", nb_sns); return; } - monitor_printf(mon, "Snapshot list (from %s):\n", - bdrv_get_device_name(bs)); - monitor_printf(mon, "%s\n", bdrv_snapshot_dump(buf, sizeof(buf), NULL)); - for(i = 0; i < nb_sns; i++) { + + if (nb_sns == 0) { + monitor_printf(mon, "There is no snapshot available.\n"); + return; + } + + available_snapshots = qemu_mallocz(sizeof(int) * nb_sns); + total = 0; + for (i = 0; i < nb_sns; i++) { sn = &sn_tab[i]; - monitor_printf(mon, "%s\n", bdrv_snapshot_dump(buf, sizeof(buf), sn)); + available = 1; + bs1 = NULL; + + while ((bs1 = bdrv_next(bs1))) { + if (bdrv_can_snapshot(bs1) && bs1 != bs) { + ret = bdrv_snapshot_find(bs1, sn_info, sn->id_str); + if (ret < 0) { + available = 0; + break; + } + } + } + + if (available) { + available_snapshots[total] = i; + total++; + } } + + if (total > 0) { + monitor_printf(mon, "%s\n", bdrv_snapshot_dump(buf, sizeof(buf), NULL)); + for (i = 0; i < total; i++) { + sn = &sn_tab[available_snapshots[i]]; + monitor_printf(mon, "%s\n", bdrv_snapshot_dump(buf, sizeof(buf), sn)); + } + } else { + monitor_printf(mon, "There is no suitable snapshot available\n"); + } + qemu_free(sn_tab); + qemu_free(available_snapshots); + }