WARNING - OLD ARCHIVES

This is an archived copy of the Xen.org mailing list, which we have preserved to ensure that existing links to archives are not broken. The live archive, which contains the latest emails, can be found at http://lists.xen.org/
   
 
 
Xen 
 
Home Products Support Community News
 
   
 

xen-tools

[Xen-tools] [PATCH] An example console driver using the store

To: Xen Tools <xen-tools@xxxxxxxxxxxxxxxxxxx>
Subject: [Xen-tools] [PATCH] An example console driver using the store
From: Rusty Russell <rusty@xxxxxxxxxxxxxxx>
Date: Mon, 08 Aug 2005 17:46:09 +1000
Cc: Robert Read <robert@xxxxxxxxxxxxx>
Delivery-date: Mon, 08 Aug 2005 07:44:29 +0000
Envelope-to: www-data@xxxxxxxxxxxxxxxxxxx
List-help: <mailto:xen-tools-request@lists.xensource.com?subject=help>
List-id: Xen control tools developers <xen-tools.lists.xensource.com>
List-post: <mailto:xen-tools@lists.xensource.com>
List-subscribe: <http://lists.xensource.com/cgi-bin/mailman/listinfo/xen-tools>, <mailto:xen-tools-request@lists.xensource.com?subject=subscribe>
List-unsubscribe: <http://lists.xensource.com/cgi-bin/mailman/listinfo/xen-tools>, <mailto:xen-tools-request@lists.xensource.com?subject=unsubscribe>
Sender: xen-tools-bounces@xxxxxxxxxxxxxxxxxxx
Sorry this took so long, distracted by other things.

This patch adds xenbus_early_write, for use before we can sleep, and
illustrates how a console driver can use the store.  The console driver
replaces the normal one for testing, and uses the same ringbuf code as
xenbus (which really should be extracted into a separate library).

The toy client only handles a single page for the buffer, doesn't handle
^C etc.

This uncovered some bugs in xenstored: patches coming. 
Cheers!
Rusty.

Signed-off-by: Rusty Russell <rusty@xxxxxxxxxxxxxxx>

diff -r 079dce7cc5e3 linux-2.6.11-xen-sparse/arch/xen/kernel/reboot.c
--- a/linux-2.6.11-xen-sparse/arch/xen/kernel/reboot.c  Sun Aug  7 13:13:19 2005
+++ b/linux-2.6.11-xen-sparse/arch/xen/kernel/reboot.c  Mon Aug  8 16:53:16 2005
@@ -21,8 +21,8 @@
 void machine_restart(char * __unused)
 {
        /* We really want to get pending console data out before we die. */
-       extern void xencons_force_flush(void);
-       xencons_force_flush();
+//     extern void xencons_force_flush(void);
+//     xencons_force_flush();
        HYPERVISOR_reboot();
 }
 
@@ -34,8 +34,8 @@
 void machine_power_off(void)
 {
        /* We really want to get pending console data out before we die. */
-       extern void xencons_force_flush(void);
-       xencons_force_flush();
+//     extern void xencons_force_flush(void);
+//     xencons_force_flush();
        HYPERVISOR_shutdown();
 }
 
diff -r 079dce7cc5e3 linux-2.6.11-xen-sparse/drivers/xen/Makefile
--- a/linux-2.6.11-xen-sparse/drivers/xen/Makefile      Sun Aug  7 13:13:19 2005
+++ b/linux-2.6.11-xen-sparse/drivers/xen/Makefile      Mon Aug  8 16:53:16 2005
@@ -1,6 +1,7 @@
 
 
 obj-y  += console/
+obj-y  += test-console/
 obj-y  += evtchn/
 obj-y  += balloon/
 obj-y  += privcmd/
diff -r 079dce7cc5e3 linux-2.6.11-xen-sparse/drivers/xen/console/Makefile
--- a/linux-2.6.11-xen-sparse/drivers/xen/console/Makefile      Sun Aug  7 
13:13:19 2005
+++ b/linux-2.6.11-xen-sparse/drivers/xen/console/Makefile      Mon Aug  8 
16:53:16 2005
@@ -1,2 +1,3 @@
 
-obj-y  := console.o
+obj-$(CONFIG_XEN_PRIVILEGED_GUEST)     += console.o
+obj-y += test-console.c
diff -r 079dce7cc5e3 linux-2.6.11-xen-sparse/drivers/xen/xenbus/xenbus_comms.c
--- a/linux-2.6.11-xen-sparse/drivers/xen/xenbus/xenbus_comms.c Sun Aug  7 
13:13:19 2005
+++ b/linux-2.6.11-xen-sparse/drivers/xen/xenbus/xenbus_comms.c Mon Aug  8 
16:53:16 2005
@@ -119,35 +119,41 @@
        return avail != 0;
 }
 
