Get QEMUFile implementation to upstream level.
Change-Id: I0c5003876c7df0246118cb903cf6b834fac82687
diff --git a/Makefile.common b/Makefile.common
index 46e4230..b4488f1 100644
--- a/Makefile.common
+++ b/Makefile.common
@@ -484,6 +484,7 @@
util/qemu-option.c \
util/qemu-sockets-android.c \
util/unicode.c \
+ util/yield-android.c \
ifeq ($(HOST_ARCH),x86)
CORE_MISC_SOURCES += disas/i386.c
diff --git a/android/boot-properties.c b/android/boot-properties.c
index acdeed9..c2b30b8 100644
--- a/android/boot-properties.c
+++ b/android/boot-properties.c
@@ -175,7 +175,7 @@
if (split == NULL) {
D("%s: save failed: illegal key/value pair \"%s\" (missing '=')\n",
__FUNCTION__, p->property);
- qemu_file_set_error(f);
+ qemu_file_set_error(f, -EINVAL);
return -1;
}
diff --git a/arch_init.c b/arch_init.c
index fddf4e3..426f7bc 100644
--- a/arch_init.c
+++ b/arch_init.c
@@ -263,7 +263,7 @@
}
if (cpu_physical_sync_dirty_bitmap(0, TARGET_PHYS_ADDR_MAX) != 0) {
- qemu_file_set_error(f);
+ qemu_file_set_error(f, -errno);
return 0;
}
@@ -449,7 +449,7 @@
qemu_get_buffer(f, host, TARGET_PAGE_SIZE);
}
- if (qemu_file_has_error(f)) {
+ if (qemu_file_get_error(f)) {
return -EIO;
}
} while (!(flags & RAM_SAVE_FLAG_EOS));
diff --git a/hw/android/goldfish/nand.c b/hw/android/goldfish/nand.c
index 1c568e9..072b504 100644
--- a/hw/android/goldfish/nand.c
+++ b/hw/android/goldfish/nand.c
@@ -219,8 +219,8 @@
lseek_ret = do_lseek(dev->fd, 0, SEEK_END);
if (lseek_ret == -1) {
+ qemu_file_set_error(f, -errno);
XLOG("%s EOF seek failed: %s\n", __FUNCTION__, strerror(errno));
- qemu_file_set_error(f);
return;
}
const uint64_t total_size = lseek_ret;
@@ -229,15 +229,15 @@
/* copy all data from the stream to the stored image */
lseek_ret = do_lseek(dev->fd, 0, SEEK_SET);
if (lseek_ret == -1) {
+ qemu_file_set_error(f, -errno);
XLOG("%s seek failed: %s\n", __FUNCTION__, strerror(errno));
- qemu_file_set_error(f);
return;
}
do {
ret = do_read(dev->fd, buffer, buf_size);
if (ret < 0) {
+ qemu_file_set_error(f, -errno);
XLOG("%s read failed: %s\n", __FUNCTION__, strerror(errno));
- qemu_file_set_error(f);
return;
}
qemu_put_buffer(f, buffer, ret);
diff --git a/include/migration/qemu-file.h b/include/migration/qemu-file.h
index d15f8df..56738c7 100644
--- a/include/migration/qemu-file.h
+++ b/include/migration/qemu-file.h
@@ -132,7 +132,7 @@
void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size);
void qemu_put_byte(QEMUFile *f, int v);
-void qemu_file_set_error(QEMUFile* f);
+void qemu_file_set_error(QEMUFile* f, int error);
int qemu_file_has_error(QEMUFile* f);
void qemu_file_put_notify(QEMUFile* f);
@@ -176,7 +176,7 @@
#endif
int qemu_file_rate_limit(QEMUFile *f);
void qemu_file_reset_rate_limit(QEMUFile *f);
-int64_t qemu_file_set_rate_limit(QEMUFile *f, int64_t new_rate);
+void qemu_file_set_rate_limit(QEMUFile *f, int64_t new_rate);
int64_t qemu_file_get_rate_limit(QEMUFile *f);
int qemu_file_get_error(QEMUFile *f);
void qemu_fflush(QEMUFile *f);
diff --git a/include/qemu/sockets.h b/include/qemu/sockets.h
index 27a6f64..0639464 100644
--- a/include/qemu/sockets.h
+++ b/include/qemu/sockets.h
@@ -27,4 +27,6 @@
int parse_host_src_port(SockAddress* haddr, SockAddress* saddr,
const char *str);
+void qemu_set_block(int fd);
+
#endif /* QEMU__SOCKET_H */
diff --git a/migration-exec.c b/migration-exec.c
index fbbad71..2878e2b 100644
--- a/migration-exec.c
+++ b/migration-exec.c
@@ -68,29 +68,15 @@
int detach)
{
FdMigrationState *s;
- FILE *f;
s = g_malloc0(sizeof(*s));
- f = popen(command, "w");
- if (f == NULL) {
- DPRINTF("Unable to popen exec target\n");
- goto err_after_alloc;
+ s->opaque = qemu_popen_cmd(command, "w");
+ if (!s->opaque) {
+ g_free(s);
+ return NULL;
}
- s->fd = fileno(f);
- if (s->fd == -1) {
- DPRINTF("Unable to retrieve file descriptor for popen'd handle\n");
- goto err_after_open;
- }
-
- if (fcntl(s->fd, F_SETFD, O_NONBLOCK) == -1) {
- DPRINTF("Unable to set nonblocking mode on file descriptor\n");
- goto err_after_open;
- }
-
- s->opaque = qemu_popen(f, "w");
-
s->close = exec_close;
s->get_error = file_errno;
s->write = file_write;
@@ -107,12 +93,6 @@
migrate_fd_connect(s);
return &s->mig_state;
-
-err_after_open:
- pclose(f);
-err_after_alloc:
- g_free(s);
- return NULL;
}
static void exec_accept_incoming_migration(void *opaque)
diff --git a/migration.c b/migration.c
index cdcdd12..bd09e0a 100644
--- a/migration.c
+++ b/migration.c
@@ -206,7 +206,7 @@
FdMigrationState *s = opaque;
qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL);
- qemu_file_put_notify(s->file);
+ //qemu_file_put_notify(s->file);
}
ssize_t migrate_fd_put_buffer(void *opaque, const void *data, size_t size)
diff --git a/savevm.c b/savevm.c
index e344fe8..005b0c3 100644
--- a/savevm.c
+++ b/savevm.c
@@ -75,6 +75,7 @@
#include "net/net.h"
#include "monitor/monitor.h"
#include "sysemu/sysemu.h"
+#include "qemu/iov.h"
#include "qemu/timer.h"
#include "sysemu/char.h"
#include "sysemu/blockdev.h"
@@ -160,19 +161,25 @@
/* savevm/loadvm support */
#define IO_BUF_SIZE 32768
+#define MAX_IOV_SIZE MIN(IOV_MAX, 64)
struct QEMUFile {
- const QEMUFileOps* ops;
+ const QEMUFileOps *ops;
void *opaque;
- int is_write;
- int64_t buf_offset; /* start of buffer when writing, end of buffer
- when reading */
+ int64_t bytes_xfer;
+ int64_t xfer_limit;
+
+ int64_t pos; /* start of buffer when writing, end of buffer
+ when reading */
int buf_index;
int buf_size; /* 0 when writing */
uint8_t buf[IO_BUF_SIZE];
- int has_error;
+ struct iovec iov[MAX_IOV_SIZE];
+ unsigned int iovcnt;
+
+ int last_error;
};
typedef struct QEMUFileStdio
@@ -187,6 +194,20 @@
QEMUFile *file;
} QEMUFileSocket;
+static ssize_t socket_writev_buffer(void *opaque, struct iovec *iov, int iovcnt,
+ int64_t pos)
+{
+ QEMUFileSocket *s = opaque;
+ ssize_t len;
+ ssize_t size = iov_size(iov, iovcnt);
+
+ len = iov_send(s->fd, iov, iovcnt, 0, size);
+ if (len < size) {
+ len = -socket_error();
+ }
+ return len;
+}
+
static int socket_get_fd(void *opaque)
{
QEMUFileSocket *s = opaque;
@@ -199,13 +220,26 @@
QEMUFileSocket *s = opaque;
ssize_t len;
- do {
- len = recv(s->fd, (void *)buf, size, 0);
- } while (len == -1 && socket_error() == EINTR);
+ for (;;) {
+ len = qemu_recv(s->fd, buf, size, 0);
+ if (len != -1) {
+ break;
+ }
+#ifndef CONFIG_ANDROID
+ if (socket_error() == EAGAIN) {
+ yield_until_fd_readable(s->fd);
+ } else if (socket_error() != EINTR) {
+ break;
+ }
+#else
+ if (socket_error() != EINTR)
+ break;
+#endif
+ }
- if (len == -1)
+ if (len == -1) {
len = -socket_error();
-
+ }
return len;
}
@@ -218,16 +252,16 @@
return 0;
}
-static int stdio_get_fd(void* opaque)
+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;
- fseek(s->stdio_file, pos, SEEK_SET);
return fwrite(buf, 1, size, s->stdio_file);
}
@@ -237,11 +271,23 @@
FILE *fp = s->stdio_file;
int bytes;
- fseek(s->stdio_file, pos, SEEK_SET);
- do {
+ for (;;) {
clearerr(fp);
bytes = fread(buf, 1, size, fp);
- } while ((bytes == 0) && ferror(fp) && (errno == EINTR));
+ if (bytes != 0 || !ferror(fp)) {
+ break;
+ }
+#ifndef CONFIG_ANDROID
+ if (errno == EAGAIN) {
+ yield_until_fd_readable(fileno(fp));
+ } else if (errno != EINTR) {
+ break;
+ }
+#else
+ if (errno != EINTR)
+ break;
+#endif
+ }
return bytes;
}
@@ -250,6 +296,12 @@
QEMUFileStdio *s = opaque;
int ret;
ret = pclose(s->stdio_file);
+ if (ret == -1) {
+ ret = -errno;
+ } else if (!WIFEXITED(ret) || WEXITSTATUS(ret) != 0) {
+ /* close succeeded, but non-zero exit code: */
+ ret = -EIO; /* fake errno value */
+ }
g_free(s);
return ret;
}
@@ -257,9 +309,30 @@
static int stdio_fclose(void *opaque)
{
QEMUFileStdio *s = opaque;
- fclose(s->stdio_file);
+ int ret = 0;
+
+ if (s->file->ops->put_buffer || s->file->ops->writev_buffer) {
+ int fd = fileno(s->stdio_file);
+ struct stat st;
+
+ ret = fstat(fd, &st);
+ if (ret == 0 && S_ISREG(st.st_mode)) {
+ /*
+ * If the file handle is a regular file make sure the
+ * data is flushed to disk before signaling success.
+ */
+ ret = fsync(fd);
+ if (ret != 0) {
+ ret = -errno;
+ return ret;
+ }
+ }
+ }
+ if (fclose(s->stdio_file) == EOF) {
+ ret = -errno;
+ }
g_free(s);
- return 0;
+ return ret;
}
static const QEMUFileOps stdio_pipe_read_ops = {
@@ -274,15 +347,21 @@
.close = stdio_pclose
};
-QEMUFile *qemu_popen(FILE *stdio_file, const char *mode)
+QEMUFile *qemu_popen_cmd(const char *command, const char *mode)
{
+ FILE *stdio_file;
QEMUFileStdio *s;
- if (stdio_file == NULL || mode == NULL || (mode[0] != 'r' && mode[0] != 'w') || mode[1] != 0) {
+ if (mode == NULL || (mode[0] != 'r' && mode[0] != 'w') || mode[1] != 0) {
fprintf(stderr, "qemu_popen: Argument validity check failed\n");
return NULL;
}
+ stdio_file = popen(command, mode);
+ if (stdio_file == NULL) {
+ return NULL;
+ }
+
s = g_malloc0(sizeof(QEMUFileStdio));
s->stdio_file = stdio_file;
@@ -295,26 +374,6 @@
return s->file;
}
-QEMUFile *qemu_popen_cmd(const char *command, const char *mode)
-{
- FILE *popen_file;
-
- popen_file = popen(command, mode);
- if(popen_file == NULL) {
- return NULL;
- }
-
- return qemu_popen(popen_file, mode);
-}
-
-int qemu_get_fd(QEMUFile *f)
-{
- if (f->ops->get_fd)
- return f->ops->get_fd(f->opaque);
-
- return -1;
-}
-
static const QEMUFileOps stdio_file_read_ops = {
.get_fd = stdio_get_fd,
.get_buffer = stdio_get_buffer,
@@ -327,32 +386,111 @@
.close = stdio_fclose
};
+static ssize_t unix_writev_buffer(void *opaque, struct iovec *iov, int iovcnt,
+ int64_t pos)
+{
+ QEMUFileSocket *s = opaque;
+ ssize_t len, offset;
+ ssize_t size = iov_size(iov, iovcnt);
+ ssize_t total = 0;
+
+ assert(iovcnt > 0);
+ offset = 0;
+ while (size > 0) {
+ /* Find the next start position; skip all full-sized vector elements */
+ while (offset >= iov[0].iov_len) {
+ offset -= iov[0].iov_len;
+ iov++, iovcnt--;
+ }
+
+ /* skip `offset' bytes from the (now) first element, undo it on exit */
+ assert(iovcnt > 0);
+ iov[0].iov_base += offset;
+ iov[0].iov_len -= offset;
+
+ do {
+ len = writev(s->fd, iov, iovcnt);
+ } while (len == -1 && errno == EINTR);
+ if (len == -1) {
+ return -errno;
+ }
+
+ /* Undo the changes above */
+ iov[0].iov_base -= offset;
+ iov[0].iov_len += offset;
+
+ /* Prepare for the next iteration */
+ offset += len;
+ total += len;
+ size -= len;
+ }
+
+ return total;
+}
+
+static int unix_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
+{
+ QEMUFileSocket *s = opaque;
+ ssize_t len;
+
+ for (;;) {
+ len = read(s->fd, buf, size);
+ if (len != -1) {
+ break;
+ }
+ if (errno == EAGAIN) {
+ yield_until_fd_readable(s->fd);
+ } else if (errno != EINTR) {
+ break;
+ }
+ }
+
+ if (len == -1) {
+ len = -errno;
+ }
+ return len;
+}
+
+static int unix_close(void *opaque)
+{
+ QEMUFileSocket *s = opaque;
+ close(s->fd);
+ g_free(s);
+ return 0;
+}
+
+static const QEMUFileOps unix_read_ops = {
+ .get_fd = socket_get_fd,
+ .get_buffer = unix_get_buffer,
+ .close = unix_close
+};
+
+static const QEMUFileOps unix_write_ops = {
+ .get_fd = socket_get_fd,
+ .writev_buffer = unix_writev_buffer,
+ .close = unix_close
+};
+
QEMUFile *qemu_fdopen(int fd, const char *mode)
{
- QEMUFileStdio *s;
+ QEMUFileSocket *s;
if (mode == NULL ||
- (mode[0] != 'r' && mode[0] != 'w') ||
- mode[1] != 'b' || mode[2] != 0) {
+ (mode[0] != 'r' && mode[0] != 'w') ||
+ mode[1] != 'b' || mode[2] != 0) {
fprintf(stderr, "qemu_fdopen: Argument validity check failed\n");
return NULL;
}
- s = g_malloc0(sizeof(QEMUFileStdio));
- s->stdio_file = fdopen(fd, mode);
- if (!s->stdio_file)
- goto fail;
+ s = g_malloc0(sizeof(QEMUFileSocket));
+ s->fd = fd;
if(mode[0] == 'r') {
- s->file = qemu_fopen_ops(s, &stdio_file_read_ops);
+ s->file = qemu_fopen_ops(s, &unix_read_ops);
} else {
- s->file = qemu_fopen_ops(s, &stdio_file_write_ops);
+ s->file = qemu_fopen_ops(s, &unix_write_ops);
}
return s->file;
-
-fail:
- g_free(s);
- return NULL;
}
static const QEMUFileOps socket_read_ops = {
@@ -363,22 +501,10 @@
static const QEMUFileOps socket_write_ops = {
.get_fd = socket_get_fd,
+ .writev_buffer = socket_writev_buffer,
.close = file_socket_close
};
-QEMUFile *qemu_fopen_socket(int fd, const char* mode)
-{
- QEMUFileSocket *s = g_malloc0(sizeof(QEMUFileSocket));
-
- s->fd = fd;
- if (mode[0] == 'r')
- s->file = qemu_fopen_ops(s, &socket_read_ops);
- else
- s->file = qemu_fopen_ops(s, &socket_write_ops);
-
- return s->file;
-}
-
bool qemu_file_mode_is_not_valid(const char *mode)
{
if (mode == NULL ||
@@ -391,6 +517,25 @@
return false;
}
+QEMUFile *qemu_fopen_socket(int fd, const char *mode)
+{
+ QEMUFileSocket *s;
+
+ if (qemu_file_mode_is_not_valid(mode)) {
+ return NULL;
+ }
+
+ s = g_malloc0(sizeof(QEMUFileSocket));
+ s->fd = fd;
+ if (mode[0] == 'w') {
+ qemu_set_block(s->fd);
+ s->file = qemu_fopen_ops(s, &socket_write_ops);
+ } else {
+ s->file = qemu_fopen_ops(s, &socket_read_ops);
+ }
+ return s->file;
+}
+
QEMUFile *qemu_fopen(const char *filename, const char *mode)
{
QEMUFileStdio *s;
@@ -404,7 +549,7 @@
s->stdio_file = fopen(filename, mode);
if (!s->stdio_file)
goto fail;
-
+
if(mode[0] == 'w') {
s->file = qemu_fopen_ops(s, &stdio_file_write_ops);
} else {
@@ -416,6 +561,24 @@
return NULL;
}
+#ifndef CONFIG_ANDROID
+// TODO(digit): Once bdrv_writev_vmstate() is implemented.
+static ssize_t block_writev_buffer(void *opaque, struct iovec *iov, int iovcnt,
+ int64_t pos)
+{
+ int ret;
+ QEMUIOVector qiov;
+
+ qemu_iovec_init_external(&qiov, iov, iovcnt);
+ ret = bdrv_writev_vmstate(opaque, &qiov, pos);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return qiov.size;
+}
+#endif
+
static int block_put_buffer(void *opaque, const uint8_t *buf,
int64_t pos, int size)
{
@@ -430,176 +593,380 @@
static int bdrv_fclose(void *opaque)
{
+ // TODO(digit): bdrv_flush() should return error code.
+ bdrv_flush(opaque);
return 0;
}
-static const QEMUFileOps bdrv_file_read_ops = {
+static const QEMUFileOps bdrv_read_ops = {
.get_buffer = block_get_buffer,
- .close = bdrv_fclose,
+ .close = bdrv_fclose
};
-static const QEMUFileOps bdrv_file_write_opts = {
- .put_buffer = block_put_buffer,
- .close = bdrv_fclose,
+static const QEMUFileOps bdrv_write_ops = {
+ .put_buffer = block_put_buffer,
+ //.writev_buffer = block_writev_buffer,
+ .close = bdrv_fclose
};
static QEMUFile *qemu_fopen_bdrv(BlockDriverState *bs, int is_writable)
{
if (is_writable)
- return qemu_fopen_ops(bs, &bdrv_file_write_opts);
- else
- return qemu_fopen_ops(bs, &bdrv_file_read_ops);
+ return qemu_fopen_ops(bs, &bdrv_write_ops);
+ return qemu_fopen_ops(bs, &bdrv_read_ops);
}
-QEMUFile *qemu_fopen_ops(void *opaque, const QEMUFileOps* file_ops)
+QEMUFile *qemu_fopen_ops(void *opaque, const QEMUFileOps *ops)
{
QEMUFile *f;
f = g_malloc0(sizeof(QEMUFile));
f->opaque = opaque;
- f->ops = file_ops;
- f->is_write = (file_ops->put_buffer != NULL);
-
+ f->ops = ops;
return f;
}
-int qemu_file_has_error(QEMUFile *f)
+/*
+ * Get last error for stream f
+ *
+ * Return negative error value if there has been an error on previous
+ * operations, return 0 if no error happened.
+ *
+ */
+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->has_error = 1;
-}
-
-void qemu_fflush(QEMUFile *f)
-{
- if (!f->ops->put_buffer)
- return;
-
- if (f->is_write && f->buf_index > 0) {
- int len;
-
- len = f->ops->put_buffer(f->opaque, f->buf, f->buf_offset, f->buf_index);
- if (len > 0)
- f->buf_offset += f->buf_index;
- else
- f->has_error = 1;
- f->buf_index = 0;
+ if (f->last_error == 0) {
+ f->last_error = ret;
}
}
+static inline bool qemu_file_is_writable(QEMUFile *f)
+{
+ return f->ops->writev_buffer || f->ops->put_buffer;
+}
+
+/**
+ * Flushes QEMUFile buffer
+ *
+ * If there is writev_buffer QEMUFileOps it uses it otherwise uses
+ * put_buffer ops.
+ */
+void qemu_fflush(QEMUFile *f)
+{
+ ssize_t ret = 0;
+
+ if (!qemu_file_is_writable(f)) {
+ return;
+ }
+
+ if (f->ops->writev_buffer) {
+ if (f->iovcnt > 0) {
+ ret = f->ops->writev_buffer(f->opaque, f->iov, f->iovcnt, f->pos);
+ }
+ } else {
+ if (f->buf_index > 0) {
+ ret = f->ops->put_buffer(f->opaque, f->buf, f->pos, f->buf_index);
+ }
+ }
+ if (ret >= 0) {
+ f->pos += ret;
+ }
+ f->buf_index = 0;
+ f->iovcnt = 0;
+ if (ret < 0) {
+ qemu_file_set_error(f, ret);
+ }
+}
+
+#ifndef CONFIG_ANDROID
+// TODO(digit).
+void ram_control_before_iterate(QEMUFile *f, uint64_t flags)
+{
+ int ret = 0;
+
+ if (f->ops->before_ram_iterate) {
+ ret = f->ops->before_ram_iterate(f, f->opaque, flags);
+ if (ret < 0) {
+ qemu_file_set_error(f, ret);
+ }
+ }
+}
+
+void ram_control_after_iterate(QEMUFile *f, uint64_t flags)
+{
+ int ret = 0;
+
+ if (f->ops->after_ram_iterate) {
+ ret = f->ops->after_ram_iterate(f, f->opaque, flags);
+ if (ret < 0) {
+ qemu_file_set_error(f, ret);
+ }
+ }
+}
+
+void ram_control_load_hook(QEMUFile *f, uint64_t flags)
+{
+ int ret = -EINVAL;
+
+ if (f->ops->hook_ram_load) {
+ ret = f->ops->hook_ram_load(f, f->opaque, flags);
+ if (ret < 0) {
+ qemu_file_set_error(f, ret);
+ }
+ } else {
+ qemu_file_set_error(f, ret);
+ }
+}
+
+size_t ram_control_save_page(QEMUFile *f, ram_addr_t block_offset,
+ ram_addr_t offset, size_t size, int *bytes_sent)
+{
+ if (f->ops->save_page) {
+ int ret = f->ops->save_page(f, f->opaque, block_offset,
+ offset, size, bytes_sent);
+
+ if (ret != RAM_SAVE_CONTROL_DELAYED) {
+ if (bytes_sent && *bytes_sent > 0) {
+ qemu_update_position(f, *bytes_sent);
+ } else if (ret < 0) {
+ qemu_file_set_error(f, ret);
+ }
+ }
+
+ return ret;
+ }
+
+ return RAM_SAVE_CONTROL_NOT_SUPP;
+}
+#endif // !CONFIG_ANDROID
+
static void qemu_fill_buffer(QEMUFile *f)
{
int len;
+ int pending;
- if (!f->ops->get_buffer)
- return;
+ assert(!qemu_file_is_writable(f));
- if (f->is_write)
- abort();
+ 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->ops->get_buffer(f->opaque, f->buf, f->buf_offset, IO_BUF_SIZE);
+ len = f->ops->get_buffer(f->opaque, f->buf + pending, f->pos,
+ IO_BUF_SIZE - pending);
if (len > 0) {
- f->buf_index = 0;
- f->buf_size = len;
- f->buf_offset += len;
+ f->buf_size += len;
+ f->pos += len;
+ } else if (len == 0) {
+ qemu_file_set_error(f, -EIO);
} else if (len != -EAGAIN)
- f->has_error = 1;
+ qemu_file_set_error(f, len);
}
+int qemu_get_fd(QEMUFile *f)
+{
+ if (f->ops->get_fd) {
+ return f->ops->get_fd(f->opaque);
+ }
+ return -1;
+}
+
+void qemu_update_position(QEMUFile *f, size_t size)
+{
+ f->pos += size;
+}
+
+/** 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 = 0;
+ int ret;
qemu_fflush(f);
- if (f->ops->close)
- ret = f->ops->close(f->opaque);
+ ret = qemu_file_get_error(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.
+ */
+ if (f->last_error) {
+ ret = f->last_error;
+ }
g_free(f);
return ret;
}
-void qemu_file_put_notify(QEMUFile *f)
+static void add_to_iovec(QEMUFile *f, const uint8_t *buf, int size)
{
- f->ops->put_buffer(f->opaque, NULL, 0, 0);
+ /* check for adjacent buffer and coalesce them */
+ if (f->iovcnt > 0 && buf == f->iov[f->iovcnt - 1].iov_base +
+ f->iov[f->iovcnt - 1].iov_len) {
+ f->iov[f->iovcnt - 1].iov_len += size;
+ } else {
+ f->iov[f->iovcnt].iov_base = (uint8_t *)buf;
+ f->iov[f->iovcnt++].iov_len = size;
+ }
+
+ if (f->iovcnt >= MAX_IOV_SIZE) {
+ qemu_fflush(f);
+ }
+}
+
+void qemu_put_buffer_async(QEMUFile *f, const uint8_t *buf, int size)
+{
+ if (!f->ops->writev_buffer) {
+ qemu_put_buffer(f, buf, size);
+ return;
+ }
+
+ if (f->last_error) {
+ return;
+ }
+
+ f->bytes_xfer += size;
+ add_to_iovec(f, buf, size);
}
void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size)
{
int l;
- if (!f->has_error && f->is_write == 0 && f->buf_index > 0) {
- fprintf(stderr,
- "Attempted to write to buffer while read buffer is not empty\n");
- abort();
+ if (f->last_error) {
+ return;
}
- while (!f->has_error && size > 0) {
+ while (size > 0) {
l = IO_BUF_SIZE - f->buf_index;
if (l > size)
l = size;
memcpy(f->buf + f->buf_index, buf, l);
- f->is_write = 1;
+ f->bytes_xfer += l;
+ if (f->ops->writev_buffer) {
+ add_to_iovec(f, f->buf + f->buf_index, l);
+ }
f->buf_index += l;
+ if (f->buf_index == IO_BUF_SIZE) {
+ qemu_fflush(f);
+ }
+ if (qemu_file_get_error(f)) {
+ break;
+ }
buf += l;
size -= l;
- if (f->buf_index >= IO_BUF_SIZE)
- qemu_fflush(f);
}
}
void qemu_put_byte(QEMUFile *f, int v)
{
- if (!f->has_error && f->is_write == 0 && f->buf_index > 0) {
- fprintf(stderr,
- "Attempted to write to buffer while read buffer is not empty\n");
- abort();
+ if (f->last_error) {
+ return;
}
- f->buf[f->buf_index++] = v;
- f->is_write = 1;
- if (f->buf_index >= IO_BUF_SIZE)
+ f->buf[f->buf_index] = v;
+ f->bytes_xfer++;
+ if (f->ops->writev_buffer) {
+ add_to_iovec(f, f->buf + f->buf_index, 1);
+ }
+ f->buf_index++;
+ if (f->buf_index == IO_BUF_SIZE) {
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->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;
- }
- if (l > size)
- l = size;
- memcpy(buf, f->buf + f->buf_index, l);
- f->buf_index += l;
- buf += l;
- size -= l;
+ if (f->buf_index + size <= f->buf_size) {
+ f->buf_index += size;
}
- return size1 - size;
+}
+
+static int qemu_peek_buffer(QEMUFile *f, uint8_t *buf, int size, size_t offset)
+{
+ int pending;
+ int index;
+
+ assert(!qemu_file_is_writable(f));
+
+ 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;
+ }
+ qemu_file_skip(f, res);
+ buf += res;
+ pending -= res;
+ done += res;
+ }
+ return done;
+}
+
+static int qemu_peek_byte(QEMUFile *f, int offset)
+{
+ int index = f->buf_index + offset;
+
+ assert(!qemu_file_is_writable(f));
+
+ if (index >= f->buf_size) {
+ qemu_fill_buffer(f);
+ index = f->buf_index + offset;
+ if (index >= f->buf_size) {
+ return 0;
+ }
+ }
+ 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;
}
#ifdef CONFIG_ANDROID
@@ -635,54 +1002,34 @@
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->ops->put_buffer) {
- qemu_fflush(f);
- f->buf_offset = pos;
- } else {
- f->buf_offset = pos;
- f->buf_index = 0;
- f->buf_size = 0;
- }
- return pos;
+ qemu_fflush(f);
+ return f->pos;
}
int qemu_file_rate_limit(QEMUFile *f)
{
- if (f->ops->rate_limit)
- return f->ops->rate_limit(f->opaque);
-
+ if (qemu_file_get_error(f)) {
+ return 1;
+ }
+ if (f->xfer_limit > 0 && f->bytes_xfer > f->xfer_limit) {
+ return 1;
+ }
return 0;
}
int64_t qemu_file_get_rate_limit(QEMUFile *f)
{
- if (f->ops->get_rate_limit)
- return f->ops->get_rate_limit(f->opaque);
-
- return 0;
+ return f->xfer_limit;
}
-int64_t qemu_file_set_rate_limit(QEMUFile *f, int64_t new_rate)
+void qemu_file_set_rate_limit(QEMUFile *f, int64_t limit)
{
- /* any failed or completed migration keeps its state to allow probing of
- * migration data, but has no associated file anymore */
- if (f && f->ops->set_rate_limit)
- return f->ops->set_rate_limit(f->opaque, new_rate);
+ f->xfer_limit = limit;
+}
- return 0;
+void qemu_file_reset_rate_limit(QEMUFile *f)
+{
+ f->bytes_xfer = 0;
}
void qemu_put_be16(QEMUFile *f, unsigned int v)
@@ -960,10 +1307,7 @@
se->save_live_state(f, QEMU_VM_SECTION_START, se->opaque);
}
- if (qemu_file_has_error(f))
- return -EIO;
-
- return 0;
+ return qemu_file_get_error(f);
}
int qemu_savevm_state_iterate(QEMUFile *f)
@@ -985,10 +1329,7 @@
if (ret)
return 1;
- if (qemu_file_has_error(f))
- return -EIO;
-
- return 0;
+ return qemu_file_get_error(f);
}
int qemu_savevm_state_complete(QEMUFile *f)
@@ -1029,10 +1370,7 @@
qemu_put_byte(f, QEMU_VM_EOF);
- if (qemu_file_has_error(f))
- return -EIO;
-
- return 0;
+ return qemu_file_get_error(f);
}
int qemu_savevm_state(QEMUFile *f)
@@ -1058,8 +1396,7 @@
ret = qemu_savevm_state_complete(f);
out:
- if (qemu_file_has_error(f))
- ret = -EIO;
+ ret = qemu_file_get_error(f);
if (!ret && saved_vm_running)
vm_start();
@@ -1118,13 +1455,10 @@
}
}
/* always seek to exact end of record */
- qemu_fseek(f, cur_pos + record_len, SEEK_SET);
+ qemu_file_skip(f, cur_pos + record_len - qemu_ftell(f));
}
- if (qemu_file_has_error(f))
- return -EIO;
-
- return 0;
+ return qemu_file_get_error(f);
}
int qemu_loadvm_state(QEMUFile *f)
@@ -1227,8 +1561,8 @@
g_free(le);
}
- if (qemu_file_has_error(f))
- ret = -EIO;
+ if (qemu_file_get_error(f))
+ ret = qemu_file_get_error(f);
return ret;
}
diff --git a/util/yield-android.c b/util/yield-android.c
new file mode 100644
index 0000000..b553ab9
--- /dev/null
+++ b/util/yield-android.c
@@ -0,0 +1,20 @@
+#include <errno.h>
+#include <stddef.h>
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include <sys/select.h>
+#endif
+
+// Wait until file descriptor |fd| becomes readable.
+void yield_until_fd_readable(int fd) {
+ for (;;) {
+ fd_set read_fds;
+ FD_ZERO(&read_fds);
+ FD_SET(fd, &read_fds);
+ int ret = select(fd + 1, &read_fds, NULL, NULL, NULL);
+ if (ret == 1 || (ret < 0 && errno != -EINTR))
+ return;
+ }
+}
+