From: "Daniel P. Berrange" <berrange@redhat.com>
To: xen-devel@lists.xensource.com
Subject: Re: PATCH 8/10: Add pv console to QEMU paravirt machine
Date: Wed, 24 Oct 2007 21:43:26 +0100 [thread overview]
Message-ID: <20071024204326.GL10807@redhat.com> (raw)
In-Reply-To: <20071024203513.GD10807@redhat.com>
This patch adds a paravirt console driver to qemu-dm. This is used when the QEMU
machine type is 'xenpv', connecting to the ring buffer provided by the guest
kernel. The '-serial' command line flag controls how the guest console is
exposed.
For parity with xenconsoled the '-serial pty' arg can be used. For guests which
are running a qemu-dm device model, the xenconsoled daemon is no longer needed
for guest consoles. The code for the xen_console.c is based on the original code
in tools/console/daemon/io.c, but simplified; since its only dealing with a
single guest there's no state tracking to worry about.
b/tools/ioemu/hw/xen_console.c | 432 ++++++++++++++++++++++++++++++++
b/tools/ioemu/hw/xen_console.h | 25 +
tools/ioemu/Makefile.target | 1
tools/ioemu/hw/xen_machine_pv.c | 9
tools/ioemu/xenstore.c | 2
tools/python/xen/xend/XendDomainInfo.py | 7
6 files changed, 471 insertions(+), 5 deletions(-)
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
diff -r 96aa5e956a9f tools/ioemu/Makefile.target
--- a/tools/ioemu/Makefile.target Wed Oct 24 15:25:24 2007 -0400
+++ b/tools/ioemu/Makefile.target Wed Oct 24 15:25:27 2007 -0400
@@ -412,6 +412,7 @@ VL_OBJS+= xen_machine_fv.o
VL_OBJS+= xen_machine_fv.o
VL_OBJS+= xen_machine_pv.o
VL_OBJS+= xenfb.o
+VL_OBJS+= xen_console.o
VL_OBJS+= tpm_tis.o
CPPFLAGS += -DHAS_AUDIO
endif
diff -r 96aa5e956a9f tools/ioemu/hw/xen_console.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/ioemu/hw/xen_console.c Wed Oct 24 15:30:41 2007 -0400
@@ -0,0 +1,432 @@
+/*
+ * Copyright (C) International Business Machines Corp., 2005
+ * Author(s): Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * Copyright (C) Red Hat 2007
+ *
+ * Xen Console
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; under version 2 of the License.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <malloc.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/select.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <termios.h>
+#include <stdarg.h>
+#include <sys/mman.h>
+#include <xs.h>
+#include <xen/io/console.h>
+#include <xenctrl.h>
+
+#include "vl.h"
+
+#include "xen_console.h"
+
+#define dolog(val, fmt, ...) fprintf(stderr, fmt "\n", ## __VA_ARGS__)
+
+struct buffer
+{
+ uint8_t *data;
+ size_t consumed;
+ size_t size;
+ size_t capacity;
+ size_t max_capacity;
+};
+
+struct domain
+{
+ int domid;
+ struct buffer buffer;
+
+ char *conspath;
+ char *serialpath;
+ int use_consolepath;
+ int ring_ref;
+ evtchn_port_t local_port;
+ evtchn_port_t remote_port;
+ int xce_handle;
+ struct xs_handle *xsh;
+ struct xencons_interface *interface;
+ CharDriverState *chr;
+};
+
+
+static void buffer_append(struct domain *dom)
+{
+ struct buffer *buffer = &dom->buffer;
+ XENCONS_RING_IDX cons, prod, size;
+ struct xencons_interface *intf = dom->interface;
+
+ cons = intf->out_cons;
+ prod = intf->out_prod;
+ mb();
+
+ size = prod - cons;
+ if ((size == 0) || (size > sizeof(intf->out)))
+ return;
+
+ if ((buffer->capacity - buffer->size) < size) {
+ buffer->capacity += (size + 1024);
+ buffer->data = realloc(buffer->data, buffer->capacity);
+ if (buffer->data == NULL) {
+ dolog(LOG_ERR, "Memory allocation failed");
+ exit(ENOMEM);
+ }
+ }
+
+ while (cons != prod)
+ buffer->data[buffer->size++] = intf->out[
+ MASK_XENCONS_IDX(cons++, intf->out)];
+
+ mb();
+ intf->out_cons = cons;
+ xc_evtchn_notify(dom->xce_handle, dom->local_port);
+
+ if (buffer->max_capacity &&
+ buffer->size > buffer->max_capacity) {
+ /* Discard the middle of the data. */
+
+ size_t over = buffer->size - buffer->max_capacity;
+ uint8_t *maxpos = buffer->data + buffer->max_capacity;
+
+ memmove(maxpos - over, maxpos, over);
+ buffer->data = realloc(buffer->data, buffer->max_capacity);
+ buffer->size = buffer->capacity = buffer->max_capacity;
+
+ if (buffer->consumed > buffer->max_capacity - over)
+ buffer->consumed = buffer->max_capacity - over;
+ }
+}
+
+static void buffer_advance(struct buffer *buffer, size_t len)
+{
+ buffer->consumed += len;
+ if (buffer->consumed == buffer->size) {
+ buffer->consumed = 0;
+ buffer->size = 0;
+ }
+}
+
+/* Takes tuples of names, scanf-style args, and void **, NULL terminated. */
+int xs_gather(struct xs_handle *xs, const char *dir, ...)
+{
+ va_list ap;
+ const char *name;
+ char *path;
+ int ret = 0;
+
+ va_start(ap, dir);
+ while (ret == 0 && (name = va_arg(ap, char *)) != NULL) {
+ const char *fmt = va_arg(ap, char *);
+ void *result = va_arg(ap, void *);
+ char *p;
+
+ if (asprintf(&path, "%s/%s", dir, name) == -1) {
+ ret = ENOMEM;
+ break;
+ }
+ p = xs_read(xs, XBT_NULL, path, NULL);
+ free(path);
+ if (p == NULL) {
+ ret = ENOENT;
+ break;
+ }
+ if (fmt) {
+ if (sscanf(p, fmt, result) == 0)
+ ret = EINVAL;
+ free(p);
+ } else
+ *(char **)result = p;
+ }
+ va_end(ap);
+ return ret;
+}
+
+static int domain_create_ring(struct domain *dom)
+{
+ int err, remote_port, ring_ref, rc;
+
+ err = xs_gather(dom->xsh, dom->serialpath,
+ "ring-ref", "%u", &ring_ref,
+ "port", "%i", &remote_port,
+ NULL);
+ if (err) {
+ err = xs_gather(dom->xsh, dom->conspath,
+ "ring-ref", "%u", &ring_ref,
+ "port", "%i", &remote_port,
+ NULL);
+ if (err) {
+ fprintf(stderr, "Console: failed to find ring-ref/port yet\n");
+ goto out;
+ }
+ dom->use_consolepath = 1;
+ } else
+ dom->use_consolepath = 0;
+ fprintf(stderr, "Console: got ring-ref %d port %d\n", ring_ref, remote_port);
+
+ if ((ring_ref == dom->ring_ref) && (remote_port == dom->remote_port))
+ goto out;
+
+ if (ring_ref != dom->ring_ref) {
+ if (dom->interface != NULL)
+ munmap(dom->interface, getpagesize());
+ dom->interface = xc_map_foreign_range(
+ xc_handle, dom->domid, getpagesize(),
+ PROT_READ|PROT_WRITE,
+ (unsigned long)ring_ref);
+ if (dom->interface == NULL) {
+ err = errno;
+ goto out;
+ }
+ dom->ring_ref = ring_ref;
+ }
+
+ dom->local_port = -1;
+ dom->remote_port = -1;
+
+ dom->xce_handle = xc_evtchn_open();
+ if (dom->xce_handle == -1) {
+ err = errno;
+ goto out;
+ }
+
+ rc = xc_evtchn_bind_interdomain(dom->xce_handle,
+ dom->domid, remote_port);
+
+ if (rc == -1) {
+ err = errno;
+ xc_evtchn_close(dom->xce_handle);
+ dom->xce_handle = -1;
+ goto out;
+ }
+ dom->local_port = rc;
+ dom->remote_port = remote_port;
+
+ out:
+ return err;
+}
+
+
+static struct domain *create_domain(int domid, CharDriverState *chr)
+{
+ struct domain *dom;
+ char *s;
+
+ dom = (struct domain *)malloc(sizeof(struct domain));
+ if (dom == NULL) {
+ dolog(LOG_ERR, "Out of memory %s:%s():L%d",
+ __FILE__, __FUNCTION__, __LINE__);
+ exit(ENOMEM);
+ }
+
+ dom->domid = domid;
+ dom->chr = chr;
+
+ dom->xsh = xs_daemon_open();
+ if (dom->xsh == NULL) {
+ fprintf(logfile, "Could not contact xenstore for console watch\n");
+ goto out;
+ }
+
+ dom->serialpath = xs_get_domain_path(dom->xsh, dom->domid);
+ s = realloc(dom->serialpath, strlen(dom->serialpath) +
+ strlen("/serial/0") + 1);
+ if (s == NULL)
+ goto out;
+ dom->serialpath = s;
+ strcat(dom->serialpath, "/serial/0");
+
+ dom->conspath = xs_get_domain_path(dom->xsh, dom->domid);
+ s = realloc(dom->conspath, strlen(dom->conspath) +
+ strlen("/console") + 1);
+ if (s == NULL)
+ goto out;
+ dom->conspath = s;
+ strcat(dom->conspath, "/console");
+
+ dom->buffer.data = 0;
+ dom->buffer.consumed = 0;
+ dom->buffer.size = 0;
+ dom->buffer.capacity = 0;
+ dom->buffer.max_capacity = 0;
+
+ dom->ring_ref = -1;
+ dom->local_port = -1;
+ dom->remote_port = -1;
+ dom->interface = NULL;
+ dom->xce_handle = -1;
+
+
+ return dom;
+ out:
+ free(dom->serialpath);
+ free(dom->conspath);
+ free(dom);
+ return NULL;
+}
+
+
+static int ring_free_bytes(struct domain *dom)
+{
+ struct xencons_interface *intf = dom->interface;
+ XENCONS_RING_IDX cons, prod, space;
+
+ cons = intf->in_cons;
+ prod = intf->in_prod;
+ mb();
+
+ space = prod - cons;
+ if (space > sizeof(intf->in))
+ return 0; /* ring is screwed: ignore it */
+
+ return (sizeof(intf->in) - space);
+}
+
+static int xencons_can_receive(void *opaque)
+{
+ struct domain *dom = (struct domain *)opaque;
+
+ return ring_free_bytes(dom);
+}
+
+static void xencons_receive(void *opaque, const uint8_t *buf, int len)
+{
+ struct domain *dom = (struct domain *)opaque;
+ int i, max;
+ struct xencons_interface *intf = dom->interface;
+ XENCONS_RING_IDX prod;
+
+ max = ring_free_bytes(dom);
+ /* The can_receive() func limits this, but check again anyway */
+ if (max < len)
+ len = max;
+
+ prod = intf->in_prod;
+ for (i = 0; i < len; i++) {
+ intf->in[MASK_XENCONS_IDX(prod++, intf->in)] =
+ buf[i];
+ }
+ wmb();
+ intf->in_prod = prod;
+ xc_evtchn_notify(dom->xce_handle, dom->local_port);
+}
+
+static void xencons_send(struct domain *dom)
+{
+ ssize_t len;
+ len = qemu_chr_write(dom->chr, dom->buffer.data + dom->buffer.consumed,
+ dom->buffer.size - dom->buffer.consumed);
+ if (len < 1) {
+ /*
+ * Disable log because if we're redirecting to /dev/pts/N we
+ * don't want to flood logs when no client has the PTY open
+ */
+ /*
+ dolog(LOG_DEBUG, "Write failed on domain %d: %zd, %d\n",
+ dom->domid, len, errno);
+ */
+ } else {
+ buffer_advance(&dom->buffer, len);
+ }
+}
+
+static void xencons_ring_read(void *opaque)
+{
+ evtchn_port_t port;
+ struct domain *dom = (struct domain *)opaque;
+
+ if ((port = xc_evtchn_pending(dom->xce_handle)) == -1)
+ return;
+
+ buffer_append(dom);
+
+ (void)xc_evtchn_unmask(dom->xce_handle, port);
+
+ if (dom->buffer.size - dom->buffer.consumed)
+ xencons_send(dom);
+}
+
+static void xencons_startup(void *opaque)
+{
+ struct domain *dom = (struct domain *)opaque;
+ unsigned dummy;
+ char **vec;
+ int err;
+ vec = xs_read_watch(dom->xsh, &dummy);
+ if (vec)
+ free(vec);
+ fprintf(stderr, "Console: got watch\n");
+ err = domain_create_ring(dom);
+ if (err)
+ return;
+
+ xs_unwatch(dom->xsh, dom->conspath, "");
+ xs_unwatch(dom->xsh, dom->serialpath, "");
+ qemu_set_fd_handler2(xs_fileno(dom->xsh), NULL, NULL, NULL, NULL);
+
+ fprintf(stderr, "Console: connected to guest frontend\n");
+ if (qemu_set_fd_handler2(dom->xce_handle, NULL, xencons_ring_read, NULL, dom) < 0)
+ return;
+
+ qemu_chr_add_handlers(dom->chr, xencons_can_receive, xencons_receive,
+ NULL, dom);
+}
+
+
+int xencons_init(int domid, CharDriverState *chr)
+{
+ struct domain *dom = create_domain(domid, chr);
+
+ if (!dom)
+ return -1;
+
+ /* Setup watches so we asynchronously connect to serial console */
+ if (!(xs_watch(dom->xsh, dom->conspath, ""))) {
+ fprintf(stderr, "Unable to watch console %s\n", dom->conspath);
+ goto fail;
+ }
+ if (!(xs_watch(dom->xsh, dom->serialpath, ""))) {
+ fprintf(stderr, "Unable to watch console %s\n", dom->conspath);
+ xs_unwatch(dom->xsh, dom->conspath, "");
+ goto fail;
+ }
+ qemu_set_fd_handler2(xs_fileno(dom->xsh), NULL, xencons_startup, NULL, dom);
+ fprintf(stderr, "Console: prepared domain, waiting for ringref at %s or %s\n",
+ dom->conspath, dom->serialpath);
+
+ return 0;
+
+fail:
+ xs_daemon_close(dom->xsh);
+ free(dom->serialpath);
+ free(dom->conspath);
+ free(dom);
+ return -1;
+}
+
+
+/*
+ * Local variables:
+ * c-file-style: "linux"
+ * indent-tabs-mode: t
+ * c-indent-level: 8
+ * c-basic-offset: 8
+ * tab-width: 8
+ * End:
+ */
diff -r 96aa5e956a9f tools/ioemu/hw/xen_console.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/ioemu/hw/xen_console.h Wed Oct 24 15:25:27 2007 -0400
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) International Business Machines Corp., 2005
+ * Author(s): Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * Copyright (C) Red Hat 2007
+ *
+ * Xen Console
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; under version 2 of the License.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "vl.h"
+
+extern int xencons_init(int domid, CharDriverState *chr);
diff -r 96aa5e956a9f tools/ioemu/hw/xen_machine_pv.c
--- a/tools/ioemu/hw/xen_machine_pv.c Wed Oct 24 15:25:24 2007 -0400
+++ b/tools/ioemu/hw/xen_machine_pv.c Wed Oct 24 15:25:27 2007 -0400
@@ -23,6 +23,7 @@
*/
#include "vl.h"
+#include "xen_console.h"
#include "xenfb.h"
/* The Xen PV machine currently provides
@@ -38,6 +39,14 @@ static void xen_init_pv(uint64_t ram_siz
{
struct xenfb *xenfb;
extern int domid;
+
+ /* Connect to text console */
+ if (serial_hds[0]) {
+ if (xencons_init(domid, serial_hds[0]) < 0) {
+ fprintf(stderr, "Could not connect to domain console\n");
+ exit(1);
+ }
+ }
/* Prepare PVFB state */
xenfb = xenfb_new(domid, ds);
diff -r 96aa5e956a9f tools/ioemu/xenstore.c
--- a/tools/ioemu/xenstore.c Wed Oct 24 15:25:24 2007 -0400
+++ b/tools/ioemu/xenstore.c Wed Oct 24 15:25:27 2007 -0400
@@ -17,7 +17,7 @@
#include <sys/stat.h>
#include <fcntl.h>
-static struct xs_handle *xsh = NULL;
+struct xs_handle *xsh = NULL;
static char *media_filename[MAX_DISKS + MAX_SCSI_DISKS];
static QEMUTimer *insert_timer = NULL;
diff -r 96aa5e956a9f tools/python/xen/xend/XendDomainInfo.py
--- a/tools/python/xen/xend/XendDomainInfo.py Wed Oct 24 15:25:24 2007 -0400
+++ b/tools/python/xen/xend/XendDomainInfo.py Wed Oct 24 15:30:26 2007 -0400
@@ -1761,10 +1761,9 @@ class XendDomainInfo:
self.console_mfn = console_mfn
self._introduceDomain()
- if self.info.is_hvm():
- self.image = image.create(self, self.info)
- if self.image:
- self.image.createDeviceModel(True)
+ self.image = image.create(self, self.info)
+ if self.image:
+ self.image.createDeviceModel(True)
self._storeDomDetails()
self._registerWatches()
self.refreshShutdown()
--
|=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=|
|=- Perl modules: http://search.cpan.org/~danberr/ -=|
|=- Projects: http://freshmeat.net/~danielpb/ -=|
|=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|
next prev parent reply other threads:[~2007-10-24 20:43 UTC|newest]
Thread overview: 19+ messages / expand[flat|nested] mbox.gz Atom feed top
2007-10-24 20:35 PATCH 0/10: Merge PV framebuffer & console into QEMU Daniel P. Berrange
2007-10-24 20:39 ` PATCH 1/10: Add a QEMU machine type for fullvirt guests Daniel P. Berrange
2007-10-24 20:40 ` PATCH 2/10: Add a QEMU machine type for paravirt guests Daniel P. Berrange
2007-10-24 20:40 ` PATCH 3/10: Remove standalone xenfb code Daniel P. Berrange
2007-10-24 20:41 ` PATCH 4/10: Refactor xenfb event handlers Daniel P. Berrange
2007-10-24 20:41 ` PATCH 5/10: Refactor QEMU console integration Daniel P. Berrange
2007-10-24 20:42 ` PATCH 6/10: Merge private & public xenfb structs Daniel P. Berrange
2007-10-24 20:42 ` PATCH 7/10: Async negotiation with xenfb frontend Daniel P. Berrange
2007-10-24 20:43 ` Daniel P. Berrange [this message]
2007-10-24 20:44 ` PATCH 9/10: XenD device model re-factoring Daniel P. Berrange
2007-10-24 20:44 ` PATCH 10/10: Make xenconsoled ignore doms with qemu-dm Daniel P. Berrange
2007-10-25 18:56 ` PATCH 0/10: Merge PV framebuffer & console into QEMU Alex Williamson
2007-10-25 19:02 ` Daniel P. Berrange
2007-10-25 21:23 ` Daniel P. Berrange
2007-10-25 21:41 ` Alex Williamson
2007-10-26 8:16 ` Markus Armbruster
2007-10-26 16:15 ` Daniel P. Berrange
-- strict thread matches above, loose matches on Subject: below --
2007-08-22 22:26 PATCH: 0/10: Merge paravirt FB & console into qemu-dm Daniel P. Berrange
2007-08-22 22:32 ` PATCH: 8/10: Add pv console to QEMU paravirt machine Daniel P. Berrange
2007-08-15 20:00 PATCH: 0/10: Merge xenfb & xenconsoled into qemu-dm Daniel P. Berrange
2007-08-15 20:10 ` PATCH: 8/10: Add pv console to QEMU paravirt machine Daniel P. Berrange
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20071024204326.GL10807@redhat.com \
--to=berrange@redhat.com \
--cc=xen-devel@lists.xensource.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.