-int xb_write(const void *data, unsigned len)
+int __xb_write(const void *data, unsigned len)
 {
        struct ringbuf_head h;
        struct ringbuf_head *out = outbuf();
-
+       unsigned int avail;
+       void *dst;
+
+       /* The world would be nicer if we could just *trust* the daemons. */
+       h = *out;
+       mb();
+       if (!check_buffer(&h))
+               return -EIO;
+
+       dst = get_output_chunk(&h, out->buf, &avail);
+       if (avail > len)
+               avail = len;
+       memcpy(dst, data, avail);
+       data += avail;
+       len -= avail;
+       update_output_chunk(out, avail);
+       notify_via_evtchn(xen_start_info.store_evtchn);
+
+       return avail;
+}
+
+int xb_write(const void *data, unsigned len)
+{
        do {
-               void *dst;
-               unsigned int avail;
-
-               wait_event(xb_waitq, output_avail(out));
-
-               /* Read, then check: not that we don't trust store.
-                * Hell, some of my best friends are daemons.  But,
-                * in this post-911 world... */
-               h = *out;
-               mb();
-               if (!check_buffer(&h)) {
-                       set_current_state(TASK_RUNNING);
-                       return -EIO; /* ETERRORIST! */
-               }
-
-               dst = get_output_chunk(&h, out->buf, &avail);
-               if (avail > len)
-                       avail = len;
-               memcpy(dst, data, avail);
-               data += avail;
-               len -= avail;
-               update_output_chunk(out, avail);
-               notify_via_evtchn(xen_start_info.store_evtchn);
+               int ret;
+
+               wait_event(xb_waitq, output_avail(outbuf()));
+               ret = __xb_write(data, len);
+               if (ret < 0)
+                       return ret;
+               len -= ret;
        } while (len != 0);
 
        return 0;
@@ -217,11 +223,6 @@
                unbind_evtchn_from_irq(xen_start_info.store_evtchn);
                return err;
        }
-
-       /* FIXME zero out page -- domain builder should probably do this*/
-       memset(machine_to_virt(xen_start_info.store_mfn << PAGE_SHIFT),
-              0, PAGE_SIZE);
-
        return 0;
 }
 
diff -r 079dce7cc5e3 linux-2.6.11-xen-sparse/drivers/xen/xenbus/xenbus_comms.h
--- a/linux-2.6.11-xen-sparse/drivers/xen/xenbus/xenbus_comms.h Sun Aug  7 
13:13:19 2005
+++ b/linux-2.6.11-xen-sparse/drivers/xen/xenbus/xenbus_comms.h Mon Aug  8 
16:53:16 2005
@@ -7,6 +7,7 @@
 
 /* Low level routines. */
 int xb_write(const void *data, unsigned len);
+int __xb_write(const void *data, unsigned len);
diff -r 079dce7cc5e3 linux-2.6.11-xen-sparse/include/asm-xen/xenbus.h
--- a/linux-2.6.11-xen-sparse/include/asm-xen/xenbus.h  Sun Aug  7 13:13:19 2005
+++ b/linux-2.6.11-xen-sparse/include/asm-xen/xenbus.h  Mon Aug  8 16:53:16 2005
@@ -121,6 +121,9 @@
 /* For backends, does lookup on uuid (up to /).  Returns domid, or -errno. */
 int xenbus_uuid_to_domid(const char *uuid);
 
+/* Can be used immediately: writes a value and doesn't wait for response. */
+void __init xenbus_early_write(const char *path, const char *value);
+
 /* Called from xen core code. */
 void xenbus_suspend(void);
 void xenbus_resume(void);
diff -r 079dce7cc5e3 linux-2.6.11-xen-sparse/drivers/xen/console/test-console.c
--- /dev/null   Sun Aug  7 13:13:19 2005
+++ b/linux-2.6.11-xen-sparse/drivers/xen/console/test-console.c        Mon Aug 
 8 16:53:16 2005
