]> xenbits.xen.org Git - xenclient/linux-2.6.27-pq.git/commitdiff
Upgrade v2v with different char device that got different semantics.
authorJean Guyader <jean.guyader@eu.citrix.com>
Thu, 19 Nov 2009 17:57:31 +0000 (17:57 +0000)
committerJean Guyader <jean.guyader@eu.citrix.com>
Thu, 19 Nov 2009 17:57:31 +0000 (17:57 +0000)
master/v2v-dev

index c8cdce0404041e7f8c0fb84d6352ee7f44b2148c..21dd14b516e04a3c4b1dc60cbc2e07606e0207c4 100644 (file)
@@ -1,9 +1,9 @@
 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.
 +
@@ -14,22 +14,22 @@ index 5966234..b3b6ef4 100644
 +        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
 + *
@@ -45,7 +45,7 @@ index 0000000..adfc6c8
 + * 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
@@ -83,461 +83,859 @@ index 0000000..adfc6c8
 +#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");