diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2014-12-16 14:53:23 +0000 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2014-12-16 14:53:23 +0000 |
commit | 4db753b1ac4aedc6cd67fb13d50e5015ce8052a5 (patch) | |
tree | 73b6bd91ecca384d5efe16e193966b663a9367eb | |
parent | dfa9c2a0f4d0a0c8b2c1449ecdbb1297427e1560 (diff) | |
parent | 44a1f94684eeaa6e312ea2d77ede266a81d31210 (diff) |
Merge remote-tracking branch 'remotes/amit-migration/tags/for-2.3-2' into staging
Migration pull for 2.3. Mostly moving the code to the migration/
directory, and updating MAINTAINERS.
I've also folded my other MAINTAINERS update patches into this, as
they're small by themselves.
# gpg: Signature made Tue 16 Dec 2014 12:21:24 GMT using RSA key ID 854083B6
# gpg: Good signature from "Amit Shah <amit@amitshah.net>"
# gpg: aka "Amit Shah <amit@kernel.org>"
# gpg: aka "Amit Shah <amitshah@gmx.net>"
* remotes/amit-migration/tags/for-2.3-2:
MAINTAINERS: Update for migrated migration code
Split the QEMU buffered file code out
Split struct QEMUFile out
Remove migration- pre/post fixes off files in migration/ dir
Start migrating migration code into a migration directory
qmp-command.hx: add missing docs for migration capabilites
cpu: verify that block->host is set
cpu: assert host pointer offset within block
exec: add wrapper for host pointer access
MAINTAINERS: add include files to virtio-serial entry
MAINTAINERS: add entry for virtio-rng
MAINTAINERS: migration: add vmstate static checker files
MAINTAINERS: Add myself to migration maintainers
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
-rw-r--r-- | MAINTAINERS | 14 | ||||
-rw-r--r-- | Makefile.objs | 10 | ||||
-rw-r--r-- | exec.c | 10 | ||||
-rw-r--r-- | include/exec/cpu-all.h | 7 | ||||
-rw-r--r-- | migration/Makefile.objs | 10 | ||||
-rw-r--r-- | migration/block.c (renamed from block-migration.c) | 0 | ||||
-rw-r--r-- | migration/exec.c (renamed from migration-exec.c) | 0 | ||||
-rw-r--r-- | migration/fd.c (renamed from migration-fd.c) | 0 | ||||
-rw-r--r-- | migration/migration.c (renamed from migration.c) | 0 | ||||
-rw-r--r-- | migration/qemu-file-buf.c (renamed from qemu-file.c) | 511 | ||||
-rw-r--r-- | migration/qemu-file-internal.h | 53 | ||||
-rw-r--r-- | migration/qemu-file-stdio.c (renamed from qemu-file-stdio.c) | 0 | ||||
-rw-r--r-- | migration/qemu-file-unix.c (renamed from qemu-file-unix.c) | 0 | ||||
-rw-r--r-- | migration/qemu-file.c | 519 | ||||
-rw-r--r-- | migration/rdma.c (renamed from migration-rdma.c) | 0 | ||||
-rw-r--r-- | migration/tcp.c (renamed from migration-tcp.c) | 0 | ||||
-rw-r--r-- | migration/unix.c (renamed from migration-unix.c) | 0 | ||||
-rw-r--r-- | migration/vmstate.c (renamed from vmstate.c) | 0 | ||||
-rw-r--r-- | migration/xbzrle.c (renamed from xbzrle.c) | 0 | ||||
-rw-r--r-- | qmp-commands.hx | 6 | ||||
-rw-r--r-- | tests/Makefile | 11 |
21 files changed, 622 insertions, 529 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index bcb69e80d2..d72d6e37d0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -696,6 +696,14 @@ M: Amit Shah <amit.shah@redhat.com> S: Supported F: hw/char/virtio-serial-bus.c F: hw/char/virtio-console.c +F: include/hw/virtio/virtio-serial.h + +virtio-rng +M: Amit Shah <amit.shah@redhat.com> +S: Supported +F: hw/virtio/virtio-rng.c +F: include/hw/virtio/virtio-rng.h +F: backends/rng*.c nvme M: Keith Busch <keith.busch@intel.com> @@ -928,12 +936,14 @@ F: scripts/checkpatch.pl Migration M: Juan Quintela <quintela@redhat.com> +M: Amit Shah <amit.shah@redhat.com> S: Maintained F: include/migration/ -F: migration* +F: migration/ F: savevm.c F: arch_init.c -F: vmstate.c +F: scripts/vmstate-static-checker.py +F: tests/vmstate-static-checker-data/ Seccomp M: Eduardo Otubo <eduardo.otubo@profitbricks.com> diff --git a/Makefile.objs b/Makefile.objs index 18fd35cf15..abeb902b58 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -48,15 +48,9 @@ common-obj-$(CONFIG_POSIX) += os-posix.o common-obj-$(CONFIG_LINUX) += fsdev/ -common-obj-y += migration.o migration-tcp.o -common-obj-y += vmstate.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 += migration/ common-obj-y += qemu-char.o #aio.o -common-obj-y += block-migration.o -common-obj-y += page_cache.o xbzrle.o - -common-obj-$(CONFIG_POSIX) += migration-exec.o migration-unix.o migration-fd.o +common-obj-y += page_cache.o common-obj-$(CONFIG_SPICE) += spice-qemu-char.o @@ -840,7 +840,7 @@ static void tlb_reset_dirty_range_all(ram_addr_t start, ram_addr_t length) block = qemu_get_ram_block(start); assert(block == qemu_get_ram_block(end - 1)); - start1 = (uintptr_t)block->host + (start - block->offset); + start1 = (uintptr_t)ramblock_ptr(block, start - block->offset); cpu_tlb_reset_dirty_all(start1, length); } @@ -1500,7 +1500,7 @@ void qemu_ram_remap(ram_addr_t addr, ram_addr_t length) QTAILQ_FOREACH(block, &ram_list.blocks, next) { offset = addr - block->offset; if (offset < block->length) { - vaddr = block->host + offset; + vaddr = ramblock_ptr(block, offset); if (block->flags & RAM_PREALLOC) { ; } else if (xen_enabled()) { @@ -1551,7 +1551,7 @@ void *qemu_get_ram_block_host_ptr(ram_addr_t addr) { RAMBlock *block = qemu_get_ram_block(addr); - return block->host; + return ramblock_ptr(block, 0); } /* Return a host pointer to ram allocated with qemu_ram_alloc. @@ -1578,7 +1578,7 @@ void *qemu_get_ram_ptr(ram_addr_t addr) xen_map_cache(block->offset, block->length, 1); } } - return block->host + (addr - block->offset); + return ramblock_ptr(block, addr - block->offset); } /* Return a host pointer to guest's ram. Similar to qemu_get_ram_ptr @@ -1597,7 +1597,7 @@ static void *qemu_ram_ptr_length(ram_addr_t addr, hwaddr *size) if (addr - block->offset < block->length) { if (addr - block->offset + *size > block->length) *size = block->length - addr + block->offset; - return block->host + (addr - block->offset); + return ramblock_ptr(block, addr - block->offset); } } diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index c085804aed..62f558103d 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -313,6 +313,13 @@ typedef struct RAMBlock { int fd; } RAMBlock; +static inline void *ramblock_ptr(RAMBlock *block, ram_addr_t offset) +{ + assert(offset < block->length); + assert(block->host); + return (char *)block->host + offset; +} + typedef struct RAMList { QemuMutex mutex; /* Protected by the iothread lock. */ diff --git a/migration/Makefile.objs b/migration/Makefile.objs new file mode 100644 index 0000000000..d929e969ae --- /dev/null +++ b/migration/Makefile.objs @@ -0,0 +1,10 @@ +common-obj-y += migration.o tcp.o +common-obj-y += vmstate.o +common-obj-y += qemu-file.o qemu-file-buf.o qemu-file-unix.o qemu-file-stdio.o +common-obj-y += xbzrle.o + +common-obj-$(CONFIG_RDMA) += rdma.o +common-obj-$(CONFIG_POSIX) += exec.o unix.o fd.o + +common-obj-y += block.o + diff --git a/block-migration.c b/migration/block.c index 74d9eb125c..74d9eb125c 100644 --- a/block-migration.c +++ b/migration/block.c diff --git a/migration-exec.c b/migration/exec.c index 479024752f..479024752f 100644 --- a/migration-exec.c +++ b/migration/exec.c diff --git a/migration-fd.c b/migration/fd.c index d2e523af74..d2e523af74 100644 --- a/migration-fd.c +++ b/migration/fd.c diff --git a/migration.c b/migration/migration.c index c49a05a165..c49a05a165 100644 --- a/migration.c +++ b/migration/migration.c diff --git a/qemu-file.c b/migration/qemu-file-buf.c index f938e36fe8..d33dd44747 100644 --- a/qemu-file.c +++ b/migration/qemu-file-buf.c @@ -27,518 +27,9 @@ #include "block/coroutine.h" #include "migration/migration.h" #include "migration/qemu-file.h" +#include "migration/qemu-file-internal.h" #include "trace.h" -#define IO_BUF_SIZE 32768 -#define MAX_IOV_SIZE MIN(IOV_MAX, 64) - -struct QEMUFile { - const QEMUFileOps *ops; - void *opaque; - - 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]; - - struct iovec iov[MAX_IOV_SIZE]; - unsigned int iovcnt; - - int last_error; -}; - -bool qemu_file_mode_is_not_valid(const char *mode) -{ - if (mode == NULL || - (mode[0] != 'r' && mode[0] != 'w') || - mode[1] != 'b' || mode[2] != 0) { - fprintf(stderr, "qemu_fopen: Argument validity check failed\n"); - return true; - } - - return false; -} - -QEMUFile *qemu_fopen_ops(void *opaque, const QEMUFileOps *ops) -{ - QEMUFile *f; - - f = g_malloc0(sizeof(QEMUFile)); - - f->opaque = opaque; - f->ops = ops; - return 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->last_error; -} - -void qemu_file_set_error(QEMUFile *f, int ret) -{ - if (f->last_error == 0) { - f->last_error = ret; - } -} - -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); - } -} - -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; -} - -/* - * Attempt to fill the buffer from the underlying file - * Returns the number of bytes read, or negative value for an error. - * - * Note that it can return a partially full buffer even in a not error/not EOF - * case if the underlying file descriptor gives a short read, and that can - * happen even on a blocking fd. - */ -static ssize_t qemu_fill_buffer(QEMUFile *f) -{ - int len; - int pending; - - assert(!qemu_file_is_writable(f)); - - 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 + pending, f->pos, - IO_BUF_SIZE - pending); - if (len > 0) { - f->buf_size += len; - f->pos += len; - } else if (len == 0) { - qemu_file_set_error(f, -EIO); - } else if (len != -EAGAIN) { - qemu_file_set_error(f, len); - } - - return 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; - qemu_fflush(f); - 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); - trace_qemu_file_fclose(); - return ret; -} - -static void add_to_iovec(QEMUFile *f, const uint8_t *buf, int size) -{ - /* 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->last_error) { - return; - } - - while (size > 0) { - l = IO_BUF_SIZE - f->buf_index; - if (l > size) { - l = size; - } - memcpy(f->buf + f->buf_index, buf, l); - 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; - } -} - -void qemu_put_byte(QEMUFile *f, int v) -{ - if (f->last_error) { - return; - } - - 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); - } -} - -void qemu_file_skip(QEMUFile *f, int size) -{ - if (f->buf_index + size <= f->buf_size) { - f->buf_index += size; - } -} - -/* - * Read 'size' bytes from file (at 'offset') into buf without moving the - * pointer. - * - * It will return size bytes unless there was an error, in which case it will - * return as many as it managed to read (assuming blocking fd's which - * all current QEMUFile are) - */ -int qemu_peek_buffer(QEMUFile *f, uint8_t *buf, int size, size_t offset) -{ - int pending; - int index; - - assert(!qemu_file_is_writable(f)); - assert(offset < IO_BUF_SIZE); - assert(size <= IO_BUF_SIZE - offset); - - /* The 1st byte to read from */ - index = f->buf_index + offset; - /* The number of available bytes starting at index */ - pending = f->buf_size - index; - - /* - * qemu_fill_buffer might return just a few bytes, even when there isn't - * an error, so loop collecting them until we get enough. - */ - while (pending < size) { - int received = qemu_fill_buffer(f); - - if (received <= 0) { - break; - } - - 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; -} - -/* - * Read 'size' bytes of data from the file into buf. - * 'size' can be larger than the internal buffer. - * - * It will return size bytes unless there was an error, in which case it will - * return as many as it managed to read (assuming blocking fd's which - * all current QEMUFile are) - */ -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, MIN(pending, IO_BUF_SIZE), 0); - if (res == 0) { - return done; - } - qemu_file_skip(f, res); - buf += res; - pending -= res; - done += res; - } - return done; -} - -/* - * Peeks a single byte from the buffer; this isn't guaranteed to work if - * offset leaves a gap after the previous read/peeked data. - */ -int qemu_peek_byte(QEMUFile *f, int offset) -{ - int index = f->buf_index + offset; - - assert(!qemu_file_is_writable(f)); - assert(offset < IO_BUF_SIZE); - - 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) -{ - int result; - - result = qemu_peek_byte(f, 0); - qemu_file_skip(f, 1); - return result; -} - -int64_t qemu_ftell(QEMUFile *f) -{ - qemu_fflush(f); - return f->pos; -} - -int qemu_file_rate_limit(QEMUFile *f) -{ - 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) -{ - return f->xfer_limit; -} - -void qemu_file_set_rate_limit(QEMUFile *f, int64_t limit) -{ - f->xfer_limit = limit; -} - -void qemu_file_reset_rate_limit(QEMUFile *f) -{ - f->bytes_xfer = 0; -} - -void qemu_put_be16(QEMUFile *f, unsigned int v) -{ - qemu_put_byte(f, v >> 8); - qemu_put_byte(f, v); -} - -void qemu_put_be32(QEMUFile *f, unsigned int v) -{ - qemu_put_byte(f, v >> 24); - qemu_put_byte(f, v >> 16); - qemu_put_byte(f, v >> 8); - qemu_put_byte(f, v); -} - -void qemu_put_be64(QEMUFile *f, uint64_t v) -{ - qemu_put_be32(f, v >> 32); - qemu_put_be32(f, v); -} - -unsigned int qemu_get_be16(QEMUFile *f) -{ - unsigned int v; - v = qemu_get_byte(f) << 8; - v |= qemu_get_byte(f); - return v; -} - -unsigned int qemu_get_be32(QEMUFile *f) -{ - unsigned int v; - v = qemu_get_byte(f) << 24; - v |= qemu_get_byte(f) << 16; - v |= qemu_get_byte(f) << 8; - v |= qemu_get_byte(f); - return v; -} - -uint64_t qemu_get_be64(QEMUFile *f) -{ - uint64_t v; - v = (uint64_t)qemu_get_be32(f) << 32; - v |= qemu_get_be32(f); - return v; -} - #define QSB_CHUNK_SIZE (1 << 10) #define QSB_MAX_CHUNK_SIZE (16 * QSB_CHUNK_SIZE) diff --git a/migration/qemu-file-internal.h b/migration/qemu-file-internal.h new file mode 100644 index 0000000000..d95e8538e7 --- /dev/null +++ b/migration/qemu-file-internal.h @@ -0,0 +1,53 @@ +/* + * 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. + */ + +#ifndef QEMU_FILE_INTERNAL_H +#define QEMU_FILE_INTERNAL_H 1 + +#include "qemu-common.h" +#include "qemu/iov.h" + +#define IO_BUF_SIZE 32768 +#define MAX_IOV_SIZE MIN(IOV_MAX, 64) + +struct QEMUFile { + const QEMUFileOps *ops; + void *opaque; + + 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]; + + struct iovec iov[MAX_IOV_SIZE]; + unsigned int iovcnt; + + int last_error; +}; + +#endif diff --git a/qemu-file-stdio.c b/migration/qemu-file-stdio.c index 285068b303..285068b303 100644 --- a/qemu-file-stdio.c +++ b/migration/qemu-file-stdio.c diff --git a/qemu-file-unix.c b/migration/qemu-file-unix.c index 9682396d97..9682396d97 100644 --- a/qemu-file-unix.c +++ b/migration/qemu-file-unix.c diff --git a/migration/qemu-file.c b/migration/qemu-file.c new file mode 100644 index 0000000000..d2d40073f0 --- /dev/null +++ b/migration/qemu-file.c @@ -0,0 +1,519 @@ +/* + * 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/migration.h" +#include "migration/qemu-file.h" +#include "migration/qemu-file-internal.h" +#include "trace.h" + +bool qemu_file_mode_is_not_valid(const char *mode) +{ + if (mode == NULL || + (mode[0] != 'r' && mode[0] != 'w') || + mode[1] != 'b' || mode[2] != 0) { + fprintf(stderr, "qemu_fopen: Argument validity check failed\n"); + return true; + } + + return false; +} + +QEMUFile *qemu_fopen_ops(void *opaque, const QEMUFileOps *ops) +{ + QEMUFile *f; + + f = g_malloc0(sizeof(QEMUFile)); + + f->opaque = opaque; + f->ops = ops; + return 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->last_error; +} + +void qemu_file_set_error(QEMUFile *f, int ret) +{ + if (f->last_error == 0) { + f->last_error = ret; + } +} + +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); + } +} + +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; +} + +/* + * Attempt to fill the buffer from the underlying file + * Returns the number of bytes read, or negative value for an error. + * + * Note that it can return a partially full buffer even in a not error/not EOF + * case if the underlying file descriptor gives a short read, and that can + * happen even on a blocking fd. + */ +static ssize_t qemu_fill_buffer(QEMUFile *f) +{ + int len; + int pending; + + assert(!qemu_file_is_writable(f)); + + 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 + pending, f->pos, + IO_BUF_SIZE - pending); + if (len > 0) { + f->buf_size += len; + f->pos += len; + } else if (len == 0) { + qemu_file_set_error(f, -EIO); + } else if (len != -EAGAIN) { + qemu_file_set_error(f, len); + } + + return 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; + qemu_fflush(f); + 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); + trace_qemu_file_fclose(); + return ret; +} + +static void add_to_iovec(QEMUFile *f, const uint8_t *buf, int size) +{ + /* 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->last_error) { + return; + } + + while (size > 0) { + l = IO_BUF_SIZE - f->buf_index; + if (l > size) { + l = size; + } + memcpy(f->buf + f->buf_index, buf, l); + 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; + } +} + +void qemu_put_byte(QEMUFile *f, int v) +{ + if (f->last_error) { + return; + } + + 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); + } +} + +void qemu_file_skip(QEMUFile *f, int size) +{ + if (f->buf_index + size <= f->buf_size) { + f->buf_index += size; + } +} + +/* + * Read 'size' bytes from file (at 'offset') into buf without moving the + * pointer. + * + * It will return size bytes unless there was an error, in which case it will + * return as many as it managed to read (assuming blocking fd's which + * all current QEMUFile are) + */ +int qemu_peek_buffer(QEMUFile *f, uint8_t *buf, int size, size_t offset) +{ + int pending; + int index; + + assert(!qemu_file_is_writable(f)); + assert(offset < IO_BUF_SIZE); + assert(size <= IO_BUF_SIZE - offset); + + /* The 1st byte to read from */ + index = f->buf_index + offset; + /* The number of available bytes starting at index */ + pending = f->buf_size - index; + + /* + * qemu_fill_buffer might return just a few bytes, even when there isn't + * an error, so loop collecting them until we get enough. + */ + while (pending < size) { + int received = qemu_fill_buffer(f); + + if (received <= 0) { + break; + } + + 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; +} + +/* + * Read 'size' bytes of data from the file into buf. + * 'size' can be larger than the internal buffer. + * + * It will return size bytes unless there was an error, in which case it will + * return as many as it managed to read (assuming blocking fd's which + * all current QEMUFile are) + */ +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, MIN(pending, IO_BUF_SIZE), 0); + if (res == 0) { + return done; + } + qemu_file_skip(f, res); + buf += res; + pending -= res; + done += res; + } + return done; +} + +/* + * Peeks a single byte from the buffer; this isn't guaranteed to work if + * offset leaves a gap after the previous read/peeked data. + */ +int qemu_peek_byte(QEMUFile *f, int offset) +{ + int index = f->buf_index + offset; + + assert(!qemu_file_is_writable(f)); + assert(offset < IO_BUF_SIZE); + + 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) +{ + int result; + + result = qemu_peek_byte(f, 0); + qemu_file_skip(f, 1); + return result; +} + +int64_t qemu_ftell(QEMUFile *f) +{ + qemu_fflush(f); + return f->pos; +} + +int qemu_file_rate_limit(QEMUFile *f) +{ + 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) +{ + return f->xfer_limit; +} + +void qemu_file_set_rate_limit(QEMUFile *f, int64_t limit) +{ + f->xfer_limit = limit; +} + +void qemu_file_reset_rate_limit(QEMUFile *f) +{ + f->bytes_xfer = 0; +} + +void qemu_put_be16(QEMUFile *f, unsigned int v) +{ + qemu_put_byte(f, v >> 8); + qemu_put_byte(f, v); +} + +void qemu_put_be32(QEMUFile *f, unsigned int v) +{ + qemu_put_byte(f, v >> 24); + qemu_put_byte(f, v >> 16); + qemu_put_byte(f, v >> 8); + qemu_put_byte(f, v); +} + +void qemu_put_be64(QEMUFile *f, uint64_t v) +{ + qemu_put_be32(f, v >> 32); + qemu_put_be32(f, v); +} + +unsigned int qemu_get_be16(QEMUFile *f) +{ + unsigned int v; + v = qemu_get_byte(f) << 8; + v |= qemu_get_byte(f); + return v; +} + +unsigned int qemu_get_be32(QEMUFile *f) +{ + unsigned int v; + v = qemu_get_byte(f) << 24; + v |= qemu_get_byte(f) << 16; + v |= qemu_get_byte(f) << 8; + v |= qemu_get_byte(f); + return v; +} + +uint64_t qemu_get_be64(QEMUFile *f) +{ + uint64_t v; + v = (uint64_t)qemu_get_be32(f) << 32; + v |= qemu_get_be32(f); + return v; +} diff --git a/migration-rdma.c b/migration/rdma.c index b32dbdfccd..b32dbdfccd 100644 --- a/migration-rdma.c +++ b/migration/rdma.c diff --git a/migration-tcp.c b/migration/tcp.c index 91c9cf381e..91c9cf381e 100644 --- a/migration-tcp.c +++ b/migration/tcp.c diff --git a/migration-unix.c b/migration/unix.c index 1cdadfbc83..1cdadfbc83 100644 --- a/migration-unix.c +++ b/migration/unix.c diff --git a/vmstate.c b/migration/vmstate.c index 3dde574c0f..3dde574c0f 100644 --- a/vmstate.c +++ b/migration/vmstate.c diff --git a/xbzrle.c b/migration/xbzrle.c index 8e220bf25b..8e220bf25b 100644 --- a/xbzrle.c +++ b/migration/xbzrle.c diff --git a/qmp-commands.hx b/qmp-commands.hx index 33487820a4..6945d30198 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -3184,6 +3184,9 @@ migrate-set-capabilities Enable/Disable migration capabilities - "xbzrle": XBZRLE support +- "rdma-pin-all": pin all pages when using RDMA during migration +- "auto-converge": throttle down guest to help convergence of migration +- "zero-blocks": compress zero blocks during block migration Arguments: @@ -3208,6 +3211,9 @@ Query current migration capabilities - "capabilities": migration capabilities state - "xbzrle" : XBZRLE state (json-bool) + - "rdma-pin-all" : RDMA Pin Page state (json-bool) + - "auto-converge" : Auto Converge state (json-bool) + - "zero-blocks" : Zero Blocks state (json-bool) Arguments: diff --git a/tests/Makefile b/tests/Makefile index 16f0e4c805..e4ddb6a8c1 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -48,8 +48,11 @@ check-unit-y += tests/test-hbitmap$(EXESUF) check-unit-y += tests/test-x86-cpuid$(EXESUF) # all code tested by test-x86-cpuid is inside topology.h gcov-files-test-x86-cpuid-y = +ifeq ($(CONFIG_SOFTMMU),y) check-unit-y += tests/test-xbzrle$(EXESUF) -gcov-files-test-xbzrle-y = xbzrle.c +gcov-files-test-xbzrle-y = migration/xbzrle.c +check-unit-$(CONFIG_POSIX) += tests/test-vmstate$(EXESUF) +endif check-unit-y += tests/test-cutils$(EXESUF) gcov-files-test-cutils-y += util/cutils.c check-unit-y += tests/test-mul64$(EXESUF) @@ -61,7 +64,6 @@ check-unit-y += tests/test-bitops$(EXESUF) check-unit-$(CONFIG_HAS_GLIB_SUBPROCESS_TESTS) += tests/test-qdev-global-props$(EXESUF) check-unit-y += tests/check-qom-interface$(EXESUF) gcov-files-check-qom-interface-y = qom/object.c -check-unit-$(CONFIG_POSIX) += tests/test-vmstate$(EXESUF) check-unit-y += tests/test-qemu-opts$(EXESUF) gcov-files-test-qemu-opts-y = qom/test-qemu-opts.c @@ -247,7 +249,7 @@ tests/test-thread-pool$(EXESUF): tests/test-thread-pool.o $(block-obj-y) libqemu tests/test-iov$(EXESUF): tests/test-iov.o libqemuutil.a tests/test-hbitmap$(EXESUF): tests/test-hbitmap.o libqemuutil.a libqemustub.a tests/test-x86-cpuid$(EXESUF): tests/test-x86-cpuid.o -tests/test-xbzrle$(EXESUF): tests/test-xbzrle.o xbzrle.o page_cache.o libqemuutil.a +tests/test-xbzrle$(EXESUF): tests/test-xbzrle.o migration/xbzrle.o page_cache.o libqemuutil.a tests/test-cutils$(EXESUF): tests/test-cutils.o util/cutils.o tests/test-int128$(EXESUF): tests/test-int128.o tests/test-qdev-global-props$(EXESUF): tests/test-qdev-global-props.o \ @@ -258,7 +260,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 qemu-file-unix.o \ + migration/vmstate.o migration/qemu-file.o migration/qemu-file-buf.o \ + migration/qemu-file-unix.o \ libqemuutil.a libqemustub.a tests/test-qapi-types.c tests/test-qapi-types.h :\ |