[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Xen-devel] PATCH: 8/10: Add pv console to QEMU paravirt machine



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  |  424 ++++++++++++++++++++++++++++++++++++++++
 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 
 5 files changed, 460 insertions(+), 1 deletion(-)


   Signed-off-by: Daniel P. Berrange <berrange@xxxxxxxxxx>

Dan.


diff -r fb720737dddb tools/ioemu/Makefile.target
--- a/tools/ioemu/Makefile.target       Tue Aug 21 22:15:30 2007 -0400
+++ b/tools/ioemu/Makefile.target       Tue Aug 21 22:16:00 2007 -0400
@@ -403,6 +403,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 fb720737dddb tools/ioemu/hw/xen_console.c
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/ioemu/hw/xen_console.c      Tue Aug 21 22:16:00 2007 -0400
@@ -0,0 +1,424 @@
+/*
+ *  Copyright (C) International Business Machines  Corp., 2005
+ *  Author(s): Anthony Liguori <aliguori@xxxxxxxxxx>
+ *
+ *  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 <xs.h>
+#include <xen/io/console.h>
+#include <xenctrl.h>
+
+#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 "vl.h"
+
+#include "xen_console.h"
+
+#define MAX(a, b) (((a) > (b)) ? (a) : (b))
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+
+/* Each 10 bits takes ~ 3 digits, plus one, plus one for nul terminator. */
+#define MAX_STRLEN(x) ((sizeof(x) * CHAR_BIT + CHAR_BIT-1) / 10 * 3 + 2)
+
+#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 */
+       xs_watch(dom->xsh, dom->conspath, "");
+       xs_watch(dom->xsh, dom->serialpath, "");
+       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;
+}
+
+
+/*
+ * Local variables:
+ *  c-file-style: "linux"
+ *  indent-tabs-mode: t
+ *  c-indent-level: 8
+ *  c-basic-offset: 8
+ *  tab-width: 8
+ * End:
+ */
diff -r fb720737dddb tools/ioemu/hw/xen_console.h
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/ioemu/hw/xen_console.h      Tue Aug 21 22:16:00 2007 -0400
@@ -0,0 +1,25 @@
+/*
+ *  Copyright (C) International Business Machines  Corp., 2005
+ *  Author(s): Anthony Liguori <aliguori@xxxxxxxxxx>
+ *
+ *  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 fb720737dddb tools/ioemu/hw/xen_machine_pv.c
--- a/tools/ioemu/hw/xen_machine_pv.c   Tue Aug 21 22:15:30 2007 -0400
+++ b/tools/ioemu/hw/xen_machine_pv.c   Tue Aug 21 22:16:00 2007 -0400
@@ -23,6 +23,7 @@
  */
 
 #include "vl.h"
+#include "xen_console.h"
 #include "xenfb.h"
 
 
@@ -39,6 +40,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 fb720737dddb tools/ioemu/xenstore.c
--- a/tools/ioemu/xenstore.c    Tue Aug 21 22:15:30 2007 -0400
+++ b/tools/ioemu/xenstore.c    Tue Aug 21 22:16:00 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;
 

-- 
|=- 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  -=| 

_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.