@@ -0,0 +1,463 @@
+/******************************************************************************
+ * Simple console.
+ *
+ * Copyright (C) 2005 Rusty Russell, IBM Corporation
+ * 
+ * This file may be distributed separately from the Linux kernel, or
+ * incorporated into other software packages, subject to the following license:
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this source file (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+#define DEBUG
+
+#include <asm-xen/hypervisor.h>
+#include <asm-xen/xenbus.h>
+#include <asm-xen/evtchn.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/console.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+
+#define XEN_CONSOLE_FLAG_OVERFLOW              0x1
+#define XEN_CONSOLE_FLAG_EIO                   0x2
+
+struct ringbuf_head
+{
+       __u32 write; /* Next place to write to */
+       __u32 read; /* Next place to read from */
+       __u8 flags;
+       char buf[0];
+} __attribute__((packed));
+
+/* FIXME: Use 1 << CONFIG_LOG_BUF_SHIFT. */
+#define BUFFER_SIZE 4096 
+
+/* Two circular buffers: small one for input, large one for output. */
+struct xen_console_buffer
+{
+       struct ringbuf_head inbuf;
+       char inbuf_buf[128 - sizeof(struct ringbuf_head)];
+
+       struct ringbuf_head outbuf;
+       char outbuf_buf[BUFFER_SIZE - 128 - sizeof(struct ringbuf_head)];
+} __attribute__((packed, aligned(PAGE_SIZE)));
+
+static struct xen_console_buffer xen_console_buffer;
+
+/* We have only one, but for future convenience we keep a struct. */
+struct xen_console {
+       struct xen_console_buffer *buf;
+       struct tty_struct *tty;
+       spinlock_t lock;
+       int count;
+       u16 evtchn;
+};
+static struct xen_console console;
+
+static inline int check_buffer(const struct ringbuf_head *h, __u32 bufsize)
+{
+       return (h->write < bufsize && h->read < bufsize);
+}
+
+/* We can't fill last byte: would look like empty buffer. */
+static char *get_write_chunk(const struct ringbuf_head *h,
+                            char *buf, __u32 bufsize,
+                            __u32 *len)
+{
+       __u32 read_mark;
+
+       if (h->read == 0)
+               read_mark = bufsize - 1;
+       else
+               read_mark = h->read - 1;
+
+       /* Here to the end of buffer, unless they haven't read some out. */
+       *len = bufsize - h->write;
+       if (read_mark >= h->write)
+               *len = read_mark - h->write;
+       return buf + h->write;
+}
+
+static void update_write_chunk(struct ringbuf_head *h,
+                              __u32 bufsize, __u32 len)
+{
+       h->write += len;
+       if (h->write == bufsize)
+               h->write = 0;
+}
+
+static const char *get_read_chunk(const struct ringbuf_head *h,
+                                 const char *buf, __u32 bufsize,
+                                 __u32 *len)
+{
+       /* Here to the end of buffer, unless they haven't written some. */
+       *len = bufsize - h->read;
+       if (h->write >= h->read)
+               *len = h->write - h->read;
+       return buf + h->read;
+}
+
+static void update_read_chunk(struct ringbuf_head *h,
+                              __u32 bufsize, __u32 len)
+{
+       h->read += len;
+       if (h->read == bufsize)
+               h->read = 0;
+}
+
+static int write_buf(struct ringbuf_head *head, __u32 bufsize,
+                    const char *src, __u32 len)
+{
+       int ret = 0;
+       struct ringbuf_head h;
+
+       /* Must read head once, and before anything else. */
+       h = *head;
+       mb();
+
+       if (!check_buffer(&h, bufsize))
+               return -EIO;
+
+       while (len > 0) {
+               __u32 thislen;
+               char *dst = get_write_chunk(&h, head->buf, bufsize, &thislen);
+
+               if (thislen == 0)
+                       break;
+               if (thislen > len)
+                       thislen = len;
+               memcpy(dst, src, thislen);
+               update_write_chunk(&h, bufsize, thislen);
+               src += thislen;
+               len -= thislen;
+               ret += thislen;
+       }
+
+       /* Must have written data before updating head. */
+       mb();
+       *head = h;
+       return ret;
+}
+
+static int read_buf(struct ringbuf_head *head, __u32 bufsize,
+                   char *dst, __u32 len)
+{
+       int ret = 0;
+       struct ringbuf_head h;
+
+       /* Must read head once, and before anything else. */
+       h = *head;
+       mb();
+
+       if (!check_buffer(&h, bufsize))
+               return -EIO;
+
+       while (len > 0) {
+               __u32 thislen;
+               const char *src;
+
+               src = get_read_chunk(&h, head->buf, bufsize, &thislen);
+
+               if (thislen == 0)
+                       break;
+               if (thislen > len)
+                       thislen = len;
+               memcpy(dst, src, thislen);
+               update_read_chunk(&h, bufsize, thislen);
+               dst += thislen;
+               len -= thislen;
+               ret += thislen;
+       }
+
+       /* Must have read data before updating head. */
+       mb();
+       *head = h;
+       return ret;
+}
+
+static int xen_console_open(struct tty_struct *tty, struct file *filp)
+{
+       if (tty->index != 0)
+               return -ENODEV;
+
+       spin_lock_irq(&console.lock);
+       console.tty = tty;
+       console.count++;
+       tty->driver_data = &console;
+       spin_unlock_irq(&console.lock);
+
+       return 0;
+}
+
+static void xen_console_hangup(struct tty_struct *tty)
+{
+       struct xen_console *c = tty->driver_data;
+
+       spin_lock_irq(&c->lock);
+       c->tty = NULL;
+       spin_unlock_irq(&c->lock);
+}
+
+static void xen_console_close(struct tty_struct *tty, struct file *filp)
+{
+       struct xen_console *c = tty->driver_data;
+
+       if (tty_hung_up_p(filp))
+               return;
+
+       spin_lock_irq(&c->lock);
+       if (--c->count == 0)
+               c->tty = NULL;
+       spin_unlock_irq(&c->lock);
+}
+
+static int xen_console_put(struct xen_console *c,
+                          const unsigned char *buf,
+                          int len)
+{
+       int done;
+       unsigned long flags;
+
+       spin_lock_irqsave(&c->lock, flags);
+       done = write_buf(&c->buf->outbuf, sizeof(c->buf->outbuf_buf), buf,len);
+
+       /* Error, overflow, or OK. */
+       if (done < 0)
+               c->buf->outbuf.flags |= XEN_CONSOLE_FLAG_EIO;
+       else if (done < len)
+               c->buf->outbuf.flags |= XEN_CONSOLE_FLAG_OVERFLOW;
+       else if (c->buf->outbuf.flags)
+               c->buf->outbuf.flags = 0;
+
+       /* Tell the other side we changed the field. */
+       notify_via_evtchn(c->evtchn);
+       spin_unlock_irqrestore(&c->lock, flags);
+       return len;
+}
+
+static int xen_console_write(struct tty_struct *tty,
+                         const unsigned char *buf, int count)
+{
+       return xen_console_put(tty->driver_data, buf, count);
+}
+
+static irqreturn_t irq_handler(int irq, void *val, struct pt_regs *regs)
+{
+       struct xen_console *c = val;
+       unsigned long flags;
+       int done = 0, len;
+
+       spin_lock_irqsave(&c->lock, flags);
+       if (c->tty) {
+               len = TTY_FLIPBUF_SIZE - c->tty->flip.count;
+               done = read_buf(&c->buf->inbuf, sizeof(c->buf->inbuf_buf), 
+                               c->tty->flip.char_buf_ptr, len);
+               if (done < 0)
+                       c->buf->outbuf.flags |= XEN_CONSOLE_FLAG_EIO;
+               else {
+                       memset(c->tty->flip.flag_buf_ptr, 0, done);
+                       c->tty->flip.count += done;
+                       tty_schedule_flip(c->tty);
+               }
+       }
+       spin_unlock_irqrestore(&c->lock, flags);
+
+       if (!done)
+               return IRQ_NONE;
+
+       return IRQ_HANDLED;
+}
+
+static int xen_console_write_room(struct tty_struct *tty)
+{
+       struct xen_console *c = tty->driver_data;
+
+       /*
+        * Pretend to be infinite: we want to discard ourselves so we know.
+        * Also, tty layer seems not to call us again if we ever return 0.
+        */
+       return sizeof(c->buf->outbuf_buf);
+}
+
+static int xen_console_chars_in_buffer(struct tty_struct *tty)
+{
+       struct xen_console *c = tty->driver_data;
+       struct ringbuf_head h;
+       unsigned long flags;
+       u32 len;
+
+       spin_lock_irqsave(&c->lock, flags);
+       h = c->buf->inbuf;
+       rmb();                  /* Ensure we read only once. */
+       get_read_chunk(&h, NULL, sizeof(c->buf->inbuf_buf), &len);
+       spin_unlock_irqrestore(&c->lock, flags);
+       return len;
+}
+
+static struct tty_operations xen_console_ops = {
+       .open = xen_console_open,
+       .close = xen_console_close,
+       .write = xen_console_write,
+       .hangup = xen_console_hangup,
+       .write_room = xen_console_write_room,
+       .chars_in_buffer = xen_console_chars_in_buffer,
+};
+
+static struct tty_driver *xen_console_driver;
+
+/* FIXME: Go dynamic.... --RR */
+#define XEN_CONSOLE_MAJOR      249
+#define XEN_CONSOLE_MINOR      0
+
+/* Upper bound to sprintf this simple type?  Each 3 bits < 1 digit. */
+#define CHAR_SIZE(type) (((sizeof(type)*8 + 2) / 3) + 1)
+
+/* FIXME: Assumes console will be read from domain 0. */
+static int __init get_eventchannel(void)
+{
+       evtchn_op_t op = { .cmd = EVTCHNOP_alloc_unbound };
+       int err;
+
+       op.u.alloc_unbound.dom = 0;
+       err = HYPERVISOR_event_channel_op(&op);
+       if (err)
+               return err;
+       return op.u.alloc_unbound.port;
+}
+
+static char *__init console_frames_string(void)
+{
+       /* Room for one long + separator per page,  */
+       static char str[CHAR_SIZE(long)
+                       * sizeof(xen_console_buffer)/PAGE_SIZE];
+       unsigned int i;
+       void *p;
+
+       p = &xen_console_buffer;
+       for (i = 0; i < sizeof(xen_console_buffer)/PAGE_SIZE; i++)
+               sprintf(str + strlen(str), "%s%li",
+                       i ? "," : "",
+                       virt_to_machine(p + i*PAGE_SIZE) >> PAGE_SHIFT);
+       return str;
+}
+
+static int __init init(void)
+{
+       int err, irq;
+
+       printk("test-console init\n");
+       if (!xen_start_info.store_evtchn)
+               return 0;
+
+       if (!console.evtchn) {
+               err = get_eventchannel();
+               if (err < 0)
+                       return err;
+               console.evtchn = err;
+               down(&xenbus_lock);
+               xenbus_write("console", "frames",
+                            console_frames_string(), O_CREAT);
+               xenbus_printf("console", "event-channel", "%i",console.evtchn);
+               up(&xenbus_lock);
+       }
+       irq = bind_evtchn_to_irq(console.evtchn);
+       err = request_irq(irq, irq_handler, SA_SHIRQ, "xenconsole", &console);
+       if (err)
+               return err;
+
+       xen_console_driver = alloc_tty_driver(1);
+       if (!xen_console_driver)
+               return -ENOMEM;
+
+       xen_console_driver->owner = THIS_MODULE;
+       xen_console_driver->devfs_name = "xencons/";
+       xen_console_driver->driver_name = "xencons";
+       xen_console_driver->name = "xencons";
+       xen_console_driver->major = XEN_CONSOLE_MAJOR;
+       xen_console_driver->minor_start = XEN_CONSOLE_MINOR;
+       xen_console_driver->type = TTY_DRIVER_TYPE_SYSTEM;
+       xen_console_driver->init_termios = tty_std_termios;
+       xen_console_driver->flags = TTY_DRIVER_REAL_RAW;
+       tty_set_operations(xen_console_driver, &xen_console_ops);
+       if (tty_register_driver(xen_console_driver))
+               panic("Couldn't register xen_console tty driver\n");
+
+       return 0;
+}
+
+static void __exit fini(void)
+{
+       tty_unregister_driver(xen_console_driver);
+       put_tty_driver(xen_console_driver);
+}
+
+module_init(init);
+module_exit(fini);
+
+static void xen_console_print(struct console *c, const char *b, unsigned count)
+{
+       xen_console_put(&console, b, count);
+}
+
+static struct tty_driver *xen_console_device(struct console *c, int *index)
+{
+       *index = c->index;
+       return xen_console_driver;
+}
+
+static struct console xen_console_console = {
+       .name           = "xencons",
+       .write          = xen_console_print,
+       .device         = xen_console_device,
+       .flags          = CON_PRINTBUFFER,
+       .index          = -1,
+};
+
+static void __init output_console_info(void)
+{
+       char evtchn[sizeof(console.evtchn) * 3 + 1];
+
+       sprintf(evtchn, "%u", console.evtchn);
+       xenbus_early_write("console/event-channel", evtchn);
+       xenbus_early_write("console/frames", console_frames_string());
+}
+
+static int __init xen_console_init(void)
+{
+       int err;
+
+       if (!xen_start_info.store_evtchn)
+               return 0;
+
+       console.buf = &xen_console_buffer;
+       spin_lock_init(&console.lock);
+
+       err = get_eventchannel();
+       if (err < 0)
+               return err;
+       console.evtchn = err;
+       output_console_info();
+       register_console(&xen_console_console);
+       return 0;
+}
+
+console_initcall(xen_console_init);

-- 
A bad analogy is like a leaky screwdriver -- Richard Braakman

Attachment: vm-console-hack.c
Description: Text Data

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