]> xenbits.xensource.com Git - qemu-xen-4.2-testing.git/commitdiff
Replacement PV console backend (Gerd Hoffman)
authorIan Jackson <ian.jackson@eu.citrix.com>
Fri, 16 Jan 2009 16:02:18 +0000 (16:02 +0000)
committerIan Jackson <Ian.Jackson@eu.citrix.com>
Fri, 16 Jan 2009 16:02:18 +0000 (16:02 +0000)
Description from Gerd's message:
    xen: add console backend driver.

    This patch adds a xenconsole backend driver.  It it based on current
    xen-unstable code.  It has been changed to make use of the common
    backend driver code.

Note that this driver is not used at all in the default configurations
of upstream Xen.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
(Applied as-is.)

hw/xen_backend.h
hw/xen_console.c
hw/xen_console.h [deleted file]
hw/xen_machine_pv.c

index 70daebd1726859350a6af2ac1027658ca3f75ebc..6ae9e076698539d4244b2b229685dc35d97d1b57 100644 (file)
@@ -84,4 +84,7 @@ int xen_be_send_notify(struct XenDevice *xendev);
 void xen_be_printf(struct XenDevice *xendev, int msg_level, const char *fmt, ...)
     __attribute__ ((format(printf, 3, 4)));
 
+/* actual backend drivers */
+extern struct XenDevOps xen_console_ops;      /* xen_console.c     */
+
 #endif /* QEMU_HW_XEN_BACKEND_H */
index 9ec08e8d90ba051b46b21f54dde5ef91df4aa76a..c172cf2bc9df36ce6c31368a4be0c913eda5f589 100644 (file)
@@ -9,23 +9,17 @@
  *  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 "hw.h"
-#include "sysemu.h"
-#include "exec-all.h"
-#include "qemu-char.h"
-
-#include <malloc.h>
 #include <stdlib.h>
 #include <errno.h>
 #include <string.h>
 #include <xen/io/console.h>
 #include <xenctrl.h>
 
-#include "xen_console.h"
+#include "hw.h"
+#include "sysemu.h"
+#include "qemu-char.h"
+#include "xen_backend.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 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;
+struct XenConsole {
+    struct XenDevice  xendev;  /* must be first */
+    struct buffer     buffer;
+    char              console[XEN_BUFSIZE];
+    int               ring_ref;
+    void              *sring;
+    CharDriverState   *chr;
+    int               backlog;
 };
 
-
-static void buffer_append(struct domain *dom)
+static void buffer_append(struct XenConsole *con)
 {
-       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;
-       xen_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);
-               }
+    struct buffer *buffer = &con->buffer;
+    XENCONS_RING_IDX cons, prod, size;
+    struct xencons_interface *intf = con->sring;
+
+    cons = intf->out_cons;
+    prod = intf->out_prod;
+    xen_mb();
+
+    size = prod - cons;
+    if ((size == 0) || (size > sizeof(intf->out)))
+       return;
+
+    if ((buffer->capacity - buffer->size) < size) {
+       buffer->capacity += (size + 1024);
+       buffer->data = qemu_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)];
+    while (cons != prod)
+       buffer->data[buffer->size++] = intf->out[
+           MASK_XENCONS_IDX(cons++, intf->out)];
 
-       xen_mb();
-       intf->out_cons = cons;
-       xc_evtchn_notify(dom->xce_handle, dom->local_port);
+    xen_mb();
+    intf->out_cons = cons;
+    xen_be_send_notify(&con->xendev);
 
