#include "sysemu.h"
#include "qemu-timer.h"
#include "qemu-char.h"
-#include "block.h"
+#include "blockdev.h"
#include "audio/audio.h"
#include "migration.h"
#include "qemu_socket.h"
#include "qemu-queue.h"
-/* point to the block driver where the snapshots are managed */
-static BlockDriverState *bs_snapshots;
-
#define SELF_ANNOUNCE_ROUNDS 5
#ifndef ETH_P_RARP
-#define ETH_P_RARP 0x0835
+#define ETH_P_RARP 0x8035
#endif
#define ARP_HTYPE_ETH 0x0001
#define ARP_PTYPE_IP 0x0800
static int stdio_pclose(void *opaque)
{
QEMUFileStdio *s = opaque;
- pclose(s->stdio_file);
+ int ret;
+ ret = pclose(s->stdio_file);
qemu_free(s);
- return 0;
+ return ret;
}
static int stdio_fclose(void *opaque)
{
QEMUFileStdio *s = opaque;
fseek(s->stdio_file, pos, SEEK_SET);
- fwrite(buf, 1, size, s->stdio_file);
- return size;
+ return fwrite(buf, 1, size, s->stdio_file);
}
static int file_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
if (mode == NULL ||
(mode[0] != 'r' && mode[0] != 'w') ||
mode[1] != 'b' || mode[2] != 0) {
- fprintf(stderr, "qemu_fdopen: Argument validity check failed\n");
+ fprintf(stderr, "qemu_fopen: Argument validity check failed\n");
return NULL;
}
QTAILQ_ENTRY(SaveStateEntry) entry;
char idstr[256];
int instance_id;
+ int alias_id;
int version_id;
int section_id;
SaveSetParamsHandler *set_params;
}
}
-int vmstate_register(int instance_id, const VMStateDescription *vmsd,
- void *opaque)
+int vmstate_register_with_alias_id(int instance_id,
+ const VMStateDescription *vmsd,
+ void *opaque, int alias_id,
+ int required_for_version)
{
SaveStateEntry *se;
+ /* If this triggers, alias support can be dropped for the vmsd. */
+ assert(alias_id == -1 || required_for_version >= vmsd->minimum_version_id);
+
se = qemu_mallocz(sizeof(SaveStateEntry));
pstrcpy(se->idstr, sizeof(se->idstr), vmsd->name);
se->version_id = vmsd->version_id;
se->load_state = NULL;
se->opaque = opaque;
se->vmsd = vmsd;
+ se->alias_id = alias_id;
if (instance_id == -1) {
se->instance_id = calculate_new_instance_id(vmsd->name);
return 0;
}
+int vmstate_register(int instance_id, const VMStateDescription *vmsd,
+ void *opaque)
+{
+ return vmstate_register_with_alias_id(instance_id, vmsd, opaque, -1, 0);
+}
+
void vmstate_unregister(const VMStateDescription *vmsd, void *opaque)
{
SaveStateEntry *se, *new_se;
}
field++;
}
- if (vmsd->post_save) {
- vmsd->post_save(opaque);
- }
}
static int vmstate_load(QEMUFile *f, SaveStateEntry *se, int version_id)
#define QEMU_VM_SECTION_END 0x03
#define QEMU_VM_SECTION_FULL 0x04
-int qemu_savevm_state_begin(QEMUFile *f, int blk_enable, int shared)
+int qemu_savevm_state_begin(Monitor *mon, QEMUFile *f, int blk_enable,
+ int shared)
{
SaveStateEntry *se;
qemu_put_be32(f, se->instance_id);
qemu_put_be32(f, se->version_id);
- se->save_live_state(f, QEMU_VM_SECTION_START, se->opaque);
+ se->save_live_state(mon, f, QEMU_VM_SECTION_START, se->opaque);
}
- if (qemu_file_has_error(f))
+ if (qemu_file_has_error(f)) {
+ qemu_savevm_state_cancel(mon, f);
return -EIO;
+ }
return 0;
}
-int qemu_savevm_state_iterate(QEMUFile *f)
+int qemu_savevm_state_iterate(Monitor *mon, QEMUFile *f)
{
SaveStateEntry *se;
int ret = 1;
qemu_put_byte(f, QEMU_VM_SECTION_PART);
qemu_put_be32(f, se->section_id);
- ret &= !!se->save_live_state(f, QEMU_VM_SECTION_PART, se->opaque);
+ ret = se->save_live_state(mon, f, QEMU_VM_SECTION_PART, se->opaque);
+ if (!ret) {
+ /* Do not proceed to the next vmstate before this one reported
+ completion of the current stage. This serializes the migration
+ and reduces the probability that a faster changing state is
+ synchronized over and over again. */
+ break;
+ }
}
if (ret)
return 1;
- if (qemu_file_has_error(f))
+ if (qemu_file_has_error(f)) {
+ qemu_savevm_state_cancel(mon, f);
return -EIO;
+ }
return 0;
}
-int qemu_savevm_state_complete(QEMUFile *f)
+int qemu_savevm_state_complete(Monitor *mon, QEMUFile *f)
{
SaveStateEntry *se;
+ cpu_synchronize_all_states();
+
QTAILQ_FOREACH(se, &savevm_handlers, entry) {
if (se->save_live_state == NULL)
continue;
qemu_put_byte(f, QEMU_VM_SECTION_END);
qemu_put_be32(f, se->section_id);
- se->save_live_state(f, QEMU_VM_SECTION_END, se->opaque);
+ se->save_live_state(mon, f, QEMU_VM_SECTION_END, se->opaque);
}
QTAILQ_FOREACH(se, &savevm_handlers, entry) {
return 0;
}
-int qemu_savevm_state(QEMUFile *f)
+void qemu_savevm_state_cancel(Monitor *mon, QEMUFile *f)
+{
+ SaveStateEntry *se;
+
+ QTAILQ_FOREACH(se, &savevm_handlers, entry) {
+ if (se->save_live_state) {
+ se->save_live_state(mon, f, -1, se->opaque);
+ }
+ }
+}
+
+static int qemu_savevm_state(Monitor *mon, QEMUFile *f)
{
int saved_vm_running;
int ret;
bdrv_flush_all();
- ret = qemu_savevm_state_begin(f, 0, 0);
+ ret = qemu_savevm_state_begin(mon, f, 0, 0);
if (ret < 0)
goto out;
do {
- ret = qemu_savevm_state_iterate(f);
+ ret = qemu_savevm_state_iterate(mon, f);
if (ret < 0)
goto out;
} while (ret == 0);
- ret = qemu_savevm_state_complete(f);
+ ret = qemu_savevm_state_complete(mon, f);
out:
if (qemu_file_has_error(f))
QTAILQ_FOREACH(se, &savevm_handlers, entry) {
if (!strcmp(se->idstr, idstr) &&
- instance_id == se->instance_id)
+ (instance_id == se->instance_id ||
+ instance_id == se->alias_id))
return se;
}
return NULL;
}
}
+ cpu_synchronize_all_post_init();
+
ret = 0;
out:
return ret;
}
-/* device can contain snapshots */
-static int bdrv_can_snapshot(BlockDriverState *bs)
-{
- return (bs &&
- !bdrv_is_removable(bs) &&
- !bdrv_is_read_only(bs));
-}
-
-/* device must be snapshots in order to have a reliable snapshot */
-static int bdrv_has_snapshot(BlockDriverState *bs)
-{
- return (bs &&
- !bdrv_is_removable(bs) &&
- !bdrv_is_read_only(bs));
-}
-
-static BlockDriverState *get_bs_snapshots(void)
-{
- BlockDriverState *bs;
- DriveInfo *dinfo;
-
- if (bs_snapshots)
- return bs_snapshots;
- QTAILQ_FOREACH(dinfo, &drives, next) {
- bs = dinfo->bdrv;
- if (bdrv_can_snapshot(bs))
- goto ok;
- }
- return NULL;
- ok:
- bs_snapshots = bs;
- return bs;
-}
-
static int bdrv_snapshot_find(BlockDriverState *bs, QEMUSnapshotInfo *sn_info,
const char *name)
{
static int del_existing_snapshots(Monitor *mon, const char *name)
{
BlockDriverState *bs;
- DriveInfo *dinfo;
QEMUSnapshotInfo sn1, *snapshot = &sn1;
int ret;
- QTAILQ_FOREACH(dinfo, &drives, next) {
- bs = dinfo->bdrv;
+ bs = NULL;
+ while ((bs = bdrv_next(bs))) {
if (bdrv_can_snapshot(bs) &&
bdrv_snapshot_find(bs, snapshot, name) >= 0)
{
void do_savevm(Monitor *mon, const QDict *qdict)
{
- DriveInfo *dinfo;
BlockDriverState *bs, *bs1;
QEMUSnapshotInfo sn1, *sn = &sn1, old_sn1, *old_sn = &old_sn1;
int ret;
#endif
const char *name = qdict_get_try_str(qdict, "name");
- bs = get_bs_snapshots();
+ /* Verify if there is a device that doesn't support snapshots and is writable */
+ bs = NULL;
+ while ((bs = bdrv_next(bs))) {
+
+ if (bdrv_is_removable(bs) || bdrv_is_read_only(bs)) {
+ continue;
+ }
+
+ if (!bdrv_can_snapshot(bs)) {
+ monitor_printf(mon, "Device '%s' is writable but does not support snapshots.\n",
+ bdrv_get_device_name(bs));
+ return;
+ }
+ }
+
+ bs = bdrv_snapshots();
if (!bs) {
monitor_printf(mon, "No block device can accept snapshots\n");
return;
}
-
/* ??? Should this occur after vm_stop? */
qemu_aio_flush();
sn->vm_clock_nsec = qemu_get_clock(vm_clock);
/* Delete old snapshots of the same name */
- if (del_existing_snapshots(mon, name) < 0) {
+ if (name && del_existing_snapshots(mon, name) < 0) {
goto the_end;
}
monitor_printf(mon, "Could not open VM state file\n");
goto the_end;
}
- ret = qemu_savevm_state(f);
+ ret = qemu_savevm_state(mon, f);
vm_state_size = qemu_ftell(f);
qemu_fclose(f);
if (ret < 0) {
/* create the snapshots */
- QTAILQ_FOREACH(dinfo, &drives, next) {
- bs1 = dinfo->bdrv;
- if (bdrv_has_snapshot(bs1)) {
+ bs1 = NULL;
+ while ((bs1 = bdrv_next(bs1))) {
+ if (bdrv_can_snapshot(bs1)) {
/* Write VM state size only to the image that contains the state */
sn->vm_state_size = (bs == bs1 ? vm_state_size : 0);
ret = bdrv_snapshot_create(bs1, sn);
vm_start();
}
-int load_vmstate(Monitor *mon, const char *name)
+int load_vmstate(const char *name)
{
- DriveInfo *dinfo;
BlockDriverState *bs, *bs1;
QEMUSnapshotInfo sn;
QEMUFile *f;
int ret;
- bs = get_bs_snapshots();
+ /* Verify if there is a device that doesn't support snapshots and is writable */
+ bs = NULL;
+ while ((bs = bdrv_next(bs))) {
+
+ if (bdrv_is_removable(bs) || bdrv_is_read_only(bs)) {
+ continue;
+ }
+
+ if (!bdrv_can_snapshot(bs)) {
+ error_report("Device '%s' is writable but does not support snapshots.",
+ bdrv_get_device_name(bs));
+ return -ENOTSUP;
+ }
+ }
+
+ bs = bdrv_snapshots();
if (!bs) {
- monitor_printf(mon, "No block device supports snapshots\n");
+ error_report("No block device supports snapshots");
return -EINVAL;
}
/* Flush all IO requests so they don't interfere with the new state. */
qemu_aio_flush();
- QTAILQ_FOREACH(dinfo, &drives, next) {
- bs1 = dinfo->bdrv;
- if (bdrv_has_snapshot(bs1)) {
+ bs1 = NULL;
+ while ((bs1 = bdrv_next(bs1))) {
+ if (bdrv_can_snapshot(bs1)) {
ret = bdrv_snapshot_goto(bs1, name);
if (ret < 0) {
- if (bs != bs1)
- monitor_printf(mon, "Warning: ");
switch(ret) {
case -ENOTSUP:
- monitor_printf(mon,
- "Snapshots not supported on device '%s'\n",
- bdrv_get_device_name(bs1));
+ error_report("%sSnapshots not supported on device '%s'",
+ bs != bs1 ? "Warning: " : "",
+ bdrv_get_device_name(bs1));
break;
case -ENOENT:
- monitor_printf(mon, "Could not find snapshot '%s' on "
- "device '%s'\n",
- name, bdrv_get_device_name(bs1));
+ error_report("%sCould not find snapshot '%s' on device '%s'",
+ bs != bs1 ? "Warning: " : "",
+ name, bdrv_get_device_name(bs1));
break;
default:
- monitor_printf(mon, "Error %d while activating snapshot on"
- " '%s'\n", ret, bdrv_get_device_name(bs1));
+ error_report("%sError %d while activating snapshot on '%s'",
+ bs != bs1 ? "Warning: " : "",
+ ret, bdrv_get_device_name(bs1));
break;
}
/* fatal on snapshot block device */
/* restore the VM state */
f = qemu_fopen_bdrv(bs, 0);
if (!f) {
- monitor_printf(mon, "Could not open VM state file\n");
+ error_report("Could not open VM state file");
return -EINVAL;
}
ret = qemu_loadvm_state(f);
qemu_fclose(f);
if (ret < 0) {
- monitor_printf(mon, "Error %d while loading VM state\n", ret);
+ error_report("Error %d while loading VM state", ret);
return ret;
}
return 0;
void do_delvm(Monitor *mon, const QDict *qdict)
{
- DriveInfo *dinfo;
BlockDriverState *bs, *bs1;
int ret;
const char *name = qdict_get_str(qdict, "name");
- bs = get_bs_snapshots();
+ bs = bdrv_snapshots();
if (!bs) {
monitor_printf(mon, "No block device supports snapshots\n");
return;
}
- QTAILQ_FOREACH(dinfo, &drives, next) {
- bs1 = dinfo->bdrv;
- if (bdrv_has_snapshot(bs1)) {
+ bs1 = NULL;
+ while ((bs1 = bdrv_next(bs1))) {
+ if (bdrv_can_snapshot(bs1)) {
ret = bdrv_snapshot_delete(bs1, name);
if (ret < 0) {
if (ret == -ENOTSUP)
void do_info_snapshots(Monitor *mon)
{
- DriveInfo *dinfo;
BlockDriverState *bs, *bs1;
QEMUSnapshotInfo *sn_tab, *sn;
int nb_sns, i;
char buf[256];
- bs = get_bs_snapshots();
+ bs = bdrv_snapshots();
if (!bs) {
monitor_printf(mon, "No available block device supports snapshots\n");
return;
}
monitor_printf(mon, "Snapshot devices:");
- QTAILQ_FOREACH(dinfo, &drives, next) {
- bs1 = dinfo->bdrv;
- if (bdrv_has_snapshot(bs1)) {
+ bs1 = NULL;
+ while ((bs1 = bdrv_next(bs1))) {
+ if (bdrv_can_snapshot(bs1)) {
if (bs == bs1)
monitor_printf(mon, " %s", bdrv_get_device_name(bs1));
}