diff --git a/migration/channel-block.c b/migration/channel-block.c new file mode 100644 index 0000000000..c55c8c93ce --- /dev/null +++ b/migration/channel-block.c @@ -0,0 +1,195 @@ +/* + * QEMU I/O channels block driver + * + * Copyright (c) 2022 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + * + */ + +#include "qemu/osdep.h" +#include "migration/channel-block.h" +#include "qapi/error.h" +#include "block/block.h" +#include "trace.h" + +QIOChannelBlock * +qio_channel_block_new(BlockDriverState *bs) +{ + QIOChannelBlock *ioc; + + ioc = QIO_CHANNEL_BLOCK(object_new(TYPE_QIO_CHANNEL_BLOCK)); + + bdrv_ref(bs); + ioc->bs = bs; + + return ioc; +} + + +static void +qio_channel_block_finalize(Object *obj) +{ + QIOChannelBlock *ioc = QIO_CHANNEL_BLOCK(obj); + + g_clear_pointer(&ioc->bs, bdrv_unref); +} + + +static ssize_t +qio_channel_block_readv(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + int **fds, + size_t *nfds, + Error **errp) +{ + QIOChannelBlock *bioc = QIO_CHANNEL_BLOCK(ioc); + QEMUIOVector qiov; + int ret; + + qemu_iovec_init_external(&qiov, (struct iovec *)iov, niov); + ret = bdrv_readv_vmstate(bioc->bs, &qiov, bioc->offset); + if (ret < 0) { + return ret; + } + + bioc->offset += qiov.size; + return qiov.size; +} + + +static ssize_t +qio_channel_block_writev(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + int *fds, + size_t nfds, + int flags, + Error **errp) +{ + QIOChannelBlock *bioc = QIO_CHANNEL_BLOCK(ioc); + QEMUIOVector qiov; + int ret; + + qemu_iovec_init_external(&qiov, (struct iovec *)iov, niov); + ret = bdrv_writev_vmstate(bioc->bs, &qiov, bioc->offset); + if (ret < 0) { + return ret; + } + + bioc->offset += qiov.size; + return qiov.size; +} + + +static int +qio_channel_block_set_blocking(QIOChannel *ioc, + bool enabled, + Error **errp) +{ + if (!enabled) { + error_setg(errp, "Non-blocking mode not supported for block devices"); + return -1; + } + return 0; +} + + +static off_t +qio_channel_block_seek(QIOChannel *ioc, + off_t offset, + int whence, + Error **errp) +{ + QIOChannelBlock *bioc = QIO_CHANNEL_BLOCK(ioc); + + switch (whence) { + case SEEK_SET: + bioc->offset = offset; + break; + case SEEK_CUR: + bioc->offset += whence; + break; + case SEEK_END: + error_setg(errp, "Size of VMstate region is unknown"); + return (off_t)-1; + default: + g_assert_not_reached(); + } + + return bioc->offset; +} + + +static int +qio_channel_block_close(QIOChannel *ioc, + Error **errp) +{ + QIOChannelBlock *bioc = QIO_CHANNEL_BLOCK(ioc); + int rv = bdrv_flush(bioc->bs); + + if (rv < 0) { + error_setg_errno(errp, -rv, + "Unable to flush VMState"); + return -1; + } + + g_clear_pointer(&bioc->bs, bdrv_unref); + bioc->offset = 0; + + return 0; +} + + +static void +qio_channel_block_set_aio_fd_handler(QIOChannel *ioc, + AioContext *ctx, + IOHandler *io_read, + IOHandler *io_write, + void *opaque) +{ + /* XXX anything we can do here ? */ +} + + +static void +qio_channel_block_class_init(ObjectClass *klass, + void *class_data G_GNUC_UNUSED) +{ + QIOChannelClass *ioc_klass = QIO_CHANNEL_CLASS(klass); + + ioc_klass->io_writev = qio_channel_block_writev; + ioc_klass->io_readv = qio_channel_block_readv; + ioc_klass->io_set_blocking = qio_channel_block_set_blocking; + ioc_klass->io_seek = qio_channel_block_seek; + ioc_klass->io_close = qio_channel_block_close; + ioc_klass->io_set_aio_fd_handler = qio_channel_block_set_aio_fd_handler; +} + +static const TypeInfo qio_channel_block_info = { + .parent = TYPE_QIO_CHANNEL, + .name = TYPE_QIO_CHANNEL_BLOCK, + .instance_size = sizeof(QIOChannelBlock), + .instance_finalize = qio_channel_block_finalize, + .class_init = qio_channel_block_class_init, +}; + +static void +qio_channel_block_register_types(void) +{ + type_register_static(&qio_channel_block_info); +} + +type_init(qio_channel_block_register_types); diff --git a/migration/channel-block.h b/migration/channel-block.h new file mode 100644 index 0000000000..31673824e6 --- /dev/null +++ b/migration/channel-block.h @@ -0,0 +1,59 @@ +/* + * QEMU I/O channels block driver + * + * Copyright (c) 2022 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + * + */ + +#ifndef QIO_CHANNEL_BLOCK_H +#define QIO_CHANNEL_BLOCK_H + +#include "io/channel.h" +#include "qom/object.h" + +#define TYPE_QIO_CHANNEL_BLOCK "qio-channel-block" +OBJECT_DECLARE_SIMPLE_TYPE(QIOChannelBlock, QIO_CHANNEL_BLOCK) + + +/** + * QIOChannelBlock: + * + * The QIOChannelBlock object provides a channel implementation + * that is able to perform I/O on the BlockDriverState objects + * to the VMState region. + */ + +struct QIOChannelBlock { + QIOChannel parent; + BlockDriverState *bs; + off_t offset; +}; + + +/** + * qio_channel_block_new: + * @bs: the block driver state + * + * Create a new IO channel object that can perform + * I/O on a BlockDriverState object to the VMState + * region + * + * Returns: the new channel object + */ +QIOChannelBlock * +qio_channel_block_new(BlockDriverState *bs); + +#endif /* QIO_CHANNEL_BLOCK_H */ diff --git a/migration/meson.build b/migration/meson.build index 6880b61b10..8d309f5849 100644 --- a/migration/meson.build +++ b/migration/meson.build @@ -13,6 +13,7 @@ softmmu_ss.add(migration_files) softmmu_ss.add(files( 'block-dirty-bitmap.c', 'channel.c', + 'channel-block.c', 'colo-failover.c', 'colo.c', 'exec.c',