-       if (buffer->max_capacity &&
-           buffer->size > buffer->max_capacity) {
-               /* Discard the middle of the data. */
+    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;
+       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;
+       memmove(maxpos - over, maxpos, over);
+       buffer->data = qemu_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;
-       }
+       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, limit, rc;
-
-       err = xs_gather(dom->xsh, dom->serialpath,
-                       "ring-ref", "%u", &ring_ref,
-                       "port", "%i", &remote_port,
-                       "limit", "%i", &limit,
-                       NULL);
-       if (err) {
-               err = xs_gather(dom->xsh, dom->conspath,
-                               "ring-ref", "%u", &ring_ref,
-                               "port", "%i", &remote_port,
-                               "limit", "%i", &limit,
-                               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;
-       dom->buffer.max_capacity = limit;
-       fprintf(stderr, "Console: got ring-ref %d port %d limit %d\n", 
-               ring_ref, remote_port, limit);
-
-       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;
+    buffer->consumed += len;
+    if (buffer->consumed == buffer->size) {
+       buffer->consumed = 0;
+       buffer->size = 0;
+    }
 }
 
-
-static int ring_free_bytes(struct domain *dom)
+static int ring_free_bytes(struct XenConsole *con)
 {
-       struct xencons_interface *intf = dom->interface;
-       XENCONS_RING_IDX cons, prod, space;
+    struct xencons_interface *intf = con->sring;
+    XENCONS_RING_IDX cons, prod, space;
 
-       cons = intf->in_cons;
-       prod = intf->in_prod;
-       xen_mb();
+    cons = intf->in_cons;
+    prod = intf->in_prod;
+    xen_mb();
 
-       space = prod - cons;
-       if (space > sizeof(intf->in))
-               return 0; /* ring is screwed: ignore it */
+    space = prod - cons;
+    if (space > sizeof(intf->in))
+       return 0; /* ring is screwed: ignore it */
 
-       return (sizeof(intf->in) - space);
+    return (sizeof(intf->in) - space);
 }
 
 static int xencons_can_receive(void *opaque)
 {
-       struct domain *dom = (struct domain *)opaque;
-
-       return ring_free_bytes(dom);
+    struct XenConsole *con = opaque;
+    return ring_free_bytes(con);
 }
 
 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];
-       }
-       xen_wmb();
-       intf->in_prod = prod;
-       xc_evtchn_notify(dom->xce_handle, dom->local_port);
+    struct XenConsole *con = opaque;
+    struct xencons_interface *intf = con->sring;
+    XENCONS_RING_IDX prod;
+    int i, max;
+
+    max = ring_free_bytes(con);
+    /* 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];
+    }
+    xen_wmb();
+    intf->in_prod = prod;
+    xen_be_send_notify(&con->xendev);
 }
 
-static void xencons_send(struct domain *dom)
+static void xencons_send(struct XenConsole *con)
 {
-       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);
+    ssize_t len, size;
+
+    size = con->buffer.size - con->buffer.consumed;
+    if (con->chr)
+        len = qemu_chr_write(con->chr, con->buffer.data + con->buffer.consumed,
+                             size);
+    else
+        len = size;
+    if (len < 1) {
+       if (!con->backlog) {
+           con->backlog = 1;
+           xen_be_printf(&con->xendev, 1, "backlog piling up, nobody listening?\n");
        }
+    } else {
+       buffer_advance(&con->buffer, len);
+       if (con->backlog && len == size) {
+           con->backlog = 0;
+           xen_be_printf(&con->xendev, 1, "backlog is gone\n");
+       }
+    }
 }
 
-static void xencons_ring_read(void *opaque)
+/* -------------------------------------------------------------------- */
+
+static int con_init(struct XenDevice *xendev)
 {
-       evtchn_port_t port;
-       struct domain *dom = (struct domain *)opaque;
+    struct XenConsole *con = container_of(xendev, struct XenConsole, xendev);
+    char *type, *dom;
 
-       if ((port = xc_evtchn_pending(dom->xce_handle)) == -1)
-               return;
+    /* setup */
+    dom = xs_get_domain_path(xenstore, con->xendev.dom);
+    snprintf(con->console, sizeof(con->console), "%s/console", dom);
+    free(dom);
 
-       buffer_append(dom);
+    type = xenstore_read_str(con->console, "type");
+    if (!type || 0 != strcmp(type, "ioemu")) {
+       xen_be_printf(xendev, 1, "not for me (type=%s)\n", type);
+       return -1;
+    }
 
-       (void)xc_evtchn_unmask(dom->xce_handle, port);
+    if (!serial_hds[con->xendev.dev])
+       xen_be_printf(xendev, 1, "WARNING: serial line %d not configured\n",
+                      con->xendev.dev);
+    else
+        con->chr = serial_hds[con->xendev.dev];
 
-       if (dom->buffer.size - dom->buffer.consumed)
-               xencons_send(dom);
+    return 0;
 }
 
