aboutsummaryrefslogtreecommitdiff
path: root/savevm.c
diff options
context:
space:
mode:
Diffstat (limited to 'savevm.c')
-rw-r--r--savevm.c331
1 files changed, 188 insertions, 143 deletions
diff --git a/savevm.c b/savevm.c
index c7fe283..5d04d59 100644
--- a/savevm.c
+++ b/savevm.c
@@ -86,6 +86,7 @@
#include "memory.h"
#include "qmp-commands.h"
#include "trace.h"
+#include "bitops.h"
#define SELF_ANNOUNCE_ROUNDS 5
@@ -162,12 +163,7 @@ void qemu_announce_self(void)
#define IO_BUF_SIZE 32768
struct QEMUFile {
- QEMUFilePutBufferFunc *put_buffer;
- QEMUFileGetBufferFunc *get_buffer;
- QEMUFileCloseFunc *close;
- QEMUFileRateLimit *rate_limit;
- QEMUFileSetRateLimit *set_rate_limit;
- QEMUFileGetRateLimit *get_rate_limit;
+ const QEMUFileOps *ops;
void *opaque;
int is_write;
@@ -192,28 +188,52 @@ typedef struct QEMUFileSocket
QEMUFile *file;
} QEMUFileSocket;
+static int socket_get_fd(void *opaque)
+{
+ QEMUFileSocket *s = opaque;
+
+ return s->fd;
+}
+
static int socket_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
{
QEMUFileSocket *s = opaque;
ssize_t len;
- do {
+ for (;;) {
len = qemu_recv(s->fd, buf, size, 0);
- } while (len == -1 && socket_error() == EINTR);
+ if (len != -1) {
+ break;
+ }
+ if (socket_error() == EAGAIN) {
+ assert(qemu_in_coroutine());
+ qemu_coroutine_yield();
+ } else if (socket_error() != EINTR) {
+ break;
+ }
+ }
- if (len == -1)
+ if (len == -1) {
len = -socket_error();
-
+ }
return len;
}
static int socket_close(void *opaque)
{
QEMUFileSocket *s = opaque;
+ closesocket(s->fd);
g_free(s);
return 0;
}
+static int stdio_get_fd(void *opaque)
+{
+ QEMUFileStdio *s = opaque;
+
+ return fileno(s->stdio_file);
+}
+
static int stdio_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, int size)
{
QEMUFileStdio *s = opaque;
@@ -226,10 +246,19 @@ static int stdio_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
FILE *fp = s->stdio_file;
int bytes;
- do {
+ for (;;) {
clearerr(fp);
bytes = fread(buf, 1, size, fp);
- } while ((bytes == 0) && ferror(fp) && (errno == EINTR));
+ if (bytes != 0 || !ferror(fp)) {
+ break;
+ }
+ if (errno == EAGAIN) {
+ assert(qemu_in_coroutine());
+ qemu_coroutine_yield();
+ } else if (errno != EINTR) {
+ break;
+ }
+ }
return bytes;
}
@@ -256,6 +285,18 @@ static int stdio_fclose(void *opaque)
return ret;
}
+static const QEMUFileOps stdio_pipe_read_ops = {
+ .get_fd = stdio_get_fd,
+ .get_buffer = stdio_get_buffer,
+ .close = stdio_pclose
+};
+
+static const QEMUFileOps stdio_pipe_write_ops = {
+ .get_fd = stdio_get_fd,
+ .put_buffer = stdio_put_buffer,
+ .close = stdio_pclose
+};
+
QEMUFile *qemu_popen(FILE *stdio_file, const char *mode)
{
QEMUFileStdio *s;
@@ -270,11 +311,9 @@ QEMUFile *qemu_popen(FILE *stdio_file, const char *mode)
s->stdio_file = stdio_file;
if(mode[0] == 'r') {
- s->file = qemu_fopen_ops(s, NULL, stdio_get_buffer, stdio_pclose,
- NULL, NULL, NULL);
+ s->file = qemu_fopen_ops(s, &stdio_pipe_read_ops);
} else {
- s->file = qemu_fopen_ops(s, stdio_put_buffer, NULL, stdio_pclose,
- NULL, NULL, NULL);
+ s->file = qemu_fopen_ops(s, &stdio_pipe_write_ops);
}
return s->file;
}
@@ -291,16 +330,17 @@ QEMUFile *qemu_popen_cmd(const char *command, const char *mode)
return qemu_popen(popen_file, mode);
}
-int qemu_stdio_fd(QEMUFile *f)
-{
- QEMUFileStdio *p;
- int fd;
-
- p = (QEMUFileStdio *)f->opaque;
- fd = fileno(p->stdio_file);
+static const QEMUFileOps stdio_file_read_ops = {
+ .get_fd = stdio_get_fd,
+ .get_buffer = stdio_get_buffer,
+ .close = stdio_fclose
+};
- return fd;
-}
+static const QEMUFileOps stdio_file_write_ops = {
+ .get_fd = stdio_get_fd,
+ .put_buffer = stdio_put_buffer,
+ .close = stdio_fclose
+};
QEMUFile *qemu_fdopen(int fd, const char *mode)
{
@@ -319,11 +359,9 @@ QEMUFile *qemu_fdopen(int fd, const char *mode)
goto fail;
if(mode[0] == 'r') {
- s->file = qemu_fopen_ops(s, NULL, stdio_get_buffer, stdio_fclose,
- NULL, NULL, NULL);
+ s->file = qemu_fopen_ops(s, &stdio_file_read_ops);
} else {
- s->file = qemu_fopen_ops(s, stdio_put_buffer, NULL, stdio_fclose,
- NULL, NULL, NULL);
+ s->file = qemu_fopen_ops(s, &stdio_file_write_ops);
}
return s->file;
@@ -332,31 +370,21 @@ fail:
return NULL;
}
+static const QEMUFileOps socket_read_ops = {
+ .get_fd = socket_get_fd,
+ .get_buffer = socket_get_buffer,
+ .close = socket_close
+};
+
QEMUFile *qemu_fopen_socket(int fd)
{
QEMUFileSocket *s = g_malloc0(sizeof(QEMUFileSocket));
s->fd = fd;
- s->file = qemu_fopen_ops(s, NULL, socket_get_buffer, socket_close,
- NULL, NULL, NULL);
+ s->file = qemu_fopen_ops(s, &socket_read_ops);
return s->file;
}
-static int file_put_buffer(void *opaque, const uint8_t *buf,
- int64_t pos, int size)
-{
- QEMUFileStdio *s = opaque;
- fseek(s->stdio_file, pos, SEEK_SET);
- return fwrite(buf, 1, size, s->stdio_file);
-}
-
-static int file_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
-{
- QEMUFileStdio *s = opaque;
- fseek(s->stdio_file, pos, SEEK_SET);
- return fread(buf, 1, size, s->stdio_file);
-}
-
QEMUFile *qemu_fopen(const char *filename, const char *mode)
{
QEMUFileStdio *s;
@@ -375,11 +403,9 @@ QEMUFile *qemu_fopen(const char *filename, const char *mode)
goto fail;
if(mode[0] == 'w') {
- s->file = qemu_fopen_ops(s, file_put_buffer, NULL, stdio_fclose,
- NULL, NULL, NULL);
+ s->file = qemu_fopen_ops(s, &stdio_file_write_ops);
} else {
- s->file = qemu_fopen_ops(s, NULL, file_get_buffer, stdio_fclose,
- NULL, NULL, NULL);
+ s->file = qemu_fopen_ops(s, &stdio_file_read_ops);
}
return s->file;
fail:
@@ -404,32 +430,31 @@ static int bdrv_fclose(void *opaque)
return bdrv_flush(opaque);
}
+static const QEMUFileOps bdrv_read_ops = {
+ .get_buffer = block_get_buffer,
+ .close = bdrv_fclose
+};
+
+static const QEMUFileOps bdrv_write_ops = {
+ .put_buffer = block_put_buffer,
+ .close = bdrv_fclose
+};
+
static QEMUFile *qemu_fopen_bdrv(BlockDriverState *bs, int is_writable)
{
if (is_writable)
- return qemu_fopen_ops(bs, block_put_buffer, NULL, bdrv_fclose,
- NULL, NULL, NULL);
- return qemu_fopen_ops(bs, NULL, block_get_buffer, bdrv_fclose, NULL, NULL, NULL);
+ return qemu_fopen_ops(bs, &bdrv_write_ops);
+ return qemu_fopen_ops(bs, &bdrv_read_ops);
}
-QEMUFile *qemu_fopen_ops(void *opaque, QEMUFilePutBufferFunc *put_buffer,
- QEMUFileGetBufferFunc *get_buffer,
- QEMUFileCloseFunc *close,
- QEMUFileRateLimit *rate_limit,
- QEMUFileSetRateLimit *set_rate_limit,
- QEMUFileGetRateLimit *get_rate_limit)
+QEMUFile *qemu_fopen_ops(void *opaque, const QEMUFileOps *ops)
{
QEMUFile *f;
f = g_malloc0(sizeof(QEMUFile));
f->opaque = opaque;
- f->put_buffer = put_buffer;
- f->get_buffer = get_buffer;
- f->close = close;
- f->rate_limit = rate_limit;
- f->set_rate_limit = set_rate_limit;
- f->get_rate_limit = get_rate_limit;
+ f->ops = ops;
f->is_write = 0;
return f;
@@ -440,42 +465,29 @@ int qemu_file_get_error(QEMUFile *f)
return f->last_error;
}
-void qemu_file_set_error(QEMUFile *f, int ret)
+static 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)
-{
- 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)
+static int qemu_fflush(QEMUFile *f)
{
- if (!f->put_buffer)
- return;
+ int ret = 0;
- if (f->is_write && f->buf_index > 0) {
- int len;
+ if (!f->ops->put_buffer)
+ return 0;
- len = f->put_buffer(f->opaque, f->buf, f->buf_offset, f->buf_index);
- if (len > 0)
+ if (f->is_write && f->buf_index > 0) {
+ ret = f->ops->put_buffer(f->opaque, f->buf, f->buf_offset, f->buf_index);
+ if (ret >= 0) {
f->buf_offset += f->buf_index;
- else
- qemu_file_set_error(f, -EINVAL);
+ }
f->buf_index = 0;
}
+ return ret;
}
static void qemu_fill_buffer(QEMUFile *f)
@@ -483,7 +495,7 @@ static void qemu_fill_buffer(QEMUFile *f)
int len;
int pending;
- if (!f->get_buffer)
+ if (!f->ops->get_buffer)
return;
if (f->is_write)
@@ -496,31 +508,23 @@ static void qemu_fill_buffer(QEMUFile *f)
f->buf_index = 0;
f->buf_size = pending;
- len = f->get_buffer(f->opaque, f->buf + pending, f->buf_offset,
+ len = f->ops->get_buffer(f->opaque, f->buf + pending, f->buf_offset,
IO_BUF_SIZE - pending);
if (len > 0) {
f->buf_size += len;
f->buf_offset += len;
} else if (len == 0) {
- f->last_error = -EIO;
+ qemu_file_set_error(f, -EIO);
} else if (len != -EAGAIN)
qemu_file_set_error(f, len);
}
-/** 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_fclose_internal(QEMUFile *f)
+int qemu_get_fd(QEMUFile *f)
{
- int ret = 0;
- if (f->close) {
- ret = f->close(f->opaque);
- qemu_file_set_if_error(f, ret);
+ if (f->ops->get_fd) {
+ return f->ops->get_fd(f->opaque);
}
- return ret;
+ return -1;
}
/** Closes the file
@@ -534,8 +538,14 @@ static int qemu_fclose_internal(QEMUFile *f)
int qemu_fclose(QEMUFile *f)
{
int ret;
- qemu_fflush(f);
- ret = qemu_fclose_internal(f);
+ ret = qemu_fflush(f);
+
+ if (f->ops->close) {
+ int ret2 = f->ops->close(f->opaque);
+ if (ret >= 0) {
+ ret = ret2;
+ }
+ }
/* If any error was spotted before closing, we should report it
* instead of the close() return value.
*/
@@ -546,22 +556,26 @@ int qemu_fclose(QEMUFile *f)
return ret;
}
-void qemu_file_put_notify(QEMUFile *f)
+int qemu_file_put_notify(QEMUFile *f)
{
- f->put_buffer(f->opaque, NULL, 0, 0);
+ return f->ops->put_buffer(f->opaque, NULL, 0, 0);
}
void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size)
{
int l;
- if (!f->last_error && f->is_write == 0 && f->buf_index > 0) {
+ if (f->last_error) {
+ return;
+ }
+
+ if (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->last_error && size > 0) {
+ while (size > 0) {
l = IO_BUF_SIZE - f->buf_index;
if (l > size)
l = size;
@@ -570,14 +584,23 @@ void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size)
f->buf_index += l;
buf += l;
size -= l;
- if (f->buf_index >= IO_BUF_SIZE)
- qemu_fflush(f);
+ if (f->buf_index >= IO_BUF_SIZE) {
+ int ret = qemu_fflush(f);
+ if (ret < 0) {
+ qemu_file_set_error(f, ret);
+ break;
+ }
+ }
}
}
void qemu_put_byte(QEMUFile *f, int v)
{
- if (!f->last_error && f->is_write == 0 && f->buf_index > 0) {
+ if (f->last_error) {
+ return;
+ }
+
+ if (f->is_write == 0 && f->buf_index > 0) {
fprintf(stderr,
"Attempted to write to buffer while read buffer is not empty\n");
abort();
@@ -585,8 +608,12 @@ void qemu_put_byte(QEMUFile *f, int v)
f->buf[f->buf_index++] = v;
f->is_write = 1;
- if (f->buf_index >= IO_BUF_SIZE)
- qemu_fflush(f);
+ if (f->buf_index >= IO_BUF_SIZE) {
+ int ret = qemu_fflush(f);
+ if (ret < 0) {
+ qemu_file_set_error(f, ret);
+ }
+ }
}
static void qemu_file_skip(QEMUFile *f, int size)
@@ -671,44 +698,23 @@ int qemu_get_byte(QEMUFile *f)
return result;
}
-int64_t qemu_ftell(QEMUFile *f)
+static int64_t qemu_ftell(QEMUFile *f)
{
return f->buf_offset - f->buf_size + f->buf_index;
}
-int64_t qemu_fseek(QEMUFile *f, int64_t pos, int whence)
-{
- if (whence == SEEK_SET) {
- /* nothing to do */
- } else if (whence == SEEK_CUR) {
- pos += qemu_ftell(f);
- } else {
- /* SEEK_END not supported */
- return -1;
- }
- if (f->put_buffer) {
- qemu_fflush(f);
- f->buf_offset = pos;
- } else {
- f->buf_offset = pos;
- f->buf_index = 0;
- f->buf_size = 0;
- }
- return pos;
-}
-
int qemu_file_rate_limit(QEMUFile *f)
{
- if (f->rate_limit)
- return f->rate_limit(f->opaque);
+ if (f->ops->rate_limit)
+ return f->ops->rate_limit(f->opaque);
return 0;
}
int64_t qemu_file_get_rate_limit(QEMUFile *f)
{
- if (f->get_rate_limit)
- return f->get_rate_limit(f->opaque);
+ if (f->ops->get_rate_limit)
+ return f->ops->get_rate_limit(f->opaque);
return 0;
}
@@ -717,8 +723,8 @@ 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 */
- if (f && f->set_rate_limit)
- return f->set_rate_limit(f->opaque, new_rate);
+ if (f && f->ops->set_rate_limit)
+ return f->ops->set_rate_limit(f->opaque, new_rate);
return 0;
}
@@ -1159,6 +1165,46 @@ const VMStateInfo vmstate_info_unused_buffer = {
.put = put_unused_buffer,
};
+/* bitmaps (as defined by bitmap.h). Note that size here is the size
+ * of the bitmap in bits. The on-the-wire format of a bitmap is 64
+ * bit words with the bits in big endian order. The in-memory format
+ * is an array of 'unsigned long', which may be either 32 or 64 bits.
+ */
+/* This is the number of 64 bit words sent over the wire */
+#define BITS_TO_U64S(nr) DIV_ROUND_UP(nr, 64)
+static int get_bitmap(QEMUFile *f, void *pv, size_t size)
+{
+ unsigned long *bmp = pv;
+ int i, idx = 0;
+ for (i = 0; i < BITS_TO_U64S(size); i++) {
+ uint64_t w = qemu_get_be64(f);
+ bmp[idx++] = w;
+ if (sizeof(unsigned long) == 4 && idx < BITS_TO_LONGS(size)) {
+ bmp[idx++] = w >> 32;
+ }
+ }
+ return 0;
+}
+
+static void put_bitmap(QEMUFile *f, void *pv, size_t size)
+{
+ unsigned long *bmp = pv;
+ int i, idx = 0;
+ for (i = 0; i < BITS_TO_U64S(size); i++) {
+ uint64_t w = bmp[idx++];
+ if (sizeof(unsigned long) == 4 && idx < BITS_TO_LONGS(size)) {
+ w |= ((uint64_t)bmp[idx++]) << 32;
+ }
+ qemu_put_be64(f, w);
+ }
+}
+
+const VMStateInfo vmstate_info_bitmap = {
+ .name = "bitmap",
+ .get = get_bitmap,
+ .put = put_bitmap,
+};
+
typedef struct CompatEntry {
char idstr[256];
int instance_id;
@@ -2201,7 +2247,6 @@ void qmp_xen_save_devices_state(const char *filename, Error **errp)
the_end:
if (saved_vm_running)
vm_start();
- return;
}
int load_vmstate(const char *name)