diff --git a/drivers/xen/v2v/Kconfig b/drivers/xen/v2v/Kconfig
-index 5966234..b3b6ef4 100644
+index dd48eaf..de67567 100644
--- a/drivers/xen/v2v/Kconfig
+++ b/drivers/xen/v2v/Kconfig
@@ -22,3 +22,10 @@ config XEN_V2V_DRV
- default n
+ default n
help
Sample for Xen V2V interdomain communication services.
+
+ help
+ Xen V2V char device for userland v2v.
diff --git a/drivers/xen/v2v/Makefile b/drivers/xen/v2v/Makefile
-index f3442d9..5230cd6 100644
+index 8e35e62..50201b1 100644
--- a/drivers/xen/v2v/Makefile
+++ b/drivers/xen/v2v/Makefile
-@@ -4,5 +4,6 @@
+@@ -4,5 +4,6 @@ obj-$(CONFIG_XEN_V2V_DRV) += v2vdrv.o
- v2vdrv-objs =
+ v2vdrv-objs =
v2vdrv-objs += v2vsamp.o v2vops.o
+obj-$(CONFIG_XEN_V2V_DEV) += v2vdev.o
ccflags-$(CONFIG_XEN_V2V_DEBUG) += -DDEBUG
diff --git a/drivers/xen/v2v/v2vdev.c b/drivers/xen/v2v/v2vdev.c
new file mode 100644
-index 0000000..adfc6c8
+index 0000000..9a42523
--- /dev/null
+++ b/drivers/xen/v2v/v2vdev.c
-@@ -0,0 +1,511 @@
+@@ -0,0 +1,909 @@
+/******************************************************************************
+ * drivers/xen/v2v/v2vdev.c
+ *
+ * software packages, subject to the following license:
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
-+ * of this source filp (the "Software"), to deal in the Software without
++ * of this source f (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
+#include <xen/v2v.h>
+#include <xen/interface/io/vring.h>
+
++#define V2V_STREAM_MSGSIZE 2048
++
+#define V2VLISTEN(len) _IOC(_IOC_WRITE, 'V', 0x01, len)
+#define V2VCONNECT(len) _IOC(_IOC_WRITE, 'V', 0x02, len)
+
-+#define MIN(_x_, _y_) ((_x_) < (_y_) ? (_x_) : (_y_))
-+
-+#define DPRINTK(fmt, args...) \
-+ printk("V2V-DEV (%s:%d) "fmt, \
-+ __FUNCTION__, __LINE__, ##args)
-+#define IPRINTK(fmt, args...) \
-+ printk(KERN_INFO "V2V-DEV: "fmt, ##args)
-+#define WPRINTK(fmt, args...) \
-+ printk(KERN_WARNING "V2V-DEV: "fmt, ##args)
++#define L_READ 0
++#define L_WRITE 1
++#define L_CONNECT 2
+
-+struct v2vdev_context
-+{
-+ int connected;
++#define MIN(a, b) ((a) < (b) ? (a) : (b))
+
-+ void *buff;
-+ char *data_ptr;
-+ size_t buff_size;
-+ size_t data_len;
++#define FISH do { printk(KERN_ERR "%s:%s:%d\n", __FILE__,__PRETTY_FUNCTION__,__LINE__ ); } while (1==0)
+
++enum v2vdev_state
++{
++ V2VDEV_UNKNOWN,
++ V2VDEV_UNBOUND,
++ V2VDEV_LISTEN,
++ V2VDEV_CONNECT,
++ V2VDEV_CONNECTED,
++ V2VDEV_DISCONNECTED
++};
+
-+ int last_err;
-+ int listenner;
-+ struct v2v_channel *channel;
++struct v2vdev_buf
++{
++ void *buf;
++ char *ptr;
++ int len;
++ int pending;
+};
+
-+#if 0
-+#define DUMP_CTX(a) dump_ctx(__func__, __LINE__, a)
-+static void dump_ctx(const char *func, int line, struct v2vdev_context *ctx)
++
++struct v2vdev
+{
-+ printk(KERN_ERR "%s:%d conn=%d buff=%p bsize=%d dptr=%p dlen=%d err=%d\n",
-+ func, line, ctx->connected,ctx->buff,ctx->buff_size,ctx->data_ptr,
-+ ctx->data_len,ctx->last_err);
-+}
-+#endif
-+#define DUMP_CTX(a) ((void)0)
++ struct v2v_channel *channel;
++
++ enum v2vdev_state state;
++
++ struct v2vdev_buf *read_buf;
++ //struct v2vdev_buf *write_buf;
++
++ int recv_blocked;
++ int send_blocked;
++
++};
+
++/* Utils */
+
+static void
+hexdump (char *prefix, void *_d, int len)
+{
-+ uint8_t *d = (uint8_t *) _d;
-+ int i, j, k;
-+ int e;
++ uint8_t *d = (uint8_t *) _d;
++ int i, j, k;
++ int e;
+
-+ printk (KERN_INFO "%s %d bytes from %p\n", prefix, len, d);
++ printk (KERN_INFO "%s %d bytes from %p\n", prefix, len, d);
+
-+ if (!d || len<0)
-+ return;
++ if (!d || len < 0)
++ return;
+
-+ e = len + 15;
-+ e &= ~15;
++ e = len + 15;
++ e &= ~15;
+
-+ for (i = 0; i < e; i += 16)
++ for (i = 0; i < e; i += 16)
+ {
-+ printk ("%s %05x:", prefix, i);
-+ for (j = 0; j < 16; ++j)
++ printk ("%s %05x:", prefix, i);
++ for (j = 0; j < 16; ++j)
+ {
-+ k = i + j;
++ k = i + j;
+
-+ if (k < len)
-+ printk ("%02x", d[k]);
-+ else
-+ printk (" ");
++ if (k < len)
++ printk ("%02x", d[k]);
++ else
++ printk (" ");
+ }
+
-+ printk(" ");
-+ for (j = 0; j < 16; ++j)
++ printk (" ");
++ for (j = 0; j < 16; ++j)
+ {
-+ k = i + j;
-+ if (k < len)
++ k = i + j;
++ if (k < len)
+ {
-+ uint8_t c = d[k];
-+ if (c < 33)
-+ c = '.';
-+ if (c > 126)
-+ c = '.';
-+ printk ("%c", c);
++ uint8_t c = d[k];
++ if (c < 33)
++ c = '.';
++ if (c > 126)
++ c = '.';
++ printk ("%c", c);
+ }
+ }
-+ printk ("\n");
++ printk ("\n");
+ }
+}
+
-+static int v2v_receive_in_queue(struct v2vdev_context *ctx)
++/* buffer */
++
++static inline int
++buffer_pending (struct v2vdev_buf *b)
+{
-+ return v2v_get_wake_reason(ctx->channel, V2V_WAKE_REASON_ANY) == V2V_WAKE_REASON_RECEIVE;
++ return b->pending;
+}
+
-+static int v2vdev_message_get(struct v2vdev_context *ctx)
++static inline int
++buffer_set_len (struct v2vdev_buf *b, size_t size)
+{
-+ volatile void *buff;
-+ ssize_t size = 0;
-+ unsigned vtype, vflags;
++ BUG_ON (b->pending);
+
++ if (size > b->len)
++ {
++ b->len = size;
++ if (b->buf)
++ {
++ b->buf = krealloc (b->buf, size, GFP_KERNEL);
++ }
++ else
++ {
++ b->buf = kmalloc (size, GFP_KERNEL);
++ }
+
-+ if (!ctx->connected) {
-+ ctx->last_err=-ENOTCONN;
-+ return ctx->last_err;
++ if (!b->buf)
++ {
++ b->len = 0;
++ return -ENOMEM;
++ }
+ }
++ return 0;
++}
+
-+ if (ctx->data_len) {
-+ DPRINTK(KERN_ERR "****** data_len not zero in v2vdev_message_get\n");
-+ return 0;
-+ }
++static inline void
++buffer_consume (struct v2vdev_buf *b, size_t bytes)
++{
++ BUG_ON (b->pending < bytes);
++ b->pending -= bytes;
++ b->ptr += bytes;
++}
+
++static inline void
++buffer_consume_all (struct v2vdev_buf *b)
++{
++ b->pending = 0;
++ b->ptr = b->buf;
++}
+
-+ vtype = vflags = 0;
-+ ctx->last_err = v2v_nc2_get_message(ctx->channel,
-+ (const volatile void **)&buff, &size,
-+ &vtype, &vflags);
++static int
++buffer_copy_in (struct v2vdev_buf *b, void *ptr, size_t size)
++{
++ int err;
++ err = buffer_set_len (b, size);
++ if (err)
++ return err;
+
-+ if (ctx->last_err) {
-+ if (ctx->last_err==-ENODATA)
-+ ctx->last_err=0;
-+ return ctx->last_err;
-+ }
++ memcpy (b->buf, ptr, size);
++ b->pending = size;
++ b->ptr = b->buf;
+
-+ hexdump("<", (void *) buff, size);
++ return 0;
++}
+
-+ if (size>ctx->buff_size) {
-+ ctx->buff_size=size;
-+ ctx->buff = krealloc(ctx->buff, ctx->buff_size, GFP_KERNEL);
-+ }
++static void
++buffer_free (struct v2vdev_buf *b)
++{
++ if (b->buf)
++ kfree (b->buf);
++ kfree (b);
++}
+
-+ memcpy(ctx->buff, (void *)buff, size);
-+ ctx->data_len=size;
-+ ctx->data_ptr=ctx->buff;
-+ //if (ctx->data_len > 2) ctx->data_len -=2;
++static struct v2vdev_buf *
++buffer_new (void)
++{
++ struct v2vdev_buf *ret = kmalloc (sizeof (struct v2vdev_buf), GFP_KERNEL);
+
++ if (!ret)
++ return ret;
+
-+ v2v_nc2_finish_message(ctx->channel);
-+ v2v_set_wake_reason(ctx->channel, V2V_WAKE_REASON_RECEIVE);
++ ret->buf = NULL;
++ ret->ptr = NULL;
++ ret->len = 0;
++ ret->pending = 0;
+
-+ return ctx->last_err;
++ return ret;
+}
+
-+static int v2vdev_wait_connected(struct v2vdev_context *ctx)
++
++
++
++
++void
++v2vdev_update_state (struct v2vdev *c, int check_state)
+{
-+ struct v2v_wait *wait_state;
-+ u8 reasons_mask = V2V_WAKE_REASON_CONTROL;
-+ int err;
-+ enum v2v_endpoint_state state;
++ enum v2v_endpoint_state state;
++
++ if (!c->channel)
++ {
++ c->state = V2VDEV_UNBOUND;
++ return;
++ }
+
-+ while (!ctx->connected)
++
++
++ while (v2v_get_wake_reason (c->channel, V2V_WAKE_REASON_CONTROL))
++ check_state++;
++
++ while (v2v_get_wake_reason (c->channel, V2V_WAKE_REASON_SEND))
++ c->send_blocked = 0;
++
++ while (v2v_get_wake_reason (c->channel, V2V_WAKE_REASON_RECEIVE))
++ c->recv_blocked = 0;
++
++
++ if (check_state)
+ {
-+ wait_state = v2v_get_wait_state(ctx->channel);
-+ DPRINTK("wait event ....\n");
-+ wait_event(wait_state->wait_event,
-+ atomic_xchg(&wait_state->wait_condition, 0) == 1);
-+ v2v_get_wake_reason(ctx->channel, reasons_mask);
-+
-+ err = v2v_get_remote_state(ctx->channel, &state);
-+ if (err)
++ if (v2v_get_remote_state (c->channel, &state) != 0)
+ {
-+ DPRINTK("failure in v2v_get_remote_state() - err: %d\n", err);
-+ ctx->connected = 0;;
-+ v2v_disconnect(ctx->channel);
-+ return err;
++ c->state = V2VDEV_UNKNOWN;
++ }
++ else
++ {
++ switch (state)
++ {
++
++ case v2v_state_unready:
++ c->state = V2VDEV_CONNECT;
++ break;
++
++ case v2v_state_listening:
++ c->state = V2VDEV_LISTEN;
++ break;
++
++ case v2v_state_connected:
++ c->state = V2VDEV_CONNECTED;
++ break;
++
++ case v2v_state_disconnecting:
++ case v2v_state_disconnected:
++ case v2v_state_crashed:
++ c->state = V2VDEV_DISCONNECTED;
++ break;
++ default:
++ c->state = V2VDEV_LISTEN;
++ break;
++ }
+ }
-+ DPRINTK("endpoint state: %s\n", v2v_endpoint_state_name(state));
-+ ctx->connected = state == v2v_state_connected;
+ }
-+ return 0;
+}
+
-+static int v2vdev_listen(struct v2vdev_context *ctx,
-+ const char __user *user_path, size_t size)
++static void
++v2vdev_wait (struct v2vdev *c)
++{
++ struct v2v_wait *wait_state;
++
++
++ wait_state = v2v_get_wait_state (c->channel);
++ wait_event_interruptible (wait_state->wait_event,
++ atomic_xchg (&wait_state->wait_condition,
++ 0) == 1);
++
++ v2vdev_update_state (c, 0);
++}
++
++
++static int
++v2vdev_loop_check (struct v2vdev *c, int where, int *err)
+{
-+ int err;
-+ char path[1024];
+
-+ if (copy_from_user(path, user_path, size) != 0)
-+ return -EINVAL;
-+ path[size] = 0;
++ if (signal_pending (current))
++ {
++ *err = -EINTR;
++ return 1;
++ }
+
-+ err = v2v_listen(path, &ctx->channel, 0, 0, 0);
-+ if (err)
++ switch (c->state)
+ {
-+ DPRINTK("failure in v2v_listen() - error: %d\n", err);
-+ return err;
++ case V2VDEV_CONNECTED:
++ return 0;
++ case V2VDEV_UNBOUND:
++ case V2VDEV_LISTEN:
++ case V2VDEV_CONNECT:
++ if (where == L_CONNECT)
++ return 0;
++ *err = -ENOTCONN;
++ break;
++ case V2VDEV_UNKNOWN:
++ *err = -EIO;
++ break;
++ case V2VDEV_DISCONNECTED:
++ *err = (where == L_WRITE) ? -EPIPE : 0;
++ break;
+ }
-+ BUG_ON(ctx->channel == NULL);
+
-+ ctx->listenner = 1;
++ return 1;
+
-+ return 0;
+}
+
-+static int v2vdev_connect(struct v2vdev_context *ctx,
-+ const char __user *user_path, size_t size)
++static int
++v2vdev_receive_to_buf (struct v2vdev *c)
+{
-+ char path[1024];
-+ int err;
++ volatile void *msg;
++ ssize_t msg_len = 0;
++ unsigned msg_type = 0;
++ unsigned msg_flags = 0;
++ int err;
+
-+ if (copy_from_user(path, user_path, size) != 0)
-+ return -EINVAL;
-+ path[size] = 0;
++ BUG_ON (buffer_pending (c->read_buf));
+
-+ DPRINTK("connect on %s\n", path);
+
-+ err = v2v_connect(path, &ctx->channel, 0);
-+ if (err)
++ err = v2v_nc2_get_message (c->channel,
++ (const volatile void **) &msg, &msg_len,
++ &msg_type, &msg_flags);
++
++ switch (err)
+ {
-+ DPRINTK("failure in connect() - err: %d\n", err);
-+ return err;
++ case 0:
++
++ err = buffer_copy_in (c->read_buf, (void *) msg, msg_len);
++ v2v_nc2_finish_message (c->channel);
++
++ return err;
++ case -ENODATA:
++ c->recv_blocked = 1;
++ return -EAGAIN;
++ default:
++ return err;
+ }
+
-+ err = v2vdev_wait_connected(ctx);
-+ if (err)
++ return err;
++}
++
++
++static ssize_t
++v2vdev_read_dgram (struct file *f, char __user * buf,
++ size_t count, loff_t * ppos)
++{
++ struct v2vdev *c = f->private_data;
++ int nonblock = f->f_flags & O_NONBLOCK;
++
++ int n;
++ int err;
++
++ if (!access_ok (VERIFY_WRITE, buf, count))
++ return -EFAULT;
++
++ v2vdev_update_state (c, 0);
++
++ while (1)
+ {
-+ DPRINTK("failure in wait_connected() - error: %d\n", err);
++ if (v2vdev_loop_check (c, L_READ, &err))
+ return err;
++
++ //FIXME - we could optimize by checking recv_blocked here and not doing the read
++
++ if (!buffer_pending (c->read_buf))
++ {
++
++ err = v2vdev_receive_to_buf (c);
++
++ switch (err)
++ {
++ case -EAGAIN:
++ if (nonblock)
++ return -EAGAIN;
++
++ v2vdev_wait (c);
++ break;
++ case 0:
++ break;
++ default:
++ return err;
++ }
++
++ }
++
++ n = MIN (count, buffer_pending (c->read_buf));
++
++ if (!n)
++ continue;
++
++ if (copy_to_user (buf, c->read_buf->buf, n))
++ return -EFAULT;
++
++ buffer_consume_all (c->read_buf);
++
++ return n;
++
+ }
-+ return 0;
++
++ return 0;
+}
+
-+static ssize_t v2vdev_read(struct file *filp, char __user *buf,
-+ size_t count, loff_t *ppos)
++
++static ssize_t
++v2vdev_write_dgram (struct file *f, const char __user * buf,
++ size_t count, loff_t * ppos)
+{
-+ struct v2vdev_context *ctx = filp->private_data;
-+ struct v2v_wait *wait_state;
-+ size_t red=0;
-+ size_t n;
-+ int nonblock=filp->f_flags & O_NONBLOCK;
-+ u8 reasons_mask = V2V_WAKE_REASON_CONTROL | V2V_WAKE_REASON_RECEIVE;
-+ u8 reason;
-+ enum v2v_endpoint_state state;
-+
-+ DUMP_CTX(ctx);
-+ if (!ctx->connected)
-+ return -ENOTCONN;
-+
-+ while (count) {
-+
-+ if (!ctx->data_len) {
-+
-+ if (ctx->last_err)
-+ return ctx->last_err;
-+
-+ if (nonblock && !v2v_receive_in_queue(ctx)) {
-+ DPRINTK("v2vdev_read needed got %d/%d\n",red,count+red);
-+ return red ? red:-EAGAIN;
-+ }
-+
-+ wait_state = v2v_get_wait_state(ctx->channel);
-+ wait_event(wait_state->wait_event,
-+ atomic_xchg(&wait_state->wait_condition, 0) == 1);
-+ reason = v2v_get_wake_reason(ctx->channel, reasons_mask);
-+ if (reason & V2V_WAKE_REASON_CONTROL)
-+ {
-+ if (v2v_get_remote_state(ctx->channel, &state) == 0 &&
-+ state != v2v_state_connected)
-+ {
-+ ctx->connected = 0;
-+ v2v_disconnect(ctx->channel);
-+ return 0;
-+ }
-+ }
-+ v2vdev_message_get(ctx);
-+ }
-+ DUMP_CTX(ctx);
-+
-+ n=MIN(count,ctx->data_len);
-+
-+ if (n) {
-+ if (copy_to_user(buf, ctx->data_ptr, n))
-+ return -EFAULT;
-+ red+=n;
-+ count-=n;
-+ ctx->data_ptr+=n;
-+ ctx->data_len-=n;
-+ buf+=n;
-+
-+ DUMP_CTX(ctx);
++ struct v2vdev *c = f->private_data;
++ int nonblock = f->f_flags & O_NONBLOCK;
++
++ volatile void *msg;
++ int err;
++
++ if (!access_ok (VERIFY_READ, buf, count))
++ return -EFAULT;
++
++ v2vdev_update_state (c, 0);
++
++ while (1)
++ {
++
++ if (v2vdev_loop_check (c, L_WRITE, &err))
++ return err;
++
++ //FIXME - we could optimize by checking send_blocked here and not doing the write
++
++
++ err = v2v_nc2_prep_message (c->channel, count, 1, 0, &msg);
++
++ switch (err)
++ {
++ case 0:
++ err = copy_from_user ((void *) msg, buf, count);
++ v2v_nc2_send_messages (c->channel);
++
++ return err ? -EFAULT : count;
++ case -EAGAIN:
++ c->send_blocked = 1;
++
++ if (nonblock)
++ return -EAGAIN;
++
++ break;
++ case -EINVAL:
++ return -EMSGSIZE;
++ default:
++ return err;
+ }
-+ }
-+
-+ DPRINTK("v2vdev_read needed got %d/%d\n",red,red);
+
-+ return red;
++ v2vdev_wait (c);
++ }
++ return 0;
+}
+
-+static ssize_t v2vdev_write(struct file *filp, const char __user *buf,
-+ size_t count, loff_t *ppos)
++
++
++
++static ssize_t
++v2vdev_read_stream (struct file *f, char __user * buf,
++ size_t count, loff_t * ppos)
+{
-+ struct v2vdev_context *ctx = filp->private_data;
-+ volatile void *msg;
-+ int err;
-+
-+ DUMP_CTX(ctx);
-+ if (!ctx->connected)
-+ return -ENOTCONN;
-+
-+ err = v2v_nc2_prep_message(ctx->channel, count, 1, 0, &msg);
-+ if (err)
++ struct v2vdev *c = f->private_data;
++ int nonblock = f->f_flags & O_NONBLOCK;
++ size_t red = 0;
++ size_t n;
++ int err;
++
++ if (!access_ok (VERIFY_WRITE, buf, count))
++ return -EFAULT;
++
++ v2vdev_update_state (c, 0);
++
++ while (count)
+ {
-+ DPRINTK("failure in v2v_nc2_prep_message() - error: %d\n", err);
-+ return -EIO;
-+ }
++ if (v2vdev_loop_check (c, L_READ, &err))
++ return red ? red : err;
+
-+ if (copy_from_user((void *)msg, buf, count))
-+ return -EFAULT;
-+
-+ hexdump(">", (void *) msg, count);
-+ v2v_nc2_send_messages(ctx->channel);
-+ v2v_set_wake_reason(ctx->channel, V2V_WAKE_REASON_SEND);
-+ return count;
-+}
++ if (!buffer_pending (c->read_buf))
++ {
+
-+static long v2vdev_ioctl(struct file *filp,
-+ unsigned int cmd, unsigned long arg)
-+{
-+ int rc;
-+ void __user *p = (void __user *)arg;
-+ struct v2vdev_context *ctx = filp->private_data;
-+
-+ rc = -ENOSYS;
-+ switch (cmd) {
-+ default:
-+ if (_IOC_TYPE(cmd) != 'V')
-+ return -ENOTTY;
++ err = v2vdev_receive_to_buf (c);
+
-+ if (_IOC_DIR(cmd) == _IOC_WRITE)
++ switch (err)
+ {
-+ if ((_IOC_NR(cmd) == _IOC_NR(V2VLISTEN(0))))
-+ rc = v2vdev_listen(ctx, p, _IOC_SIZE(cmd));
-+ if ((_IOC_NR(cmd) == _IOC_NR(V2VCONNECT(0))))
-+ rc = v2vdev_connect(ctx, p, _IOC_SIZE(cmd));
++ case -EAGAIN:
++ if (nonblock)
++ return red ? red : -EAGAIN;
++
++ v2vdev_wait (c);
++ break;
++ case 0:
++ break;
++ default:
++ return red ? red : err;
+ }
-+ break;
++
++ }
++
++ n = MIN (count, buffer_pending (c->read_buf));
++
++ if (!n)
++ continue;
++
++ if (copy_to_user (buf, c->read_buf->ptr, n))
++ return -EFAULT;
++
++ red += n;
++ count -= n;
++ buf += n;
++
++ buffer_consume (c->read_buf, n);
++
+ }
-+ return rc;
-+}
+
-+static int v2vdev_open(struct inode *inode, struct file *filp)
-+{
-+ struct v2vdev_context *ctx;
+
-+ ctx = kmalloc(sizeof (struct v2vdev_context), GFP_KERNEL);
-+ memset(ctx, 0, sizeof (struct v2vdev_context));
-+ filp->private_data = ctx;
-+ return 0;
++ return red;
+}
+
-+static int v2vdev_release(struct inode *inode, struct file *filp)
++#if 0
++static ssize_t
++not_v2vdev_read_stream (struct file *f, char __user * buf,
++ size_t count, loff_t * ppos)
+{
-+ struct v2vdev_context *ctx = filp->private_data;
-+
-+ DPRINTK("Release device\n");
-+ if (ctx->connected)
-+ v2v_disconnect(ctx->channel);
-+ if (ctx->buff)
-+ kfree(ctx->buff);
-+ kfree(filp->private_data);
-+ return 0;
++ ssize_t ret;
++
++ printk (KERN_ERR "v2v_read_stream(...\n");
++ ret = wrap_v2vdev_read_stream (f, buf, count, ppos);
++ printk (KERN_ERR "v2v_read_stream(%p,%p,%d,%p)=%d\n", f, buf, count, ppos,
++ ret);
++ if (ret > 0)
++ hexdump ("<", buf, ret);
++ return ret;
+}
++#endif
+
-+static unsigned int v2vdev_poll(struct file *filp, poll_table *wait)
++
++static ssize_t
++v2vdev_write_stream (struct file *f, const char __user * buf,
++ size_t count, loff_t * ppos)
+{
-+ unsigned int mask = 0;
-+ struct v2vdev_context *ctx = filp->private_data;
-+ struct v2v_wait *wait_state;
-+ u8 reasons_mask = V2V_WAKE_REASON_CONTROL;
-+ u8 reason;
-+ int err;
-+ enum v2v_endpoint_state state;
-+
-+ wait_state = v2v_get_wait_state(ctx->channel);
-+ poll_wait(filp, &wait_state->wait_event, wait);
-+
-+ if (ctx->connected)
-+ reasons_mask |= V2V_WAKE_REASON_RECEIVE;
-+
-+ reason = v2v_get_wake_reason(ctx->channel, reasons_mask);
-+ if (reason & V2V_WAKE_REASON_CONTROL)
++ struct v2vdev *c = f->private_data;
++ int nonblock = f->f_flags & O_NONBLOCK;
++ size_t writ = 0;
++ size_t n;
++
++ volatile void *msg;
++ int err;
++
++
++ if (!access_ok (VERIFY_WRITE, buf, count))
++ return -EFAULT;
++
++ v2vdev_update_state (c, 0);
++
++
++ while (count)
+ {
-+ if (!ctx->connected) {
-+ err = v2v_get_remote_state(ctx->channel, &state);
-+ if (ctx->listenner && (err == 0 && state == v2v_state_connected) &&
-+ v2v_accept(ctx->channel, 1) == 0)
-+ {
-+ v2vdev_wait_connected(ctx);
-+ v2v_set_wake_reason(ctx->channel, V2V_WAKE_REASON_SEND);
-+ DPRINTK("connected !\n");
-+ return 0;
-+ }
-+ }
-+ else
++ if (v2vdev_loop_check (c, L_WRITE, &err))
++ return writ ? writ : err;
++
++ if (!nonblock)
++ n = MIN (count, V2V_STREAM_MSGSIZE);
++ else
++ n = count;
++
++ err = v2v_nc2_prep_message (c->channel, n, 1, 0, &msg);
++
++ switch (err)
+ {
-+ err = v2v_get_remote_state(ctx->channel, &state);
-+ if (err == 0 && ctx->connected && state != v2v_state_connected)
-+ {
-+ DPRINTK("Remote end %s, v2v_disconnect\n", v2v_endpoint_state_name(state));
-+ ctx->connected = 0;
-+ v2v_disconnect(ctx->channel);
-+ return POLLIN | POLLRDNORM;
-+ }
++ case 0:
++
++ err = copy_from_user ((void *) msg, buf, n);
++ v2v_nc2_send_messages (c->channel);
++
++ if (err)
++ return -EFAULT;
++
++ count -= n;
++ buf += n;
++ writ += n;
++
++ break;
++ case -EAGAIN:
++ if (nonblock)
++ return writ ? writ : -EAGAIN;
++ c->send_blocked = 1;
++ //printk(KERN_ERR"write block start\n");
++ v2vdev_wait (c);
++ //printk(KERN_ERR"write block end\n");
++ break;
++ case -EINVAL:
++ return -EMSGSIZE;
++ default:
++ return writ ? writ : err;
+ }
++
+ }
-+ else if (reason & V2V_WAKE_REASON_RECEIVE)
++
++ return writ;
++}
++
++#if 0
++static ssize_t
++not_v2vdev_write_stream (struct file *f, const char __user * buf,
++ size_t count, loff_t * ppos)
++{
++ ssize_t ret;
++ printk (KERN_ERR "v2v_write_stream(...\n");
++ ret = wrap_v2vdev_write_stream (f, buf, count, ppos);
++ printk (KERN_ERR "v2v_write_stream(%p,%p,%d,%p)=%d\n", f, buf, count, ppos,
++ ret);
++ if (count > 0)
++ hexdump (">", buf, count);
++ return ret;
++}
++#endif
++
++
++static int
++v2vdev_listen (struct file *f, const char __user * user_path, size_t size)
++{
++ struct v2vdev *c = f->private_data;
++ int err;
++ char path[1024];
++
++ //FIXME - check for approriate state first
++
++ if (size > (sizeof (path) + 1))
++ return -EINVAL;
++
++ if (copy_from_user (path, user_path, size))
++ return -EFAULT;
++
++ path[size] = 0;
++
++ err = v2v_listen (path, &c->channel, 0, 0, 0);
++ if (err)
++ return err;
++
++ BUG_ON (c->channel == NULL);
++
++ v2vdev_update_state (c, 0);
++
++ return 0;
++}
++
++static int
++v2vdev_connect (struct file *f, const char __user * user_path, size_t size)
++{
++ int nonblock = f->f_flags & O_NONBLOCK;
++ struct v2vdev *c = f->private_data;
++ int err;
++ char path[1024];
++
++ //FIXME - check for approriate state first
++
++ if (size > (sizeof (path) + 1))
++ return -EINVAL;
++
++ if (copy_from_user (path, user_path, size))
++ return -EFAULT;
++
++ path[size] = 0;
++
++ err = v2v_connect (path, &c->channel, 0);
++ if (err)
++ return err;
++
++ BUG_ON (c->channel == NULL);
++
++ v2vdev_update_state (c, 0);
++
++ if (nonblock)
++ return -EINPROGRESS;
++
++ while (c->state != V2VDEV_CONNECTED)
+ {
-+ DUMP_CTX(ctx);
-+ v2vdev_message_get(ctx);
++ if (v2vdev_loop_check (c, L_CONNECT, &err))
++ return err;
++
++ v2vdev_wait (c);
+ }
+
-+ if (ctx->data_len)
-+ mask = POLLIN | POLLRDNORM;
++ return 0;
++}
++
++
++static long
++v2vdev_ioctl (struct file *f, unsigned int cmd, unsigned long arg)
++{
++ void __user *p = (void __user *) arg;
++ int len = _IOC_SIZE (cmd);
++ int rc = -ENOTTY;
++
++ if (_IOC_TYPE (cmd) != 'V')
++ return rc;
++
++
++ if (_IOC_DIR (cmd) != _IOC_WRITE)
++ return rc;
++
++
++ switch (_IOC_NR (cmd))
++ {
++ case _IOC_NR (V2VLISTEN (0)):
++ rc = v2vdev_listen (f, p, len);
++ break;
++ case _IOC_NR (V2VCONNECT (0)):
++ rc = v2vdev_connect (f, p, len);
++ break;
++ }
++ return rc;
++}
+
++static unsigned int
++v2vdev_poll (struct file *f, poll_table * pt)
++{
++ struct v2vdev *c = f->private_data;
++ unsigned int mask = 0;
++ struct v2v_wait *wait_state;
+
++ if (!c->channel)
+ return mask;
++
++ v2vdev_update_state (c, 0);
++
++
++ switch (c->state)
++ {
++ case V2VDEV_UNKNOWN:
++ case V2VDEV_UNBOUND:
++ case V2VDEV_LISTEN:
++ case V2VDEV_CONNECT:
++ break;
++ case V2VDEV_CONNECTED:
++ if (!c->recv_blocked && !buffer_pending (c->read_buf))
++ v2vdev_receive_to_buf (c);
++
++ if (buffer_pending (c->read_buf))
++ mask |= POLLIN | POLLRDNORM;
++ if (!c->send_blocked)
++ mask |= POLLOUT | POLLWRNORM;
++ break;
++ case V2VDEV_DISCONNECTED:
++ mask |= POLLIN | POLLRDNORM;
++ break;
++ }
++ wait_state = v2v_get_wait_state (c->channel);
++ poll_wait (f, &wait_state->wait_event, pt);
++
++ return mask;
++}
++
++
++
++static int
++v2vdev_open (struct inode *inode, struct file *f)
++{
++ struct v2vdev *c;
++
++ c = kmalloc (sizeof (struct v2vdev), GFP_KERNEL);
++ if (!c)
++ return -ENOMEM;
++
++ memset (c, 0, sizeof (struct v2vdev));
++
++ c->read_buf = buffer_new ();
++ if (!c->read_buf)
++ {
++ kfree (c);
++ return -ENOMEM;
++ }
++
++ f->private_data = c;
++
++ return 0;
++}
++
++static int
++v2vdev_release (struct inode *inode, struct file *f)
++{
++ struct v2vdev *c = f->private_data;
++
++
++ //FIXME - race
++ if (c->state == V2VDEV_CONNECTED)
++ v2v_disconnect (c->channel);
++
++ if (c->read_buf)
++ buffer_free (c->read_buf);
++
++ kfree (c);
++ return 0;
+}
+
-+static const struct file_operations v2vdev_fops = {
-+ .owner = THIS_MODULE,
-+ .write = v2vdev_write,
-+ .read = v2vdev_read,
-+ .unlocked_ioctl = v2vdev_ioctl,
-+ .open = v2vdev_open,
-+ .release = v2vdev_release,
-+ .poll = v2vdev_poll,
++
++
++static const struct file_operations v2vdev_fops_dgram = {
++ .owner = THIS_MODULE,
++ .write = v2vdev_write_dgram,
++ .read = v2vdev_read_dgram,
++ .unlocked_ioctl = v2vdev_ioctl,
++ .open = v2vdev_open,
++ .release = v2vdev_release,
++ .poll = v2vdev_poll,
++};
++
++
++static struct miscdevice v2vdev_miscdev_dgram = {
++ .minor = MISC_DYNAMIC_MINOR,
++ .name = "v2v_dgram",
++ .fops = &v2vdev_fops_dgram,
++};
++
++static const struct file_operations v2vdev_fops_stream = {
++ .owner = THIS_MODULE,
++ .write = v2vdev_write_stream,
++ .read = v2vdev_read_stream,
++ .unlocked_ioctl = v2vdev_ioctl,
++ .open = v2vdev_open,
++ .release = v2vdev_release,
++ .poll = v2vdev_poll,
+};
+
-+static struct miscdevice v2vdev_miscdev = {
-+ .minor = MISC_DYNAMIC_MINOR,
-+ .name = "v2v",
-+ .fops = &v2vdev_fops,
++static struct miscdevice v2vdev_miscdev_stream = {
++ .minor = MISC_DYNAMIC_MINOR,
++ .name = "v2v_stream",
++ .fops = &v2vdev_fops_stream,
+};
+
-+static int __init v2vdev_init(void)
++
++static int __init
++v2vdev_init (void)
+{
-+ int err = 0;
++ int err = 0;
+
-+ if (!is_running_on_xen())
-+ return -ENODEV;
++ if (!is_running_on_xen ())
++ return -ENODEV;
+
-+ err = misc_register(&v2vdev_miscdev);
-+ if (err != 0) {
-+ DPRINTK(KERN_ALERT "Could not register /dev/v2v\n");
-+ return err;
++ err = misc_register (&v2vdev_miscdev_stream);
++ if (err != 0)
++ {
++ printk (KERN_ERR "Could not register /dev/v2v_stream\n");
++ return err;
+ }
-+
-+ DPRINTK("Xen V2V device installed.\n");
+
-+ return 0;
++ err = misc_register (&v2vdev_miscdev_dgram);
++ if (err != 0)
++ {
++ misc_deregister (&v2vdev_miscdev_stream);
++ printk (KERN_ERR "Could not register /dev/v2v_dgram\n");
++ return err;
++ }
++
++ printk (KERN_INFO "Xen V2V device installed.\n");
++
++ return 0;
+}
+
-+static void __exit v2vdev_cleanup(void)
++static void __exit
++v2vdev_cleanup (void)
+{
-+ misc_deregister(&v2vdev_miscdev);
++ misc_deregister (&v2vdev_miscdev_dgram);
++ misc_deregister (&v2vdev_miscdev_stream);
+}
+
-+module_init(v2vdev_init);
-+module_exit(v2vdev_cleanup);
++module_init (v2vdev_init);
++module_exit (v2vdev_cleanup);
+
-+MODULE_LICENSE("Dual BSD/GPL");
++MODULE_LICENSE ("Dual BSD/GPL");