From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Thu, 13 Oct 2022 11:33:50 +0200 Subject: [PATCH] PVE: add IOChannel implementation for savevm-async based on migration/channel-block.c and the implementation that was present in migration/savevm-async.c before QEMU 7.1. Passes along read/write requests to the given BlockBackend, while ensuring that a read request going beyond the end results in a graceful short read. Additionally, allows tracking the current position from the outside (intended to be used for progress tracking). Signed-off-by: Fiona Ebner Signed-off-by: Thomas Lamprecht --- migration/channel-savevm-async.c | 184 +++++++++++++++++++++++++++++++ migration/channel-savevm-async.h | 51 +++++++++ migration/meson.build | 1 + 3 files changed, 236 insertions(+) create mode 100644 migration/channel-savevm-async.c create mode 100644 migration/channel-savevm-async.h diff --git a/migration/channel-savevm-async.c b/migration/channel-savevm-async.c new file mode 100644 index 0000000000..081a192f49 --- /dev/null +++ b/migration/channel-savevm-async.c @@ -0,0 +1,184 @@ +/* + * QIO Channel implementation to be used by savevm-async QMP calls + */ +#include "qemu/osdep.h" +#include "migration/channel-savevm-async.h" +#include "qapi/error.h" +#include "sysemu/block-backend.h" +#include "trace.h" + +QIOChannelSavevmAsync * +qio_channel_savevm_async_new(BlockBackend *be, size_t *bs_pos) +{ + QIOChannelSavevmAsync *ioc; + + ioc = QIO_CHANNEL_SAVEVM_ASYNC(object_new(TYPE_QIO_CHANNEL_SAVEVM_ASYNC)); + + bdrv_ref(blk_bs(be)); + ioc->be = be; + ioc->bs_pos = bs_pos; + + return ioc; +} + + +static void +qio_channel_savevm_async_finalize(Object *obj) +{ + QIOChannelSavevmAsync *ioc = QIO_CHANNEL_SAVEVM_ASYNC(obj); + + if (ioc->be) { + bdrv_unref(blk_bs(ioc->be)); + ioc->be = NULL; + } + ioc->bs_pos = NULL; +} + + +static ssize_t +qio_channel_savevm_async_readv(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + int **fds, + size_t *nfds, + int flags, + Error **errp) +{ + QIOChannelSavevmAsync *saioc = QIO_CHANNEL_SAVEVM_ASYNC(ioc); + BlockBackend *be = saioc->be; + int64_t maxlen = blk_getlength(be); + QEMUIOVector qiov; + size_t size; + int ret; + + qemu_iovec_init_external(&qiov, (struct iovec *)iov, niov); + + if (*saioc->bs_pos >= maxlen) { + error_setg(errp, "cannot read beyond maxlen"); + return -1; + } + + if (maxlen - *saioc->bs_pos < qiov.size) { + size = maxlen - *saioc->bs_pos; + } else { + size = qiov.size; + } + + // returns 0 on success + ret = blk_preadv(be, *saioc->bs_pos, size, &qiov, 0); + if (ret < 0) { + error_setg_errno(errp, -ret, "blk_preadv failed"); + return -1; + } + + *saioc->bs_pos += size; + return size; +} + + +static ssize_t +qio_channel_savevm_async_writev(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + int *fds, + size_t nfds, + int flags, + Error **errp) +{ + QIOChannelSavevmAsync *saioc = QIO_CHANNEL_SAVEVM_ASYNC(ioc); + BlockBackend *be = saioc->be; + QEMUIOVector qiov; + int ret; + + qemu_iovec_init_external(&qiov, (struct iovec *)iov, niov); + + if (qemu_in_coroutine()) { + ret = blk_co_pwritev(be, *saioc->bs_pos, qiov.size, &qiov, 0); + aio_wait_kick(); + } else { + ret = blk_pwritev(be, *saioc->bs_pos, qiov.size, &qiov, 0); + } + + if (ret < 0) { + error_setg_errno(errp, -ret, "blk(_co)_pwritev failed"); + return -1; + } + + *saioc->bs_pos += qiov.size; + return qiov.size; +} + + +static int +qio_channel_savevm_async_set_blocking(QIOChannel *ioc, + bool enabled, + Error **errp) +{ + if (!enabled) { + error_setg(errp, "Non-blocking mode not supported for savevm-async"); + return -1; + } + return 0; +} + + +static int +qio_channel_savevm_async_close(QIOChannel *ioc, + Error **errp) +{ + QIOChannelSavevmAsync *saioc = QIO_CHANNEL_SAVEVM_ASYNC(ioc); + int rv = bdrv_flush(blk_bs(saioc->be)); + + if (rv < 0) { + error_setg_errno(errp, -rv, "Unable to flush VMState"); + return -1; + } + + bdrv_unref(blk_bs(saioc->be)); + saioc->be = NULL; + saioc->bs_pos = NULL; + + return 0; +} + + +static void +qio_channel_savevm_async_set_aio_fd_handler(QIOChannel *ioc, + AioContext *read_ctx, + IOHandler *io_read, + AioContext *write_ctx, + IOHandler *io_write, + void *opaque) +{ + // if channel-block starts doing something, check if this needs adaptation +} + + +static void +qio_channel_savevm_async_class_init(ObjectClass *klass, + void *class_data G_GNUC_UNUSED) +{ + QIOChannelClass *ioc_klass = QIO_CHANNEL_CLASS(klass); + + ioc_klass->io_writev = qio_channel_savevm_async_writev; + ioc_klass->io_readv = qio_channel_savevm_async_readv; + ioc_klass->io_set_blocking = qio_channel_savevm_async_set_blocking; + ioc_klass->io_close = qio_channel_savevm_async_close; + ioc_klass->io_set_aio_fd_handler = qio_channel_savevm_async_set_aio_fd_handler; +} + +static const TypeInfo qio_channel_savevm_async_info = { + .parent = TYPE_QIO_CHANNEL, + .name = TYPE_QIO_CHANNEL_SAVEVM_ASYNC, + .instance_size = sizeof(QIOChannelSavevmAsync), + .instance_finalize = qio_channel_savevm_async_finalize, + .class_init = qio_channel_savevm_async_class_init, +}; + +static void +qio_channel_savevm_async_register_types(void) +{ + type_register_static(&qio_channel_savevm_async_info); +} + +type_init(qio_channel_savevm_async_register_types); diff --git a/migration/channel-savevm-async.h b/migration/channel-savevm-async.h new file mode 100644 index 0000000000..17ae2cb261 --- /dev/null +++ b/migration/channel-savevm-async.h @@ -0,0 +1,51 @@ +/* + * QEMU I/O channels driver for savevm-async.c + * + * Copyright (c) 2022 Proxmox Server Solutions + * + * Authors: + * Fiona Ebner (f.ebner@proxmox.com) + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef QIO_CHANNEL_SAVEVM_ASYNC_H +#define QIO_CHANNEL_SAVEVM_ASYNC_H + +#include "io/channel.h" +#include "qom/object.h" + +#define TYPE_QIO_CHANNEL_SAVEVM_ASYNC "qio-channel-savevm-async" +OBJECT_DECLARE_SIMPLE_TYPE(QIOChannelSavevmAsync, QIO_CHANNEL_SAVEVM_ASYNC) + + +/** + * QIOChannelSavevmAsync: + * + * The QIOChannelBlock object provides a channel implementation that is able to + * perform I/O on any BlockBackend whose BlockDriverState directly contains a + * VMState (as opposed to indirectly, like qcow2). It allows tracking the + * current position from the outside. + */ +struct QIOChannelSavevmAsync { + QIOChannel parent; + BlockBackend *be; + size_t *bs_pos; +}; + + +/** + * qio_channel_savevm_async_new: + * @be: the block backend + * @bs_pos: used to keep track of the IOChannels current position + * + * Create a new IO channel object that can perform I/O on a BlockBackend object + * whose BlockDriverState directly contains a VMState. + * + * Returns: the new channel object + */ +QIOChannelSavevmAsync * +qio_channel_savevm_async_new(BlockBackend *be, size_t *bs_pos); + +#endif /* QIO_CHANNEL_SAVEVM_ASYNC_H */ diff --git a/migration/meson.build b/migration/meson.build index 1eeb915ff6..95d1cf2250 100644 --- a/migration/meson.build +++ b/migration/meson.build @@ -13,6 +13,7 @@ system_ss.add(files( 'block-dirty-bitmap.c', 'channel.c', 'channel-block.c', + 'channel-savevm-async.c', 'dirtyrate.c', 'exec.c', 'fd.c',