From patchwork Thu Dec 17 13:45:15 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?q?Daniel_P=2E_Berrang=C3=A9?= X-Patchwork-Id: 558361 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 325BE14029E for ; Fri, 18 Dec 2015 00:53:09 +1100 (AEDT) Received: from localhost ([::1]:53788 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1a9YzT-0005dg-8j for incoming@patchwork.ozlabs.org; Thu, 17 Dec 2015 08:53:07 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:37493) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1a9YsI-0004Gb-6c for qemu-devel@nongnu.org; Thu, 17 Dec 2015 08:45:44 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1a9YsC-0002Fg-Vc for qemu-devel@nongnu.org; Thu, 17 Dec 2015 08:45:42 -0500 Received: from mx1.redhat.com ([209.132.183.28]:53653) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1a9YsC-0002FR-HT for qemu-devel@nongnu.org; Thu, 17 Dec 2015 08:45:36 -0500 Received: from int-mx13.intmail.prod.int.phx2.redhat.com (int-mx13.intmail.prod.int.phx2.redhat.com [10.5.11.26]) by mx1.redhat.com (Postfix) with ESMTPS id 2D1F0A4525; Thu, 17 Dec 2015 13:45:36 +0000 (UTC) Received: from localhost.localdomain.com (vpn1-6-106.ams2.redhat.com [10.36.6.106]) by int-mx13.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id tBHDjOxp018157; Thu, 17 Dec 2015 08:45:35 -0500 From: "Daniel P. Berrange" To: qemu-devel@nongnu.org Date: Thu, 17 Dec 2015 13:45:15 +0000 Message-Id: <1450359915-14889-10-git-send-email-berrange@redhat.com> In-Reply-To: <1450359915-14889-1-git-send-email-berrange@redhat.com> References: <1450359915-14889-1-git-send-email-berrange@redhat.com> X-Scanned-By: MIMEDefang 2.68 on 10.5.11.26 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x X-Received-From: 209.132.183.28 Cc: Peter Maydell Subject: [Qemu-devel] [PULL v1 9/9] io: add QIOChannelBuffer class X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org 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 --- include/io/channel-buffer.h | 60 ++++++++++ io/Makefile.objs | 1 + io/channel-buffer.c | 249 +++++++++++++++++++++++++++++++++++++++++ tests/.gitignore | 1 + tests/Makefile | 3 + tests/test-io-channel-buffer.c | 50 +++++++++ 6 files changed, 364 insertions(+) create mode 100644 include/io/channel-buffer.h create mode 100644 io/channel-buffer.c create mode 100644 tests/test-io-channel-buffer.c diff --git a/include/io/channel-buffer.h b/include/io/channel-buffer.h new file mode 100644 index 0000000..91a52b3 --- /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 . + * + */ + +#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 1a58ccb..0e3de31 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 0000000..c68dd1a --- /dev/null +++ b/io/channel-buffer.c @@ -0,0 +1,249 @@ +/* + * 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 . + * + */ + +#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 off64_t qio_channel_buffer_seek(QIOChannel *ioc, + off64_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)); + g_source_set_name(source, "QIOChannelBuffer"); + 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 cc9aeec..77aaba6 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 40c3855..6ff4627 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 0000000..6637501 --- /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 . + * + */ + +#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(); +}