#include "migration.h"
#include "qemu_socket.h"
#include "qemu-queue.h"
+#include "qemu-timer.h"
#include "cpus.h"
+#include "memory.h"
#define SELF_ANNOUNCE_ROUNDS 5
int buf_size; /* 0 when writing */
uint8_t buf[IO_BUF_SIZE];
- int has_error;
+ int last_error;
};
typedef struct QEMUFileStdio
ssize_t len;
do {
- len = recv(s->fd, (void *)buf, size, 0);
+ len = qemu_recv(s->fd, buf, size, 0);
} while (len == -1 && socket_error() == EINTR);
if (len == -1)
static int socket_close(void *opaque)
{
QEMUFileSocket *s = opaque;
- qemu_free(s);
+ g_free(s);
return 0;
}
QEMUFileStdio *s = opaque;
int ret;
ret = pclose(s->stdio_file);
- qemu_free(s);
+ if (ret == -1) {
+ ret = -errno;
+ }
+ g_free(s);
return ret;
}
static int stdio_fclose(void *opaque)
{
QEMUFileStdio *s = opaque;
- fclose(s->stdio_file);
- qemu_free(s);
- return 0;
+ int ret = 0;
+ if (fclose(s->stdio_file) == EOF) {
+ ret = -errno;
+ }
+ g_free(s);
+ return ret;
}
QEMUFile *qemu_popen(FILE *stdio_file, const char *mode)
return NULL;
}
- s = qemu_mallocz(sizeof(QEMUFileStdio));
+ s = g_malloc0(sizeof(QEMUFileStdio));
s->stdio_file = stdio_file;
return NULL;
}
- s = qemu_mallocz(sizeof(QEMUFileStdio));
+ s = g_malloc0(sizeof(QEMUFileStdio));
s->stdio_file = fdopen(fd, mode);
if (!s->stdio_file)
goto fail;
return s->file;
fail:
- qemu_free(s);
+ g_free(s);
return NULL;
}
QEMUFile *qemu_fopen_socket(int fd)
{
- QEMUFileSocket *s = qemu_mallocz(sizeof(QEMUFileSocket));
+ QEMUFileSocket *s = g_malloc0(sizeof(QEMUFileSocket));
s->fd = fd;
s->file = qemu_fopen_ops(s, NULL, socket_get_buffer, socket_close,
return NULL;
}
- s = qemu_mallocz(sizeof(QEMUFileStdio));
+ s = g_malloc0(sizeof(QEMUFileStdio));
s->stdio_file = fopen(filename, mode);
if (!s->stdio_file)
}
return s->file;
fail:
- qemu_free(s);
+ g_free(s);
return NULL;
}
{
QEMUFile *f;
- f = qemu_mallocz(sizeof(QEMUFile));
+ f = g_malloc0(sizeof(QEMUFile));
f->opaque = opaque;
f->put_buffer = put_buffer;
return f;
}
-int qemu_file_has_error(QEMUFile *f)
+int qemu_file_get_error(QEMUFile *f)
{
- return f->has_error;
+ return f->last_error;
}
-void qemu_file_set_error(QEMUFile *f)
+void qemu_file_set_error(QEMUFile *f, int ret)
+{
+ f->last_error = ret;
+}
+
+/** Sets last_error conditionally
+ *
+ * Sets last_error only if ret is negative _and_ no error
+ * was set before.
+ */
+static void qemu_file_set_if_error(QEMUFile *f, int ret)
{
- f->has_error = 1;
+ if (ret < 0 && !f->last_error) {
+ qemu_file_set_error(f, ret);
+ }
}
+/** Flushes QEMUFile buffer
+ *
+ * In case of error, last_error is set.
+ */
void qemu_fflush(QEMUFile *f)
{
if (!f->put_buffer)
if (len > 0)
f->buf_offset += f->buf_index;
else
- f->has_error = 1;
+ qemu_file_set_error(f, -EINVAL);
f->buf_index = 0;
}
}
static void qemu_fill_buffer(QEMUFile *f)
{
int len;
+ int pending;
if (!f->get_buffer)
return;
if (f->is_write)
abort();
- len = f->get_buffer(f->opaque, f->buf, f->buf_offset, IO_BUF_SIZE);
+ pending = f->buf_size - f->buf_index;
+ if (pending > 0) {
+ memmove(f->buf, f->buf + f->buf_index, pending);
+ }
+ f->buf_index = 0;
+ f->buf_size = pending;
+
+ len = f->get_buffer(f->opaque, f->buf + pending, f->buf_offset,
+ IO_BUF_SIZE - pending);
if (len > 0) {
- f->buf_index = 0;
- f->buf_size = len;
+ f->buf_size += len;
f->buf_offset += len;
+ } else if (len == 0) {
+ f->last_error = -EIO;
} else if (len != -EAGAIN)
- f->has_error = 1;
+ qemu_file_set_error(f, len);
}
-int qemu_fclose(QEMUFile *f)
+/** Calls close function and set last_error if needed
+ *
+ * Internal function. qemu_fflush() must be called before this.
+ *
+ * Returns f->close() return value, or 0 if close function is not set.
+ */
+static int qemu_close(QEMUFile *f)
{
int ret = 0;
- qemu_fflush(f);
- if (f->close)
+ if (f->close) {
ret = f->close(f->opaque);
- qemu_free(f);
+ qemu_file_set_if_error(f, ret);
+ }
+ return ret;
+}
+
+/** Closes the file
+ *
+ * Returns negative error value if any error happened on previous operations or
+ * while closing the file. Returns 0 or positive number on success.
+ *
+ * The meaning of return value on success depends on the specific backend
+ * being used.
+ */
+int qemu_fclose(QEMUFile *f)
+{
+ int ret;
+ qemu_fflush(f);
+ ret = qemu_close(f);
+ /* If any error was spotted before closing, we should report it
+ * instead of the close() return value.
+ */
+ if (f->last_error) {
+ ret = f->last_error;
+ }
+ g_free(f);
return ret;
}
{
int l;
- if (!f->has_error && f->is_write == 0 && f->buf_index > 0) {
+ if (!f->last_error && f->is_write == 0 && f->buf_index > 0) {
fprintf(stderr,
"Attempted to write to buffer while read buffer is not empty\n");
abort();
}
- while (!f->has_error && size > 0) {
+ while (!f->last_error && size > 0) {
l = IO_BUF_SIZE - f->buf_index;
if (l > size)
l = size;
void qemu_put_byte(QEMUFile *f, int v)
{
- if (!f->has_error && f->is_write == 0 && f->buf_index > 0) {
+ if (!f->last_error && f->is_write == 0 && f->buf_index > 0) {
fprintf(stderr,
"Attempted to write to buffer while read buffer is not empty\n");
abort();
qemu_fflush(f);
}
-int qemu_get_buffer(QEMUFile *f, uint8_t *buf, int size1)
+static void qemu_file_skip(QEMUFile *f, int size)
{
- int size, l;
+ if (f->buf_index + size <= f->buf_size) {
+ f->buf_index += size;
+ }
+}
- if (f->is_write)
+static int qemu_peek_buffer(QEMUFile *f, uint8_t *buf, int size, size_t offset)
+{
+ int pending;
+ int index;
+
+ if (f->is_write) {
abort();
+ }
- size = size1;
- while (size > 0) {
- l = f->buf_size - f->buf_index;
- if (l == 0) {
- qemu_fill_buffer(f);
- l = f->buf_size - f->buf_index;
- if (l == 0)
- break;
+ index = f->buf_index + offset;
+ pending = f->buf_size - index;
+ if (pending < size) {
+ qemu_fill_buffer(f);
+ index = f->buf_index + offset;
+ pending = f->buf_size - index;
+ }
+
+ if (pending <= 0) {
+ return 0;
+ }
+ if (size > pending) {
+ size = pending;
+ }
+
+ memcpy(buf, f->buf + index, size);
+ return size;
+}
+
+int qemu_get_buffer(QEMUFile *f, uint8_t *buf, int size)
+{
+ int pending = size;
+ int done = 0;
+
+ while (pending > 0) {
+ int res;
+
+ res = qemu_peek_buffer(f, buf, pending, 0);
+ if (res == 0) {
+ return done;
}
- if (l > size)
- l = size;
- memcpy(buf, f->buf + f->buf_index, l);
- f->buf_index += l;
- buf += l;
- size -= l;
+ qemu_file_skip(f, res);
+ buf += res;
+ pending -= res;
+ done += res;
}
- return size1 - size;
+ return done;
}
-static int qemu_peek_byte(QEMUFile *f)
+static int qemu_peek_byte(QEMUFile *f, int offset)
{
- if (f->is_write)
+ int index = f->buf_index + offset;
+
+ if (f->is_write) {
abort();
+ }
- if (f->buf_index >= f->buf_size) {
+ if (index >= f->buf_size) {
qemu_fill_buffer(f);
- if (f->buf_index >= f->buf_size)
+ index = f->buf_index + offset;
+ if (index >= f->buf_size) {
return 0;
+ }
}
- return f->buf[f->buf_index];
+ return f->buf[index];
}
int qemu_get_byte(QEMUFile *f)
{
- if (f->is_write)
- abort();
+ int result;
- if (f->buf_index >= f->buf_size) {
- qemu_fill_buffer(f);
- if (f->buf_index >= f->buf_size)
- return 0;
- }
- return f->buf[f->buf_index++];
+ result = qemu_peek_byte(f, 0);
+ qemu_file_skip(f, 1);
+ return result;
}
int64_t qemu_ftell(QEMUFile *f)
return v;
}
+
+/* timer */
+
+void qemu_put_timer(QEMUFile *f, QEMUTimer *ts)
+{
+ uint64_t expire_time;
+
+ expire_time = qemu_timer_expire_time_ns(ts);
+ qemu_put_be64(f, expire_time);
+}
+
+void qemu_get_timer(QEMUFile *f, QEMUTimer *ts)
+{
+ uint64_t expire_time;
+
+ expire_time = qemu_get_be64(f);
+ if (expire_time != -1) {
+ qemu_mod_timer_ns(ts, expire_time);
+ } else {
+ qemu_del_timer(ts);
+ }
+}
+
+
/* bool */
static int get_bool(QEMUFile *f, void *pv, size_t size)
{
SaveStateEntry *se;
- se = qemu_mallocz(sizeof(SaveStateEntry));
+ se = g_malloc0(sizeof(SaveStateEntry));
se->version_id = version_id;
se->section_id = global_section_id++;
se->set_params = set_params;
if (id) {
pstrcpy(se->idstr, sizeof(se->idstr), id);
pstrcat(se->idstr, sizeof(se->idstr), "/");
- qemu_free(id);
+ g_free(id);
- se->compat = qemu_mallocz(sizeof(CompatEntry));
+ se->compat = g_malloc0(sizeof(CompatEntry));
pstrcpy(se->compat->idstr, sizeof(se->compat->idstr), idstr);
se->compat->instance_id = instance_id == -1 ?
calculate_compat_instance_id(idstr) : instance_id;
if (path) {
pstrcpy(id, sizeof(id), path);
pstrcat(id, sizeof(id), "/");
- qemu_free(path);
+ g_free(path);
}
}
pstrcat(id, sizeof(id), idstr);
if (strcmp(se->idstr, id) == 0 && se->opaque == opaque) {
QTAILQ_REMOVE(&savevm_handlers, se, entry);
if (se->compat) {
- qemu_free(se->compat);
+ g_free(se->compat);
}
- qemu_free(se);
- }
- }
-}
-
-/* mark a device as not to be migrated, that is the device should be
- unplugged before migration */
-void register_device_unmigratable(DeviceState *dev, const char *idstr,
- void *opaque)
-{
- SaveStateEntry *se;
- char id[256] = "";
-
- if (dev && dev->parent_bus && dev->parent_bus->info->get_dev_path) {
- char *path = dev->parent_bus->info->get_dev_path(dev);
- if (path) {
- pstrcpy(id, sizeof(id), path);
- pstrcat(id, sizeof(id), "/");
- qemu_free(path);
- }
- }
- pstrcat(id, sizeof(id), idstr);
-
- QTAILQ_FOREACH(se, &savevm_handlers, entry) {
- if (strcmp(se->idstr, id) == 0 && se->opaque == opaque) {
- se->no_migrate = 1;
+ g_free(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));
+ se = g_malloc0(sizeof(SaveStateEntry));
se->version_id = vmsd->version_id;
se->section_id = global_section_id++;
se->save_live_state = NULL;
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);
if (id) {
pstrcpy(se->idstr, sizeof(se->idstr), id);
pstrcat(se->idstr, sizeof(se->idstr), "/");
- qemu_free(id);
+ g_free(id);
- se->compat = qemu_mallocz(sizeof(CompatEntry));
+ se->compat = g_malloc0(sizeof(CompatEntry));
pstrcpy(se->compat->idstr, sizeof(se->compat->idstr), vmsd->name);
se->compat->instance_id = instance_id == -1 ?
calculate_compat_instance_id(vmsd->name) : instance_id;
if (se->vmsd == vmsd && se->opaque == opaque) {
QTAILQ_REMOVE(&savevm_handlers, se, entry);
if (se->compat) {
- qemu_free(se->compat);
+ g_free(se->compat);
}
- qemu_free(se);
+ g_free(se);
}
}
}
int shared)
{
SaveStateEntry *se;
+ int ret;
QTAILQ_FOREACH(se, &savevm_handlers, entry) {
if(se->set_params == NULL) {
qemu_put_be32(f, se->instance_id);
qemu_put_be32(f, se->version_id);
- se->save_live_state(mon, f, QEMU_VM_SECTION_START, se->opaque);
+ ret = se->save_live_state(mon, f, QEMU_VM_SECTION_START, se->opaque);
+ if (ret < 0) {
+ qemu_savevm_state_cancel(mon, f);
+ return ret;
+ }
}
-
- if (qemu_file_has_error(f)) {
+ ret = qemu_file_get_error(f);
+ if (ret != 0) {
qemu_savevm_state_cancel(mon, f);
- return -EIO;
}
- return 0;
+ return ret;
+
}
+/*
+ * this function has three return values:
+ * negative: there was one error, and we have -errno.
+ * 0 : We haven't finished, caller have to go again
+ * 1 : We have finished, we can go to complete phase
+ */
int qemu_savevm_state_iterate(Monitor *mon, QEMUFile *f)
{
SaveStateEntry *se;
qemu_put_be32(f, se->section_id);
ret = se->save_live_state(mon, f, QEMU_VM_SECTION_PART, se->opaque);
- if (!ret) {
+ if (ret <= 0) {
/* 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
break;
}
}
-
- if (ret)
- return 1;
-
- if (qemu_file_has_error(f)) {
+ if (ret != 0) {
+ return ret;
+ }
+ ret = qemu_file_get_error(f);
+ if (ret != 0) {
qemu_savevm_state_cancel(mon, f);
- return -EIO;
}
-
- return 0;
+ return ret;
}
int qemu_savevm_state_complete(Monitor *mon, QEMUFile *f)
{
SaveStateEntry *se;
+ int ret;
cpu_synchronize_all_states();
qemu_put_byte(f, QEMU_VM_SECTION_END);
qemu_put_be32(f, se->section_id);
- se->save_live_state(mon, f, QEMU_VM_SECTION_END, se->opaque);
+ ret = se->save_live_state(mon, f, QEMU_VM_SECTION_END, se->opaque);
+ if (ret < 0) {
+ return ret;
+ }
}
QTAILQ_FOREACH(se, &savevm_handlers, entry) {
qemu_put_byte(f, QEMU_VM_EOF);
- if (qemu_file_has_error(f))
- return -EIO;
-
- return 0;
+ return qemu_file_get_error(f);
}
void qemu_savevm_state_cancel(Monitor *mon, QEMUFile *f)
static int qemu_savevm_state(Monitor *mon, QEMUFile *f)
{
- int saved_vm_running;
int ret;
- saved_vm_running = vm_running;
- vm_stop(VMSTOP_SAVEVM);
-
if (qemu_savevm_state_blocked(mon)) {
ret = -EINVAL;
goto out;
ret = qemu_savevm_state_complete(mon, f);
out:
- if (qemu_file_has_error(f))
- ret = -EIO;
-
- if (!ret && saved_vm_running)
- vm_start();
+ if (ret == 0) {
+ ret = qemu_file_get_error(f);
+ }
return ret;
}
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) {
+ while (qemu_peek_byte(f, 0) == QEMU_VM_SUBSECTION) {
char idstr[256];
int ret;
- uint8_t version_id, len;
+ uint8_t version_id, len, size;
const VMStateDescription *sub_vmsd;
- 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);
+ len = qemu_peek_byte(f, 1);
+ if (len < strlen(vmsd->name) + 1) {
+ /* subsection name has be be "section_name/a" */
+ return 0;
+ }
+ size = qemu_peek_buffer(f, (uint8_t *)idstr, len, 2);
+ if (size != len) {
+ return 0;
+ }
+ idstr[size] = 0;
- sub_vmsd = vmstate_get_subsection(sub, idstr);
+ if (strncmp(vmsd->name, idstr, strlen(vmsd->name)) != 0) {
+ /* it don't have a valid subsection name */
+ return 0;
+ }
+ sub_vmsd = vmstate_get_subsection(vmsd->subsections, idstr);
if (sub_vmsd == NULL) {
return -ENOENT;
}
- assert(!sub_vmsd->subsections);
+ qemu_file_skip(f, 1); /* subsection */
+ qemu_file_skip(f, 1); /* len */
+ qemu_file_skip(f, len); /* idstr */
+ version_id = qemu_get_be32(f);
+
ret = vmstate_load_state(f, sub_vmsd, opaque, version_id);
if (ret) {
return ret;
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++;
}
/* Add entry */
- le = qemu_mallocz(sizeof(*le));
+ le = g_malloc0(sizeof(*le));
le->se = se;
le->section_id = section_id;
out:
QLIST_FOREACH_SAFE(le, &loadvm_handlers, entry, new_le) {
QLIST_REMOVE(le, entry);
- qemu_free(le);
+ g_free(le);
}
- if (qemu_file_has_error(f))
- ret = -EIO;
+ if (ret == 0) {
+ ret = qemu_file_get_error(f);
+ }
return ret;
}
break;
}
}
- qemu_free(sn_tab);
+ g_free(sn_tab);
return ret;
}
int ret;
QEMUFile *f;
int saved_vm_running;
- uint32_t vm_state_size;
+ uint64_t vm_state_size;
#ifdef _WIN32
struct _timeb tb;
struct tm *ptm;
bs = NULL;
while ((bs = bdrv_next(bs))) {
- if (bdrv_is_removable(bs) || bdrv_is_read_only(bs)) {
+ if (!bdrv_is_inserted(bs) || bdrv_is_read_only(bs)) {
continue;
}
return;
}
- saved_vm_running = vm_running;
- vm_stop(VMSTOP_SAVEVM);
+ saved_vm_running = runstate_is_running();
+ vm_stop(RUN_STATE_SAVE_VM);
memset(sn, 0, sizeof(*sn));
bs = NULL;
while ((bs = bdrv_next(bs))) {
- if (bdrv_is_removable(bs) || bdrv_is_read_only(bs)) {
+ if (!bdrv_is_inserted(bs) || bdrv_is_read_only(bs)) {
continue;
}
}
/* Flush all IO requests so they don't interfere with the new state. */
- qemu_aio_flush();
+ bdrv_drain_all();
bs = NULL;
while ((bs = bdrv_next(bs))) {
return;
}
- available_snapshots = qemu_mallocz(sizeof(int) * nb_sns);
+ available_snapshots = g_malloc0(sizeof(int) * nb_sns);
total = 0;
for (i = 0; i < nb_sns; i++) {
sn = &sn_tab[i];
monitor_printf(mon, "There is no suitable snapshot available\n");
}
- qemu_free(sn_tab);
- qemu_free(available_snapshots);
+ g_free(sn_tab);
+ g_free(available_snapshots);
}
+
+void vmstate_register_ram(MemoryRegion *mr, DeviceState *dev)
+{
+ qemu_ram_set_idstr(memory_region_get_ram_addr(mr) & TARGET_PAGE_MASK,
+ memory_region_name(mr), dev);
+}
+
+void vmstate_unregister_ram(MemoryRegion *mr, DeviceState *dev)
+{
+ /* Nothing do to while the implementation is in RAMBlock */
+}
+
+void vmstate_register_ram_global(MemoryRegion *mr)
+{
+ vmstate_register_ram(mr, NULL);
+}