diff options
author | Daniel P. Berrange <berrange@redhat.com> | 2015-09-15 17:27:33 +0100 |
---|---|---|
committer | Daniel P. Berrange <berrange@redhat.com> | 2015-12-18 12:18:31 +0000 |
commit | d98e4eb7de93290f7921b0dbe869c7dd3c567945 (patch) | |
tree | 3f9d266d76b202a93b1bf7fd8a47affa4d521308 | |
parent | 195e14d0262c5676cb9ff4b253e4b2b15c19d3f0 (diff) |
io: add QIOChannelBuffer class
Add a QIOChannel subclass that is capable of performing I/O
to/from a memory buffer. This implementation does not attempt
to support concurrent readers & writers. It is designed for
serialized access where by a single thread at a time may write
data, seek and then read data back out.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
-rw-r--r-- | include/io/channel-buffer.h | 60 | ||||
-rw-r--r-- | io/Makefile.objs | 1 | ||||
-rw-r--r-- | io/channel-buffer.c | 248 | ||||
-rw-r--r-- | tests/.gitignore | 1 | ||||
-rw-r--r-- | tests/Makefile | 3 | ||||
-rw-r--r-- | tests/test-io-channel-buffer.c | 50 |
6 files changed, 363 insertions, 0 deletions
diff --git a/include/io/channel-buffer.h b/include/io/channel-buffer.h new file mode 100644 index 0000000000..91a52b3373 --- /dev/null +++ b/include/io/channel-buffer.h @@ -0,0 +1,60 @@ +/* + * QEMU I/O channels memory buffer driver + * + * 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_BUFFER_H__ +#define QIO_CHANNEL_BUFFER_H__ + +#include "io/channel.h" + +#define TYPE_QIO_CHANNEL_BUFFER "qio-channel-buffer" +#define QIO_CHANNEL_BUFFER(obj) \ + OBJECT_CHECK(QIOChannelBuffer, (obj), TYPE_QIO_CHANNEL_BUFFER) + +typedef struct QIOChannelBuffer QIOChannelBuffer; + +/** + * QIOChannelBuffer: + * + * The QIOChannelBuffer object provides a channel implementation + * that is able to perform I/O to/from a memory buffer. + * + */ + +struct QIOChannelBuffer { + QIOChannel parent; + size_t capacity; /* Total allocated memory */ + size_t usage; /* Current size of data */ + size_t offset; /* Offset for future I/O ops */ + char *data; +}; + + +/** + * qio_channel_buffer_new: + * @capacity: the initial buffer capacity to allocate + * + * Allocate a new buffer which is initially empty + * + * Returns: the new channel object + */ +QIOChannelBuffer * +qio_channel_buffer_new(size_t capacity); + +#endif /* QIO_CHANNEL_BUFFER_H__ */ diff --git a/io/Makefile.objs b/io/Makefile.objs index 1a58ccb5c2..0e3de31a5a 100644 --- a/io/Makefile.objs +++ b/io/Makefile.objs @@ -1,4 +1,5 @@ io-obj-y = channel.o +io-obj-y += channel-buffer.o io-obj-y += channel-command.o io-obj-y += channel-file.o io-obj-y += channel-socket.o diff --git a/io/channel-buffer.c b/io/channel-buffer.c new file mode 100644 index 0000000000..daebc92bd8 --- /dev/null +++ b/io/channel-buffer.c @@ -0,0 +1,248 @@ +/* + * QEMU I/O channels memory buffer driver + * + * 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-buffer.h" +#include "io/channel-watch.h" +#include "qemu/sockets.h" +#include "trace.h" + +QIOChannelBuffer * +qio_channel_buffer_new(size_t capacity) +{ + QIOChannelBuffer *ioc; + + ioc = QIO_CHANNEL_BUFFER(object_new(TYPE_QIO_CHANNEL_BUFFER)); + + if (capacity) { + ioc->data = g_new0(char, capacity); + ioc->capacity = capacity; + } + + return ioc; +} + + +static void qio_channel_buffer_finalize(Object *obj) +{ + QIOChannelBuffer *ioc = QIO_CHANNEL_BUFFER(obj); + g_free(ioc->data); + ioc->capacity = ioc->usage = ioc->offset = 0; +} + + +static ssize_t qio_channel_buffer_readv(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + int **fds, + size_t *nfds, + Error **errp) +{ + QIOChannelBuffer *bioc = QIO_CHANNEL_BUFFER(ioc); + ssize_t ret = 0; + size_t i; + + for (i = 0; i < niov; i++) { + size_t want = iov[i].iov_len; + if (bioc->offset >= bioc->usage) { + break; + } + if ((bioc->offset + want) > bioc->usage) { + want = bioc->usage - bioc->offset; + } + memcpy(iov[i].iov_base, bioc->data + bioc->offset, want); + ret += want; + bioc->offset += want; + } + + return ret; +} + +static ssize_t qio_channel_buffer_writev(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + int *fds, + size_t nfds, + Error **errp) +{ + QIOChannelBuffer *bioc = QIO_CHANNEL_BUFFER(ioc); + ssize_t ret = 0; + size_t i; + size_t towrite = 0; + + for (i = 0; i < niov; i++) { + towrite += iov[i].iov_len; + } + + if ((bioc->offset + towrite) > bioc->capacity) { + bioc->capacity = bioc->offset + towrite; + bioc->data = g_realloc(bioc->data, bioc->capacity); + } + + if (bioc->offset > bioc->usage) { + memset(bioc->data, 0, bioc->offset - bioc->usage); + bioc->usage = bioc->offset; + } + + for (i = 0; i < niov; i++) { + memcpy(bioc->data + bioc->usage, + iov[i].iov_base, + iov[i].iov_len); + bioc->usage += iov[i].iov_len; + bioc->offset += iov[i].iov_len; + ret += iov[i].iov_len; + } + + return ret; +} + +static int qio_channel_buffer_set_blocking(QIOChannel *ioc G_GNUC_UNUSED, + bool enabled G_GNUC_UNUSED, + Error **errp G_GNUC_UNUSED) +{ + return 0; +} + + +static off_t qio_channel_buffer_seek(QIOChannel *ioc, + off_t offset, + int whence, + Error **errp) +{ + QIOChannelBuffer *bioc = QIO_CHANNEL_BUFFER(ioc); + + bioc->offset = offset; + + return offset; +} + + +static int qio_channel_buffer_close(QIOChannel *ioc, + Error **errp) +{ + QIOChannelBuffer *bioc = QIO_CHANNEL_BUFFER(ioc); + + g_free(bioc->data); + bioc->capacity = bioc->usage = bioc->offset = 0; + + return 0; +} + + +typedef struct QIOChannelBufferSource QIOChannelBufferSource; +struct QIOChannelBufferSource { + GSource parent; + QIOChannelBuffer *bioc; + GIOCondition condition; +}; + +static gboolean +qio_channel_buffer_source_prepare(GSource *source, + gint *timeout) +{ + QIOChannelBufferSource *bsource = (QIOChannelBufferSource *)source; + + *timeout = -1; + + return (G_IO_IN | G_IO_OUT) & bsource->condition; +} + +static gboolean +qio_channel_buffer_source_check(GSource *source) +{ + QIOChannelBufferSource *bsource = (QIOChannelBufferSource *)source; + + return (G_IO_IN | G_IO_OUT) & bsource->condition; +} + +static gboolean +qio_channel_buffer_source_dispatch(GSource *source, + GSourceFunc callback, + gpointer user_data) +{ + QIOChannelFunc func = (QIOChannelFunc)callback; + QIOChannelBufferSource *bsource = (QIOChannelBufferSource *)source; + + return (*func)(QIO_CHANNEL(bsource->bioc), + ((G_IO_IN | G_IO_OUT) & bsource->condition), + user_data); +} + +static void +qio_channel_buffer_source_finalize(GSource *source) +{ + QIOChannelBufferSource *ssource = (QIOChannelBufferSource *)source; + + object_unref(OBJECT(ssource->bioc)); +} + +GSourceFuncs qio_channel_buffer_source_funcs = { + qio_channel_buffer_source_prepare, + qio_channel_buffer_source_check, + qio_channel_buffer_source_dispatch, + qio_channel_buffer_source_finalize +}; + +static GSource *qio_channel_buffer_create_watch(QIOChannel *ioc, + GIOCondition condition) +{ + QIOChannelBuffer *bioc = QIO_CHANNEL_BUFFER(ioc); + QIOChannelBufferSource *ssource; + GSource *source; + + source = g_source_new(&qio_channel_buffer_source_funcs, + sizeof(QIOChannelBufferSource)); + ssource = (QIOChannelBufferSource *)source; + + ssource->bioc = bioc; + object_ref(OBJECT(bioc)); + + ssource->condition = condition; + + return source; +} + + +static void qio_channel_buffer_class_init(ObjectClass *klass, + void *class_data G_GNUC_UNUSED) +{ + QIOChannelClass *ioc_klass = QIO_CHANNEL_CLASS(klass); + + ioc_klass->io_writev = qio_channel_buffer_writev; + ioc_klass->io_readv = qio_channel_buffer_readv; + ioc_klass->io_set_blocking = qio_channel_buffer_set_blocking; + ioc_klass->io_seek = qio_channel_buffer_seek; + ioc_klass->io_close = qio_channel_buffer_close; + ioc_klass->io_create_watch = qio_channel_buffer_create_watch; +} + +static const TypeInfo qio_channel_buffer_info = { + .parent = TYPE_QIO_CHANNEL, + .name = TYPE_QIO_CHANNEL_BUFFER, + .instance_size = sizeof(QIOChannelBuffer), + .instance_finalize = qio_channel_buffer_finalize, + .class_init = qio_channel_buffer_class_init, +}; + +static void qio_channel_buffer_register_types(void) +{ + type_register_static(&qio_channel_buffer_info); +} + +type_init(qio_channel_buffer_register_types); diff --git a/tests/.gitignore b/tests/.gitignore index cc9aeec717..77aaba6c2f 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -24,6 +24,7 @@ test-cutils test-hbitmap test-int128 test-iov +test-io-channel-buffer test-io-channel-command test-io-channel-command.fifo test-io-channel-file diff --git a/tests/Makefile b/tests/Makefile index 40c3855c4d..6ff4627d0c 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -89,6 +89,7 @@ check-unit-y += tests/test-io-channel-socket$(EXESUF) check-unit-y += tests/test-io-channel-file$(EXESUF) check-unit-$(CONFIG_GNUTLS) += tests/test-io-channel-tls$(EXESUF) check-unit-y += tests/test-io-channel-command$(EXESUF) +check-unit-y += tests/test-io-channel-buffer$(EXESUF) check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh @@ -485,6 +486,8 @@ tests/test-io-channel-tls$(EXESUF): tests/test-io-channel-tls.o \ tests/io-channel-helpers.o $(test-io-obj-y) tests/test-io-channel-command$(EXESUF): tests/test-io-channel-command.o \ tests/io-channel-helpers.o $(test-io-obj-y) +tests/test-io-channel-buffer$(EXESUF): tests/test-io-channel-buffer.o \ + tests/io-channel-helpers.o $(test-io-obj-y) libqos-obj-y = tests/libqos/pci.o tests/libqos/fw_cfg.o tests/libqos/malloc.o libqos-obj-y += tests/libqos/i2c.o tests/libqos/libqos.o diff --git a/tests/test-io-channel-buffer.c b/tests/test-io-channel-buffer.c new file mode 100644 index 0000000000..6637501b41 --- /dev/null +++ b/tests/test-io-channel-buffer.c @@ -0,0 +1,50 @@ +/* + * QEMU I/O channel buffer test + * + * 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-buffer.h" +#include "io-channel-helpers.h" + + +static void test_io_channel_buf(void) +{ + QIOChannelBuffer *buf; + QIOChannelTest *test; + + buf = qio_channel_buffer_new(0); + + test = qio_channel_test_new(); + qio_channel_test_run_writer(test, QIO_CHANNEL(buf)); + buf->offset = 0; + qio_channel_test_run_reader(test, QIO_CHANNEL(buf)); + qio_channel_test_validate(test); + + object_unref(OBJECT(buf)); +} + + +int main(int argc, char **argv) +{ + module_call_init(MODULE_INIT_QOM); + + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/io/channel/buf", test_io_channel_buf); + return g_test_run(); +} |