summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MAINTAINERS7
-rw-r--r--Makefile2
-rw-r--r--Makefile.objs5
-rw-r--r--Makefile.target2
-rw-r--r--include/io/channel.h502
-rw-r--r--io/Makefile.objs1
-rw-r--r--io/channel.c291
7 files changed, 810 insertions, 0 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index e8cee1e266..55a0fd8ae5 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1243,6 +1243,13 @@ S: Odd fixes
F: util/buffer.c
F: include/qemu/buffer.h
+I/O Channels
+M: Daniel P. Berrange <berrange@redhat.com>
+S: Maintained
+F: io/
+F: include/io/
+F: tests/test-io-*
+
Usermode Emulation
------------------
Overall
diff --git a/Makefile b/Makefile
index 930ac2796b..af3e5f104d 100644
--- a/Makefile
+++ b/Makefile
@@ -159,6 +159,7 @@ dummy := $(call unnest-vars,, \
crypto-obj-y \
crypto-aes-obj-y \
qom-obj-y \
+ io-obj-y \
common-obj-y \
common-obj-m)
@@ -178,6 +179,7 @@ SOFTMMU_SUBDIR_RULES=$(filter %-softmmu,$(SUBDIR_RULES))
$(SOFTMMU_SUBDIR_RULES): $(block-obj-y)
$(SOFTMMU_SUBDIR_RULES): $(crypto-obj-y)
+$(SOFTMMU_SUBDIR_RULES): $(io-obj-y)
$(SOFTMMU_SUBDIR_RULES): config-all-devices.mak
subdir-%:
diff --git a/Makefile.objs b/Makefile.objs
index 77be052dde..dac2c02d9f 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -28,6 +28,11 @@ crypto-aes-obj-y = crypto/
qom-obj-y = qom/
+#######################################################################
+# io-obj-y is code used by both qemu system emulation and qemu-img
+
+io-obj-y = io/
+
######################################################################
# Target independent part of system emulation. The long term path is to
# suppress *all* target specific code in case of system emulation, i.e. a
diff --git a/Makefile.target b/Makefile.target
index 962d0045ff..34ddb7e762 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -176,6 +176,7 @@ dummy := $(call unnest-vars,.., \
crypto-obj-y \
crypto-aes-obj-y \
qom-obj-y \
+ io-obj-y \
common-obj-y \
common-obj-m)
target-obj-y := $(target-obj-y-save)
@@ -185,6 +186,7 @@ all-obj-y += $(qom-obj-y)
all-obj-$(CONFIG_SOFTMMU) += $(block-obj-y)
all-obj-$(CONFIG_USER_ONLY) += $(crypto-aes-obj-y)
all-obj-$(CONFIG_SOFTMMU) += $(crypto-obj-y)
+all-obj-$(CONFIG_SOFTMMU) += $(io-obj-y)
$(QEMU_PROG_BUILD): config-devices.mak
diff --git a/include/io/channel.h b/include/io/channel.h
new file mode 100644
index 0000000000..4ecec98bf4
--- /dev/null
+++ b/include/io/channel.h
@@ -0,0 +1,502 @@
+/*
+ * QEMU I/O channels
+ *
+ * Copyright (c) 2015 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 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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef QIO_CHANNEL_H__
+#define QIO_CHANNEL_H__
+
+#include "qemu-common.h"
+#include "qapi/error.h"
+#include "qom/object.h"
+
+#define TYPE_QIO_CHANNEL "qio-channel"
+#define QIO_CHANNEL(obj) \
+ OBJECT_CHECK(QIOChannel, (obj), TYPE_QIO_CHANNEL)
+#define QIO_CHANNEL_CLASS(klass) \
+ OBJECT_CLASS_CHECK(QIOChannelClass, klass, TYPE_QIO_CHANNEL)
+#define QIO_CHANNEL_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(QIOChannelClass, obj, TYPE_QIO_CHANNEL)
+
+typedef struct QIOChannel QIOChannel;
+typedef struct QIOChannelClass QIOChannelClass;
+
+#define QIO_CHANNEL_ERR_BLOCK -2
+
+typedef enum QIOChannelFeature QIOChannelFeature;
+
+enum QIOChannelFeature {
+ QIO_CHANNEL_FEATURE_FD_PASS = (1 << 0),
+ QIO_CHANNEL_FEATURE_SHUTDOWN = (1 << 1),
+};
+
+
+typedef enum QIOChannelShutdown QIOChannelShutdown;
+
+enum QIOChannelShutdown {
+ QIO_CHANNEL_SHUTDOWN_BOTH,
+ QIO_CHANNEL_SHUTDOWN_READ,
+ QIO_CHANNEL_SHUTDOWN_WRITE,
+};
+
+typedef gboolean (*QIOChannelFunc)(QIOChannel *ioc,
+ GIOCondition condition,
+ gpointer data);
+
+/**
+ * QIOChannel:
+ *
+ * The QIOChannel defines the core API for a generic I/O channel
+ * class hierarchy. It is inspired by GIOChannel, but has the
+ * following differences
+ *
+ * - Use QOM to properly support arbitrary subclassing
+ * - Support use of iovecs for efficient I/O with multiple blocks
+ * - None of the character set translation, binary data exclusively
+ * - Direct support for QEMU Error object reporting
+ * - File descriptor passing
+ *
+ * This base class is abstract so cannot be instantiated. There
+ * will be subclasses for dealing with sockets, files, and higher
+ * level protocols such as TLS, WebSocket, etc.
+ */
+
+struct QIOChannel {
+ Object parent;
+ unsigned int features; /* bitmask of QIOChannelFeatures */
+};
+
+/**
+ * QIOChannelClass:
+ *
+ * This class defines the contract that all subclasses
+ * must follow to provide specific channel implementations.
+ * The first five callbacks are mandatory to support, others
+ * provide additional optional features.
+ *
+ * Consult the corresponding public API docs for a description
+ * of the semantics of each callback
+ */
+struct QIOChannelClass {
+ ObjectClass parent;
+
+ /* Mandatory callbacks */
+ ssize_t (*io_writev)(QIOChannel *ioc,
+ const struct iovec *iov,
+ size_t niov,
+ int *fds,
+ size_t nfds,
+ Error **errp);
+ ssize_t (*io_readv)(QIOChannel *ioc,
+ const struct iovec *iov,
+ size_t niov,
+ int **fds,
+ size_t *nfds,
+ Error **errp);
+ int (*io_close)(QIOChannel *ioc,
+ Error **errp);
+ GSource * (*io_create_watch)(QIOChannel *ioc,
+ GIOCondition condition);
+ int (*io_set_blocking)(QIOChannel *ioc,
+ bool enabled,
+ Error **errp);
+
+ /* Optional callbacks */
+ int (*io_shutdown)(QIOChannel *ioc,
+ QIOChannelShutdown how,
+ Error **errp);
+ void (*io_set_cork)(QIOChannel *ioc,
+ bool enabled);
+ void (*io_set_delay)(QIOChannel *ioc,
+ bool enabled);
+ off_t (*io_seek)(QIOChannel *ioc,
+ off_t offset,
+ int whence,
+ Error **errp);
+};
+
+/* General I/O handling functions */
+
+/**
+ * qio_channel_has_feature:
+ * @ioc: the channel object
+ * @feature: the feature to check support of
+ *
+ * Determine whether the channel implementation supports
+ * the optional feature named in @feature.
+ *
+ * Returns: true if supported, false otherwise.
+ */
+bool qio_channel_has_feature(QIOChannel *ioc,
+ QIOChannelFeature feature);
+
+/**
+ * qio_channel_readv_full:
+ * @ioc: the channel object
+ * @iov: the array of memory regions to read data into
+ * @niov: the length of the @iov array
+ * @fds: pointer to an array that will received file handles
+ * @nfds: pointer filled with number of elements in @fds on return
+ * @errp: pointer to an uninitialized error object
+ *
+ * Read data from the IO channel, storing it in the
+ * memory regions referenced by @iov. Each element
+ * in the @iov will be fully populated with data
+ * before the next one is used. The @niov parameter
+ * specifies the total number of elements in @iov.
+ *
+ * It is not required for all @iov to be filled with
+ * data. If the channel is in blocking mode, at least
+ * one byte of data will be read, but no more is
+ * guaranteed. If the channel is non-blocking and no
+ * data is available, it will return QIO_CHANNEL_ERR_BLOCK
+ *
+ * If the channel has passed any file descriptors,
+ * the @fds array pointer will be allocated and
+ * the elements filled with the received file
+ * descriptors. The @nfds pointer will be updated
+ * to indicate the size of the @fds array that
+ * was allocated. It is the callers responsibility
+ * to call close() on each file descriptor and to
+ * call g_free() on the array pointer in @fds.
+ *
+ * It is an error to pass a non-NULL @fds parameter
+ * unless qio_channel_has_feature() returns a true
+ * value for the QIO_CHANNEL_FEATURE_FD_PASS constant.
+ *
+ * Returns: the number of bytes read, or -1 on error,
+ * or QIO_CHANNEL_ERR_BLOCK if no data is available
+ * and the channel is non-blocking
+ */
+ssize_t qio_channel_readv_full(QIOChannel *ioc,
+ const struct iovec *iov,
+ size_t niov,
+ int **fds,
+ size_t *nfds,
+ Error **errp);
+
+
+/**
+ * qio_channel_writev_full:
+ * @ioc: the channel object
+ * @iov: the array of memory regions to write data from
+ * @niov: the length of the @iov array
+ * @fds: an array of file handles to send
+ * @nfds: number of file handles in @fds
+ * @errp: pointer to an uninitialized error object
+ *
+ * Write data to the IO channel, reading it from the
+ * memory regions referenced by @iov. Each element
+ * in the @iov will be fully sent, before the next
+ * one is used. The @niov parameter specifies the
+ * total number of elements in @iov.
+ *
+ * It is not required for all @iov data to be fully
+ * sent. If the channel is in blocking mode, at least
+ * one byte of data will be sent, but no more is
+ * guaranteed. If the channel is non-blocking and no
+ * data can be sent, it will return QIO_CHANNEL_ERR_BLOCK
+ *
+ * If there are file descriptors to send, the @fds
+ * array should be non-NULL and provide the handles.
+ * All file descriptors will be sent if at least one
+ * byte of data was sent.
+ *
+ * It is an error to pass a non-NULL @fds parameter
+ * unless qio_channel_has_feature() returns a true
+ * value for the QIO_CHANNEL_FEATURE_FD_PASS constant.
+ *
+ * Returns: the number of bytes sent, or -1 on error,
+ * or QIO_CHANNEL_ERR_BLOCK if no data is can be sent
+ * and the channel is non-blocking
+ */
+ssize_t qio_channel_writev_full(QIOChannel *ioc,
+ const struct iovec *iov,
+ size_t niov,
+ int *fds,
+ size_t nfds,
+ Error **errp);
+
+/**
+ * qio_channel_readv:
+ * @ioc: the channel object
+ * @iov: the array of memory regions to read data into
+ * @niov: the length of the @iov array
+ * @errp: pointer to an uninitialized error object
+ *
+ * Behaves as qio_channel_readv_full() but does not support
+ * receiving of file handles.
+ */
+ssize_t qio_channel_readv(QIOChannel *ioc,
+ const struct iovec *iov,
+ size_t niov,
+ Error **errp);
+
+/**
+ * qio_channel_writev:
+ * @ioc: the channel object
+ * @iov: the array of memory regions to write data from
+ * @niov: the length of the @iov array
+ * @errp: pointer to an uninitialized error object
+ *
+ * Behaves as qio_channel_writev_full() but does not support
+ * sending of file handles.
+ */
+ssize_t qio_channel_writev(QIOChannel *ioc,
+ const struct iovec *iov,
+ size_t niov,
+ Error **errp);
+
+/**
+ * qio_channel_readv:
+ * @ioc: the channel object
+ * @buf: the memory region to read data into
+ * @buflen: the length of @buf
+ * @errp: pointer to an uninitialized error object
+ *
+ * Behaves as qio_channel_readv_full() but does not support
+ * receiving of file handles, and only supports reading into
+ * a single memory region.
+ */
+ssize_t qio_channel_read(QIOChannel *ioc,
+ char *buf,
+ size_t buflen,
+ Error **errp);
+
+/**
+ * qio_channel_writev:
+ * @ioc: the channel object
+ * @buf: the memory regions to send data from
+ * @buflen: the length of @buf
+ * @errp: pointer to an uninitialized error object
+ *
+ * Behaves as qio_channel_writev_full() but does not support
+ * sending of file handles, and only supports writing from a
+ * single memory region.
+ */
+ssize_t qio_channel_write(QIOChannel *ioc,
+ const char *buf,
+ size_t buflen,
+ Error **errp);
+
+/**
+ * qio_channel_set_blocking:
+ * @ioc: the channel object
+ * @enabled: the blocking flag state
+ * @errp: pointer to an uninitialized error object
+ *
+ * If @enabled is true, then the channel is put into
+ * blocking mode, otherwise it will be non-blocking.
+ *
+ * In non-blocking mode, read/write operations may
+ * return QIO_CHANNEL_ERR_BLOCK if they would otherwise
+ * block on I/O
+ */
+int qio_channel_set_blocking(QIOChannel *ioc,
+ bool enabled,
+ Error **errp);
+
+/**
+ * qio_channel_close:
+ * @ioc: the channel object
+ * @errp: pointer to an uninitialized error object
+ *
+ * Close the channel, flushing any pending I/O
+ *
+ * Returns: 0 on success, -1 on error
+ */
+int qio_channel_close(QIOChannel *ioc,
+ Error **errp);
+
+/**
+ * qio_channel_shutdown:
+ * @ioc: the channel object
+ * @how: the direction to shutdown
+ * @errp: pointer to an uninitialized error object
+ *
+ * Shutdowns transmission and/or receiving of data
+ * without closing the underlying transport.
+ *
+ * Not all implementations will support this facility,
+ * so may report an error. To avoid errors, the
+ * caller may check for the feature flag
+ * QIO_CHANNEL_FEATURE_SHUTDOWN prior to calling
+ * this method.
+ *
+ * Returns: 0 on success, -1 on error
+ */
+int qio_channel_shutdown(QIOChannel *ioc,
+ QIOChannelShutdown how,
+ Error **errp);
+
+/**
+ * qio_channel_set_delay:
+ * @ioc: the channel object
+ * @enabled: the new flag state
+ *
+ * Controls whether the underlying transport is
+ * permitted to delay writes in order to merge
+ * small packets. If @enabled is true, then the
+ * writes may be delayed in order to opportunistically
+ * merge small packets into larger ones. If @enabled
+ * is false, writes are dispatched immediately with
+ * no delay.
+ *
+ * When @enabled is false, applications may wish to
+ * use the qio_channel_set_cork() method to explicitly
+ * control write merging.
+ *
+ * On channels which are backed by a socket, this
+ * API corresponds to the inverse of TCP_NODELAY flag,
+ * controlling whether the Nagle algorithm is active.
+ *
+ * This setting is merely a hint, so implementations are
+ * free to ignore this without it being considered an
+ * error.
+ */
+void qio_channel_set_delay(QIOChannel *ioc,
+ bool enabled);
+
+/**
+ * qio_channel_set_cork:
+ * @ioc: the channel object
+ * @enabled: the new flag state
+ *
+ * Controls whether the underlying transport is
+ * permitted to dispatch data that is written.
+ * If @enabled is true, then any data written will
+ * be queued in local buffers until @enabled is
+ * set to false once again.
+ *
+ * This feature is typically used when the automatic
+ * write coalescing facility is disabled via the
+ * qio_channel_set_delay() method.
+ *
+ * On channels which are backed by a socket, this
+ * API corresponds to the TCP_CORK flag.
+ *
+ * This setting is merely a hint, so implementations are
+ * free to ignore this without it being considered an
+ * error.
+ */
+void qio_channel_set_cork(QIOChannel *ioc,
+ bool enabled);
+
+
+/**
+ * qio_channel_seek:
+ * @ioc: the channel object
+ * @offset: the position to seek to, relative to @whence
+ * @whence: one of the (POSIX) SEEK_* constants listed below
+ * @errp: pointer to an uninitialized error object
+ *
+ * Moves the current I/O position within the channel
+ * @ioc, to be @offset. The value of @offset is
+ * interpreted relative to @whence:
+ *
+ * SEEK_SET - the position is set to @offset bytes
+ * SEEK_CUR - the position is moved by @offset bytes
+ * SEEK_END - the position is set to end of the file plus @offset bytes
+ *
+ * Not all implementations will support this facility,
+ * so may report an error.
+ *
+ * Returns: the new position on success, (off_t)-1 on failure
+ */
+off_t qio_channel_io_seek(QIOChannel *ioc,
+ off_t offset,
+ int whence,
+ Error **errp);
+
+
+/**
+ * qio_channel_create_watch:
+ * @ioc: the channel object
+ * @condition: the I/O condition to monitor
+ *
+ * Create a new main loop source that is used to watch
+ * for the I/O condition @condition. Typically the
+ * qio_channel_add_watch() method would be used instead
+ * of this, since it directly attaches a callback to
+ * the source
+ *
+ * Returns: the new main loop source.
+ */
+GSource *qio_channel_create_watch(QIOChannel *ioc,
+ GIOCondition condition);
+
+/**
+ * qio_channel_add_watch:
+ * @ioc: the channel object
+ * @condition: the I/O condition to monitor
+ * @func: callback to invoke when the source becomes ready
+ * @user_data: opaque data to pass to @func
+ * @notify: callback to free @user_data
+ *
+ * Create a new main loop source that is used to watch
+ * for the I/O condition @condition. The callback @func
+ * will be registered against the source, to be invoked
+ * when the source becomes ready. The optional @user_data
+ * will be passed to @func when it is invoked. The @notify
+ * callback will be used to free @user_data when the
+ * watch is deleted
+ *
+ * The returned source ID can be used with g_source_remove()
+ * to remove and free the source when no longer required.
+ * Alternatively the @func callback can return a FALSE
+ * value.
+ *
+ * Returns: the source ID
+ */
+guint qio_channel_add_watch(QIOChannel *ioc,
+ GIOCondition condition,
+ QIOChannelFunc func,
+ gpointer user_data,
+ GDestroyNotify notify);
+
+
+/**
+ * qio_channel_yield:
+ * @ioc: the channel object
+ * @condition: the I/O condition to wait for
+ *
+ * Yields execution from the current coroutine until
+ * the condition indicated by @condition becomes
+ * available.
+ *
+ * This must only be called from coroutine context
+ */
+void qio_channel_yield(QIOChannel *ioc,
+ GIOCondition condition);
+
+/**
+ * qio_channel_wait:
+ * @ioc: the channel object
+ * @condition: the I/O condition to wait for
+ *
+ * Block execution from the current thread until
+ * the condition indicated by @condition becomes
+ * available.
+ *
+ * This will enter a nested event loop to perform
+ * the wait.
+ */
+void qio_channel_wait(QIOChannel *ioc,
+ GIOCondition condition);
+
+#endif /* QIO_CHANNEL_H__ */
diff --git a/io/Makefile.objs b/io/Makefile.objs
new file mode 100644
index 0000000000..a6ed3619b4
--- /dev/null
+++ b/io/Makefile.objs
@@ -0,0 +1 @@
+io-obj-y = channel.o
diff --git a/io/channel.c b/io/channel.c
new file mode 100644
index 0000000000..5e94469de2
--- /dev/null
+++ b/io/channel.c
@@ -0,0 +1,291 @@
+/*
+ * QEMU I/O channels
+ *
+ * Copyright (c) 2015 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 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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "io/channel.h"
+#include "qemu/coroutine.h"
+
+bool qio_channel_has_feature(QIOChannel *ioc,
+ QIOChannelFeature feature)
+{
+ return ioc->features & (1 << feature);
+}
+
+
+ssize_t qio_channel_readv_full(QIOChannel *ioc,
+ const struct iovec *iov,
+ size_t niov,
+ int **fds,
+ size_t *nfds,
+ Error **errp)
+{
+ QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc);
+
+ if ((fds || nfds) &&
+ !(ioc->features & (1 << QIO_CHANNEL_FEATURE_FD_PASS))) {
+ error_setg_errno(errp, EINVAL,
+ "Channel does not support file descriptor passing");
+ return -1;
+ }
+
+ return klass->io_readv(ioc, iov, niov, fds, nfds, errp);
+}
+
+
+ssize_t qio_channel_writev_full(QIOChannel *ioc,
+ const struct iovec *iov,
+ size_t niov,
+ int *fds,
+ size_t nfds,
+ Error **errp)
+{
+ QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc);
+
+ if ((fds || nfds) &&
+ !(ioc->features & (1 << QIO_CHANNEL_FEATURE_FD_PASS))) {
+ error_setg_errno(errp, EINVAL,
+ "Channel does not support file descriptor passing");
+ return -1;
+ }
+
+ return klass->io_writev(ioc, iov, niov, fds, nfds, errp);
+}
+
+
+ssize_t qio_channel_readv(QIOChannel *ioc,
+ const struct iovec *iov,
+ size_t niov,
+ Error **errp)
+{
+ return qio_channel_readv_full(ioc, iov, niov, NULL, NULL, errp);
+}
+
+
+ssize_t qio_channel_writev(QIOChannel *ioc,
+ const struct iovec *iov,
+ size_t niov,
+ Error **errp)
+{
+ return qio_channel_writev_full(ioc, iov, niov, NULL, 0, errp);
+}
+
+
+ssize_t qio_channel_read(QIOChannel *ioc,
+ char *buf,
+ size_t buflen,
+ Error **errp)
+{
+ struct iovec iov = { .iov_base = buf, .iov_len = buflen };
+ return qio_channel_readv_full(ioc, &iov, 1, NULL, NULL, errp);
+}
+
+
+ssize_t qio_channel_write(QIOChannel *ioc,
+ const char *buf,
+ size_t buflen,
+ Error **errp)
+{
+ struct iovec iov = { .iov_base = (char *)buf, .iov_len = buflen };
+ return qio_channel_writev_full(ioc, &iov, 1, NULL, 0, errp);
+}
+
+
+int qio_channel_set_blocking(QIOChannel *ioc,
+ bool enabled,
+ Error **errp)
+{
+ QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc);
+ return klass->io_set_blocking(ioc, enabled, errp);
+}
+
+
+int qio_channel_close(QIOChannel *ioc,
+ Error **errp)
+{
+ QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc);
+ return klass->io_close(ioc, errp);
+}
+
+
+GSource *qio_channel_create_watch(QIOChannel *ioc,
+ GIOCondition condition)
+{
+ QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc);
+ return klass->io_create_watch(ioc, condition);
+}
+
+
+guint qio_channel_add_watch(QIOChannel *ioc,
+ GIOCondition condition,
+ QIOChannelFunc func,
+ gpointer user_data,
+ GDestroyNotify notify)
+{
+ GSource *source;
+ guint id;
+
+ source = qio_channel_create_watch(ioc, condition);
+
+ g_source_set_callback(source, (GSourceFunc)func, user_data, notify);
+
+ id = g_source_attach(source, NULL);
+ g_source_unref(source);
+
+ return id;
+}
+
+
+int qio_channel_shutdown(QIOChannel *ioc,
+ QIOChannelShutdown how,
+ Error **errp)
+{
+ QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc);
+
+ if (!klass->io_shutdown) {
+ error_setg(errp, "Data path shutdown not supported");
+ return -1;
+ }
+
+ return klass->io_shutdown(ioc, how, errp);
+}
+
+
+void qio_channel_set_delay(QIOChannel *ioc,
+ bool enabled)
+{
+ QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc);
+
+ if (klass->io_set_delay) {
+ klass->io_set_delay(ioc, enabled);
+ }
+}
+
+
+void qio_channel_set_cork(QIOChannel *ioc,
+ bool enabled)
+{
+ QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc);
+
+ if (klass->io_set_cork) {
+ klass->io_set_cork(ioc, enabled);
+ }
+}
+
+
+off_t qio_channel_io_seek(QIOChannel *ioc,
+ off_t offset,
+ int whence,
+ Error **errp)
+{
+ QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc);
+
+ if (!klass->io_seek) {
+ error_setg(errp, "Channel does not support random access");
+ return -1;
+ }
+
+ return klass->io_seek(ioc, offset, whence, errp);
+}
+
+
+typedef struct QIOChannelYieldData QIOChannelYieldData;
+struct QIOChannelYieldData {
+ QIOChannel *ioc;
+ Coroutine *co;
+};
+
+
+static gboolean qio_channel_yield_enter(QIOChannel *ioc,
+ GIOCondition condition,
+ gpointer opaque)
+{
+ QIOChannelYieldData *data = opaque;
+ qemu_coroutine_enter(data->co, NULL);
+ return FALSE;
+}
+
+
+void coroutine_fn qio_channel_yield(QIOChannel *ioc,
+ GIOCondition condition)
+{
+ QIOChannelYieldData data;
+
+ assert(qemu_in_coroutine());
+ data.ioc = ioc;
+ data.co = qemu_coroutine_self();
+ qio_channel_add_watch(ioc,
+ condition,
+ qio_channel_yield_enter,
+ &data,
+ NULL);
+ qemu_coroutine_yield();
+}
+
+
+static gboolean qio_channel_wait_complete(QIOChannel *ioc,
+ GIOCondition condition,
+ gpointer opaque)
+{
+ GMainLoop *loop = opaque;
+
+ g_main_loop_quit(loop);
+ return FALSE;
+}
+
+
+void qio_channel_wait(QIOChannel *ioc,
+ GIOCondition condition)
+{
+ GMainContext *ctxt = g_main_context_new();
+ GMainLoop *loop = g_main_loop_new(ctxt, TRUE);
+ GSource *source;
+
+ source = qio_channel_create_watch(ioc, condition);
+
+ g_source_set_callback(source,
+ (GSourceFunc)qio_channel_wait_complete,
+ loop,
+ NULL);
+
+ g_source_attach(source, ctxt);
+
+ g_main_loop_run(loop);
+
+ g_source_unref(source);
+ g_main_loop_unref(loop);
+ g_main_context_unref(ctxt);
+}
+
+
+static const TypeInfo qio_channel_info = {
+ .parent = TYPE_OBJECT,
+ .name = TYPE_QIO_CHANNEL,
+ .instance_size = sizeof(QIOChannel),
+ .abstract = true,
+ .class_size = sizeof(QIOChannelClass),
+};
+
+
+static void qio_channel_register_types(void)
+{
+ type_register_static(&qio_channel_info);
+}
+
+
+type_init(qio_channel_register_types);