-static void xencons_startup(void *opaque)
+static int con_connect(struct XenDevice *xendev)
 {
-       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(xc_evtchn_fd(dom->xce_handle), NULL, xencons_ring_read, NULL, dom) < 0)
-               return;
-
-       qemu_chr_add_handlers(dom->chr, xencons_can_receive, xencons_receive,
-                             NULL, dom);
-}
+    struct XenConsole *con = container_of(xendev, struct XenConsole, xendev);
+    int limit;
 
+    if (-1 == xenstore_read_int(con->console, "ring-ref", &con->ring_ref))
+       return -1;
+    if (-1 == xenstore_read_int(con->console, "port", &con->xendev.remote_port))
+       return -1;
+    if (0 == xenstore_read_int(con->console, "limit", &limit))
+       con->buffer.max_capacity = limit;
+
+    con->sring = xc_map_foreign_range(xen_xc, con->xendev.dom,
+                                     XC_PAGE_SIZE,
+                                     PROT_READ|PROT_WRITE,
+                                     con->ring_ref);
+    if (!con->sring)
+       return -1;
 
-int xencons_init(int domid, CharDriverState *chr)
+    xen_be_bind_evtchn(&con->xendev);
+    if (con->chr)
+        qemu_chr_add_handlers(con->chr, xencons_can_receive, xencons_receive,
+                              NULL, con);
+
+    xen_be_printf(xendev, 1, "ring mfn %d, remote port %d, local port %d, limit %zd\n",
+                 con->ring_ref,
+                 con->xendev.remote_port,
+                 con->xendev.local_port,
+                 con->buffer.max_capacity);
+    return 0;
+}
+
+static void con_disconnect(struct XenDevice *xendev)
 {
-       struct domain *dom = create_domain(domid, chr);
+    struct XenConsole *con = container_of(xendev, struct XenConsole, xendev);
 
-       if (!dom)
-               return -1;
+    if (con->chr)
+        qemu_chr_add_handlers(con->chr, NULL, NULL, NULL, NULL);
+    xen_be_unbind_evtchn(&con->xendev);
 
-       /* 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);
+    if (con->sring) {
+       munmap(con->sring, XC_PAGE_SIZE);
+       con->sring = NULL;
+    }
+}
 
-       return 0;
+static void con_event(struct XenDevice *xendev)
+{
+    struct XenConsole *con = container_of(xendev, struct XenConsole, xendev);
 
-fail:
-       xs_daemon_close(dom->xsh);
-       free(dom->serialpath);
-       free(dom->conspath);
-       free(dom);
-       return -1;
+    buffer_append(con);
+    if (con->buffer.size - con->buffer.consumed)
+       xencons_send(con);
 }
 
+/* -------------------------------------------------------------------- */
 
-/*
- * Local variables:
- *  c-file-style: "linux"
- *  indent-tabs-mode: t
- *  c-indent-level: 8
- *  c-basic-offset: 8
- *  tab-width: 8
- * End:
- */
+struct XenDevOps xen_console_ops = {
+    .size       = sizeof(struct XenConsole),
+    .flags      = DEVOPS_FLAG_IGNORE_STATE,
+    .init       = con_init,
+    .connect    = con_connect,
+    .event      = con_event,
+    .disconnect = con_disconnect,
+};
diff --git a/hw/xen_console.h b/hw/xen_console.h
deleted file mode 100644 (file)
index bbea610..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- *  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 "hw.h"
-#include "qemu-xen.h"
-
-extern int xencons_init(int domid, CharDriverState *chr);
index 7d5c07cbd937331f551b1ad1239a708303e09656..e4ffd46c4f3e79c746d949520ca9bf4a03def3a7 100644 (file)
@@ -24,7 +24,6 @@
 
 #include "hw.h"
 #include "pc.h"
-#include "xen_console.h"
 #include "xenfb.h"
 #include "sysemu.h"
 #include "boards.h"
@@ -65,14 +64,7 @@ static void xen_init_pv(ram_addr_t ram_size, int vga_ram_size,
         fprintf(stderr, "%s: xen backend core setup failed\n", __FUNCTION__);
         exit(1);
     }
-
-    /* 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);
-        }
-    }
+    xen_be_register("console", &xen_console_ops);
 
     /* Prepare PVFB state */
     xenfb = xenfb_new(domid, ds);