mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
Merge remote-tracking branch 'remotes/juanquintela/tags/migration/20141015' into staging
migration/next for 20141015 # gpg: Signature made Wed 15 Oct 2014 09:21:54 BST using RSA key ID 5872D723 # gpg: Can't check signature: public key not found * remotes/juanquintela/tags/migration/20141015: migration: catch unknown flag combinations in ram_load qemu-file: Move stdio implementation to qemu-file-stdio.c qemu-file: Move unix and socket implementations to qemu-file-unix.c qemu-file: Use qemu_file_is_writable() on stdio_fclose() qemu-file: Make qemu_file_is_writable() non-static qemu-file: Add copyright header to qemu-file.c vmstate: Allow dynamic allocation for VBUFFER during migration block/migration: Disable cache invalidate for incoming migration Tests: QEMUSizedBuffer/QEMUBuffer QEMUSizedBuffer based QEMUFile Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
+1
-1
@@ -50,7 +50,7 @@ common-obj-$(CONFIG_LINUX) += fsdev/
|
||||
|
||||
common-obj-y += migration.o migration-tcp.o
|
||||
common-obj-y += vmstate.o
|
||||
common-obj-y += qemu-file.o
|
||||
common-obj-y += qemu-file.o qemu-file-unix.o qemu-file-stdio.o
|
||||
common-obj-$(CONFIG_RDMA) += migration-rdma.o
|
||||
common-obj-y += qemu-char.o #aio.o
|
||||
common-obj-y += block-migration.o
|
||||
|
||||
+32
-30
@@ -1040,8 +1040,7 @@ void ram_handle_compressed(void *host, uint8_t ch, uint64_t size)
|
||||
|
||||
static int ram_load(QEMUFile *f, void *opaque, int version_id)
|
||||
{
|
||||
ram_addr_t addr;
|
||||
int flags, ret = 0;
|
||||
int flags = 0, ret = 0;
|
||||
static uint64_t seq_iter;
|
||||
|
||||
seq_iter++;
|
||||
@@ -1050,21 +1049,24 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id)
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
while (!ret) {
|
||||
addr = qemu_get_be64(f);
|
||||
while (!ret && !(flags & RAM_SAVE_FLAG_EOS)) {
|
||||
ram_addr_t addr, total_ram_bytes;
|
||||
void *host;
|
||||
uint8_t ch;
|
||||
|
||||
addr = qemu_get_be64(f);
|
||||
flags = addr & ~TARGET_PAGE_MASK;
|
||||
addr &= TARGET_PAGE_MASK;
|
||||
|
||||
if (flags & RAM_SAVE_FLAG_MEM_SIZE) {
|
||||
switch (flags & ~RAM_SAVE_FLAG_CONTINUE) {
|
||||
case RAM_SAVE_FLAG_MEM_SIZE:
|
||||
/* Synchronize RAM block list */
|
||||
char id[256];
|
||||
ram_addr_t length;
|
||||
ram_addr_t total_ram_bytes = addr;
|
||||
|
||||
while (total_ram_bytes) {
|
||||
total_ram_bytes = addr;
|
||||
while (!ret && total_ram_bytes) {
|
||||
RAMBlock *block;
|
||||
uint8_t len;
|
||||
char id[256];
|
||||
ram_addr_t length;
|
||||
|
||||
len = qemu_get_byte(f);
|
||||
qemu_get_buffer(f, (uint8_t *)id, len);
|
||||
@@ -1088,16 +1090,11 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id)
|
||||
"accept migration", id);
|
||||
ret = -EINVAL;
|
||||
}
|
||||
if (ret) {
|
||||
break;
|
||||
}
|
||||
|
||||
total_ram_bytes -= length;
|
||||
}
|
||||
} else if (flags & RAM_SAVE_FLAG_COMPRESS) {
|
||||
void *host;
|
||||
uint8_t ch;
|
||||
|
||||
break;
|
||||
case RAM_SAVE_FLAG_COMPRESS:
|
||||
host = host_from_stream_offset(f, addr, flags);
|
||||
if (!host) {
|
||||
error_report("Illegal RAM offset " RAM_ADDR_FMT, addr);
|
||||
@@ -1107,9 +1104,8 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id)
|
||||
|
||||
ch = qemu_get_byte(f);
|
||||
ram_handle_compressed(host, ch, TARGET_PAGE_SIZE);
|
||||
} else if (flags & RAM_SAVE_FLAG_PAGE) {
|
||||
void *host;
|
||||
|
||||
break;
|
||||
case RAM_SAVE_FLAG_PAGE:
|
||||
host = host_from_stream_offset(f, addr, flags);
|
||||
if (!host) {
|
||||
error_report("Illegal RAM offset " RAM_ADDR_FMT, addr);
|
||||
@@ -1118,8 +1114,9 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id)
|
||||
}
|
||||
|
||||
qemu_get_buffer(f, host, TARGET_PAGE_SIZE);
|
||||
} else if (flags & RAM_SAVE_FLAG_XBZRLE) {
|
||||
void *host = host_from_stream_offset(f, addr, flags);
|
||||
break;
|
||||
case RAM_SAVE_FLAG_XBZRLE:
|
||||
host = host_from_stream_offset(f, addr, flags);
|
||||
if (!host) {
|
||||
error_report("Illegal RAM offset " RAM_ADDR_FMT, addr);
|
||||
ret = -EINVAL;
|
||||
@@ -1132,17 +1129,22 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id)
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
} else if (flags & RAM_SAVE_FLAG_HOOK) {
|
||||
ram_control_load_hook(f, flags);
|
||||
} else if (flags & RAM_SAVE_FLAG_EOS) {
|
||||
break;
|
||||
case RAM_SAVE_FLAG_EOS:
|
||||
/* normal exit */
|
||||
break;
|
||||
} else {
|
||||
error_report("Unknown migration flags: %#x", flags);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
default:
|
||||
if (flags & RAM_SAVE_FLAG_HOOK) {
|
||||
ram_control_load_hook(f, flags);
|
||||
} else {
|
||||
error_report("Unknown combination of migration flags: %#x",
|
||||
flags);
|
||||
ret = -EINVAL;
|
||||
}
|
||||
}
|
||||
if (!ret) {
|
||||
ret = qemu_file_get_error(f);
|
||||
}
|
||||
ret = qemu_file_get_error(f);
|
||||
}
|
||||
|
||||
DPRINTF("Completed load of VM with exit code %d seq iteration "
|
||||
|
||||
@@ -5043,6 +5043,11 @@ void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(bs->open_flags & BDRV_O_INCOMING)) {
|
||||
return;
|
||||
}
|
||||
bs->open_flags &= ~BDRV_O_INCOMING;
|
||||
|
||||
if (bs->drv->bdrv_invalidate_cache) {
|
||||
bs->drv->bdrv_invalidate_cache(bs, &local_err);
|
||||
} else if (bs->file) {
|
||||
@@ -5078,19 +5083,6 @@ void bdrv_invalidate_cache_all(Error **errp)
|
||||
}
|
||||
}
|
||||
|
||||
void bdrv_clear_incoming_migration_all(void)
|
||||
{
|
||||
BlockDriverState *bs;
|
||||
|
||||
QTAILQ_FOREACH(bs, &bdrv_states, device_list) {
|
||||
AioContext *aio_context = bdrv_get_aio_context(bs);
|
||||
|
||||
aio_context_acquire(aio_context);
|
||||
bs->open_flags = bs->open_flags & ~(BDRV_O_INCOMING);
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
}
|
||||
|
||||
int bdrv_flush(BlockDriverState *bs)
|
||||
{
|
||||
Coroutine *co;
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
#define QEMU_FILE_H 1
|
||||
#include "exec/cpu-common.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* This function writes a chunk of data to a file at the given position.
|
||||
* The pos argument can be ignored if the file is only being used for
|
||||
* streaming. The handler should try to write all of the data it can.
|
||||
@@ -94,11 +96,19 @@ typedef struct QEMUFileOps {
|
||||
QEMURamSaveFunc *save_page;
|
||||
} QEMUFileOps;
|
||||
|
||||
struct QEMUSizedBuffer {
|
||||
struct iovec *iov;
|
||||
size_t n_iov;
|
||||
size_t size; /* total allocated size in all iov's */
|
||||
size_t used; /* number of used bytes */
|
||||
};
|
||||
|
||||
QEMUFile *qemu_fopen_ops(void *opaque, const QEMUFileOps *ops);
|
||||
QEMUFile *qemu_fopen(const char *filename, const char *mode);
|
||||
QEMUFile *qemu_fdopen(int fd, const char *mode);
|
||||
QEMUFile *qemu_fopen_socket(int fd, const char *mode);
|
||||
QEMUFile *qemu_popen_cmd(const char *command, const char *mode);
|
||||
QEMUFile *qemu_bufopen(const char *mode, QEMUSizedBuffer *input);
|
||||
int qemu_get_fd(QEMUFile *f);
|
||||
int qemu_fclose(QEMUFile *f);
|
||||
int64_t qemu_ftell(QEMUFile *f);
|
||||
@@ -110,6 +120,23 @@ void qemu_put_byte(QEMUFile *f, int v);
|
||||
*/
|
||||
void qemu_put_buffer_async(QEMUFile *f, const uint8_t *buf, int size);
|
||||
bool qemu_file_mode_is_not_valid(const char *mode);
|
||||
bool qemu_file_is_writable(QEMUFile *f);
|
||||
|
||||
QEMUSizedBuffer *qsb_create(const uint8_t *buffer, size_t len);
|
||||
QEMUSizedBuffer *qsb_clone(const QEMUSizedBuffer *);
|
||||
void qsb_free(QEMUSizedBuffer *);
|
||||
size_t qsb_set_length(QEMUSizedBuffer *qsb, size_t length);
|
||||
size_t qsb_get_length(const QEMUSizedBuffer *qsb);
|
||||
ssize_t qsb_get_buffer(const QEMUSizedBuffer *, off_t start, size_t count,
|
||||
uint8_t *buf);
|
||||
ssize_t qsb_write_at(QEMUSizedBuffer *qsb, const uint8_t *buf,
|
||||
off_t pos, size_t count);
|
||||
|
||||
|
||||
/*
|
||||
* For use on files opened with qemu_bufopen
|
||||
*/
|
||||
const QEMUSizedBuffer *qemu_buf_get(QEMUFile *f);
|
||||
|
||||
static inline void qemu_put_ubyte(QEMUFile *f, unsigned int v)
|
||||
{
|
||||
|
||||
@@ -484,6 +484,17 @@ extern const VMStateInfo vmstate_info_bitmap;
|
||||
.start = (_start), \
|
||||
}
|
||||
|
||||
#define VMSTATE_VBUFFER_ALLOC_UINT32(_field, _state, _version, _test, _start, _field_size) { \
|
||||
.name = (stringify(_field)), \
|
||||
.version_id = (_version), \
|
||||
.field_exists = (_test), \
|
||||
.size_offset = vmstate_offset_value(_state, _field_size, uint32_t),\
|
||||
.info = &vmstate_info_buffer, \
|
||||
.flags = VMS_VBUFFER|VMS_POINTER|VMS_ALLOC, \
|
||||
.offset = offsetof(_state, _field), \
|
||||
.start = (_start), \
|
||||
}
|
||||
|
||||
#define VMSTATE_BUFFER_UNSAFE_INFO(_field, _state, _version, _info, _size) { \
|
||||
.name = (stringify(_field)), \
|
||||
.version_id = (_version), \
|
||||
|
||||
@@ -71,6 +71,7 @@ typedef struct SSIBus SSIBus;
|
||||
typedef struct EventNotifier EventNotifier;
|
||||
typedef struct VirtIODevice VirtIODevice;
|
||||
typedef struct QEMUSGList QEMUSGList;
|
||||
typedef struct QEMUSizedBuffer QEMUSizedBuffer;
|
||||
typedef struct SHPCDevice SHPCDevice;
|
||||
typedef struct FWCfgState FWCfgState;
|
||||
typedef struct PcGuestInfo PcGuestInfo;
|
||||
|
||||
@@ -103,7 +103,6 @@ static void process_incoming_migration_co(void *opaque)
|
||||
}
|
||||
qemu_announce_self();
|
||||
|
||||
bdrv_clear_incoming_migration_all();
|
||||
/* Make sure all file formats flush their mutable metadata */
|
||||
bdrv_invalidate_cache_all(&local_err);
|
||||
if (local_err) {
|
||||
|
||||
@@ -972,6 +972,12 @@ NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset,
|
||||
exp->ctx = bdrv_get_aio_context(bs);
|
||||
bdrv_ref(bs);
|
||||
bdrv_add_aio_context_notifier(bs, bs_aio_attached, bs_aio_detach, exp);
|
||||
/*
|
||||
* NBD exports are used for non-shared storage migration. Make sure
|
||||
* that BDRV_O_INCOMING is cleared and the image is ready for write
|
||||
* access since the export could be available before migration handover.
|
||||
*/
|
||||
bdrv_invalidate_cache(bs, NULL);
|
||||
return exp;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
* QEMU System Emulator
|
||||
*
|
||||
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu-common.h"
|
||||
#include "block/coroutine.h"
|
||||
#include "migration/qemu-file.h"
|
||||
|
||||
typedef struct QEMUFileStdio {
|
||||
FILE *stdio_file;
|
||||
QEMUFile *file;
|
||||
} QEMUFileStdio;
|
||||
|
||||
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;
|
||||
int res;
|
||||
|
||||
res = fwrite(buf, 1, size, s->stdio_file);
|
||||
|
||||
if (res != size) {
|
||||
return -errno;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int stdio_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
|
||||
{
|
||||
QEMUFileStdio *s = opaque;
|
||||
FILE *fp = s->stdio_file;
|
||||
int bytes;
|
||||
|
||||
for (;;) {
|
||||
clearerr(fp);
|
||||
bytes = fread(buf, 1, size, fp);
|
||||
if (bytes != 0 || !ferror(fp)) {
|
||||
break;
|
||||
}
|
||||
if (errno == EAGAIN) {
|
||||
yield_until_fd_readable(fileno(fp));
|
||||
} else if (errno != EINTR) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
static int stdio_pclose(void *opaque)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
static int stdio_fclose(void *opaque)
|
||||
{
|
||||
QEMUFileStdio *s = opaque;
|
||||
int ret = 0;
|
||||
|
||||
if (qemu_file_is_writable(s->file)) {
|
||||
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 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_cmd(const char *command, const char *mode)
|
||||
{
|
||||
FILE *stdio_file;
|
||||
QEMUFileStdio *s;
|
||||
|
||||
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;
|
||||
|
||||
if (mode[0] == 'r') {
|
||||
s->file = qemu_fopen_ops(s, &stdio_pipe_read_ops);
|
||||
} else {
|
||||
s->file = qemu_fopen_ops(s, &stdio_pipe_write_ops);
|
||||
}
|
||||
return s->file;
|
||||
}
|
||||
|
||||
static const QEMUFileOps stdio_file_read_ops = {
|
||||
.get_fd = stdio_get_fd,
|
||||
.get_buffer = stdio_get_buffer,
|
||||
.close = stdio_fclose
|
||||
};
|
||||
|
||||
static const QEMUFileOps stdio_file_write_ops = {
|
||||
.get_fd = stdio_get_fd,
|
||||
.put_buffer = stdio_put_buffer,
|
||||
.close = stdio_fclose
|
||||
};
|
||||
|
||||
QEMUFile *qemu_fopen(const char *filename, const char *mode)
|
||||
{
|
||||
QEMUFileStdio *s;
|
||||
|
||||
if (qemu_file_mode_is_not_valid(mode)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
s = g_malloc0(sizeof(QEMUFileStdio));
|
||||
|
||||
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 {
|
||||
s->file = qemu_fopen_ops(s, &stdio_file_read_ops);
|
||||
}
|
||||
return s->file;
|
||||
fail:
|
||||
g_free(s);
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* QEMU System Emulator
|
||||
*
|
||||
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/iov.h"
|
||||
#include "qemu/sockets.h"
|
||||
#include "block/coroutine.h"
|
||||
#include "migration/qemu-file.h"
|
||||
|
||||
typedef struct QEMUFileSocket {
|
||||
int fd;
|
||||
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;
|
||||
|
||||
return s->fd;
|
||||
}
|
||||
|
||||
static int socket_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
|
||||
{
|
||||
QEMUFileSocket *s = opaque;
|
||||
ssize_t len;
|
||||
|
||||
for (;;) {
|
||||
len = qemu_recv(s->fd, buf, size, 0);
|
||||
if (len != -1) {
|
||||
break;
|
||||
}
|
||||
if (socket_error() == EAGAIN) {
|
||||
yield_until_fd_readable(s->fd);
|
||||
} else if (socket_error() != EINTR) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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 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)
|
||||
{
|
||||
QEMUFileSocket *s;
|
||||
|
||||
if (mode == NULL ||
|
||||
(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(QEMUFileSocket));
|
||||
s->fd = fd;
|
||||
|
||||
if (mode[0] == 'r') {
|
||||
s->file = qemu_fopen_ops(s, &unix_read_ops);
|
||||
} else {
|
||||
s->file = qemu_fopen_ops(s, &unix_write_ops);
|
||||
}
|
||||
return s->file;
|
||||
}
|
||||
|
||||
static const QEMUFileOps socket_read_ops = {
|
||||
.get_fd = socket_get_fd,
|
||||
.get_buffer = socket_get_buffer,
|
||||
.close = socket_close
|
||||
};
|
||||
|
||||
static const QEMUFileOps socket_write_ops = {
|
||||
.get_fd = socket_get_fd,
|
||||
.writev_buffer = socket_writev_buffer,
|
||||
.close = socket_close
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
+479
-364
File diff suppressed because it is too large
Load Diff
+2
-2
@@ -258,8 +258,8 @@ tests/test-qdev-global-props$(EXESUF): tests/test-qdev-global-props.o \
|
||||
$(test-qapi-obj-y) \
|
||||
libqemuutil.a libqemustub.a
|
||||
tests/test-vmstate$(EXESUF): tests/test-vmstate.o \
|
||||
vmstate.o qemu-file.o \
|
||||
libqemuutil.a
|
||||
vmstate.o qemu-file.o qemu-file-unix.o \
|
||||
libqemuutil.a libqemustub.a
|
||||
|
||||
tests/test-qapi-types.c tests/test-qapi-types.h :\
|
||||
$(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-types.py
|
||||
|
||||
+38
-36
@@ -43,6 +43,12 @@ void yield_until_fd_readable(int fd)
|
||||
select(fd + 1, &fds, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Some tests use 'open_test_file' to work on a real fd, some use
|
||||
* an in memory file (QEMUSizedBuffer+qemu_bufopen); we could pick one
|
||||
* but this way we test both.
|
||||
*/
|
||||
|
||||
/* Duplicate temp_fd and seek to the beginning of the file */
|
||||
static QEMUFile *open_test_file(bool write)
|
||||
{
|
||||
@@ -54,6 +60,30 @@ static QEMUFile *open_test_file(bool write)
|
||||
return qemu_fdopen(fd, write ? "wb" : "rb");
|
||||
}
|
||||
|
||||
/* Open a read-only qemu-file from an existing memory block */
|
||||
static QEMUFile *open_mem_file_read(const void *data, size_t len)
|
||||
{
|
||||
/* The qsb gets freed by qemu_fclose */
|
||||
QEMUSizedBuffer *qsb = qsb_create(data, len);
|
||||
g_assert(qsb);
|
||||
|
||||
return qemu_bufopen("r", qsb);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check that the contents of the memory-buffered file f match
|
||||
* the given size/data.
|
||||
*/
|
||||
static void check_mem_file(QEMUFile *f, void *data, size_t size)
|
||||
{
|
||||
uint8_t *result = g_malloc(size);
|
||||
const QEMUSizedBuffer *qsb = qemu_buf_get(f);
|
||||
g_assert_cmpint(qsb_get_length(qsb), ==, size);
|
||||
g_assert_cmpint(qsb_get_buffer(qsb, 0, size, result), ==, size);
|
||||
g_assert_cmpint(memcmp(result, data, size), ==, 0);
|
||||
g_free(result);
|
||||
}
|
||||
|
||||
#define SUCCESS(val) \
|
||||
g_assert_cmpint((val), ==, 0)
|
||||
|
||||
@@ -371,14 +401,12 @@ static const VMStateDescription vmstate_skipping = {
|
||||
|
||||
static void test_save_noskip(void)
|
||||
{
|
||||
QEMUFile *fsave = open_test_file(true);
|
||||
QEMUFile *fsave = qemu_bufopen("w", NULL);
|
||||
TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6,
|
||||
.skip_c_e = false };
|
||||
vmstate_save_state(fsave, &vmstate_skipping, &obj);
|
||||
g_assert(!qemu_file_get_error(fsave));
|
||||
qemu_fclose(fsave);
|
||||
|
||||
QEMUFile *loading = open_test_file(false);
|
||||
uint8_t expected[] = {
|
||||
0, 0, 0, 1, /* a */
|
||||
0, 0, 0, 2, /* b */
|
||||
@@ -387,52 +415,31 @@ static void test_save_noskip(void)
|
||||
0, 0, 0, 5, /* e */
|
||||
0, 0, 0, 0, 0, 0, 0, 6, /* f */
|
||||
};
|
||||
uint8_t result[sizeof(expected)];
|
||||
g_assert_cmpint(qemu_get_buffer(loading, result, sizeof(result)), ==,
|
||||
sizeof(result));
|
||||
g_assert(!qemu_file_get_error(loading));
|
||||
g_assert_cmpint(memcmp(result, expected, sizeof(result)), ==, 0);
|
||||
|
||||
/* Must reach EOF */
|
||||
qemu_get_byte(loading);
|
||||
g_assert_cmpint(qemu_file_get_error(loading), ==, -EIO);
|
||||
|
||||
qemu_fclose(loading);
|
||||
check_mem_file(fsave, expected, sizeof(expected));
|
||||
qemu_fclose(fsave);
|
||||
}
|
||||
|
||||
static void test_save_skip(void)
|
||||
{
|
||||
QEMUFile *fsave = open_test_file(true);
|
||||
QEMUFile *fsave = qemu_bufopen("w", NULL);
|
||||
TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6,
|
||||
.skip_c_e = true };
|
||||
vmstate_save_state(fsave, &vmstate_skipping, &obj);
|
||||
g_assert(!qemu_file_get_error(fsave));
|
||||
qemu_fclose(fsave);
|
||||
|
||||
QEMUFile *loading = open_test_file(false);
|
||||
uint8_t expected[] = {
|
||||
0, 0, 0, 1, /* a */
|
||||
0, 0, 0, 2, /* b */
|
||||
0, 0, 0, 0, 0, 0, 0, 4, /* d */
|
||||
0, 0, 0, 0, 0, 0, 0, 6, /* f */
|
||||
};
|
||||
uint8_t result[sizeof(expected)];
|
||||
g_assert_cmpint(qemu_get_buffer(loading, result, sizeof(result)), ==,
|
||||
sizeof(result));
|
||||
g_assert(!qemu_file_get_error(loading));
|
||||
g_assert_cmpint(memcmp(result, expected, sizeof(result)), ==, 0);
|
||||
check_mem_file(fsave, expected, sizeof(expected));
|
||||
|
||||
|
||||
/* Must reach EOF */
|
||||
qemu_get_byte(loading);
|
||||
g_assert_cmpint(qemu_file_get_error(loading), ==, -EIO);
|
||||
|
||||
qemu_fclose(loading);
|
||||
qemu_fclose(fsave);
|
||||
}
|
||||
|
||||
static void test_load_noskip(void)
|
||||
{
|
||||
QEMUFile *fsave = open_test_file(true);
|
||||
uint8_t buf[] = {
|
||||
0, 0, 0, 10, /* a */
|
||||
0, 0, 0, 20, /* b */
|
||||
@@ -442,10 +449,8 @@ static void test_load_noskip(void)
|
||||
0, 0, 0, 0, 0, 0, 0, 60, /* f */
|
||||
QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */
|
||||
};
|
||||
qemu_put_buffer(fsave, buf, sizeof(buf));
|
||||
qemu_fclose(fsave);
|
||||
|
||||
QEMUFile *loading = open_test_file(false);
|
||||
QEMUFile *loading = open_mem_file_read(buf, sizeof(buf));
|
||||
TestStruct obj = { .skip_c_e = false };
|
||||
vmstate_load_state(loading, &vmstate_skipping, &obj, 2);
|
||||
g_assert(!qemu_file_get_error(loading));
|
||||
@@ -460,7 +465,6 @@ static void test_load_noskip(void)
|
||||
|
||||
static void test_load_skip(void)
|
||||
{
|
||||
QEMUFile *fsave = open_test_file(true);
|
||||
uint8_t buf[] = {
|
||||
0, 0, 0, 10, /* a */
|
||||
0, 0, 0, 20, /* b */
|
||||
@@ -468,10 +472,8 @@ static void test_load_skip(void)
|
||||
0, 0, 0, 0, 0, 0, 0, 60, /* f */
|
||||
QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */
|
||||
};
|
||||
qemu_put_buffer(fsave, buf, sizeof(buf));
|
||||
qemu_fclose(fsave);
|
||||
|
||||
QEMUFile *loading = open_test_file(false);
|
||||
QEMUFile *loading = open_mem_file_read(buf, sizeof(buf));
|
||||
TestStruct obj = { .skip_c_e = true, .c = 300, .e = 500 };
|
||||
vmstate_load_state(loading, &vmstate_skipping, &obj, 2);
|
||||
g_assert(!qemu_file_get_error(loading));
|
||||
|
||||
@@ -49,9 +49,16 @@ static void *vmstate_base_addr(void *opaque, VMStateField *field, bool alloc)
|
||||
|
||||
if (field->flags & VMS_POINTER) {
|
||||
if (alloc && (field->flags & VMS_ALLOC)) {
|
||||
int n_elems = vmstate_n_elems(opaque, field);
|
||||
if (n_elems) {
|
||||
gsize size = n_elems * field->size;
|
||||
gsize size = 0;
|
||||
if (field->flags & VMS_VBUFFER) {
|
||||
size = vmstate_size(opaque, field);
|
||||
} else {
|
||||
int n_elems = vmstate_n_elems(opaque, field);
|
||||
if (n_elems) {
|
||||
size = n_elems * field->size;
|
||||
}
|
||||
}
|
||||
if (size) {
|
||||
*((void **)base_addr + field->start) = g_malloc(size);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user