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
vm-console-hack.c
Description: Text Data
_______________________________________________
Xen-tools mailing list
Xen-tools@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-tools
|