[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH RFC] qemu-char: add an "overlay" backend type
From: |
Daniel P. Berrange |
Subject: |
[Qemu-devel] [PATCH RFC] qemu-char: add an "overlay" backend type |
Date: |
Wed, 16 Dec 2015 17:56:11 +0000 |
Introduce a new QEMU chardev backend called "overlay" which
allows you to splice together a pair of chardev backends into
one combined backend. The master backend permits full input/output
but the slave backend is output only.
The primary use case for this is to allow arbitrary backends to
have their data logged to a file, eg a 'file' backend would be
setup as the slave.
$ qemu-system-x86_64 \
-chardev socket,host=localhost,port=9000,server=on,nowait,id=char0master \
-chardev file,path=/some/log/file.log,id=char0slave \
-chardev overlay,id=char0,master=char0master,slave=char0slave \
-device isa-serial,chardev=char0 \
...other args...
---
This idea was suggsted in
https://lists.gnu.org/archive/html/qemu-devel/2015-12/msg01256.html
this patch is a very quick proof of concept impl to illustrate the
idea.
qapi-schema.json | 14 +++++
qemu-char.c | 152 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 166 insertions(+)
diff --git a/qapi-schema.json b/qapi-schema.json
index 4be151c..1adb220 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -3156,6 +3156,19 @@
{ 'struct': 'ChardevRingbuf', 'data': { '*size' : 'int' } }
##
+# @ChardevOverlay:
+#
+# Configuration info for overlay chardevs.
+#
+# @master: the master chardev for input / output
+# @slave: the slave chardev for output only
+#
+# Since: 2.6
+##
+{ 'struct': 'ChardevOverlay', 'data': { 'master' : 'str',
+ 'slave' : 'str'} }
+
+##
# @ChardevBackend:
#
# Configuration info for the new chardev backend.
@@ -3182,6 +3195,7 @@
'spiceport' : 'ChardevSpicePort',
'vc' : 'ChardevVC',
'ringbuf': 'ChardevRingbuf',
+ 'overlay': 'ChardevOverlay',
# next one is just for compatibility
'memory' : 'ChardevRingbuf' } }
diff --git a/qemu-char.c b/qemu-char.c
index 45fdb77..0328eb9 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -3223,6 +3223,131 @@ char *qmp_ringbuf_read(const char *device, int64_t size,
return data;
}
+
+typedef struct {
+ CharDriverState *master;
+ CharDriverState *slave;
+
+ IOCanReadHandler *chr_can_read;
+ IOReadHandler *chr_read;
+ IOEventHandler *chr_event;
+ void *handler_opaque;
+} OverlayCharDriver;
+
+static int overlay_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
+{
+ OverlayCharDriver *s = chr->opaque;
+ int ret;
+
+ ret = qemu_chr_fe_write(s->master, buf, len);
+ if (ret <= 0) {
+ return ret;
+ }
+
+ /* Write all will block, but how else can we ensure
+ * that all the data we just sent to the master also
+ * gets sent to the slave ? Could buffer data internally
+ * if slave was full and setup a backgroupd I/O watch
+ * to send the data later, but the ChardevDriverState
+ * API design doesn't make that very easy since. Of
+ * course if the slave is a "file" backend, then it
+ * will block regardless thanks to POSIX not handling
+ * O_NONBLOCK on regular files, so this is no worse
+ * in that scenario.
+ */
+ qemu_chr_fe_write_all(s->slave, buf, ret);
+ return ret;
+}
+
+static GSource *overlay_chr_add_watch(CharDriverState *chr, GIOCondition cond)
+{
+ OverlayCharDriver *d = chr->opaque;
+ return d->master->chr_add_watch(d->master, cond);
+}
+
+static int overlay_chr_can_read(void *opaque)
+{
+ CharDriverState *chr = opaque;
+ OverlayCharDriver *d = chr->opaque;
+
+ if (d->chr_can_read) {
+ return d->chr_can_read(d->handler_opaque);
+ }
+ return 0;
+}
+
+static void overlay_chr_read(void *opaque, const uint8_t *buf, int size)
+{
+ CharDriverState *chr = opaque;
+ OverlayCharDriver *d = chr->opaque;
+
+ if (d->chr_read) {
+ d->chr_read(d->handler_opaque, buf, size);
+ }
+}
+
+static void overlay_chr_event(void *opaque, int event)
+{
+ CharDriverState *chr = opaque;
+ OverlayCharDriver *d = chr->opaque;
+
+ if (d->chr_event) {
+ d->chr_event(d->handler_opaque, event);
+ }
+}
+
+static void overlay_chr_update_read_handler(CharDriverState *chr)
+{
+ OverlayCharDriver *d = chr->opaque;
+
+ d->handler_opaque = chr->handler_opaque;
+ d->chr_can_read = chr->chr_can_read;
+ d->chr_read = chr->chr_read;
+ d->chr_event = chr->chr_event;
+
+ qemu_chr_add_handlers(d->master, overlay_chr_can_read, overlay_chr_read,
+ overlay_chr_event, chr);
+ /* Slave is output only, so don't wire it up for input */
+}
+
+static CharDriverState *qemu_chr_open_overlay(const char *id,
+ ChardevBackend *backend,
+ ChardevReturn *ret, Error **errp)
+{
+ ChardevOverlay *overlay = backend->u.overlay;
+ CharDriverState *chr, *master, *slave;
+ OverlayCharDriver *d;
+
+ master = qemu_chr_find(overlay->master);
+ if (master == NULL) {
+ error_setg(errp, "overlay: master chardev %s not found",
overlay->master);
+ return NULL;
+ }
+
+ slave = qemu_chr_find(overlay->slave);
+ if (slave == NULL) {
+ error_setg(errp, "overlay: slave chardev %s not found",
overlay->slave);
+ return NULL;
+ }
+
+ chr = qemu_chr_alloc();
+ d = g_new0(OverlayCharDriver, 1);
+
+ chr->opaque = d;
+ d->master = master;
+ d->slave = slave;
+
+ chr->chr_write = overlay_chr_write;
+ chr->chr_update_read_handler = overlay_chr_update_read_handler;
+ if (master->chr_add_watch) {
+ chr->chr_add_watch = overlay_chr_add_watch;
+ }
+ chr->explicit_be_open = master->explicit_be_open;
+
+ return chr;
+}
+
+
QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename)
{
char host[65], port[33], width[8], height[8];
@@ -3443,6 +3568,25 @@ static void qemu_chr_parse_ringbuf(QemuOpts *opts,
ChardevBackend *backend,
}
}
+static void qemu_chr_parse_overlay(QemuOpts *opts, ChardevBackend *backend,
+ Error **errp)
+{
+ const char *master = qemu_opt_get(opts, "master");
+ const char *slave = qemu_opt_get(opts, "slave");
+
+ if (master == NULL) {
+ error_setg(errp, "chardev: overlay: no master chardev given");
+ return;
+ }
+ if (slave == NULL) {
+ error_setg(errp, "chardev: overlay: no slave chardev given");
+ return;
+ }
+ backend->u.overlay = g_new0(ChardevOverlay, 1);
+ backend->u.overlay->master = g_strdup(master);
+ backend->u.overlay->slave = g_strdup(slave);
+}
+
static void qemu_chr_parse_mux(QemuOpts *opts, ChardevBackend *backend,
Error **errp)
{
@@ -3943,6 +4087,12 @@ QemuOptsList qemu_chardev_opts = {
},{
.name = "chardev",
.type = QEMU_OPT_STRING,
+ },{
+ .name = "master",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "slave",
+ .type = QEMU_OPT_STRING,
},
{ /* end of list */ }
},
@@ -4315,6 +4465,8 @@ static void register_types(void)
/* Bug-compatibility: */
register_char_driver("memory", CHARDEV_BACKEND_KIND_MEMORY,
qemu_chr_parse_ringbuf, qemu_chr_open_ringbuf);
+ register_char_driver("overlay", CHARDEV_BACKEND_KIND_OVERLAY,
+ qemu_chr_parse_overlay, qemu_chr_open_overlay);
/* this must be done after machine init, since we register FEs with muxes
* as part of realize functions like serial_isa_realizefn when -nographic
* is specified
--
2.5.0
- [Qemu-devel] [PATCH RFC] qemu-char: add an "overlay" backend type,
Daniel P. Berrange <=