292 lines
7.6 KiB
C
292 lines
7.6 KiB
C
|
/*
|
||
|
* 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);
|