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

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



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

 


Rackspace

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