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

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



Cool!

Do we want to go with this? I can update the console daemon to use this code if we do.

Regards,

Anthony Liguori

Rusty Russell wrote:

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);

------------------------------------------------------------------------

/* Simple hacked up console userspace. */
#include <xc.h>
#include <xs.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/select.h>
#include <sys/mman.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define XEN_CONSOLE_FLAG_OVERFLOW               0x1
#define XEN_CONSOLE_FLAG_EIO                    0x2

#define EVENTCHN_BIND           _IO('E', 2)
#define EVENTCHN_UNBIND         _IO('E', 3)

struct ringbuf_head
{
        u32 write; /* Next place to write to */
        u32 read; /* Next place to read from */
        u8 flags;
        char buf[0];
} __attribute__((packed));

/* 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[4096 - 128 - sizeof(struct ringbuf_head)];
} __attribute__((packed, aligned(4096)));

static int xc_handle;

struct xen_console
{
        struct xen_console_buffer *buf;
        unsigned int evtchn;
};

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)) {
                errno = EIO;
                return -1;
        }

        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)) {
                errno = EIO;
                return -1;
        }

        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 write_console(struct xen_console *con,
                         const void *data, unsigned int len)
{
        int ret;

        ret = write_buf(&con->buf->inbuf, sizeof(con->buf->inbuf_buf),
                        data, len);
        if (ret >= 0)
                xc_evtchn_send(xc_handle, con->evtchn);
        return ret;
}

static int read_console(struct xen_console *con, void *data, unsigned int len)
{
        int ret;

        if (con->buf->outbuf.flags)
                fprintf(stderr, "*** WARNING: console %s\n",
                        con->buf->outbuf.flags & XEN_CONSOLE_FLAG_EIO
                        ? "reports IO error"
                        : con->buf->outbuf.flags & XEN_CONSOLE_FLAG_OVERFLOW
                        ? "has overflowed: missed output"
                        : "deeply confused");

        ret = read_buf(&con->buf->outbuf, sizeof(con->buf->outbuf_buf),
                       data, len);
        if (ret >= 0)
                xc_evtchn_send(xc_handle, con->evtchn);
        return ret;
}

static void report_buffer(const struct xen_console *con)
{
        printf("input: write marker %i read marker %i\n",
               con->buf->inbuf.write, con->buf->inbuf.read);
        printf("output: write marker %i read marker %i\n",
               con->buf->outbuf.write, con->buf->outbuf.read);
}

int main(int argc, char **argv)
{
        int port1 = 0, port2 = 0, eventchn_fd, rlen;
        long frame;
        unsigned int len;
        struct xs_handle *xs;
        char buffer[4096];
        struct xen_console console;
        char *p;

        if (argc != 2) {
                printf("Usage: %s DOMID\n", argv[0]);
                return 1;
        }

        xc_handle = xc_interface_open();
        if (xc_handle == -1) {
                printf("xc_interface_open() failed: %m");
                return 1;
        }

        xs = xs_daemon_open();
        if (xs == NULL) {
                printf("xs_daemon_open() failed: %m\n");
                return 1;
        }

        sprintf(buffer, "/domain/%s/console/event-channel", argv[1]);
        p = xs_read(xs, buffer, &len);
        if (!p) {
                printf("reading %s failed: %m\n", buffer);
                return 1;
        }
        port2 = atoi(p);

        /* FIXME: Handle multi-page */
        sprintf(buffer, "/domain/%s/console/frames", argv[1]);
        p = xs_read(xs, buffer, &len);
        if (!p) {
                printf("reading %s failed: %m\n", buffer);
                return 1;
        }
        frame = atol(p);

        if (xc_evtchn_bind_interdomain(xc_handle,
                                       DOMID_SELF, atoi(argv[1]),
                                       &port1, &port2) == -1) {
                printf("xc_evtchn_bind_interdomain() failed: %m\n");
                return 1;
        }

        console.evtchn = port1;
        console.buf = xc_map_foreign_range(xc_handle, atoi(argv[1]),
                                           getpagesize(),
                                           PROT_READ|PROT_WRITE,
                                           frame);
        if (!console.buf) {
                printf("xc_map_foreign_range of %li failed: %m\n",
                       frame);
                return 1;
        }

        eventchn_fd = open("/dev/xen/evtchn", O_RDWR);
        if (eventchn_fd < 0) {
                printf("opening %s: %m\n", "/dev/xen/evtchn");
                return 1;
        }

        if (ioctl(eventchn_fd, EVENTCHN_BIND, console.evtchn) != 0) {
                printf("binding to %i: %m\n", console.evtchn);
                return 1;
        }

        printf("Got event channel %i, frame %li\n", port2, frame);

        /* Dump backlog. */
        while ((rlen = read_console(&console, buffer, 4096))>0)
                write(STDOUT_FILENO, buffer, rlen);
        if (rlen < 0) {
                printf("Failed to read from console: %m\n");
                exit(1);
        }

        /* FIXME: We assume we can write without blocking. */
        for (;;) {
                fd_set inset;

                FD_ZERO(&inset);
                FD_SET(STDIN_FILENO, &inset);
                FD_SET(eventchn_fd, &inset);
                select(eventchn_fd+1, &inset, NULL, NULL, NULL);
                report_buffer(&console);
                if (FD_ISSET(eventchn_fd, &inset)) {
                        u16 port;

                        printf("Eventchn fd!\n");
                        if (read(eventchn_fd, &port, 2) != 2) {
                                printf("Failed to read from event fd: %m\n");
                                exit(1);
                        }

                        while ((rlen = read_console(&console, buffer, 4096))>0)
                                write(STDOUT_FILENO, buffer, rlen);
                        if (rlen < 0) {
                                printf("Failed to read from console: %m\n");
                                exit(1);
                        }
                        if (write(eventchn_fd, &port, 2) != 2) {
                                printf("Failed to write to event fd: %m\n");
                                exit(1);
                        }
                }
                if (FD_ISSET(STDIN_FILENO, &inset)) {
                        printf("Stdin fd!\n");
                        rlen = read(STDIN_FILENO, buffer, 4096);
                        if (rlen < 0) {
                                printf("Failed to read from stdin: %m\n");
                                exit(1);
                        }
                        if (write_console(&console, buffer, rlen) != rlen) {
                                printf("Failed to write to console: %m\n");
                                exit(1);
                        }
                }
                report_buffer(&console);
        }
}
------------------------------------------------------------------------

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



_______________________________________________
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®.