]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/commitdiff
Merge branch 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/ericvh/v9fs
authorLinus Torvalds <torvalds@linux-foundation.org>
Mon, 20 Oct 2008 16:39:47 +0000 (09:39 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 20 Oct 2008 16:39:47 +0000 (09:39 -0700)
* 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/ericvh/v9fs: (26 commits)
  9p: add more conservative locking
  9p: fix oops in protocol stat parsing error path.
  9p: fix device file handling
  9p: Improve debug support
  9p: eliminate depricated conv functions
  9p: rework client code to use new protocol support functions
  9p: remove unnecessary tag field from p9_req_t structure
  9p: remove 9p fcall debug prints
  9p: add new protocol support code
  9p: encapsulate version function
  9p: move dirread to fs layer
  9p: adjust 9p vfs write operation
  9p: move readn meta-function from client to fs layer
  9p: consolidate read/write functions
  9p: drop broken unused error path from p9_conn_create()
  9p: make rpc code common and rework flush code
  9p: use the rcall structure passed in the request in trans_fd read_work
  9p: apply common request code to trans_fd
  9p: apply common tagpool handling to trans_fd
  9p: move request management to client code
  ...

20 files changed:
fs/9p/v9fs.c
fs/9p/v9fs_vfs.h
fs/9p/vfs_addr.c
fs/9p/vfs_dir.c
fs/9p/vfs_file.c
fs/9p/vfs_inode.c
fs/9p/vfs_super.c
include/net/9p/9p.h
include/net/9p/client.h
include/net/9p/transport.h
net/9p/Makefile
net/9p/client.c
net/9p/conv.c [deleted file]
net/9p/fcprint.c [deleted file]
net/9p/mod.c
net/9p/protocol.c [new file with mode: 0644]
net/9p/protocol.h [new file with mode: 0644]
net/9p/trans_fd.c
net/9p/trans_virtio.c
net/9p/util.c

index c061c3f18e7cbc47d427c03610e88f8d95d5cdf4..24eb01087b6d63313404dff82b4f192177189342 100644 (file)
@@ -30,8 +30,8 @@
 #include <linux/parser.h>
 #include <linux/idr.h>
 #include <net/9p/9p.h>
-#include <net/9p/transport.h>
 #include <net/9p/client.h>
+#include <net/9p/transport.h>
 #include "v9fs.h"
 #include "v9fs_vfs.h"
 
@@ -234,7 +234,7 @@ struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses,
        if (!v9ses->clnt->dotu)
                v9ses->flags &= ~V9FS_EXTENDED;
 
-       v9ses->maxdata = v9ses->clnt->msize;
+       v9ses->maxdata = v9ses->clnt->msize - P9_IOHDRSZ;
 
        /* for legacy mode, fall back to V9FS_ACCESS_ANY */
        if (!v9fs_extended(v9ses) &&
index 57997fa14e69d4260fc5b9a990e030355a321963..c295ba786edd9082dbd9026ef2324a2d684ea1a3 100644 (file)
@@ -46,9 +46,11 @@ extern struct dentry_operations v9fs_cached_dentry_operations;
 
 struct inode *v9fs_get_inode(struct super_block *sb, int mode);
 ino_t v9fs_qid2ino(struct p9_qid *qid);
-void v9fs_stat2inode(struct p9_stat *, struct inode *, struct super_block *);
+void v9fs_stat2inode(struct p9_wstat *, struct inode *, struct super_block *);
 int v9fs_dir_release(struct inode *inode, struct file *filp);
 int v9fs_file_open(struct inode *inode, struct file *file);
-void v9fs_inode2stat(struct inode *inode, struct p9_stat *stat);
+void v9fs_inode2stat(struct inode *inode, struct p9_wstat *stat);
 void v9fs_dentry_release(struct dentry *);
 int v9fs_uflags2omode(int uflags, int extended);
+
+ssize_t v9fs_file_readn(struct file *, char *, char __user *, u32, u64);
index 97d3aed57983905f7caa660ce9474b7dc734d8e6..6fcb1e7095cfe0a97255170656d76e3fe8ed9a30 100644 (file)
@@ -38,7 +38,6 @@
 
 #include "v9fs.h"
 #include "v9fs_vfs.h"
-#include "fid.h"
 
 /**
  * v9fs_vfs_readpage - read an entire page in from 9P
@@ -53,14 +52,12 @@ static int v9fs_vfs_readpage(struct file *filp, struct page *page)
        int retval;
        loff_t offset;
        char *buffer;
-       struct p9_fid *fid;
 
        P9_DPRINTK(P9_DEBUG_VFS, "\n");
-       fid = filp->private_data;
        buffer = kmap(page);
        offset = page_offset(page);
 
-       retval = p9_client_readn(fid, buffer, offset, PAGE_CACHE_SIZE);
+       retval = v9fs_file_readn(filp, buffer, NULL, offset, PAGE_CACHE_SIZE);
        if (retval < 0)
                goto done;
 
index e298fe1940933544f537401d13b8295aaafe3875..873cd31baa47c6c77022918665a5f343d93f8425 100644 (file)
@@ -45,7 +45,7 @@
  *
  */
 
-static inline int dt_type(struct p9_stat *mistat)
+static inline int dt_type(struct p9_wstat *mistat)
 {
        unsigned long perm = mistat->mode;
        int rettype = DT_REG;
@@ -69,32 +69,58 @@ static inline int dt_type(struct p9_stat *mistat)
 static int v9fs_dir_readdir(struct file *filp, void *dirent, filldir_t filldir)
 {
        int over;
+       struct p9_wstat st;
+       int err;
        struct p9_fid *fid;
-       struct v9fs_session_info *v9ses;
-       struct inode *inode;
-       struct p9_stat *st;
+       int buflen;
+       char *statbuf;
+       int n, i = 0;
 
        P9_DPRINTK(P9_DEBUG_VFS, "name %s\n", filp->f_path.dentry->d_name.name);
-       inode = filp->f_path.dentry->d_inode;
-       v9ses = v9fs_inode2v9ses(inode);
        fid = filp->private_data;
-       while ((st = p9_client_dirread(fid, filp->f_pos)) != NULL) {
-               if (IS_ERR(st))
-                       return PTR_ERR(st);
 
-               over = filldir(dirent, st->name.str, st->name.len, filp->f_pos,
-                       v9fs_qid2ino(&st->qid), dt_type(st));
+       buflen = fid->clnt->msize - P9_IOHDRSZ;
+       statbuf = kmalloc(buflen, GFP_KERNEL);
+       if (!statbuf)
+               return -ENOMEM;
 
-               if (over)
+       while (1) {
+               err = v9fs_file_readn(filp, statbuf, NULL, buflen,
+                                                               fid->rdir_fpos);
+               if (err <= 0)
                        break;
 
-               filp->f_pos += st->size;
-               kfree(st);
-               st = NULL;
+               n = err;
+               while (i < n) {
+                       err = p9stat_read(statbuf + i, buflen-i, &st,
+                                                       fid->clnt->dotu);
+                       if (err) {
+                               P9_DPRINTK(P9_DEBUG_VFS, "returned %d\n", err);
+                               err = -EIO;
+                               p9stat_free(&st);
+                               goto free_and_exit;
+                       }
+
+                       i += st.size+2;
+                       fid->rdir_fpos += st.size+2;
+
+                       over = filldir(dirent, st.name, strlen(st.name),
+                           filp->f_pos, v9fs_qid2ino(&st.qid), dt_type(&st));
+
+                       filp->f_pos += st.size+2;
+
+                       p9stat_free(&st);
+
+                       if (over) {
+                               err = 0;
+                               goto free_and_exit;
+                       }
+               }
        }
 
-       kfree(st);
-       return 0;
+free_and_exit:
+       kfree(statbuf);
+       return err;
 }
 
 
index 52944d2249a49a1e05c7fdc05d63919180dc3a29..041c526922845433c5a871f3afed75be7f35b0bd 100644 (file)
@@ -120,23 +120,72 @@ static int v9fs_file_lock(struct file *filp, int cmd, struct file_lock *fl)
 }
 
 /**
- * v9fs_file_read - read from a file
+ * v9fs_file_readn - read from a file
  * @filp: file pointer to read
  * @data: data buffer to read data into
+ * @udata: user data buffer to read data into
  * @count: size of buffer
  * @offset: offset at which to read data
  *
  */
+
+ssize_t
+v9fs_file_readn(struct file *filp, char *data, char __user *udata, u32 count,
+              u64 offset)
+{
+       int n, total;
+       struct p9_fid *fid = filp->private_data;
+
+       P9_DPRINTK(P9_DEBUG_VFS, "fid %d offset %llu count %d\n", fid->fid,
+                                       (long long unsigned) offset, count);
+
+       n = 0;
+       total = 0;
+       do {
+               n = p9_client_read(fid, data, udata, offset, count);
+               if (n <= 0)
+                       break;
+
+               if (data)
+                       data += n;
+               if (udata)
+                       udata += n;
+
+               offset += n;
+               count -= n;
+               total += n;
+       } while (count > 0 && n == (fid->clnt->msize - P9_IOHDRSZ));
+
+       if (n < 0)
+               total = n;
+
+       return total;
+}
+
+/**
+ * v9fs_file_read - read from a file
+ * @filp: file pointer to read
+ * @udata: user data buffer to read data into
+ * @count: size of buffer
+ * @offset: offset at which to read data
+ *
+ */
+
 static ssize_t
-v9fs_file_read(struct file *filp, char __user * data, size_t count,
+v9fs_file_read(struct file *filp, char __user *udata, size_t count,
               loff_t * offset)
 {
        int ret;
        struct p9_fid *fid;
 
-       P9_DPRINTK(P9_DEBUG_VFS, "\n");
+       P9_DPRINTK(P9_DEBUG_VFS, "count %d offset %lld\n", count, *offset);
        fid = filp->private_data;
-       ret = p9_client_uread(fid, data, *offset, count);
+
+       if (count > (fid->clnt->msize - P9_IOHDRSZ))
+               ret = v9fs_file_readn(filp, NULL, udata, count, *offset);
+       else
+               ret = p9_client_read(fid, NULL, udata, *offset, count);
+
        if (ret > 0)
                *offset += ret;
 
@@ -156,19 +205,38 @@ static ssize_t
 v9fs_file_write(struct file *filp, const char __user * data,
                size_t count, loff_t * offset)
 {
-       int ret;
+       int n, rsize, total = 0;
        struct p9_fid *fid;
+       struct p9_client *clnt;
        struct inode *inode = filp->f_path.dentry->d_inode;
+       int origin = *offset;
 
        P9_DPRINTK(P9_DEBUG_VFS, "data %p count %d offset %x\n", data,
                (int)count, (int)*offset);
 
        fid = filp->private_data;
-       ret = p9_client_uwrite(fid, data, *offset, count);
-       if (ret > 0) {
-               invalidate_inode_pages2_range(inode->i_mapping, *offset,
-                                                               *offset+ret);
-               *offset += ret;
+       clnt = fid->clnt;
+
+       rsize = fid->iounit;
+       if (!rsize || rsize > clnt->msize-P9_IOHDRSZ)
+               rsize = clnt->msize - P9_IOHDRSZ;
+
+       do {
+               if (count < rsize)
+                       rsize = count;
+
+               n = p9_client_write(fid, NULL, data+total, *offset+total,
+                                                                       rsize);
+               if (n <= 0)
+                       break;
+               count -= n;
+               total += n;
+       } while (count > 0);
+
+       if (total > 0) {
+               invalidate_inode_pages2_range(inode->i_mapping, origin,
+                                                               origin+total);
+               *offset += total;
        }
 
        if (*offset > inode->i_size) {
@@ -176,7 +244,10 @@ v9fs_file_write(struct file *filp, const char __user * data,
                inode->i_blocks = (inode->i_size + 512 - 1) >> 9;
        }
 
-       return ret;
+       if (n < 0)
+               return n;
+
+       return total;
 }
 
 static const struct file_operations v9fs_cached_file_operations = {
index e83aa5ebe861d2a8454985875d4f74a6736291a2..8314d3f43b716f41560e4517d42eaade5cfc1459 100644 (file)
@@ -334,7 +334,7 @@ v9fs_inode_from_fid(struct v9fs_session_info *v9ses, struct p9_fid *fid,
 {
        int err, umode;
        struct inode *ret;
-       struct p9_stat *st;
+       struct p9_wstat *st;
 
        ret = NULL;
        st = p9_client_stat(fid);
@@ -417,6 +417,8 @@ v9fs_create(struct v9fs_session_info *v9ses, struct inode *dir,
        struct p9_fid *dfid, *ofid, *fid;
        struct inode *inode;
 
+       P9_DPRINTK(P9_DEBUG_VFS, "name %s\n", dentry->d_name.name);
+
        err = 0;
        ofid = NULL;
        fid = NULL;
@@ -424,6 +426,7 @@ v9fs_create(struct v9fs_session_info *v9ses, struct inode *dir,
        dfid = v9fs_fid_clone(dentry->d_parent);
        if (IS_ERR(dfid)) {
                err = PTR_ERR(dfid);
+               P9_DPRINTK(P9_DEBUG_VFS, "fid clone failed %d\n", err);
                dfid = NULL;
                goto error;
        }
@@ -432,18 +435,22 @@ v9fs_create(struct v9fs_session_info *v9ses, struct inode *dir,
        ofid = p9_client_walk(dfid, 0, NULL, 1);
        if (IS_ERR(ofid)) {
                err = PTR_ERR(ofid);
+               P9_DPRINTK(P9_DEBUG_VFS, "p9_client_walk failed %d\n", err);
                ofid = NULL;
                goto error;
        }
 
        err = p9_client_fcreate(ofid, name, perm, mode, extension);
-       if (err < 0)
+       if (err < 0) {
+               P9_DPRINTK(P9_DEBUG_VFS, "p9_client_fcreate failed %d\n", err);
                goto error;
+       }
 
        /* now walk from the parent so we can get unopened fid */
        fid = p9_client_walk(dfid, 1, &name, 0);
        if (IS_ERR(fid)) {
                err = PTR_ERR(fid);
+               P9_DPRINTK(P9_DEBUG_VFS, "p9_client_walk failed %d\n", err);
                fid = NULL;
                goto error;
        } else
@@ -453,6 +460,7 @@ v9fs_create(struct v9fs_session_info *v9ses, struct inode *dir,
        inode = v9fs_inode_from_fid(v9ses, fid, dir->i_sb);
        if (IS_ERR(inode)) {
                err = PTR_ERR(inode);
+               P9_DPRINTK(P9_DEBUG_VFS, "inode creation failed %d\n", err);
                goto error;
        }
 
@@ -734,7 +742,7 @@ v9fs_vfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
        int err;
        struct v9fs_session_info *v9ses;
        struct p9_fid *fid;
-       struct p9_stat *st;
+       struct p9_wstat *st;
 
        P9_DPRINTK(P9_DEBUG_VFS, "dentry: %p\n", dentry);
        err = -EPERM;
@@ -815,10 +823,9 @@ static int v9fs_vfs_setattr(struct dentry *dentry, struct iattr *iattr)
  */
 
 void
-v9fs_stat2inode(struct p9_stat *stat, struct inode *inode,
+v9fs_stat2inode(struct p9_wstat *stat, struct inode *inode,
        struct super_block *sb)
 {
-       int n;
        char ext[32];
        struct v9fs_session_info *v9ses = sb->s_fs_info;
 
@@ -842,11 +849,7 @@ v9fs_stat2inode(struct p9_stat *stat, struct inode *inode,
                int major = -1;
                int minor = -1;
 
-               n = stat->extension.len;
-               if (n > sizeof(ext)-1)
-                       n = sizeof(ext)-1;
-               memmove(ext, stat->extension.str, n);
-               ext[n] = 0;
+               strncpy(ext, stat->extension, sizeof(ext));
                sscanf(ext, "%c %u %u", &type, &major, &minor);
                switch (type) {
                case 'c':
@@ -857,10 +860,11 @@ v9fs_stat2inode(struct p9_stat *stat, struct inode *inode,
                        break;
                default:
                        P9_DPRINTK(P9_DEBUG_ERROR,
-                               "Unknown special type %c (%.*s)\n", type,
-                               stat->extension.len, stat->extension.str);
+                               "Unknown special type %c %s\n", type,
+                               stat->extension);
                };
                inode->i_rdev = MKDEV(major, minor);
+               init_special_inode(inode, inode->i_mode, inode->i_rdev);
        } else
                inode->i_rdev = 0;
 
@@ -904,7 +908,7 @@ static int v9fs_readlink(struct dentry *dentry, char *buffer, int buflen)
 
        struct v9fs_session_info *v9ses;
        struct p9_fid *fid;
-       struct p9_stat *st;
+       struct p9_wstat *st;
 
        P9_DPRINTK(P9_DEBUG_VFS, " %s\n", dentry->d_name.name);
        retval = -EPERM;
@@ -926,15 +930,10 @@ static int v9fs_readlink(struct dentry *dentry, char *buffer, int buflen)
        }
 
        /* copy extension buffer into buffer */
-       if (st->extension.len < buflen)
-               buflen = st->extension.len + 1;
-
-       memmove(buffer, st->extension.str, buflen - 1);
-       buffer[buflen-1] = 0;
+       strncpy(buffer, st->extension, buflen);
 
        P9_DPRINTK(P9_DEBUG_VFS,
-               "%s -> %.*s (%s)\n", dentry->d_name.name, st->extension.len,
-               st->extension.str, buffer);
+               "%s -> %s (%s)\n", dentry->d_name.name, st->extension, buffer);
 
        retval = buflen;
 
index bf59c3960494c76a91bb3b0809be897166725852..d6cb1a0ca724d772e2d8bec5093ea621bbe8d864 100644 (file)
@@ -111,7 +111,7 @@ static int v9fs_get_sb(struct file_system_type *fs_type, int flags,
        struct inode *inode = NULL;
        struct dentry *root = NULL;
        struct v9fs_session_info *v9ses = NULL;
-       struct p9_stat *st = NULL;
+       struct p9_wstat *st = NULL;
        int mode = S_IRWXUGO | S_ISVTX;
        uid_t uid = current->fsuid;
        gid_t gid = current->fsgid;
@@ -161,10 +161,14 @@ static int v9fs_get_sb(struct file_system_type *fs_type, int flags,
 
        sb->s_root = root;
        root->d_inode->i_ino = v9fs_qid2ino(&st->qid);
+
        v9fs_stat2inode(st, root->d_inode, sb);
+
        v9fs_fid_add(root, fid);
+       p9stat_free(st);
        kfree(st);
 
+P9_DPRINTK(P9_DEBUG_VFS, " return simple set mount\n");
        return simple_set_mnt(mnt, sb);
 
 release_sb:
index fb163e2e0de60c2c9df90b0980c4f84aadf6b9a1..d2c60c73619dc38827ea57af9b4761addbe1f3f9 100644 (file)
@@ -27,8 +27,6 @@
 #ifndef NET_9P_H
 #define NET_9P_H
 
-#ifdef CONFIG_NET_9P_DEBUG
-
 /**
  * enum p9_debug_flags - bits for mount time debug parameter
  * @P9_DEBUG_ERROR: more verbose error messages including original error string
@@ -39,6 +37,7 @@
  * @P9_DEBUG_TRANS: transport tracing
  * @P9_DEBUG_SLABS: memory management tracing
  * @P9_DEBUG_FCALL: verbose dump of protocol messages
+ * @P9_DEBUG_FID: fid allocation/deallocation tracking
  *
  * These flags are passed at mount time to turn on various levels of
  * verbosity and tracing which will be output to the system logs.
@@ -53,24 +52,27 @@ enum p9_debug_flags {
        P9_DEBUG_TRANS =        (1<<6),
        P9_DEBUG_SLABS =        (1<<7),
        P9_DEBUG_FCALL =        (1<<8),
+       P9_DEBUG_FID =          (1<<9),
+       P9_DEBUG_PKT =          (1<<10),
 };
 
 extern unsigned int p9_debug_level;
 
+#ifdef CONFIG_NET_9P_DEBUG
 #define P9_DPRINTK(level, format, arg...) \
 do {  \
-       if ((p9_debug_level & level) == level) \
-               printk(KERN_NOTICE "-- %s (%d): " \
-               format , __func__, task_pid_nr(current) , ## arg); \
+       if ((p9_debug_level & level) == level) {\
+               if (level == P9_DEBUG_9P) \
+                       printk(KERN_NOTICE "(%8.8d) " \
+                       format , task_pid_nr(current) , ## arg); \
+               else \
+                       printk(KERN_NOTICE "-- %s (%d): " \
+                       format , __func__, task_pid_nr(current) , ## arg); \
+       } \
 } while (0)
 
-#define PRINT_FCALL_ERROR(s, fcall) P9_DPRINTK(P9_DEBUG_ERROR,   \
-       "%s: %.*s\n", s, fcall?fcall->params.rerror.error.len:0, \
-       fcall?fcall->params.rerror.error.str:"");
-
 #else
 #define P9_DPRINTK(level, format, arg...)  do { } while (0)
-#define PRINT_FCALL_ERROR(s, fcall) do { } while (0)
 #endif
 
 #define P9_EPRINTK(level, format, arg...) \
@@ -325,33 +327,6 @@ struct p9_qid {
  * See Also: http://plan9.bell-labs.com/magic/man2html/2/stat
  */
 
-struct p9_stat {
-       u16 size;
-       u16 type;
-       u32 dev;
-       struct p9_qid qid;
-       u32 mode;
-       u32 atime;
-       u32 mtime;
-       u64 length;
-       struct p9_str name;
-       struct p9_str uid;
-       struct p9_str gid;
-       struct p9_str muid;
-       struct p9_str extension;        /* 9p2000.u extensions */
-       u32 n_uid;                      /* 9p2000.u extensions */
-       u32 n_gid;                      /* 9p2000.u extensions */
-       u32 n_muid;                     /* 9p2000.u extensions */
-};
-
-/*
- * file metadata (stat) structure used to create Twstat message
- * The is identical to &p9_stat, but the strings don't point to
- * the same memory block and should be freed separately
- *
- * See Also: http://plan9.bell-labs.com/magic/man2html/2/stat
- */
-
 struct p9_wstat {
        u16 size;
        u16 type;
@@ -493,12 +468,12 @@ struct p9_tstat {
 };
 
 struct p9_rstat {
-       struct p9_stat stat;
+       struct p9_wstat stat;
 };
 
 struct p9_twstat {
        u32 fid;
-       struct p9_stat stat;
+       struct p9_wstat stat;
 };
 
 struct p9_rwstat {
@@ -509,8 +484,9 @@ struct p9_rwstat {
  * @size: prefixed length of the structure
  * @id: protocol operating identifier of type &p9_msg_t
  * @tag: transaction id of the request
+ * @offset: used by marshalling routines to track currentposition in buffer
+ * @capacity: used by marshalling routines to track total capacity
  * @sdata: payload
- * @params: per-operation parameters
  *
  * &p9_fcall represents the structure for all 9P RPC
  * transactions.  Requests are packaged into fcalls, and reponses
@@ -523,68 +499,15 @@ struct p9_fcall {
        u32 size;
        u8 id;
        u16 tag;
-       void *sdata;
-
-       union {
-               struct p9_tversion tversion;
-               struct p9_rversion rversion;
-               struct p9_tauth tauth;
-               struct p9_rauth rauth;
-               struct p9_rerror rerror;
-               struct p9_tflush tflush;
-               struct p9_rflush rflush;
-               struct p9_tattach tattach;
-               struct p9_rattach rattach;
-               struct p9_twalk twalk;
-               struct p9_rwalk rwalk;
-               struct p9_topen topen;
-               struct p9_ropen ropen;
-               struct p9_tcreate tcreate;
-               struct p9_rcreate rcreate;
-               struct p9_tread tread;
-               struct p9_rread rread;
-               struct p9_twrite twrite;
-               struct p9_rwrite rwrite;
-               struct p9_tclunk tclunk;
-               struct p9_rclunk rclunk;
-               struct p9_tremove tremove;
-               struct p9_rremove rremove;
-               struct p9_tstat tstat;
-               struct p9_rstat rstat;
-               struct p9_twstat twstat;
-               struct p9_rwstat rwstat;
-       } params;
+
+       size_t offset;
+       size_t capacity;
+
+       uint8_t *sdata;
 };
 
 struct p9_idpool;
 
-int p9_deserialize_stat(void *buf, u32 buflen, struct p9_stat *stat,
-       int dotu);
-int p9_deserialize_fcall(void *buf, u32 buflen, struct p9_fcall *fc, int dotu);
-void p9_set_tag(struct p9_fcall *fc, u16 tag);
-struct p9_fcall *p9_create_tversion(u32 msize, char *version);
-struct p9_fcall *p9_create_tattach(u32 fid, u32 afid, char *uname,
-       char *aname, u32 n_uname, int dotu);
-struct p9_fcall *p9_create_tauth(u32 afid, char *uname, char *aname,
-       u32 n_uname, int dotu);
-struct p9_fcall *p9_create_tflush(u16 oldtag);
-struct p9_fcall *p9_create_twalk(u32 fid, u32 newfid, u16 nwname,
-       char **wnames);
-struct p9_fcall *p9_create_topen(u32 fid, u8 mode);
-struct p9_fcall *p9_create_tcreate(u32 fid, char *name, u32 perm, u8 mode,
-       char *extension, int dotu);
-struct p9_fcall *p9_create_tread(u32 fid, u64 offset, u32 count);
-struct p9_fcall *p9_create_twrite(u32 fid, u64 offset, u32 count,
-       const char *data);
-struct p9_fcall *p9_create_twrite_u(u32 fid, u64 offset, u32 count,
-       const char __user *data);
-struct p9_fcall *p9_create_tclunk(u32 fid);
-struct p9_fcall *p9_create_tremove(u32 fid);
-struct p9_fcall *p9_create_tstat(u32 fid);
-struct p9_fcall *p9_create_twstat(u32 fid, struct p9_wstat *wstat,
-       int dotu);
-
-int p9_printfcall(char *buf, int buflen, struct p9_fcall *fc, int dotu);
 int p9_errstr2errno(char *errstr, int len);
 
 struct p9_idpool *p9_idpool_create(void);
index c936dd14de41be35466b8bd55a9e42cd36f91a91..1f17f3d93727ee4873e3c7c8a7c61c8cdcbb0204 100644 (file)
 #ifndef NET_9P_CLIENT_H
 #define NET_9P_CLIENT_H
 
+/* Number of requests per row */
+#define P9_ROW_MAXTAG 255
+
+/**
+ * enum p9_trans_status - different states of underlying transports
+ * @Connected: transport is connected and healthy
+ * @Disconnected: transport has been disconnected
+ * @Hung: transport is connected by wedged
+ *
+ * This enumeration details the various states a transport
+ * instatiation can be in.
+ */
+
+enum p9_trans_status {
+       Connected,
+       Disconnected,
+       Hung,
+};
+
+/**
+ * enum p9_req_status_t - virtio request status
+ * @REQ_STATUS_IDLE: request slot unused
+ * @REQ_STATUS_ALLOC: request has been allocated but not sent
+ * @REQ_STATUS_UNSENT: request waiting to be sent
+ * @REQ_STATUS_SENT: request sent to server
+ * @REQ_STATUS_FLSH: a flush has been sent for this request
+ * @REQ_STATUS_RCVD: response received from server
+ * @REQ_STATUS_FLSHD: request has been flushed
+ * @REQ_STATUS_ERROR: request encountered an error on the client side
+ *
+ * The @REQ_STATUS_IDLE state is used to mark a request slot as unused
+ * but use is actually tracked by the idpool structure which handles tag
+ * id allocation.
+ *
+ */
+
+enum p9_req_status_t {
+       REQ_STATUS_IDLE,
+       REQ_STATUS_ALLOC,
+       REQ_STATUS_UNSENT,
+       REQ_STATUS_SENT,
+       REQ_STATUS_FLSH,
+       REQ_STATUS_RCVD,
+       REQ_STATUS_FLSHD,
+       REQ_STATUS_ERROR,
+};
+
+/**
+ * struct p9_req_t - request slots
+ * @status: status of this request slot
+ * @t_err: transport error
+ * @flush_tag: tag of request being flushed (for flush requests)
+ * @wq: wait_queue for the client to block on for this request
+ * @tc: the request fcall structure
+ * @rc: the response fcall structure
+ * @aux: transport specific data (provided for trans_fd migration)
+ * @req_list: link for higher level objects to chain requests
+ *
+ * Transport use an array to track outstanding requests
+ * instead of a list.  While this may incurr overhead during initial
+ * allocation or expansion, it makes request lookup much easier as the
+ * tag id is a index into an array.  (We use tag+1 so that we can accomodate
+ * the -1 tag for the T_VERSION request).
+ * This also has the nice effect of only having to allocate wait_queues
+ * once, instead of constantly allocating and freeing them.  Its possible
+ * other resources could benefit from this scheme as well.
+ *
+ */
+
+struct p9_req_t {
+       int status;
+       int t_err;
+       u16 flush_tag;
+       wait_queue_head_t *wq;
+       struct p9_fcall *tc;
+       struct p9_fcall *rc;
+       void *aux;
+
+       struct list_head req_list;
+};
+
 /**
  * struct p9_client - per client instance state
  * @lock: protect @fidlist
  * @conn: connection state information used by trans_fd
  * @fidpool: fid handle accounting for session
  * @fidlist: List of active fid handles
+ * @tagpool - transaction id accounting for session
+ * @reqs - 2D array of requests
+ * @max_tag - current maximum tag id allocated
  *
  * The client structure is used to keep track of various per-client
  * state that has been instantiated.
+ * In order to minimize per-transaction overhead we use a
+ * simple array to lookup requests instead of a hash table
+ * or linked list.  In order to support larger number of
+ * transactions, we make this a 2D array, allocating new rows
+ * when we need to grow the total number of the transactions.
+ *
+ * Each row is 256 requests and we'll support up to 256 rows for
+ * a total of 64k concurrent requests per session.
  *
  * Bugs: duplicated data and potentially unnecessary elements.
  */
@@ -48,11 +140,16 @@ struct p9_client {
        int msize;
        unsigned char dotu;
        struct p9_trans_module *trans_mod;
-       struct p9_trans *trans;
+       enum p9_trans_status status;
+       void *trans;
        struct p9_conn *conn;
 
        struct p9_idpool *fidpool;
        struct list_head fidlist;
+
+       struct p9_idpool *tagpool;
+       struct p9_req_t *reqs[P9_ROW_MAXTAG];
+       int max_tag;
 };
 
 /**
@@ -65,8 +162,6 @@ struct p9_client {
  * @uid: the numeric uid of the local user who owns this handle
  * @aux: transport specific information (unused?)
  * @rdir_fpos: tracks offset of file position when reading directory contents
- * @rdir_pos: (unused?)
- * @rdir_fcall: holds response of last directory read request
  * @flist: per-client-instance fid tracking
  * @dlist: per-dentry fid tracking
  *
@@ -83,8 +178,6 @@ struct p9_fid {
        void *aux;
 
        int rdir_fpos;
-       int rdir_pos;
-       struct p9_fcall *rdir_fcall;
        struct list_head flist;
        struct list_head dlist; /* list of all fids attached to a dentry */
 };
@@ -103,15 +196,18 @@ int p9_client_fcreate(struct p9_fid *fid, char *name, u32 perm, int mode,
                                                        char *extension);
 int p9_client_clunk(struct p9_fid *fid);
 int p9_client_remove(struct p9_fid *fid);
-int p9_client_read(struct p9_fid *fid, char *data, u64 offset, u32 count);
-int p9_client_readn(struct p9_fid *fid, char *data, u64 offset, u32 count);
-int p9_client_write(struct p9_fid *fid, char *data, u64 offset, u32 count);
-int p9_client_uread(struct p9_fid *fid, char __user *data, u64 offset,
-                                                               u32 count);
-int p9_client_uwrite(struct p9_fid *fid, const char __user *data, u64 offset,
-                                                               u32 count);
-struct p9_stat *p9_client_stat(struct p9_fid *fid);
+int p9_client_read(struct p9_fid *fid, char *data, char __user *udata,
+                                                       u64 offset, u32 count);
+int p9_client_write(struct p9_fid *fid, char *data, const char __user *udata,
+                                                       u64 offset, u32 count);
+struct p9_wstat *p9_client_stat(struct p9_fid *fid);
 int p9_client_wstat(struct p9_fid *fid, struct p9_wstat *wst);
-struct p9_stat *p9_client_dirread(struct p9_fid *fid, u64 offset);
+
+struct p9_req_t *p9_tag_lookup(struct p9_client *, u16);
+void p9_client_cb(struct p9_client *c, struct p9_req_t *req);
+
+int p9stat_read(char *, int, struct p9_wstat *, int);
+void p9stat_free(struct p9_wstat *);
+
 
 #endif /* NET_9P_CLIENT_H */
index 3ca737120a90cff4f845d8ccc66fc9173e0fcd6f..6d5886efb102ecb0b0e74e4b66f3da713b98751c 100644 (file)
 #ifndef NET_9P_TRANSPORT_H
 #define NET_9P_TRANSPORT_H
 
-#include <linux/module.h>
-
-/**
- * enum p9_trans_status - different states of underlying transports
- * @Connected: transport is connected and healthy
- * @Disconnected: transport has been disconnected
- * @Hung: transport is connected by wedged
- *
- * This enumeration details the various states a transport
- * instatiation can be in.
- */
-
-enum p9_trans_status {
-       Connected,
-       Disconnected,
-       Hung,
-};
-
-/**
- * struct p9_trans - per-transport state and API
- * @status: transport &p9_trans_status
- * @msize: negotiated maximum packet size (duplicate from client)
- * @extended: negotiated protocol extensions (duplicate from client)
- * @priv: transport private data
- * @close: member function to disconnect and close the transport
- * @rpc: member function to issue a request to the transport
- *
- * This is the basic API for a transport instance.  It is used as
- * a handle by the client to issue requests.  This interface is currently
- * in flux during reorganization.
- *
- * Bugs: there is lots of duplicated data here and its not clear that
- * the member functions need to be per-instance versus per transport
- * module.
- */
-
-struct p9_trans {
-       enum p9_trans_status status;
-       int msize;
-       unsigned char extended;
-       void *priv;
-       void (*close) (struct p9_trans *);
-       int (*rpc) (struct p9_trans *t, struct p9_fcall *tc,
-                                                       struct p9_fcall **rc);
-};
-
 /**
  * struct p9_trans_module - transport module interface
  * @list: used to maintain a list of currently available transports
@@ -79,12 +33,14 @@ struct p9_trans {
  * @maxsize: transport provided maximum packet size
  * @def: set if this transport should be considered the default
  * @create: member function to create a new connection on this transport
+ * @request: member function to issue a request to the transport
+ * @cancel: member function to cancel a request (if it hasn't been sent)
  *
  * This is the basic API for a transport module which is registered by the
  * transport module with the 9P core network module and used by the client
  * to instantiate a new connection on a transport.
  *
- * Bugs: the transport module list isn't protected.
+ * BUGS: the transport module list isn't protected.
  */
 
 struct p9_trans_module {
@@ -92,8 +48,11 @@ struct p9_trans_module {
        char *name;             /* name of transport */
        int maxsize;            /* max message size of transport */
        int def;                /* this transport should be default */
-       struct p9_trans * (*create)(const char *, char *, int, unsigned char);
        struct module *owner;
+       int (*create)(struct p9_client *, const char *, char *);
+       void (*close) (struct p9_client *);
+       int (*request) (struct p9_client *, struct p9_req_t *req);
+       int (*cancel) (struct p9_client *, struct p9_req_t *req);
 };
 
 void v9fs_register_trans(struct p9_trans_module *m);
index 519219480db1a7080e646d612000f3f351cdcc2a..1041b7bd12e29af0e3be78ff364d8fd7aa2bcec4 100644 (file)
@@ -4,10 +4,9 @@ obj-$(CONFIG_NET_9P_VIRTIO) += 9pnet_virtio.o
 9pnet-objs := \
        mod.o \
        client.o \
-       conv.o \
        error.o \
-       fcprint.o \
        util.o \
+       protocol.o \
        trans_fd.o \
 
 9pnet_virtio-objs := \
index e053e06028a551fc38c43b438c49d6870767d408..bbac2f72b4d27c00e8692ee10760b3f5fa56d9b8 100644 (file)
 #include <linux/uaccess.h>
 #include <net/9p/9p.h>
 #include <linux/parser.h>
-#include <net/9p/transport.h>
 #include <net/9p/client.h>
-
-static struct p9_fid *p9_fid_create(struct p9_client *clnt);
-static void p9_fid_destroy(struct p9_fid *fid);
-static struct p9_stat *p9_clone_stat(struct p9_stat *st, int dotu);
+#include <net/9p/transport.h>
+#include "protocol.h"
 
 /*
   * Client Option Parsing (code inspired by NFS code)
@@ -59,6 +56,9 @@ static const match_table_t tokens = {
        {Opt_err, NULL},
 };
 
+static struct p9_req_t *
+p9_client_rpc(struct p9_client *c, int8_t type, const char *fmt, ...);
+
 /**
  * v9fs_parse_options - parse mount options into session structure
  * @options: options string passed from mount
@@ -124,31 +124,585 @@ static int parse_opts(char *opts, struct p9_client *clnt)
        return ret;
 }
 
+/**
+ * p9_tag_alloc - lookup/allocate a request by tag
+ * @c: client session to lookup tag within
+ * @tag: numeric id for transaction
+ *
+ * this is a simple array lookup, but will grow the
+ * request_slots as necessary to accomodate transaction
+ * ids which did not previously have a slot.
+ *
+ * this code relies on the client spinlock to manage locks, its
+ * possible we should switch to something else, but I'd rather
+ * stick with something low-overhead for the common case.
+ *
+ */
+
+static struct p9_req_t *p9_tag_alloc(struct p9_client *c, u16 tag)
+{
+       unsigned long flags;
+       int row, col;
+       struct p9_req_t *req;
+
+       /* This looks up the original request by tag so we know which
+        * buffer to read the data into */
+       tag++;
+
+       if (tag >= c->max_tag) {
+               spin_lock_irqsave(&c->lock, flags);
+               /* check again since original check was outside of lock */
+               while (tag >= c->max_tag) {
+                       row = (tag / P9_ROW_MAXTAG);
+                       c->reqs[row] = kcalloc(P9_ROW_MAXTAG,
+                                       sizeof(struct p9_req_t), GFP_ATOMIC);
+
+                       if (!c->reqs[row]) {
+                               printk(KERN_ERR "Couldn't grow tag array\n");
+                               return ERR_PTR(-ENOMEM);
+                       }
+                       for (col = 0; col < P9_ROW_MAXTAG; col++) {
+                               c->reqs[row][col].status = REQ_STATUS_IDLE;
+                               c->reqs[row][col].tc = NULL;
+                       }
+                       c->max_tag += P9_ROW_MAXTAG;
+               }
+               spin_unlock_irqrestore(&c->lock, flags);
+       }
+       row = tag / P9_ROW_MAXTAG;
+       col = tag % P9_ROW_MAXTAG;
+
+       req = &c->reqs[row][col];
+       if (!req->tc) {
+               req->wq = kmalloc(sizeof(wait_queue_head_t), GFP_KERNEL);
+               if (!req->wq) {
+                       printk(KERN_ERR "Couldn't grow tag array\n");
+                       return ERR_PTR(-ENOMEM);
+               }
+               init_waitqueue_head(req->wq);
+               req->tc = kmalloc(sizeof(struct p9_fcall)+c->msize,
+                                                               GFP_KERNEL);
+               req->rc = kmalloc(sizeof(struct p9_fcall)+c->msize,
+                                                               GFP_KERNEL);
+               if ((!req->tc) || (!req->rc)) {
+                       printk(KERN_ERR "Couldn't grow tag array\n");
+                       kfree(req->tc);
+                       kfree(req->rc);
+                       return ERR_PTR(-ENOMEM);
+               }
+               req->tc->sdata = (char *) req->tc + sizeof(struct p9_fcall);
+               req->tc->capacity = c->msize;
+               req->rc->sdata = (char *) req->rc + sizeof(struct p9_fcall);
+               req->rc->capacity = c->msize;
+       }
+
+       p9pdu_reset(req->tc);
+       p9pdu_reset(req->rc);
+
+       req->flush_tag = 0;
+       req->tc->tag = tag-1;
+       req->status = REQ_STATUS_ALLOC;
+
+       return &c->reqs[row][col];
+}
+
+/**
+ * p9_tag_lookup - lookup a request by tag
+ * @c: client session to lookup tag within
+ * @tag: numeric id for transaction
+ *
+ */
+
+struct p9_req_t *p9_tag_lookup(struct p9_client *c, u16 tag)
+{
+       int row, col;
+
+       /* This looks up the original request by tag so we know which
+        * buffer to read the data into */
+       tag++;
+
+       BUG_ON(tag >= c->max_tag);
+
+       row = tag / P9_ROW_MAXTAG;
+       col = tag % P9_ROW_MAXTAG;
+
+       return &c->reqs[row][col];
+}
+EXPORT_SYMBOL(p9_tag_lookup);
+
+/**
+ * p9_tag_init - setup tags structure and contents
+ * @tags: tags structure from the client struct
+ *
+ * This initializes the tags structure for each client instance.
+ *
+ */
+
+static int p9_tag_init(struct p9_client *c)
+{
+       int err = 0;
+
+       c->tagpool = p9_idpool_create();
+       if (IS_ERR(c->tagpool)) {
+               err = PTR_ERR(c->tagpool);
+               c->tagpool = NULL;
+               goto error;
+       }
+
+       p9_idpool_get(c->tagpool); /* reserve tag 0 */
+
+       c->max_tag = 0;
+error:
+       return err;
+}
 
 /**
- * p9_client_rpc - sends 9P request and waits until a response is available.
- *      The function can be interrupted.
- * @c: client data
- * @tc: request to be sent
- * @rc: pointer where a pointer to the response is stored
+ * p9_tag_cleanup - cleans up tags structure and reclaims resources
+ * @tags: tags structure from the client struct
+ *
+ * This frees resources associated with the tags structure
+ *
  */
+static void p9_tag_cleanup(struct p9_client *c)
+{
+       int row, col;
+
+       /* check to insure all requests are idle */
+       for (row = 0; row < (c->max_tag/P9_ROW_MAXTAG); row++) {
+               for (col = 0; col < P9_ROW_MAXTAG; col++) {
+                       if (c->reqs[row][col].status != REQ_STATUS_IDLE) {
+                               P9_DPRINTK(P9_DEBUG_MUX,
+                                 "Attempting to cleanup non-free tag %d,%d\n",
+                                 row, col);
+                               /* TODO: delay execution of cleanup */
+                               return;
+                       }
+               }
+       }
+
+       if (c->tagpool)
+               p9_idpool_destroy(c->tagpool);
+
+       /* free requests associated with tags */
+       for (row = 0; row < (c->max_tag/P9_ROW_MAXTAG); row++) {
+               for (col = 0; col < P9_ROW_MAXTAG; col++) {
+                       kfree(c->reqs[row][col].wq);
+                       kfree(c->reqs[row][col].tc);
+                       kfree(c->reqs[row][col].rc);
+               }
+               kfree(c->reqs[row]);
+       }
+       c->max_tag = 0;
+}
+
+/**
+ * p9_free_req - free a request and clean-up as necessary
+ * c: client state
+ * r: request to release
+ *
+ */
+
+static void p9_free_req(struct p9_client *c, struct p9_req_t *r)
+{
+       int tag = r->tc->tag;
+       P9_DPRINTK(P9_DEBUG_MUX, "clnt %p req %p tag: %d\n", c, r, tag);
+
+       r->status = REQ_STATUS_IDLE;
+       if (tag != P9_NOTAG && p9_idpool_check(tag, c->tagpool))
+               p9_idpool_put(tag, c->tagpool);
+
+       /* if this was a flush request we have to free response fcall */
+       if (r->rc->id == P9_RFLUSH) {
+               kfree(r->tc);
+               kfree(r->rc);
+       }
+}
+
+/**
+ * p9_client_cb - call back from transport to client
+ * c: client state
+ * req: request received
+ *
+ */
+void p9_client_cb(struct p9_client *c, struct p9_req_t *req)
+{
+       struct p9_req_t *other_req;
+       unsigned long flags;
+
+       P9_DPRINTK(P9_DEBUG_MUX, " tag %d\n", req->tc->tag);
+
+       if (req->status == REQ_STATUS_ERROR)
+               wake_up(req->wq);
+
+       if (req->flush_tag) {                   /* flush receive path */
+               P9_DPRINTK(P9_DEBUG_9P, "<<< RFLUSH %d\n", req->tc->tag);
+               spin_lock_irqsave(&c->lock, flags);
+               other_req = p9_tag_lookup(c, req->flush_tag);
+               if (other_req->status != REQ_STATUS_FLSH) /* stale flush */
+                       spin_unlock_irqrestore(&c->lock, flags);
+               else {
+                       other_req->status = REQ_STATUS_FLSHD;
+                       spin_unlock_irqrestore(&c->lock, flags);
+                       wake_up(other_req->wq);
+               }
+               p9_free_req(c, req);
+       } else {                                /* normal receive path */
+               P9_DPRINTK(P9_DEBUG_MUX, "normal: tag %d\n", req->tc->tag);
+               spin_lock_irqsave(&c->lock, flags);
+               if (req->status != REQ_STATUS_FLSHD)
+                       req->status = REQ_STATUS_RCVD;
+               spin_unlock_irqrestore(&c->lock, flags);
+               wake_up(req->wq);
+               P9_DPRINTK(P9_DEBUG_MUX, "wakeup: %d\n", req->tc->tag);
+       }
+}
+EXPORT_SYMBOL(p9_client_cb);
+
+/**
+ * p9_parse_header - parse header arguments out of a packet
+ * @pdu: packet to parse
+ * @size: size of packet
+ * @type: type of request
+ * @tag: tag of packet
+ * @rewind: set if we need to rewind offset afterwards
+ */
+
 int
-p9_client_rpc(struct p9_client *c, struct p9_fcall *tc,
-       struct p9_fcall **rc)
+p9_parse_header(struct p9_fcall *pdu, int32_t *size, int8_t *type, int16_t *tag,
+                                                               int rewind)
 {
-       return c->trans->rpc(c->trans, tc, rc);
+       int8_t r_type;
+       int16_t r_tag;
+       int32_t r_size;
+       int offset = pdu->offset;
+       int err;
+
+       pdu->offset = 0;
+       if (pdu->size == 0)
+               pdu->size = 7;
+
+       err = p9pdu_readf(pdu, 0, "dbw", &r_size, &r_type, &r_tag);
+       if (err)
+               goto rewind_and_exit;
+
+       pdu->size = r_size;
+       pdu->id = r_type;
+       pdu->tag = r_tag;
+
+       P9_DPRINTK(P9_DEBUG_9P, "<<< size=%d type: %d tag: %d\n", pdu->size,
+                                                       pdu->id, pdu->tag);
+
+       if (type)
+               *type = r_type;
+       if (tag)
+               *tag = r_tag;
+       if (size)
+               *size = r_size;
+
+
+rewind_and_exit:
+       if (rewind)
+               pdu->offset = offset;
+       return err;
 }
+EXPORT_SYMBOL(p9_parse_header);
+
+/**
+ * p9_check_errors - check 9p packet for error return and process it
+ * @c: current client instance
+ * @req: request to parse and check for error conditions
+ *
+ * returns error code if one is discovered, otherwise returns 0
+ *
+ * this will have to be more complicated if we have multiple
+ * error packet types
+ */
+
+static int p9_check_errors(struct p9_client *c, struct p9_req_t *req)
+{
+       int8_t type;
+       int err;
+
+       err = p9_parse_header(req->rc, NULL, &type, NULL, 0);
+       if (err) {
+               P9_DPRINTK(P9_DEBUG_ERROR, "couldn't parse header %d\n", err);
+               return err;
+       }
+
+       if (type == P9_RERROR) {
+               int ecode;
+               char *ename;
+
+               err = p9pdu_readf(req->rc, c->dotu, "s?d", &ename, &ecode);
+               if (err) {
+                       P9_DPRINTK(P9_DEBUG_ERROR, "couldn't parse error%d\n",
+                                                                       err);
+                       return err;
+               }
+
+               if (c->dotu)
+                       err = -ecode;
+
+               if (!err) {
+                       err = p9_errstr2errno(ename, strlen(ename));
+
+                       /* string match failed */
+                       if (!err)
+                               err = -ESERVERFAULT;
+               }
+
+               P9_DPRINTK(P9_DEBUG_9P, "<<< RERROR (%d) %s\n", -ecode, ename);
+
+               kfree(ename);
+       } else
+               err = 0;
+
+       return err;
+}
+
+/**
+ * p9_client_flush - flush (cancel) a request
+ * c: client state
+ * req: request to cancel
+ *
+ * This sents a flush for a particular requests and links
+ * the flush request to the original request.  The current
+ * code only supports a single flush request although the protocol
+ * allows for multiple flush requests to be sent for a single request.
+ *
+ */
+
+static int p9_client_flush(struct p9_client *c, struct p9_req_t *oldreq)
+{
+       struct p9_req_t *req;
+       int16_t oldtag;
+       int err;
+
+       err = p9_parse_header(oldreq->tc, NULL, NULL, &oldtag, 1);
+       if (err)
+               return err;
+
+       P9_DPRINTK(P9_DEBUG_9P, ">>> TFLUSH tag %d\n", oldtag);
+
+       req = p9_client_rpc(c, P9_TFLUSH, "w", oldtag);
+       if (IS_ERR(req))
+               return PTR_ERR(req);
+
+       req->flush_tag = oldtag;
+
+       /* we don't free anything here because RPC isn't complete */
+       return 0;
+}
+
+/**
+ * p9_client_rpc - issue a request and wait for a response
+ * @c: client session
+ * @type: type of request
+ * @fmt: protocol format string (see protocol.c)
+ *
+ * Returns request structure (which client must free using p9_free_req)
+ */
+
+static struct p9_req_t *
+p9_client_rpc(struct p9_client *c, int8_t type, const char *fmt, ...)
+{
+       va_list ap;
+       int tag, err;
+       struct p9_req_t *req;
+       unsigned long flags;
+       int sigpending;
+       int flushed = 0;
+
+       P9_DPRINTK(P9_DEBUG_MUX, "client %p op %d\n", c, type);
+
+       if (c->status != Connected)
+               return ERR_PTR(-EIO);
+
+       if (signal_pending(current)) {
+               sigpending = 1;
+               clear_thread_flag(TIF_SIGPENDING);
+       } else
+               sigpending = 0;
+
+       tag = P9_NOTAG;
+       if (type != P9_TVERSION) {
+               tag = p9_idpool_get(c->tagpool);
+               if (tag < 0)
+                       return ERR_PTR(-ENOMEM);
+       }
+
+       req = p9_tag_alloc(c, tag);
+       if (IS_ERR(req))
+               return req;
+
+       /* marshall the data */
+       p9pdu_prepare(req->tc, tag, type);
+       va_start(ap, fmt);
+       err = p9pdu_vwritef(req->tc, c->dotu, fmt, ap);
+       va_end(ap);
+       p9pdu_finalize(req->tc);
+
+       err = c->trans_mod->request(c, req);
+       if (err < 0) {
+               c->status = Disconnected;
+               goto reterr;
+       }
+
+       /* if it was a flush we just transmitted, return our tag */
+       if (type == P9_TFLUSH)
+               return req;
+again:
+       P9_DPRINTK(P9_DEBUG_MUX, "wait %p tag: %d\n", req->wq, tag);
+       err = wait_event_interruptible(*req->wq,
+                                               req->status >= REQ_STATUS_RCVD);
+       P9_DPRINTK(P9_DEBUG_MUX, "wait %p tag: %d returned %d (flushed=%d)\n",
+                                               req->wq, tag, err, flushed);
+
+       if (req->status == REQ_STATUS_ERROR) {
+               P9_DPRINTK(P9_DEBUG_ERROR, "req_status error %d\n", req->t_err);
+               err = req->t_err;
+       } else if (err == -ERESTARTSYS && flushed) {
+               P9_DPRINTK(P9_DEBUG_MUX, "flushed - going again\n");
+               goto again;
+       } else if (req->status == REQ_STATUS_FLSHD) {
+               P9_DPRINTK(P9_DEBUG_MUX, "flushed - erestartsys\n");
+               err = -ERESTARTSYS;
+       }
+
+       if ((err == -ERESTARTSYS) && (c->status == Connected) && (!flushed)) {
+               P9_DPRINTK(P9_DEBUG_MUX, "flushing\n");
+               spin_lock_irqsave(&c->lock, flags);
+               if (req->status == REQ_STATUS_SENT)
+                       req->status = REQ_STATUS_FLSH;
+               spin_unlock_irqrestore(&c->lock, flags);
+               sigpending = 1;
+               flushed = 1;
+               clear_thread_flag(TIF_SIGPENDING);
+
+               if (c->trans_mod->cancel(c, req)) {
+                       err = p9_client_flush(c, req);
+                       if (err == 0)
+                               goto again;
+               }
+       }
+
+       if (sigpending) {
+               spin_lock_irqsave(&current->sighand->siglock, flags);
+               recalc_sigpending();
+               spin_unlock_irqrestore(&current->sighand->siglock, flags);
+       }
+
+       if (err < 0)
+               goto reterr;
+
+       err = p9_check_errors(c, req);
+       if (!err) {
+               P9_DPRINTK(P9_DEBUG_MUX, "exit: client %p op %d\n", c, type);
+               return req;
+       }
+
+reterr:
+       P9_DPRINTK(P9_DEBUG_MUX, "exit: client %p op %d error: %d\n", c, type,
+                                                                       err);
+       p9_free_req(c, req);
+       return ERR_PTR(err);
+}
+
+static struct p9_fid *p9_fid_create(struct p9_client *clnt)
+{
+       int err;
+       struct p9_fid *fid;
+
+       P9_DPRINTK(P9_DEBUG_FID, "clnt %p\n", clnt);
+       fid = kmalloc(sizeof(struct p9_fid), GFP_KERNEL);
+       if (!fid)
+               return ERR_PTR(-ENOMEM);
+
+       fid->fid = p9_idpool_get(clnt->fidpool);
+       if (fid->fid < 0) {
+               err = -ENOSPC;
+               goto error;
+       }
+
+       memset(&fid->qid, 0, sizeof(struct p9_qid));
+       fid->mode = -1;
+       fid->rdir_fpos = 0;
+       fid->uid = current->fsuid;
+       fid->clnt = clnt;
+       fid->aux = NULL;
+
+       spin_lock(&clnt->lock);
+       list_add(&fid->flist, &clnt->fidlist);
+       spin_unlock(&clnt->lock);
+
+       return fid;
+
+error:
+       kfree(fid);
+       return ERR_PTR(err);
+}
+
+static void p9_fid_destroy(struct p9_fid *fid)
+{
+       struct p9_client *clnt;
+
+       P9_DPRINTK(P9_DEBUG_FID, "fid %d\n", fid->fid);
+       clnt = fid->clnt;
+       p9_idpool_put(fid->fid, clnt->fidpool);
+       spin_lock(&clnt->lock);
+       list_del(&fid->flist);
+       spin_unlock(&clnt->lock);
+       kfree(fid);
+}
+
+int p9_client_version(struct p9_client *c)
+{
+       int err = 0;
+       struct p9_req_t *req;
+       char *version;
+       int msize;
+
+       P9_DPRINTK(P9_DEBUG_9P, ">>> TVERSION msize %d extended %d\n",
+                                                       c->msize, c->dotu);
+       req = p9_client_rpc(c, P9_TVERSION, "ds", c->msize,
+                               c->dotu ? "9P2000.u" : "9P2000");
+       if (IS_ERR(req))
+               return PTR_ERR(req);
+
+       err = p9pdu_readf(req->rc, c->dotu, "ds", &msize, &version);
+       if (err) {
+               P9_DPRINTK(P9_DEBUG_9P, "version error %d\n", err);
+               p9pdu_dump(1, req->rc);
+               goto error;
+       }
+
+       P9_DPRINTK(P9_DEBUG_9P, "<<< RVERSION msize %d %s\n", msize, version);
+       if (!memcmp(version, "9P2000.u", 8))
+               c->dotu = 1;
+       else if (!memcmp(version, "9P2000", 6))
+               c->dotu = 0;
+       else {
+               err = -EREMOTEIO;
+               goto error;
+       }
+
+       if (msize < c->msize)
+               c->msize = msize;
+
+error:
+       kfree(version);
+       p9_free_req(c, req);
+
+       return err;
+}
+EXPORT_SYMBOL(p9_client_version);
 
 struct p9_client *p9_client_create(const char *dev_name, char *options)
 {
-       int err, n;
+       int err;
        struct p9_client *clnt;
-       struct p9_fcall *tc, *rc;
-       struct p9_str *version;
 
        err = 0;
-       tc = NULL;
-       rc = NULL;
        clnt = kmalloc(sizeof(struct p9_client), GFP_KERNEL);
        if (!clnt)
                return ERR_PTR(-ENOMEM);
@@ -164,6 +718,8 @@ struct p9_client *p9_client_create(const char *dev_name, char *options)
                goto error;
        }
 
+       p9_tag_init(clnt);
+
        err = parse_opts(options, clnt);
        if (err < 0)
                goto error;
@@ -175,53 +731,23 @@ struct p9_client *p9_client_create(const char *dev_name, char *options)
                goto error;
        }
 
-       P9_DPRINTK(P9_DEBUG_9P, "clnt %p trans %p msize %d dotu %d\n",
+       P9_DPRINTK(P9_DEBUG_MUX, "clnt %p trans %p msize %d dotu %d\n",
                clnt, clnt->trans_mod, clnt->msize, clnt->dotu);
 
-
-       clnt->trans = clnt->trans_mod->create(dev_name, options, clnt->msize,
-                                                               clnt->dotu);
-       if (IS_ERR(clnt->trans)) {
-               err = PTR_ERR(clnt->trans);
-               clnt->trans = NULL;
+       err = clnt->trans_mod->create(clnt, dev_name, options);
+       if (err)
                goto error;
-       }
 
        if ((clnt->msize+P9_IOHDRSZ) > clnt->trans_mod->maxsize)
                clnt->msize = clnt->trans_mod->maxsize-P9_IOHDRSZ;
 
-       tc = p9_create_tversion(clnt->msize, clnt->dotu?"9P2000.u":"9P2000");
-       if (IS_ERR(tc)) {
-               err = PTR_ERR(tc);
-               tc = NULL;
-               goto error;
-       }
-
-       err = p9_client_rpc(clnt, tc, &rc);
+       err = p9_client_version(clnt);
        if (err)
                goto error;
 
-       version = &rc->params.rversion.version;
-       if (version->len == 8 && !memcmp(version->str, "9P2000.u", 8))
-               clnt->dotu = 1;
-       else if (version->len == 6 && !memcmp(version->str, "9P2000", 6))
-               clnt->dotu = 0;
-       else {
-               err = -EREMOTEIO;
-               goto error;
-       }
-
-       n = rc->params.rversion.msize;
-       if (n < clnt->msize)
-               clnt->msize = n;
-
-       kfree(tc);
-       kfree(rc);
        return clnt;
 
 error:
-       kfree(tc);
-       kfree(rc);
        p9_client_destroy(clnt);
        return ERR_PTR(err);
 }
@@ -231,13 +757,10 @@ void p9_client_destroy(struct p9_client *clnt)
 {
        struct p9_fid *fid, *fidptr;
 
-       P9_DPRINTK(P9_DEBUG_9P, "clnt %p\n", clnt);
+       P9_DPRINTK(P9_DEBUG_MUX, "clnt %p\n", clnt);
 
-       if (clnt->trans) {
-               clnt->trans->close(clnt->trans);
-               kfree(clnt->trans);
-               clnt->trans = NULL;
-       }
+       if (clnt->trans_mod)
+               clnt->trans_mod->close(clnt);
 
        v9fs_put_trans(clnt->trans_mod);
 
@@ -247,6 +770,8 @@ void p9_client_destroy(struct p9_client *clnt)
        if (clnt->fidpool)
                p9_idpool_destroy(clnt->fidpool);
 
+       p9_tag_cleanup(clnt);
+
        kfree(clnt);
 }
 EXPORT_SYMBOL(p9_client_destroy);
@@ -254,7 +779,7 @@ EXPORT_SYMBOL(p9_client_destroy);
 void p9_client_disconnect(struct p9_client *clnt)
 {
        P9_DPRINTK(P9_DEBUG_9P, "clnt %p\n", clnt);
-       clnt->trans->status = Disconnected;
+       clnt->status = Disconnected;
 }
 EXPORT_SYMBOL(p9_client_disconnect);
 
@@ -262,14 +787,13 @@ struct p9_fid *p9_client_attach(struct p9_client *clnt, struct p9_fid *afid,
        char *uname, u32 n_uname, char *aname)
 {
        int err;
-       struct p9_fcall *tc, *rc;
+       struct p9_req_t *req;
        struct p9_fid *fid;
+       struct p9_qid qid;
 
-       P9_DPRINTK(P9_DEBUG_9P, "clnt %p afid %d uname %s aname %s\n",
-               clnt, afid?afid->fid:-1, uname, aname);
+       P9_DPRINTK(P9_DEBUG_9P, ">>> TATTACH afid %d uname %s aname %s\n",
+                                       afid ? afid->fid : -1, uname, aname);
        err = 0;
-       tc = NULL;
-       rc = NULL;
 
        fid = p9_fid_create(clnt);
        if (IS_ERR(fid)) {
@@ -278,73 +802,77 @@ struct p9_fid *p9_client_attach(struct p9_client *clnt, struct p9_fid *afid,
                goto error;
        }
 
-       tc = p9_create_tattach(fid->fid, afid?afid->fid:P9_NOFID, uname, aname,
-               n_uname, clnt->dotu);
-       if (IS_ERR(tc)) {
-               err = PTR_ERR(tc);
-               tc = NULL;
+       req = p9_client_rpc(clnt, P9_TATTACH, "ddss?d", fid->fid,
+                       afid ? afid->fid : P9_NOFID, uname, aname, n_uname);
+       if (IS_ERR(req)) {
+               err = PTR_ERR(req);
                goto error;
        }
 
-       err = p9_client_rpc(clnt, tc, &rc);
-       if (err)
+       err = p9pdu_readf(req->rc, clnt->dotu, "Q", &qid);
+       if (err) {
+               p9pdu_dump(1, req->rc);
+               p9_free_req(clnt, req);
                goto error;
+       }
+
+       P9_DPRINTK(P9_DEBUG_9P, "<<< RATTACH qid %x.%llx.%x\n",
+                                       qid.type, qid.path, qid.version);
 
-       memmove(&fid->qid, &rc->params.rattach.qid, sizeof(struct p9_qid));
-       kfree(tc);
-       kfree(rc);
+       memmove(&fid->qid, &qid, sizeof(struct p9_qid));
+
+       p9_free_req(clnt, req);
        return fid;
 
 error:
-       kfree(tc);
-       kfree(rc);
        if (fid)
                p9_fid_destroy(fid);
        return ERR_PTR(err);
 }
 EXPORT_SYMBOL(p9_client_attach);
 
-struct p9_fid *p9_client_auth(struct p9_client *clnt, char *uname,
-       u32 n_uname, char *aname)
+struct p9_fid *
+p9_client_auth(struct p9_client *clnt, char *uname, u32 n_uname, char *aname)
 {
        int err;
-       struct p9_fcall *tc, *rc;
-       struct p9_fid *fid;
+       struct p9_req_t *req;
+       struct p9_qid qid;
+       struct p9_fid *afid;
 
-       P9_DPRINTK(P9_DEBUG_9P, "clnt %p uname %s aname %s\n", clnt, uname,
-                                                                       aname);
+       P9_DPRINTK(P9_DEBUG_9P, ">>> TAUTH uname %s aname %s\n", uname, aname);
        err = 0;
-       tc = NULL;
-       rc = NULL;
 
-       fid = p9_fid_create(clnt);
-       if (IS_ERR(fid)) {
-               err = PTR_ERR(fid);
-               fid = NULL;
+       afid = p9_fid_create(clnt);
+       if (IS_ERR(afid)) {
+               err = PTR_ERR(afid);
+               afid = NULL;
                goto error;
        }
 
-       tc = p9_create_tauth(fid->fid, uname, aname, n_uname, clnt->dotu);
-       if (IS_ERR(tc)) {
-               err = PTR_ERR(tc);
-               tc = NULL;
+       req = p9_client_rpc(clnt, P9_TAUTH, "dss?d",
+                       afid ? afid->fid : P9_NOFID, uname, aname, n_uname);
+       if (IS_ERR(req)) {
+               err = PTR_ERR(req);
                goto error;
        }
 
-       err = p9_client_rpc(clnt, tc, &rc);
-       if (err)
+       err = p9pdu_readf(req->rc, clnt->dotu, "Q", &qid);
+       if (err) {
+               p9pdu_dump(1, req->rc);
+               p9_free_req(clnt, req);
                goto error;
+       }
 
-       memmove(&fid->qid, &rc->params.rauth.qid, sizeof(struct p9_qid));
-       kfree(tc);
-       kfree(rc);
-       return fid;
+       P9_DPRINTK(P9_DEBUG_9P, "<<< RAUTH qid %x.%llx.%x\n",
+                                       qid.type, qid.path, qid.version);
+
+       memmove(&afid->qid, &qid, sizeof(struct p9_qid));
+       p9_free_req(clnt, req);
+       return afid;
 
 error:
-       kfree(tc);
-       kfree(rc);
-       if (fid)
-               p9_fid_destroy(fid);
+       if (afid)
+               p9_fid_destroy(afid);
        return ERR_PTR(err);
 }
 EXPORT_SYMBOL(p9_client_auth);
@@ -353,15 +881,13 @@ struct p9_fid *p9_client_walk(struct p9_fid *oldfid, int nwname, char **wnames,
        int clone)
 {
        int err;
-       struct p9_fcall *tc, *rc;
        struct p9_client *clnt;
        struct p9_fid *fid;
+       struct p9_qid *wqids;
+       struct p9_req_t *req;
+       int16_t nwqids, count;
 
-       P9_DPRINTK(P9_DEBUG_9P, "fid %d nwname %d wname[0] %s\n",
-               oldfid->fid, nwname, wnames?wnames[0]:NULL);
        err = 0;
-       tc = NULL;
-       rc = NULL;
        clnt = oldfid->clnt;
        if (clone) {
                fid = p9_fid_create(clnt);
@@ -375,53 +901,49 @@ struct p9_fid *p9_client_walk(struct p9_fid *oldfid, int nwname, char **wnames,
        } else
                fid = oldfid;
 
-       tc = p9_create_twalk(oldfid->fid, fid->fid, nwname, wnames);
-       if (IS_ERR(tc)) {
-               err = PTR_ERR(tc);
-               tc = NULL;
+
+       P9_DPRINTK(P9_DEBUG_9P, ">>> TWALK fids %d,%d nwname %d wname[0] %s\n",
+               oldfid->fid, fid->fid, nwname, wnames ? wnames[0] : NULL);
+
+       req = p9_client_rpc(clnt, P9_TWALK, "ddT", oldfid->fid, fid->fid,
+                                                               nwname, wnames);
+       if (IS_ERR(req)) {
+               err = PTR_ERR(req);
                goto error;
        }
 
-       err = p9_client_rpc(clnt, tc, &rc);
+       err = p9pdu_readf(req->rc, clnt->dotu, "R", &nwqids, &wqids);
        if (err) {
-               if (rc && rc->id == P9_RWALK)
-                       goto clunk_fid;
-               else
-                       goto error;
+               p9pdu_dump(1, req->rc);
+               p9_free_req(clnt, req);
+               goto clunk_fid;
        }
+       p9_free_req(clnt, req);
 
-       if (rc->params.rwalk.nwqid != nwname) {
+       P9_DPRINTK(P9_DEBUG_9P, "<<< RWALK nwqid %d:\n", nwqids);
+
+       if (nwqids != nwname) {
                err = -ENOENT;
                goto clunk_fid;
        }
 
+       for (count = 0; count < nwqids; count++)
+               P9_DPRINTK(P9_DEBUG_9P, "<<<     [%d] %x.%llx.%x\n",
+                       count, wqids[count].type, wqids[count].path,
+                       wqids[count].version);
+
        if (nwname)
-               memmove(&fid->qid,
-                       &rc->params.rwalk.wqids[rc->params.rwalk.nwqid - 1],
-                       sizeof(struct p9_qid));
+               memmove(&fid->qid, &wqids[nwqids - 1], sizeof(struct p9_qid));
        else
                fid->qid = oldfid->qid;
 
-       kfree(tc);
-       kfree(rc);
        return fid;
 
 clunk_fid:
-       kfree(tc);
-       kfree(rc);
-       rc = NULL;
-       tc = p9_create_tclunk(fid->fid);
-       if (IS_ERR(tc)) {
-               err = PTR_ERR(tc);
-               tc = NULL;
-               goto error;
-       }
-
-       p9_client_rpc(clnt, tc, &rc);
+       p9_client_clunk(fid);
+       fid = NULL;
 
 error:
-       kfree(tc);
-       kfree(rc);
        if (fid && (fid != oldfid))
                p9_fid_destroy(fid);
 
@@ -432,35 +954,39 @@ EXPORT_SYMBOL(p9_client_walk);
 int p9_client_open(struct p9_fid *fid, int mode)
 {
        int err;
-       struct p9_fcall *tc, *rc;
        struct p9_client *clnt;
+       struct p9_req_t *req;
+       struct p9_qid qid;
+       int iounit;
 
-       P9_DPRINTK(P9_DEBUG_9P, "fid %d mode %d\n", fid->fid, mode);
+       P9_DPRINTK(P9_DEBUG_9P, ">>> TOPEN fid %d mode %d\n", fid->fid, mode);
        err = 0;
-       tc = NULL;
-       rc = NULL;
        clnt = fid->clnt;
 
        if (fid->mode != -1)
                return -EINVAL;
 
-       tc = p9_create_topen(fid->fid, mode);
-       if (IS_ERR(tc)) {
-               err = PTR_ERR(tc);
-               tc = NULL;
-               goto done;
+       req = p9_client_rpc(clnt, P9_TOPEN, "db", fid->fid, mode);
+       if (IS_ERR(req)) {
+               err = PTR_ERR(req);
+               goto error;
        }
 
-       err = p9_client_rpc(clnt, tc, &rc);
-       if (err)
-               goto done;
+       err = p9pdu_readf(req->rc, clnt->dotu, "Qd", &qid, &iounit);
+       if (err) {
+               p9pdu_dump(1, req->rc);
+               goto free_and_error;
+       }
+
+       P9_DPRINTK(P9_DEBUG_9P, "<<< ROPEN qid %x.%llx.%x iounit %x\n",
+                               qid.type, qid.path, qid.version, iounit);
 
        fid->mode = mode;
-       fid->iounit = rc->params.ropen.iounit;
+       fid->iounit = iounit;
 
-done:
-       kfree(tc);
-       kfree(rc);
+free_and_error:
+       p9_free_req(clnt, req);
+error:
        return err;
 }
 EXPORT_SYMBOL(p9_client_open);
@@ -469,37 +995,41 @@ int p9_client_fcreate(struct p9_fid *fid, char *name, u32 perm, int mode,
                     char *extension)
 {
        int err;
-       struct p9_fcall *tc, *rc;
        struct p9_client *clnt;
+       struct p9_req_t *req;
+       struct p9_qid qid;
+       int iounit;
 
-       P9_DPRINTK(P9_DEBUG_9P, "fid %d name %s perm %d mode %d\n", fid->fid,
-               name, perm, mode);
+       P9_DPRINTK(P9_DEBUG_9P, ">>> TCREATE fid %d name %s perm %d mode %d\n",
+                                               fid->fid, name, perm, mode);
        err = 0;
-       tc = NULL;
-       rc = NULL;
        clnt = fid->clnt;
 
        if (fid->mode != -1)
                return -EINVAL;
 
-       tc = p9_create_tcreate(fid->fid, name, perm, mode, extension,
-                                                              clnt->dotu);
-       if (IS_ERR(tc)) {
-               err = PTR_ERR(tc);
-               tc = NULL;
-               goto done;
+       req = p9_client_rpc(clnt, P9_TCREATE, "dsdb?s", fid->fid, name, perm,
+                               mode, extension);
+       if (IS_ERR(req)) {
+               err = PTR_ERR(req);
+               goto error;
        }
 
-       err = p9_client_rpc(clnt, tc, &rc);
-       if (err)
-               goto done;
+       err = p9pdu_readf(req->rc, clnt->dotu, "Qd", &qid, &iounit);
+       if (err) {
+               p9pdu_dump(1, req->rc);
+               goto free_and_error;
+       }
+
+       P9_DPRINTK(P9_DEBUG_9P, "<<< RCREATE qid %x.%llx.%x iounit %x\n",
+                               qid.type, qid.path, qid.version, iounit);
 
        fid->mode = mode;
-       fid->iounit = rc->params.ropen.iounit;
+       fid->iounit = iounit;
 
-done:
-       kfree(tc);
-       kfree(rc);
+free_and_error:
+       p9_free_req(clnt, req);
+error:
        return err;
 }
 EXPORT_SYMBOL(p9_client_fcreate);
@@ -507,31 +1037,25 @@ EXPORT_SYMBOL(p9_client_fcreate);
 int p9_client_clunk(struct p9_fid *fid)
 {
        int err;
-       struct p9_fcall *tc, *rc;
        struct p9_client *clnt;
+       struct p9_req_t *req;
 
-       P9_DPRINTK(P9_DEBUG_9P, "fid %d\n", fid->fid);
+       P9_DPRINTK(P9_DEBUG_9P, ">>> TCLUNK fid %d\n", fid->fid);
        err = 0;
-       tc = NULL;
-       rc = NULL;
        clnt = fid->clnt;
 
-       tc = p9_create_tclunk(fid->fid);
-       if (IS_ERR(tc)) {
-               err = PTR_ERR(tc);
-               tc = NULL;
-               goto done;
+       req = p9_client_rpc(clnt, P9_TCLUNK, "d", fid->fid);
+       if (IS_ERR(req)) {
+               err = PTR_ERR(req);
+               goto error;
        }
 
-       err = p9_client_rpc(clnt, tc, &rc);
-       if (err)
-               goto done;
+       P9_DPRINTK(P9_DEBUG_9P, "<<< RCLUNK fid %d\n", fid->fid);
 
+       p9_free_req(clnt, req);
        p9_fid_destroy(fid);
 
-done:
-       kfree(tc);
-       kfree(rc);
+error:
        return err;
 }
 EXPORT_SYMBOL(p9_client_clunk);
@@ -539,157 +1063,41 @@ EXPORT_SYMBOL(p9_client_clunk);
 int p9_client_remove(struct p9_fid *fid)
 {
        int err;
-       struct p9_fcall *tc, *rc;
        struct p9_client *clnt;
+       struct p9_req_t *req;
 
-       P9_DPRINTK(P9_DEBUG_9P, "fid %d\n", fid->fid);
+       P9_DPRINTK(P9_DEBUG_9P, ">>> TREMOVE fid %d\n", fid->fid);
        err = 0;
-       tc = NULL;
-       rc = NULL;
        clnt = fid->clnt;
 
-       tc = p9_create_tremove(fid->fid);
-       if (IS_ERR(tc)) {
-               err = PTR_ERR(tc);
-               tc = NULL;
-               goto done;
+       req = p9_client_rpc(clnt, P9_TREMOVE, "d", fid->fid);
+       if (IS_ERR(req)) {
+               err = PTR_ERR(req);
+               goto error;
        }
 
-       err = p9_client_rpc(clnt, tc, &rc);
-       if (err)
-               goto done;
+       P9_DPRINTK(P9_DEBUG_9P, "<<< RREMOVE fid %d\n", fid->fid);
 
+       p9_free_req(clnt, req);
        p9_fid_destroy(fid);
 
-done:
-       kfree(tc);
-       kfree(rc);
-       return err;
-}
-EXPORT_SYMBOL(p9_client_remove);
-
-int p9_client_read(struct p9_fid *fid, char *data, u64 offset, u32 count)
-{
-       int err, n, rsize, total;
-       struct p9_fcall *tc, *rc;
-       struct p9_client *clnt;
-
-       P9_DPRINTK(P9_DEBUG_9P, "fid %d offset %llu %d\n", fid->fid,
-                                       (long long unsigned) offset, count);
-       err = 0;
-       tc = NULL;
-       rc = NULL;
-       clnt = fid->clnt;
-       total = 0;
-
-       rsize = fid->iounit;
-       if (!rsize || rsize > clnt->msize-P9_IOHDRSZ)
-               rsize = clnt->msize - P9_IOHDRSZ;
-
-       do {
-               if (count < rsize)
-                       rsize = count;
-
-               tc = p9_create_tread(fid->fid, offset, rsize);
-               if (IS_ERR(tc)) {
-                       err = PTR_ERR(tc);
-                       tc = NULL;
-                       goto error;
-               }
-
-               err = p9_client_rpc(clnt, tc, &rc);
-               if (err)
-                       goto error;
-
-               n = rc->params.rread.count;
-               if (n > count)
-                       n = count;
-
-               memmove(data, rc->params.rread.data, n);
-               count -= n;
-               data += n;
-               offset += n;
-               total += n;
-               kfree(tc);
-               tc = NULL;
-               kfree(rc);
-               rc = NULL;
-       } while (count > 0 && n == rsize);
-
-       return total;
-
 error:
-       kfree(tc);
-       kfree(rc);
        return err;
 }
-EXPORT_SYMBOL(p9_client_read);
-
-int p9_client_write(struct p9_fid *fid, char *data, u64 offset, u32 count)
-{
-       int err, n, rsize, total;
-       struct p9_fcall *tc, *rc;
-       struct p9_client *clnt;
-
-       P9_DPRINTK(P9_DEBUG_9P, "fid %d offset %llu count %d\n", fid->fid,
-                                       (long long unsigned) offset, count);
-       err = 0;
-       tc = NULL;
-       rc = NULL;
-       clnt = fid->clnt;
-       total = 0;
-
-       rsize = fid->iounit;
-       if (!rsize || rsize > clnt->msize-P9_IOHDRSZ)
-               rsize = clnt->msize - P9_IOHDRSZ;
-
-       do {
-               if (count < rsize)
-                       rsize = count;
-
-               tc = p9_create_twrite(fid->fid, offset, rsize, data);
-               if (IS_ERR(tc)) {
-                       err = PTR_ERR(tc);
-                       tc = NULL;
-                       goto error;
-               }
-
-               err = p9_client_rpc(clnt, tc, &rc);
-               if (err)
-                       goto error;
-
-               n = rc->params.rread.count;
-               count -= n;
-               data += n;
-               offset += n;
-               total += n;
-               kfree(tc);
-               tc = NULL;
-               kfree(rc);
-               rc = NULL;
-       } while (count > 0);
-
-       return total;
-
-error:
-       kfree(tc);
-       kfree(rc);
-       return err;
-}
-EXPORT_SYMBOL(p9_client_write);
+EXPORT_SYMBOL(p9_client_remove);
 
 int
-p9_client_uread(struct p9_fid *fid, char __user *data, u64 offset, u32 count)
+p9_client_read(struct p9_fid *fid, char *data, char __user *udata, u64 offset,
+                                                               u32 count)
 {
-       int err, n, rsize, total;
-       struct p9_fcall *tc, *rc;
+       int err, rsize, total;
        struct p9_client *clnt;
+       struct p9_req_t *req;
+       char *dataptr;
 
-       P9_DPRINTK(P9_DEBUG_9P, "fid %d offset %llu count %d\n", fid->fid,
+       P9_DPRINTK(P9_DEBUG_9P, ">>> TREAD fid %d offset %llu %d\n", fid->fid,
                                        (long long unsigned) offset, count);
        err = 0;
-       tc = NULL;
-       rc = NULL;
        clnt = fid->clnt;
        total = 0;
 
@@ -697,63 +1105,57 @@ p9_client_uread(struct p9_fid *fid, char __user *data, u64 offset, u32 count)
        if (!rsize || rsize > clnt->msize-P9_IOHDRSZ)
                rsize = clnt->msize - P9_IOHDRSZ;
 
-       do {
-               if (count < rsize)
-                       rsize = count;
+       if (count < rsize)
+               rsize = count;
 
-               tc = p9_create_tread(fid->fid, offset, rsize);
-               if (IS_ERR(tc)) {
-                       err = PTR_ERR(tc);
-                       tc = NULL;
-                       goto error;
-               }
+       req = p9_client_rpc(clnt, P9_TREAD, "dqd", fid->fid, offset, rsize);
+       if (IS_ERR(req)) {
+               err = PTR_ERR(req);
+               goto error;
+       }
 
-               err = p9_client_rpc(clnt, tc, &rc);
-               if (err)
-                       goto error;
+       err = p9pdu_readf(req->rc, clnt->dotu, "D", &count, &dataptr);
+       if (err) {
+               p9pdu_dump(1, req->rc);
+               goto free_and_error;
+       }
+
+       P9_DPRINTK(P9_DEBUG_9P, "<<< RREAD count %d\n", count);
 
-               n = rc->params.rread.count;
-               if (n > count)
-                       n = count;
+       if (data) {
+               memmove(data, dataptr, count);
+               data += count;
+       }
 
-               err = copy_to_user(data, rc->params.rread.data, n);
+       if (udata) {
+               err = copy_to_user(udata, dataptr, count);
                if (err) {
                        err = -EFAULT;
-                       goto error;
+                       goto free_and_error;
                }
+       }
 
-               count -= n;
-               data += n;
-               offset += n;
-               total += n;
-               kfree(tc);
-               tc = NULL;
-               kfree(rc);
-               rc = NULL;
-       } while (count > 0 && n == rsize);
-
-       return total;
+       p9_free_req(clnt, req);
+       return count;
 
+free_and_error:
+       p9_free_req(clnt, req);
 error:
-       kfree(tc);
-       kfree(rc);
        return err;
 }
-EXPORT_SYMBOL(p9_client_uread);
+EXPORT_SYMBOL(p9_client_read);
 
 int
-p9_client_uwrite(struct p9_fid *fid, const char __user *data, u64 offset,
-                                                                  u32 count)
+p9_client_write(struct p9_fid *fid, char *data, const char __user *udata,
+                                                       u64 offset, u32 count)
 {
-       int err, n, rsize, total;
-       struct p9_fcall *tc, *rc;
+       int err, rsize, total;
        struct p9_client *clnt;
+       struct p9_req_t *req;
 
-       P9_DPRINTK(P9_DEBUG_9P, "fid %d offset %llu count %d\n", fid->fid,
-                                       (long long unsigned) offset, count);
+       P9_DPRINTK(P9_DEBUG_9P, ">>> TWRITE fid %d offset %llu count %d\n",
+                               fid->fid, (long long unsigned) offset, count);
        err = 0;
-       tc = NULL;
-       rc = NULL;
        clnt = fid->clnt;
        total = 0;
 
@@ -761,325 +1163,114 @@ p9_client_uwrite(struct p9_fid *fid, const char __user *data, u64 offset,
        if (!rsize || rsize > clnt->msize-P9_IOHDRSZ)
                rsize = clnt->msize - P9_IOHDRSZ;
 
-       do {
-               if (count < rsize)
-                       rsize = count;
-
-               tc = p9_create_twrite_u(fid->fid, offset, rsize, data);
-               if (IS_ERR(tc)) {
-                       err = PTR_ERR(tc);
-                       tc = NULL;
-                       goto error;
-               }
+       if (count < rsize)
+               rsize = count;
+       if (data)
+               req = p9_client_rpc(clnt, P9_TWRITE, "dqD", fid->fid, offset,
+                                                               rsize, data);
+       else
+               req = p9_client_rpc(clnt, P9_TWRITE, "dqU", fid->fid, offset,
+                                                               rsize, udata);
+       if (IS_ERR(req)) {
+               err = PTR_ERR(req);
+               goto error;
+       }
 
-               err = p9_client_rpc(clnt, tc, &rc);
-               if (err)
-                       goto error;
+       err = p9pdu_readf(req->rc, clnt->dotu, "d", &count);
+       if (err) {
+               p9pdu_dump(1, req->rc);
+               goto free_and_error;
+       }
 
-               n = rc->params.rread.count;
-               count -= n;
-               data += n;
-               offset += n;
-               total += n;
-               kfree(tc);
-               tc = NULL;
-               kfree(rc);
-               rc = NULL;
-       } while (count > 0);
+       P9_DPRINTK(P9_DEBUG_9P, "<<< RWRITE count %d\n", count);
 
-       return total;
+       p9_free_req(clnt, req);
+       return count;
 
+free_and_error:
+       p9_free_req(clnt, req);
 error:
-       kfree(tc);
-       kfree(rc);
        return err;
 }
-EXPORT_SYMBOL(p9_client_uwrite);
-
-int p9_client_readn(struct p9_fid *fid, char *data, u64 offset, u32 count)
-{
-       int n, total;
-
-       P9_DPRINTK(P9_DEBUG_9P, "fid %d offset %llu count %d\n", fid->fid,
-                                       (long long unsigned) offset, count);
-       n = 0;
-       total = 0;
-       while (count) {
-               n = p9_client_read(fid, data, offset, count);
-               if (n <= 0)
-                       break;
-
-               data += n;
-               offset += n;
-               count -= n;
-               total += n;
-       }
-
-       if (n < 0)
-               total = n;
-
-       return total;
-}
-EXPORT_SYMBOL(p9_client_readn);
+EXPORT_SYMBOL(p9_client_write);
 
-struct p9_stat *p9_client_stat(struct p9_fid *fid)
+struct p9_wstat *p9_client_stat(struct p9_fid *fid)
 {
        int err;
-       struct p9_fcall *tc, *rc;
        struct p9_client *clnt;
-       struct p9_stat *ret;
+       struct p9_wstat *ret = kmalloc(sizeof(struct p9_wstat), GFP_KERNEL);
+       struct p9_req_t *req;
+       u16 ignored;
+
+       P9_DPRINTK(P9_DEBUG_9P, ">>> TSTAT fid %d\n", fid->fid);
+
+       if (!ret)
+               return ERR_PTR(-ENOMEM);
 
-       P9_DPRINTK(P9_DEBUG_9P, "fid %d\n", fid->fid);
        err = 0;
-       tc = NULL;
-       rc = NULL;
-       ret = NULL;
        clnt = fid->clnt;
 
-       tc = p9_create_tstat(fid->fid);
-       if (IS_ERR(tc)) {
-               err = PTR_ERR(tc);
-               tc = NULL;
+       req = p9_client_rpc(clnt, P9_TSTAT, "d", fid->fid);
+       if (IS_ERR(req)) {
+               err = PTR_ERR(req);
                goto error;
        }
 
-       err = p9_client_rpc(clnt, tc, &rc);
-       if (err)
-               goto error;
-
-       ret = p9_clone_stat(&rc->params.rstat.stat, clnt->dotu);
-       if (IS_ERR(ret)) {
-               err = PTR_ERR(ret);
-               ret = NULL;
-               goto error;
+       err = p9pdu_readf(req->rc, clnt->dotu, "wS", &ignored, ret);
+       if (err) {
+               ret = ERR_PTR(err);
+               p9pdu_dump(1, req->rc);
+               goto free_and_error;
        }
 
-       kfree(tc);
-       kfree(rc);
-       return ret;
-
+       P9_DPRINTK(P9_DEBUG_9P,
+               "<<< RSTAT sz=%x type=%x dev=%x qid=%x.%llx.%x\n"
+               "<<<    mode=%8.8x atime=%8.8x mtime=%8.8x length=%llx\n"
+               "<<<    name=%s uid=%s gid=%s muid=%s extension=(%s)\n"
+               "<<<    uid=%d gid=%d n_muid=%d\n",
+               ret->size, ret->type, ret->dev, ret->qid.type,
+               ret->qid.path, ret->qid.version, ret->mode,
+               ret->atime, ret->mtime, ret->length, ret->name,
+               ret->uid, ret->gid, ret->muid, ret->extension,
+               ret->n_uid, ret->n_gid, ret->n_muid);
+
+free_and_error:
+       p9_free_req(clnt, req);
 error:
-       kfree(tc);
-       kfree(rc);
-       kfree(ret);
-       return ERR_PTR(err);
+       return ret;
 }
 EXPORT_SYMBOL(p9_client_stat);
 
 int p9_client_wstat(struct p9_fid *fid, struct p9_wstat *wst)
 {
        int err;
-       struct p9_fcall *tc, *rc;
+       struct p9_req_t *req;
        struct p9_client *clnt;
 
-       P9_DPRINTK(P9_DEBUG_9P, "fid %d\n", fid->fid);
+       P9_DPRINTK(P9_DEBUG_9P, ">>> TWSTAT fid %d\n", fid->fid);
+       P9_DPRINTK(P9_DEBUG_9P,
+               "     sz=%x type=%x dev=%x qid=%x.%llx.%x\n"
+               "     mode=%8.8x atime=%8.8x mtime=%8.8x length=%llx\n"
+               "     name=%s uid=%s gid=%s muid=%s extension=(%s)\n"
+               "     uid=%d gid=%d n_muid=%d\n",
+               wst->size, wst->type, wst->dev, wst->qid.type,
+               wst->qid.path, wst->qid.version, wst->mode,
+               wst->atime, wst->mtime, wst->length, wst->name,
+               wst->uid, wst->gid, wst->muid, wst->extension,
+               wst->n_uid, wst->n_gid, wst->n_muid);
        err = 0;
-       tc = NULL;
-       rc = NULL;
        clnt = fid->clnt;
 
-       tc = p9_create_twstat(fid->fid, wst, clnt->dotu);
-       if (IS_ERR(tc)) {
-               err = PTR_ERR(tc);
-               tc = NULL;
-               goto done;
-       }
-
-       err = p9_client_rpc(clnt, tc, &rc);
-
-done:
-       kfree(tc);
-       kfree(rc);
-       return err;
-}
-EXPORT_SYMBOL(p9_client_wstat);
-
-struct p9_stat *p9_client_dirread(struct p9_fid *fid, u64 offset)
-{
-       int err, n, m;
-       struct p9_fcall *tc, *rc;
-       struct p9_client *clnt;
-       struct p9_stat st, *ret;
-
-       P9_DPRINTK(P9_DEBUG_9P, "fid %d offset %llu\n", fid->fid,
-                                               (long long unsigned) offset);
-       err = 0;
-       tc = NULL;
-       rc = NULL;
-       ret = NULL;
-       clnt = fid->clnt;
-
-       /* if the offset is below or above the current response, free it */
-       if (offset < fid->rdir_fpos || (fid->rdir_fcall &&
-               offset >= fid->rdir_fpos+fid->rdir_fcall->params.rread.count)) {
-               fid->rdir_pos = 0;
-               if (fid->rdir_fcall)
-                       fid->rdir_fpos += fid->rdir_fcall->params.rread.count;
-
-               kfree(fid->rdir_fcall);
-               fid->rdir_fcall = NULL;
-               if (offset < fid->rdir_fpos)
-                       fid->rdir_fpos = 0;
-       }
-
-       if (!fid->rdir_fcall) {
-               n = fid->iounit;
-               if (!n || n > clnt->msize-P9_IOHDRSZ)
-                       n = clnt->msize - P9_IOHDRSZ;
-
-               while (1) {
-                       if (fid->rdir_fcall) {
-                               fid->rdir_fpos +=
-                                       fid->rdir_fcall->params.rread.count;
-                               kfree(fid->rdir_fcall);
-                               fid->rdir_fcall = NULL;
-                       }
-
-                       tc = p9_create_tread(fid->fid, fid->rdir_fpos, n);
-                       if (IS_ERR(tc)) {
-                               err = PTR_ERR(tc);
-                               tc = NULL;
-                               goto error;
-                       }
-
-                       err = p9_client_rpc(clnt, tc, &rc);
-                       if (err)
-                               goto error;
-
-                       n = rc->params.rread.count;
-                       if (n == 0)
-                               goto done;
-
-                       fid->rdir_fcall = rc;
-                       rc = NULL;
-                       if (offset >= fid->rdir_fpos &&
-                                               offset < fid->rdir_fpos+n)
-                               break;
-               }
-
-               fid->rdir_pos = 0;
-       }
-
-       m = offset - fid->rdir_fpos;
-       if (m < 0)
-               goto done;
-
-       n = p9_deserialize_stat(fid->rdir_fcall->params.rread.data + m,
-               fid->rdir_fcall->params.rread.count - m, &st, clnt->dotu);
-
-       if (!n) {
-               err = -EIO;
-               goto error;
-       }
-
-       fid->rdir_pos += n;
-       st.size = n;
-       ret = p9_clone_stat(&st, clnt->dotu);
-       if (IS_ERR(ret)) {
-               err = PTR_ERR(ret);
-               ret = NULL;
-               goto error;
-       }
-
-done:
-       kfree(tc);
-       kfree(rc);
-       return ret;
-
-error:
-       kfree(tc);
-       kfree(rc);
-       kfree(ret);
-       return ERR_PTR(err);
-}
-EXPORT_SYMBOL(p9_client_dirread);
-
-static struct p9_stat *p9_clone_stat(struct p9_stat *st, int dotu)
-{
-       int n;
-       char *p;
-       struct p9_stat *ret;
-
-       n = sizeof(struct p9_stat) + st->name.len + st->uid.len + st->gid.len +
-               st->muid.len;
-
-       if (dotu)
-               n += st->extension.len;
-
-       ret = kmalloc(n, GFP_KERNEL);
-       if (!ret)
-               return ERR_PTR(-ENOMEM);
-
-       memmove(ret, st, sizeof(struct p9_stat));
-       p = ((char *) ret) + sizeof(struct p9_stat);
-       memmove(p, st->name.str, st->name.len);
-       ret->name.str = p;
-       p += st->name.len;
-       memmove(p, st->uid.str, st->uid.len);
-       ret->uid.str = p;
-       p += st->uid.len;
-       memmove(p, st->gid.str, st->gid.len);
-       ret->gid.str = p;
-       p += st->gid.len;
-       memmove(p, st->muid.str, st->muid.len);
-       ret->muid.str = p;
-       p += st->muid.len;
-
-       if (dotu) {
-               memmove(p, st->extension.str, st->extension.len);
-               ret->extension.str = p;
-               p += st->extension.len;
-       }
-
-       return ret;
-}
-
-static struct p9_fid *p9_fid_create(struct p9_client *clnt)
-{
-       int err;
-       struct p9_fid *fid;
-
-       P9_DPRINTK(P9_DEBUG_9P, "clnt %p\n", clnt);
-       fid = kmalloc(sizeof(struct p9_fid), GFP_KERNEL);
-       if (!fid)
-               return ERR_PTR(-ENOMEM);
-
-       fid->fid = p9_idpool_get(clnt->fidpool);
-       if (fid->fid < 0) {
-               err = -ENOSPC;
+       req = p9_client_rpc(clnt, P9_TWSTAT, "dwS", fid->fid, 0, wst);
+       if (IS_ERR(req)) {
+               err = PTR_ERR(req);
                goto error;
        }
 
-       memset(&fid->qid, 0, sizeof(struct p9_qid));
-       fid->mode = -1;
-       fid->rdir_fpos = 0;
-       fid->rdir_pos = 0;
-       fid->rdir_fcall = NULL;
-       fid->uid = current->fsuid;
-       fid->clnt = clnt;
-       fid->aux = NULL;
-
-       spin_lock(&clnt->lock);
-       list_add(&fid->flist, &clnt->fidlist);
-       spin_unlock(&clnt->lock);
-
-       return fid;
+       P9_DPRINTK(P9_DEBUG_9P, "<<< RWSTAT fid %d\n", fid->fid);
 
+       p9_free_req(clnt, req);
 error:
-       kfree(fid);
-       return ERR_PTR(err);
-}
-
-static void p9_fid_destroy(struct p9_fid *fid)
-{
-       struct p9_client *clnt;
-
-       P9_DPRINTK(P9_DEBUG_9P, "fid %d\n", fid->fid);
-       clnt = fid->clnt;
-       p9_idpool_put(fid->fid, clnt->fidpool);
-       spin_lock(&clnt->lock);
-       list_del(&fid->flist);
-       spin_unlock(&clnt->lock);
-       kfree(fid->rdir_fcall);
-       kfree(fid);
+       return err;
 }
+EXPORT_SYMBOL(p9_client_wstat);
diff --git a/net/9p/conv.c b/net/9p/conv.c
deleted file mode 100644 (file)
index 5ad3a3b..0000000
+++ /dev/null
@@ -1,1054 +0,0 @@
-/*
- * net/9p/conv.c
- *
- * 9P protocol conversion functions
- *
- *  Copyright (C) 2004, 2005 by Latchesar Ionkov <lucho@ionkov.net>
- *  Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
- *  Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License version 2
- *  as published by the Free Software Foundation.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to:
- *  Free Software Foundation
- *  51 Franklin Street, Fifth Floor
- *  Boston, MA  02111-1301  USA
- *
- */
-
-#include <linux/module.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/sched.h>
-#include <linux/idr.h>
-#include <linux/uaccess.h>
-#include <net/9p/9p.h>
-
-/*
- * Buffer to help with string parsing
- */
-struct cbuf {
-       unsigned char *sp;
-       unsigned char *p;
-       unsigned char *ep;
-};
-
-static inline void buf_init(struct cbuf *buf, void *data, int datalen)
-{
-       buf->sp = buf->p = data;
-       buf->ep = data + datalen;
-}
-
-static inline int buf_check_overflow(struct cbuf *buf)
-{
-       return buf->p > buf->ep;
-}
-
-static int buf_check_size(struct cbuf *buf, int len)
-{
-       if (buf->p + len > buf->ep) {
-               if (buf->p < buf->ep) {
-                       P9_EPRINTK(KERN_ERR,
-                               "buffer overflow: want %d has %d\n", len,
-                               (int)(buf->ep - buf->p));
-                       dump_stack();
-                       buf->p = buf->ep + 1;
-               }
-
-               return 0;
-       }
-
-       return 1;
-}
-
-static void *buf_alloc(struct cbuf *buf, int len)
-{
-       void *ret = NULL;
-
-       if (buf_check_size(buf, len)) {
-               ret = buf->p;
-               buf->p += len;
-       }
-
-       return ret;
-}
-
-static void buf_put_int8(struct cbuf *buf, u8 val)
-{
-       if (buf_check_size(buf, 1)) {
-               buf->p[0] = val;
-               buf->p++;
-       }
-}
-
-static void buf_put_int16(struct cbuf *buf, u16 val)
-{
-       if (buf_check_size(buf, 2)) {
-               *(__le16 *) buf->p = cpu_to_le16(val);
-               buf->p += 2;
-       }
-}
-
-static void buf_put_int32(struct cbuf *buf, u32 val)
-{
-       if (buf_check_size(buf, 4)) {
-               *(__le32 *)buf->p = cpu_to_le32(val);
-               buf->p += 4;
-       }
-}
-
-static void buf_put_int64(struct cbuf *buf, u64 val)
-{
-       if (buf_check_size(buf, 8)) {
-               *(__le64 *)buf->p = cpu_to_le64(val);
-               buf->p += 8;
-       }
-}
-
-static char *buf_put_stringn(struct cbuf *buf, const char *s, u16 slen)
-{
-       char *ret;
-
-       ret = NULL;
-       if (buf_check_size(buf, slen + 2)) {
-               buf_put_int16(buf, slen);
-               ret = buf->p;
-               memcpy(buf->p, s, slen);
-               buf->p += slen;
-       }
-
-       return ret;
-}
-
-static u8 buf_get_int8(struct cbuf *buf)
-{
-       u8 ret = 0;
-
-       if (buf_check_size(buf, 1)) {
-               ret = buf->p[0];
-               buf->p++;
-       }
-
-       return ret;
-}
-
-static u16 buf_get_int16(struct cbuf *buf)
-{
-       u16 ret = 0;
-
-       if (buf_check_size(buf, 2)) {
-               ret = le16_to_cpu(*(__le16 *)buf->p);
-               buf->p += 2;
-       }
-
-       return ret;
-}
-
-static u32 buf_get_int32(struct cbuf *buf)
-{
-       u32 ret = 0;
-
-       if (buf_check_size(buf, 4)) {
-               ret = le32_to_cpu(*(__le32 *)buf->p);
-               buf->p += 4;
-       }
-
-       return ret;
-}
-
-static u64 buf_get_int64(struct cbuf *buf)
-{
-       u64 ret = 0;
-
-       if (buf_check_size(buf, 8)) {
-               ret = le64_to_cpu(*(__le64 *)buf->p);
-               buf->p += 8;
-       }
-
-       return ret;
-}
-
-static void buf_get_str(struct cbuf *buf, struct p9_str *vstr)
-{
-       vstr->len = buf_get_int16(buf);
-       if (!buf_check_overflow(buf) && buf_check_size(buf, vstr->len)) {
-               vstr->str = buf->p;
-               buf->p += vstr->len;
-       } else {
-               vstr->len = 0;
-               vstr->str = NULL;
-       }
-}
-
-static void buf_get_qid(struct cbuf *bufp, struct p9_qid *qid)
-{
-       qid->type = buf_get_int8(bufp);
-       qid->version = buf_get_int32(bufp);
-       qid->path = buf_get_int64(bufp);
-}
-
-/**
- * p9_size_wstat - calculate the size of a variable length stat struct
- * @wstat: metadata (stat) structure
- * @dotu: non-zero if 9P2000.u
- *
- */
-
-static int p9_size_wstat(struct p9_wstat *wstat, int dotu)
-{
-       int size = 0;
-
-       if (wstat == NULL) {
-               P9_EPRINTK(KERN_ERR, "p9_size_stat: got a NULL stat pointer\n");
-               return 0;
-       }
-
-       size =                  /* 2 + *//* size[2] */
-           2 +                 /* type[2] */
-           4 +                 /* dev[4] */
-           1 +                 /* qid.type[1] */
-           4 +                 /* qid.vers[4] */
-           8 +                 /* qid.path[8] */
-           4 +                 /* mode[4] */
-           4 +                 /* atime[4] */
-           4 +                 /* mtime[4] */
-           8 +                 /* length[8] */
-           8;                  /* minimum sum of string lengths */
-
-       if (wstat->name)
-               size += strlen(wstat->name);
-       if (wstat->uid)
-               size += strlen(wstat->uid);
-       if (wstat->gid)
-               size += strlen(wstat->gid);
-       if (wstat->muid)
-               size += strlen(wstat->muid);
-
-       if (dotu) {
-               size += 4 +     /* n_uid[4] */
-                   4 +         /* n_gid[4] */
-                   4 +         /* n_muid[4] */
-                   2;          /* string length of extension[4] */
-               if (wstat->extension)
-                       size += strlen(wstat->extension);
-       }
-
-       return size;
-}
-
-/**
- * buf_get_stat - safely decode a recieved metadata (stat) structure
- * @bufp: buffer to deserialize
- * @stat: metadata (stat) structure
- * @dotu: non-zero if 9P2000.u
- *
- */
-
-static void
-buf_get_stat(struct cbuf *bufp, struct p9_stat *stat, int dotu)
-{
-       stat->size = buf_get_int16(bufp);
-       stat->type = buf_get_int16(bufp);
-       stat->dev = buf_get_int32(bufp);
-       stat->qid.type = buf_get_int8(bufp);
-       stat->qid.version = buf_get_int32(bufp);
-       stat->qid.path = buf_get_int64(bufp);
-       stat->mode = buf_get_int32(bufp);
-       stat->atime = buf_get_int32(bufp);
-       stat->mtime = buf_get_int32(bufp);
-       stat->length = buf_get_int64(bufp);
-       buf_get_str(bufp, &stat->name);
-       buf_get_str(bufp, &stat->uid);
-       buf_get_str(bufp, &stat->gid);
-       buf_get_str(bufp, &stat->muid);
-
-       if (dotu) {
-               buf_get_str(bufp, &stat->extension);
-               stat->n_uid = buf_get_int32(bufp);
-               stat->n_gid = buf_get_int32(bufp);
-               stat->n_muid = buf_get_int32(bufp);
-       }
-}
-
-/**
- * p9_deserialize_stat - decode a received metadata structure
- * @buf: buffer to deserialize
- * @buflen: length of received buffer
- * @stat: metadata structure to decode into
- * @dotu: non-zero if 9P2000.u
- *
- * Note: stat will point to the buf region.
- */
-
-int
-p9_deserialize_stat(void *buf, u32 buflen, struct p9_stat *stat,
-               int dotu)
-{
-       struct cbuf buffer;
-       struct cbuf *bufp = &buffer;
-       unsigned char *p;
-
-       buf_init(bufp, buf, buflen);
-       p = bufp->p;
-       buf_get_stat(bufp, stat, dotu);
-
-       if (buf_check_overflow(bufp))
-               return 0;
-       else
-               return bufp->p - p;
-}
-EXPORT_SYMBOL(p9_deserialize_stat);
-
-/**
- * deserialize_fcall - unmarshal a response
- * @buf: recieved buffer
- * @buflen: length of received buffer
- * @rcall: fcall structure to populate
- * @rcalllen: length of fcall structure to populate
- * @dotu: non-zero if 9P2000.u
- *
- */
-
-int
-p9_deserialize_fcall(void *buf, u32 buflen, struct p9_fcall *rcall,
-                      int dotu)
-{
-
-       struct cbuf buffer;
-       struct cbuf *bufp = &buffer;
-       int i = 0;
-
-       buf_init(bufp, buf, buflen);
-
-       rcall->size = buf_get_int32(bufp);
-       rcall->id = buf_get_int8(bufp);
-       rcall->tag = buf_get_int16(bufp);
-
-       P9_DPRINTK(P9_DEBUG_CONV, "size %d id %d tag %d\n", rcall->size,
-                                                       rcall->id, rcall->tag);
-
-       switch (rcall->id) {
-       default:
-               P9_EPRINTK(KERN_ERR, "unknown message type: %d\n", rcall->id);
-               return -EPROTO;
-       case P9_RVERSION:
-               rcall->params.rversion.msize = buf_get_int32(bufp);
-               buf_get_str(bufp, &rcall->params.rversion.version);
-               break;
-       case P9_RFLUSH:
-               break;
-       case P9_RATTACH:
-               rcall->params.rattach.qid.type = buf_get_int8(bufp);
-               rcall->params.rattach.qid.version = buf_get_int32(bufp);
-               rcall->params.rattach.qid.path = buf_get_int64(bufp);
-               break;
-       case P9_RWALK:
-               rcall->params.rwalk.nwqid = buf_get_int16(bufp);
-               if (rcall->params.rwalk.nwqid > P9_MAXWELEM) {
-                       P9_EPRINTK(KERN_ERR,
-                                       "Rwalk with more than %d qids: %d\n",
-                                       P9_MAXWELEM, rcall->params.rwalk.nwqid);
-                       return -EPROTO;
-               }
-
-               for (i = 0; i < rcall->params.rwalk.nwqid; i++)
-                       buf_get_qid(bufp, &rcall->params.rwalk.wqids[i]);
-               break;
-       case P9_ROPEN:
-               buf_get_qid(bufp, &rcall->params.ropen.qid);
-               rcall->params.ropen.iounit = buf_get_int32(bufp);
-               break;
-       case P9_RCREATE:
-               buf_get_qid(bufp, &rcall->params.rcreate.qid);
-               rcall->params.rcreate.iounit = buf_get_int32(bufp);
-               break;
-       case P9_RREAD:
-               rcall->params.rread.count = buf_get_int32(bufp);
-               rcall->params.rread.data = bufp->p;
-               buf_check_size(bufp, rcall->params.rread.count);
-               break;
-       case P9_RWRITE:
-               rcall->params.rwrite.count = buf_get_int32(bufp);
-               break;
-       case P9_RCLUNK:
-               break;
-       case P9_RREMOVE:
-               break;
-       case P9_RSTAT:
-               buf_get_int16(bufp);
-               buf_get_stat(bufp, &rcall->params.rstat.stat, dotu);
-               break;
-       case P9_RWSTAT:
-               break;
-       case P9_RERROR:
-               buf_get_str(bufp, &rcall->params.rerror.error);
-               if (dotu)
-                       rcall->params.rerror.errno = buf_get_int16(bufp);
-               break;
-       }
-
-       if (buf_check_overflow(bufp)) {
-               P9_DPRINTK(P9_DEBUG_ERROR, "buffer overflow\n");
-               return -EIO;
-       }
-
-       return bufp->p - bufp->sp;
-}
-EXPORT_SYMBOL(p9_deserialize_fcall);
-
-static inline void p9_put_int8(struct cbuf *bufp, u8 val, u8 * p)
-{
-       *p = val;
-       buf_put_int8(bufp, val);
-}
-
-static inline void p9_put_int16(struct cbuf *bufp, u16 val, u16 * p)
-{
-       *p = val;
-       buf_put_int16(bufp, val);
-}
-
-static inline void p9_put_int32(struct cbuf *bufp, u32 val, u32 * p)
-{
-       *p = val;
-       buf_put_int32(bufp, val);
-}
-
-static inline void p9_put_int64(struct cbuf *bufp, u64 val, u64 * p)
-{
-       *p = val;
-       buf_put_int64(bufp, val);
-}
-
-static void
-p9_put_str(struct cbuf *bufp, char *data, struct p9_str *str)
-{
-       int len;
-       char *s;
-
-       if (data)
-               len = strlen(data);
-       else
-               len = 0;
-
-       s = buf_put_stringn(bufp, data, len);
-       if (str) {
-               str->len = len;
-               str->str = s;
-       }
-}
-
-static int
-p9_put_data(struct cbuf *bufp, const char *data, int count,
-                  unsigned char **pdata)
-{
-       *pdata = buf_alloc(bufp, count);
-       if (*pdata == NULL)
-               return -ENOMEM;
-       memmove(*pdata, data, count);
-       return 0;
-}
-
-static int
-p9_put_user_data(struct cbuf *bufp, const char __user *data, int count,
-                  unsigned char **pdata)
-{
-       *pdata = buf_alloc(bufp, count);
-       if (*pdata == NULL)
-               return -ENOMEM;
-       return copy_from_user(*pdata, data, count);
-}
-
-static void
-p9_put_wstat(struct cbuf *bufp, struct p9_wstat *wstat,
-              struct p9_stat *stat, int statsz, int dotu)
-{
-       p9_put_int16(bufp, statsz, &stat->size);
-       p9_put_int16(bufp, wstat->type, &stat->type);
-       p9_put_int32(bufp, wstat->dev, &stat->dev);
-       p9_put_int8(bufp, wstat->qid.type, &stat->qid.type);
-       p9_put_int32(bufp, wstat->qid.version, &stat->qid.version);
-       p9_put_int64(bufp, wstat->qid.path, &stat->qid.path);
-       p9_put_int32(bufp, wstat->mode, &stat->mode);
-       p9_put_int32(bufp, wstat->atime, &stat->atime);
-       p9_put_int32(bufp, wstat->mtime, &stat->mtime);
-       p9_put_int64(bufp, wstat->length, &stat->length);
-
-       p9_put_str(bufp, wstat->name, &stat->name);
-       p9_put_str(bufp, wstat->uid, &stat->uid);
-       p9_put_str(bufp, wstat->gid, &stat->gid);
-       p9_put_str(bufp, wstat->muid, &stat->muid);
-
-       if (dotu) {
-               p9_put_str(bufp, wstat->extension, &stat->extension);
-               p9_put_int32(bufp, wstat->n_uid, &stat->n_uid);
-               p9_put_int32(bufp, wstat->n_gid, &stat->n_gid);
-               p9_put_int32(bufp, wstat->n_muid, &stat->n_muid);
-       }
-}
-
-static struct p9_fcall *
-p9_create_common(struct cbuf *bufp, u32 size, u8 id)
-{
-       struct p9_fcall *fc;
-
-       size += 4 + 1 + 2;      /* size[4] id[1] tag[2] */
-       fc = kmalloc(sizeof(struct p9_fcall) + size, GFP_KERNEL);
-       if (!fc)
-               return ERR_PTR(-ENOMEM);
-
-       fc->sdata = (char *)fc + sizeof(*fc);
-
-       buf_init(bufp, (char *)fc->sdata, size);
-       p9_put_int32(bufp, size, &fc->size);
-       p9_put_int8(bufp, id, &fc->id);
-       p9_put_int16(bufp, P9_NOTAG, &fc->tag);
-
-       return fc;
-}
-
-/**
- * p9_set_tag - set the tag field of an &p9_fcall structure
- * @fc: fcall structure to set tag within
- * @tag: tag id to set
- */
-
-void p9_set_tag(struct p9_fcall *fc, u16 tag)
-{
-       fc->tag = tag;
-       *(__le16 *) (fc->sdata + 5) = cpu_to_le16(tag);
-}
-EXPORT_SYMBOL(p9_set_tag);
-
-/**
- * p9_create_tversion - allocates and creates a T_VERSION request
- * @msize: requested maximum data size
- * @version: version string to negotiate
- *
- */
-struct p9_fcall *p9_create_tversion(u32 msize, char *version)
-{
-       int size;
-       struct p9_fcall *fc;
-       struct cbuf buffer;
-       struct cbuf *bufp = &buffer;
-
-       size = 4 + 2 + strlen(version); /* msize[4] version[s] */
-       fc = p9_create_common(bufp, size, P9_TVERSION);
-       if (IS_ERR(fc))
-               goto error;
-
-       p9_put_int32(bufp, msize, &fc->params.tversion.msize);
-       p9_put_str(bufp, version, &fc->params.tversion.version);
-
-       if (buf_check_overflow(bufp)) {
-               kfree(fc);
-               fc = ERR_PTR(-ENOMEM);
-       }
-error:
-       return fc;
-}
-EXPORT_SYMBOL(p9_create_tversion);
-
-/**
- * p9_create_tauth - allocates and creates a T_AUTH request
- * @afid: handle to use for authentication protocol
- * @uname: user name attempting to authenticate
- * @aname: mount specifier for remote server
- * @n_uname: numeric id for user attempting to authneticate
- * @dotu: 9P2000.u extension flag
- *
- */
-
-struct p9_fcall *p9_create_tauth(u32 afid, char *uname, char *aname,
-       u32 n_uname, int dotu)
-{
-       int size;
-       struct p9_fcall *fc;
-       struct cbuf buffer;
-       struct cbuf *bufp = &buffer;
-
-       /* afid[4] uname[s] aname[s] */
-       size = 4 + 2 + 2;
-       if (uname)
-               size += strlen(uname);
-
-       if (aname)
-               size += strlen(aname);
-
-       if (dotu)
-               size += 4;      /* n_uname */
-
-       fc = p9_create_common(bufp, size, P9_TAUTH);
-       if (IS_ERR(fc))
-               goto error;
-
-       p9_put_int32(bufp, afid, &fc->params.tauth.afid);
-       p9_put_str(bufp, uname, &fc->params.tauth.uname);
-       p9_put_str(bufp, aname, &fc->params.tauth.aname);
-       if (dotu)
-               p9_put_int32(bufp, n_uname, &fc->params.tauth.n_uname);
-
-       if (buf_check_overflow(bufp)) {
-               kfree(fc);
-               fc = ERR_PTR(-ENOMEM);
-       }
-error:
-       return fc;
-}
-EXPORT_SYMBOL(p9_create_tauth);
-
-/**
- * p9_create_tattach - allocates and creates a T_ATTACH request
- * @fid: handle to use for the new mount point
- * @afid: handle to use for authentication protocol
- * @uname: user name attempting to attach
- * @aname: mount specifier for remote server
- * @n_uname: numeric id for user attempting to attach
- * @n_uname: numeric id for user attempting to attach
- * @dotu: 9P2000.u extension flag
- *
- */
-
-struct p9_fcall *
-p9_create_tattach(u32 fid, u32 afid, char *uname, char *aname,
-       u32 n_uname, int dotu)
-{
-       int size;
-       struct p9_fcall *fc;
-       struct cbuf buffer;
-       struct cbuf *bufp = &buffer;
-
-       /* fid[4] afid[4] uname[s] aname[s] */
-       size = 4 + 4 + 2 + 2;
-       if (uname)
-               size += strlen(uname);
-
-       if (aname)
-               size += strlen(aname);
-
-       if (dotu)
-               size += 4;      /* n_uname */
-
-       fc = p9_create_common(bufp, size, P9_TATTACH);
-       if (IS_ERR(fc))
-               goto error;
-
-       p9_put_int32(bufp, fid, &fc->params.tattach.fid);
-       p9_put_int32(bufp, afid, &fc->params.tattach.afid);
-       p9_put_str(bufp, uname, &fc->params.tattach.uname);
-       p9_put_str(bufp, aname, &fc->params.tattach.aname);
-       if (dotu)
-               p9_put_int32(bufp, n_uname, &fc->params.tattach.n_uname);
-
-error:
-       return fc;
-}
-EXPORT_SYMBOL(p9_create_tattach);
-
-/**
- * p9_create_tflush - allocates and creates a T_FLUSH request
- * @oldtag: tag id for the transaction we are attempting to cancel
- *
- */
-
-struct p9_fcall *p9_create_tflush(u16 oldtag)
-{
-       int size;
-       struct p9_fcall *fc;
-       struct cbuf buffer;
-       struct cbuf *bufp = &buffer;
-
-       size = 2;               /* oldtag[2] */
-       fc = p9_create_common(bufp, size, P9_TFLUSH);
-       if (IS_ERR(fc))
-               goto error;
-
-       p9_put_int16(bufp, oldtag, &fc->params.tflush.oldtag);
-
-       if (buf_check_overflow(bufp)) {
-               kfree(fc);
-               fc = ERR_PTR(-ENOMEM);
-       }
-error:
-       return fc;
-}
-EXPORT_SYMBOL(p9_create_tflush);
-
-/**
- * p9_create_twalk - allocates and creates a T_FLUSH request
- * @fid: handle we are traversing from
- * @newfid: a new handle for this transaction
- * @nwname: number of path elements to traverse
- * @wnames: array of path elements
- *
- */
-
-struct p9_fcall *p9_create_twalk(u32 fid, u32 newfid, u16 nwname,
-                                    char **wnames)
-{
-       int i, size;
-       struct p9_fcall *fc;
-       struct cbuf buffer;
-       struct cbuf *bufp = &buffer;
-
-       if (nwname > P9_MAXWELEM) {
-               P9_DPRINTK(P9_DEBUG_ERROR, "nwname > %d\n", P9_MAXWELEM);
-               return NULL;
-       }
-
-       size = 4 + 4 + 2;       /* fid[4] newfid[4] nwname[2] ... */
-       for (i = 0; i < nwname; i++) {
-               size += 2 + strlen(wnames[i]);  /* wname[s] */
-       }
-
-       fc = p9_create_common(bufp, size, P9_TWALK);
-       if (IS_ERR(fc))
-               goto error;
-
-       p9_put_int32(bufp, fid, &fc->params.twalk.fid);
-       p9_put_int32(bufp, newfid, &fc->params.twalk.newfid);
-       p9_put_int16(bufp, nwname, &fc->params.twalk.nwname);
-       for (i = 0; i < nwname; i++) {
-               p9_put_str(bufp, wnames[i], &fc->params.twalk.wnames[i]);
-       }
-
-       if (buf_check_overflow(bufp)) {
-               kfree(fc);
-               fc = ERR_PTR(-ENOMEM);
-       }
-error:
-       return fc;
-}
-EXPORT_SYMBOL(p9_create_twalk);
-
-/**
- * p9_create_topen - allocates and creates a T_OPEN request
- * @fid: handle we are trying to open
- * @mode: what mode we are trying to open the file in
- *
- */
-
-struct p9_fcall *p9_create_topen(u32 fid, u8 mode)
-{
-       int size;
-       struct p9_fcall *fc;
-       struct cbuf buffer;
-       struct cbuf *bufp = &buffer;
-
-       size = 4 + 1;           /* fid[4] mode[1] */
-       fc = p9_create_common(bufp, size, P9_TOPEN);
-       if (IS_ERR(fc))
-               goto error;
-
-       p9_put_int32(bufp, fid, &fc->params.topen.fid);
-       p9_put_int8(bufp, mode, &fc->params.topen.mode);
-
-       if (buf_check_overflow(bufp)) {
-               kfree(fc);
-               fc = ERR_PTR(-ENOMEM);
-       }
-error:
-       return fc;
-}
-EXPORT_SYMBOL(p9_create_topen);
-
-/**
- * p9_create_tcreate - allocates and creates a T_CREATE request
- * @fid: handle of directory we are trying to create in
- * @name: name of the file we are trying to create
- * @perm: permissions for the file we are trying to create
- * @mode: what mode we are trying to open the file in
- * @extension: 9p2000.u extension string (for special files)
- * @dotu: 9p2000.u enabled flag
- *
- * Note: Plan 9 create semantics include opening the resulting file
- * which is why mode is included.
- */
-
-struct p9_fcall *p9_create_tcreate(u32 fid, char *name, u32 perm, u8 mode,
-       char *extension, int dotu)
-{
-       int size;
-       struct p9_fcall *fc;
-       struct cbuf buffer;
-       struct cbuf *bufp = &buffer;
-
-       /* fid[4] name[s] perm[4] mode[1] */
-       size = 4 + 2 + strlen(name) + 4 + 1;
-       if (dotu) {
-               size += 2 +                     /* extension[s] */
-                   (extension == NULL ? 0 : strlen(extension));
-       }
-
-       fc = p9_create_common(bufp, size, P9_TCREATE);
-       if (IS_ERR(fc))
-               goto error;
-
-       p9_put_int32(bufp, fid, &fc->params.tcreate.fid);
-       p9_put_str(bufp, name, &fc->params.tcreate.name);
-       p9_put_int32(bufp, perm, &fc->params.tcreate.perm);
-       p9_put_int8(bufp, mode, &fc->params.tcreate.mode);
-       if (dotu)
-               p9_put_str(bufp, extension, &fc->params.tcreate.extension);
-
-       if (buf_check_overflow(bufp)) {
-               kfree(fc);
-               fc = ERR_PTR(-ENOMEM);
-       }
-error:
-       return fc;
-}
-EXPORT_SYMBOL(p9_create_tcreate);
-
-/**
- * p9_create_tread - allocates and creates a T_READ request
- * @fid: handle of the file we are trying to read
- * @offset: offset to start reading from
- * @count: how many bytes to read
- */
-
-struct p9_fcall *p9_create_tread(u32 fid, u64 offset, u32 count)
-{
-       int size;
-       struct p9_fcall *fc;
-       struct cbuf buffer;
-       struct cbuf *bufp = &buffer;
-
-       size = 4 + 8 + 4;       /* fid[4] offset[8] count[4] */
-       fc = p9_create_common(bufp, size, P9_TREAD);
-       if (IS_ERR(fc))
-               goto error;
-
-       p9_put_int32(bufp, fid, &fc->params.tread.fid);
-       p9_put_int64(bufp, offset, &fc->params.tread.offset);
-       p9_put_int32(bufp, count, &fc->params.tread.count);
-
-       if (buf_check_overflow(bufp)) {
-               kfree(fc);
-               fc = ERR_PTR(-ENOMEM);
-       }
-error:
-       return fc;
-}
-EXPORT_SYMBOL(p9_create_tread);
-
-/**
- * p9_create_twrite - allocates and creates a T_WRITE request from the kernel
- * @fid: handle of the file we are trying to write
- * @offset: offset to start writing at
- * @count: how many bytes to write
- * @data: data to write
- *
- * This function will create a requst with data buffers from the kernel
- * such as the page cache.
- */
-
-struct p9_fcall *p9_create_twrite(u32 fid, u64 offset, u32 count,
-                                     const char *data)
-{
-       int size, err;
-       struct p9_fcall *fc;
-       struct cbuf buffer;
-       struct cbuf *bufp = &buffer;
-
-       /* fid[4] offset[8] count[4] data[count] */
-       size = 4 + 8 + 4 + count;
-       fc = p9_create_common(bufp, size, P9_TWRITE);
-       if (IS_ERR(fc))
-               goto error;
-
-       p9_put_int32(bufp, fid, &fc->params.twrite.fid);
-       p9_put_int64(bufp, offset, &fc->params.twrite.offset);
-       p9_put_int32(bufp, count, &fc->params.twrite.count);
-       err = p9_put_data(bufp, data, count, &fc->params.twrite.data);
-       if (err) {
-               kfree(fc);
-               fc = ERR_PTR(err);
-               goto error;
-       }
-
-       if (buf_check_overflow(bufp)) {
-               kfree(fc);
-               fc = ERR_PTR(-ENOMEM);
-       }
-error:
-       return fc;
-}
-EXPORT_SYMBOL(p9_create_twrite);
-
-/**
- * p9_create_twrite_u - allocates and creates a T_WRITE request from userspace
- * @fid: handle of the file we are trying to write
- * @offset: offset to start writing at
- * @count: how many bytes to write
- * @data: data to write
- *
- * This function will create a request with data buffers from userspace
- */
-
-struct p9_fcall *p9_create_twrite_u(u32 fid, u64 offset, u32 count,
-                                     const char __user *data)
-{
-       int size, err;
-       struct p9_fcall *fc;
-       struct cbuf buffer;
-       struct cbuf *bufp = &buffer;
-
-       /* fid[4] offset[8] count[4] data[count] */
-       size = 4 + 8 + 4 + count;
-       fc = p9_create_common(bufp, size, P9_TWRITE);
-       if (IS_ERR(fc))
-               goto error;
-
-       p9_put_int32(bufp, fid, &fc->params.twrite.fid);
-       p9_put_int64(bufp, offset, &fc->params.twrite.offset);
-       p9_put_int32(bufp, count, &fc->params.twrite.count);
-       err = p9_put_user_data(bufp, data, count, &fc->params.twrite.data);
-       if (err) {
-               kfree(fc);
-               fc = ERR_PTR(err);
-               goto error;
-       }
-
-       if (buf_check_overflow(bufp)) {
-               kfree(fc);
-               fc = ERR_PTR(-ENOMEM);
-       }
-error:
-       return fc;
-}
-EXPORT_SYMBOL(p9_create_twrite_u);
-
-/**
- * p9_create_tclunk - allocate a request to forget about a file handle
- * @fid: handle of the file we closing or forgetting about
- *
- * clunk is used both to close open files and to discard transient handles
- * which may be created during meta-data operations and hierarchy traversal.
- */
-
-struct p9_fcall *p9_create_tclunk(u32 fid)
-{
-       int size;
-       struct p9_fcall *fc;
-       struct cbuf buffer;
-       struct cbuf *bufp = &buffer;
-
-       size = 4;               /* fid[4] */
-       fc = p9_create_common(bufp, size, P9_TCLUNK);
-       if (IS_ERR(fc))
-               goto error;
-
-       p9_put_int32(bufp, fid, &fc->params.tclunk.fid);
-
-       if (buf_check_overflow(bufp)) {
-               kfree(fc);
-               fc = ERR_PTR(-ENOMEM);
-       }
-error:
-       return fc;
-}
-EXPORT_SYMBOL(p9_create_tclunk);
-
-/**
- * p9_create_tremove - allocate and create a request to remove a file
- * @fid: handle of the file or directory we are removing
- *
- */
-
-struct p9_fcall *p9_create_tremove(u32 fid)
-{
-       int size;
-       struct p9_fcall *fc;
-       struct cbuf buffer;
-       struct cbuf *bufp = &buffer;
-
-       size = 4;               /* fid[4] */
-       fc = p9_create_common(bufp, size, P9_TREMOVE);
-       if (IS_ERR(fc))
-               goto error;
-
-       p9_put_int32(bufp, fid, &fc->params.tremove.fid);
-
-       if (buf_check_overflow(bufp)) {
-               kfree(fc);
-               fc = ERR_PTR(-ENOMEM);
-       }
-error:
-       return fc;
-}
-EXPORT_SYMBOL(p9_create_tremove);
-
-/**
- * p9_create_tstat - allocate and populate a request for attributes
- * @fid: handle of the file or directory we are trying to get the attributes of
- *
- */
-
-struct p9_fcall *p9_create_tstat(u32 fid)
-{
-       int size;
-       struct p9_fcall *fc;
-       struct cbuf buffer;
-       struct cbuf *bufp = &buffer;
-
-       size = 4;               /* fid[4] */
-       fc = p9_create_common(bufp, size, P9_TSTAT);
-       if (IS_ERR(fc))
-               goto error;
-
-       p9_put_int32(bufp, fid, &fc->params.tstat.fid);
-
-       if (buf_check_overflow(bufp)) {
-               kfree(fc);
-               fc = ERR_PTR(-ENOMEM);
-       }
-error:
-       return fc;
-}
-EXPORT_SYMBOL(p9_create_tstat);
-
-/**
- * p9_create_tstat - allocate and populate a request to change attributes
- * @fid: handle of the file or directory we are trying to change
- * @wstat: &p9_stat structure with attributes we wish to set
- * @dotu: 9p2000.u enabled flag
- *
- */
-
-struct p9_fcall *p9_create_twstat(u32 fid, struct p9_wstat *wstat,
-                                     int dotu)
-{
-       int size, statsz;
-       struct p9_fcall *fc;
-       struct cbuf buffer;
-       struct cbuf *bufp = &buffer;
-
-       statsz = p9_size_wstat(wstat, dotu);
-       size = 4 + 2 + 2 + statsz;      /* fid[4] stat[n] */
-       fc = p9_create_common(bufp, size, P9_TWSTAT);
-       if (IS_ERR(fc))
-               goto error;
-
-       p9_put_int32(bufp, fid, &fc->params.twstat.fid);
-       buf_put_int16(bufp, statsz + 2);
-       p9_put_wstat(bufp, wstat, &fc->params.twstat.stat, statsz, dotu);
-
-       if (buf_check_overflow(bufp)) {
-               kfree(fc);
-               fc = ERR_PTR(-ENOMEM);
-       }
-error:
-       return fc;
-}
-EXPORT_SYMBOL(p9_create_twstat);
-
diff --git a/net/9p/fcprint.c b/net/9p/fcprint.c
deleted file mode 100644 (file)
index 53dd8e2..0000000
+++ /dev/null
@@ -1,366 +0,0 @@
-/*
- *  net/9p/fcprint.c
- *
- *  Print 9P call.
- *
- *  Copyright (C) 2005 by Latchesar Ionkov <lucho@ionkov.net>
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License version 2
- *  as published by the Free Software Foundation.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to:
- *  Free Software Foundation
- *  51 Franklin Street, Fifth Floor
- *  Boston, MA  02111-1301  USA
- *
- */
-#include <linux/module.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/idr.h>
-#include <net/9p/9p.h>
-
-#ifdef CONFIG_NET_9P_DEBUG
-
-static int
-p9_printqid(char *buf, int buflen, struct p9_qid *q)
-{
-       int n;
-       char b[10];
-
-       n = 0;
-       if (q->type & P9_QTDIR)
-               b[n++] = 'd';
-       if (q->type & P9_QTAPPEND)
-               b[n++] = 'a';
-       if (q->type & P9_QTAUTH)
-               b[n++] = 'A';
-       if (q->type & P9_QTEXCL)
-               b[n++] = 'l';
-       if (q->type & P9_QTTMP)
-               b[n++] = 't';
-       if (q->type & P9_QTSYMLINK)
-               b[n++] = 'L';
-       b[n] = '\0';
-
-       return scnprintf(buf, buflen, "(%.16llx %x %s)",
-                                       (long long int) q->path, q->version, b);
-}
-
-static int
-p9_printperm(char *buf, int buflen, int perm)
-{
-       int n;
-       char b[15];
-
-       n = 0;
-       if (perm & P9_DMDIR)
-               b[n++] = 'd';
-       if (perm & P9_DMAPPEND)
-               b[n++] = 'a';
-       if (perm & P9_DMAUTH)
-               b[n++] = 'A';
-       if (perm & P9_DMEXCL)
-               b[n++] = 'l';
-       if (perm & P9_DMTMP)
-               b[n++] = 't';
-       if (perm & P9_DMDEVICE)
-               b[n++] = 'D';
-       if (perm & P9_DMSOCKET)
-               b[n++] = 'S';
-       if (perm & P9_DMNAMEDPIPE)
-               b[n++] = 'P';
-       if (perm & P9_DMSYMLINK)
-               b[n++] = 'L';
-       b[n] = '\0';
-
-       return scnprintf(buf, buflen, "%s%03o", b, perm&077);
-}
-
-static int
-p9_printstat(char *buf, int buflen, struct p9_stat *st, int extended)
-{
-       int n;
-
-       n = scnprintf(buf, buflen, "'%.*s' '%.*s'", st->name.len,
-               st->name.str, st->uid.len, st->uid.str);
-       if (extended)
-               n += scnprintf(buf+n, buflen-n, "(%d)", st->n_uid);
-
-       n += scnprintf(buf+n, buflen-n, " '%.*s'", st->gid.len, st->gid.str);
-       if (extended)
-               n += scnprintf(buf+n, buflen-n, "(%d)", st->n_gid);
-
-       n += scnprintf(buf+n, buflen-n, " '%.*s'", st->muid.len, st->muid.str);
-       if (extended)
-               n += scnprintf(buf+n, buflen-n, "(%d)", st->n_muid);
-
-       n += scnprintf(buf+n, buflen-n, " q ");
-       n += p9_printqid(buf+n, buflen-n, &st->qid);
-       n += scnprintf(buf+n, buflen-n, " m ");
-       n += p9_printperm(buf+n, buflen-n, st->mode);
-       n += scnprintf(buf+n, buflen-n, " at %d mt %d l %lld",
-               st->atime, st->mtime, (long long int) st->length);
-
-       if (extended)
-               n += scnprintf(buf+n, buflen-n, " ext '%.*s'",
-                       st->extension.len, st->extension.str);
-
-       return n;
-}
-
-static int
-p9_dumpdata(char *buf, int buflen, u8 *data, int datalen)
-{
-       int i, n;
-
-       i = n = 0;
-       while (i < datalen) {
-               n += scnprintf(buf + n, buflen - n, "%02x", data[i]);
-               if (i%4 == 3)
-                       n += scnprintf(buf + n, buflen - n, " ");
-               if (i%32 == 31)
-                       n += scnprintf(buf + n, buflen - n, "\n");
-
-               i++;
-       }
-       n += scnprintf(buf + n, buflen - n, "\n");
-
-       return n;
-}
-
-static int
-p9_printdata(char *buf, int buflen, u8 *data, int datalen)
-{
-       return p9_dumpdata(buf, buflen, data, datalen < 16?datalen:16);
-}
-
-/**
- * p9_printfcall - decode and print a protocol structure into a buffer
- * @buf: buffer to deposit decoded structure into
- * @buflen: available space in buffer
- * @fc: protocol rpc structure of type &p9_fcall
- * @extended: whether or not session is operating with extended protocol
- */
-
-int
-p9_printfcall(char *buf, int buflen, struct p9_fcall *fc, int extended)
-{
-       int i, ret, type, tag;
-
-       if (!fc)
-               return scnprintf(buf, buflen, "<NULL>");
-
-       type = fc->id;
-       tag = fc->tag;
-
-       ret = 0;
-       switch (type) {
-       case P9_TVERSION:
-               ret += scnprintf(buf+ret, buflen-ret,
-                               "Tversion tag %u msize %u version '%.*s'", tag,
-                               fc->params.tversion.msize,
-                               fc->params.tversion.version.len,
-                               fc->params.tversion.version.str);
-               break;
-
-       case P9_RVERSION:
-               ret += scnprintf(buf+ret, buflen-ret,
-                               "Rversion tag %u msize %u version '%.*s'", tag,
-                               fc->params.rversion.msize,
-                               fc->params.rversion.version.len,
-                               fc->params.rversion.version.str);
-               break;
-
-       case P9_TAUTH:
-               ret += scnprintf(buf+ret, buflen-ret,
-                       "Tauth tag %u afid %d uname '%.*s' aname '%.*s'", tag,
-                       fc->params.tauth.afid, fc->params.tauth.uname.len,
-                       fc->params.tauth.uname.str, fc->params.tauth.aname.len,
-                       fc->params.tauth.aname.str);
-               break;
-
-       case P9_RAUTH:
-               ret += scnprintf(buf+ret, buflen-ret, "Rauth tag %u qid ", tag);
-               p9_printqid(buf+ret, buflen-ret, &fc->params.rauth.qid);
-               break;
-
-       case P9_TATTACH:
-               ret += scnprintf(buf+ret, buflen-ret,
-                "Tattach tag %u fid %d afid %d uname '%.*s' aname '%.*s'", tag,
-                fc->params.tattach.fid, fc->params.tattach.afid,
-                fc->params.tattach.uname.len, fc->params.tattach.uname.str,
-                fc->params.tattach.aname.len, fc->params.tattach.aname.str);
-               break;
-
-       case P9_RATTACH:
-               ret += scnprintf(buf+ret, buflen-ret, "Rattach tag %u qid ",
-                                                                       tag);
-               p9_printqid(buf+ret, buflen-ret, &fc->params.rattach.qid);
-               break;
-
-       case P9_RERROR:
-               ret += scnprintf(buf+ret, buflen-ret,
-                               "Rerror tag %u ename '%.*s'", tag,
-                               fc->params.rerror.error.len,
-                               fc->params.rerror.error.str);
-               if (extended)
-                       ret += scnprintf(buf+ret, buflen-ret, " ecode %d\n",
-                               fc->params.rerror.errno);
-               break;
-
-       case P9_TFLUSH:
-               ret += scnprintf(buf+ret, buflen-ret, "Tflush tag %u oldtag %u",
-                       tag, fc->params.tflush.oldtag);
-               break;
-
-       case P9_RFLUSH:
-               ret += scnprintf(buf+ret, buflen-ret, "Rflush tag %u", tag);
-               break;
-
-       case P9_TWALK:
-               ret += scnprintf(buf+ret, buflen-ret,
-                       "Twalk tag %u fid %d newfid %d nwname %d", tag,
-                       fc->params.twalk.fid, fc->params.twalk.newfid,
-                       fc->params.twalk.nwname);
-               for (i = 0; i < fc->params.twalk.nwname; i++)
-                       ret += scnprintf(buf+ret, buflen-ret, " '%.*s'",
-                               fc->params.twalk.wnames[i].len,
-                               fc->params.twalk.wnames[i].str);
-               break;
-
-       case P9_RWALK:
-               ret += scnprintf(buf+ret, buflen-ret, "Rwalk tag %u nwqid %d",
-                       tag, fc->params.rwalk.nwqid);
-               for (i = 0; i < fc->params.rwalk.nwqid; i++)
-                       ret += p9_printqid(buf+ret, buflen-ret,
-                               &fc->params.rwalk.wqids[i]);
-               break;
-
-       case P9_TOPEN:
-               ret += scnprintf(buf+ret, buflen-ret,
-                       "Topen tag %u fid %d mode %d", tag,
-                       fc->params.topen.fid, fc->params.topen.mode);
-               break;
-
-       case P9_ROPEN:
-               ret += scnprintf(buf+ret, buflen-ret, "Ropen tag %u", tag);
-               ret += p9_printqid(buf+ret, buflen-ret, &fc->params.ropen.qid);
-               ret += scnprintf(buf+ret, buflen-ret, " iounit %d",
-                       fc->params.ropen.iounit);
-               break;
-
-       case P9_TCREATE:
-               ret += scnprintf(buf+ret, buflen-ret,
-                       "Tcreate tag %u fid %d name '%.*s' perm ", tag,
-                       fc->params.tcreate.fid, fc->params.tcreate.name.len,
-                       fc->params.tcreate.name.str);
-
-               ret += p9_printperm(buf+ret, buflen-ret,
-                                               fc->params.tcreate.perm);
-               ret += scnprintf(buf+ret, buflen-ret, " mode %d",
-                       fc->params.tcreate.mode);
-               break;
-
-       case P9_RCREATE:
-               ret += scnprintf(buf+ret, buflen-ret, "Rcreate tag %u", tag);
-               ret += p9_printqid(buf+ret, buflen-ret,
-                                               &fc->params.rcreate.qid);
-               ret += scnprintf(buf+ret, buflen-ret, " iounit %d",
-                       fc->params.rcreate.iounit);
-               break;
-
-       case P9_TREAD:
-               ret += scnprintf(buf+ret, buflen-ret,
-                       "Tread tag %u fid %d offset %lld count %u", tag,
-                       fc->params.tread.fid,
-                       (long long int) fc->params.tread.offset,
-                       fc->params.tread.count);
-               break;
-
-       case P9_RREAD:
-               ret += scnprintf(buf+ret, buflen-ret,
-                       "Rread tag %u count %u data ", tag,
-                       fc->params.rread.count);
-               ret += p9_printdata(buf+ret, buflen-ret, fc->params.rread.data,
-                       fc->params.rread.count);
-               break;
-
-       case P9_TWRITE:
-               ret += scnprintf(buf+ret, buflen-ret,
-                       "Twrite tag %u fid %d offset %lld count %u data ",
-                       tag, fc->params.twrite.fid,
-                       (long long int) fc->params.twrite.offset,
-                       fc->params.twrite.count);
-               ret += p9_printdata(buf+ret, buflen-ret, fc->params.twrite.data,
-                       fc->params.twrite.count);
-               break;
-
-       case P9_RWRITE:
-               ret += scnprintf(buf+ret, buflen-ret, "Rwrite tag %u count %u",
-                       tag, fc->params.rwrite.count);
-               break;
-
-       case P9_TCLUNK:
-               ret += scnprintf(buf+ret, buflen-ret, "Tclunk tag %u fid %d",
-                       tag, fc->params.tclunk.fid);
-               break;
-
-       case P9_RCLUNK:
-               ret += scnprintf(buf+ret, buflen-ret, "Rclunk tag %u", tag);
-               break;
-
-       case P9_TREMOVE:
-               ret += scnprintf(buf+ret, buflen-ret, "Tremove tag %u fid %d",
-                       tag, fc->params.tremove.fid);
-               break;
-
-       case P9_RREMOVE:
-               ret += scnprintf(buf+ret, buflen-ret, "Rremove tag %u", tag);
-               break;
-
-       case P9_TSTAT:
-               ret += scnprintf(buf+ret, buflen-ret, "Tstat tag %u fid %d",
-                       tag, fc->params.tstat.fid);
-               break;
-
-       case P9_RSTAT:
-               ret += scnprintf(buf+ret, buflen-ret, "Rstat tag %u ", tag);
-               ret += p9_printstat(buf+ret, buflen-ret, &fc->params.rstat.stat,
-                       extended);
-               break;
-
-       case P9_TWSTAT:
-               ret += scnprintf(buf+ret, buflen-ret, "Twstat tag %u fid %d ",
-                       tag, fc->params.twstat.fid);
-               ret += p9_printstat(buf+ret, buflen-ret,
-                                       &fc->params.twstat.stat, extended);
-               break;
-
-       case P9_RWSTAT:
-               ret += scnprintf(buf+ret, buflen-ret, "Rwstat tag %u", tag);
-               break;
-
-       default:
-               ret += scnprintf(buf+ret, buflen-ret, "unknown type %d", type);
-               break;
-       }
-
-       return ret;
-}
-#else
-int
-p9_printfcall(char *buf, int buflen, struct p9_fcall *fc, int extended)
-{
-       return 0;
-}
-#endif /* CONFIG_NET_9P_DEBUG */
-EXPORT_SYMBOL(p9_printfcall);
-
index 1084feb24cb0a3a9ef2409071c1ba3c4c06695aa..cf8a4128cd5c03560bfb93971779965176bea7dd 100644 (file)
@@ -29,6 +29,7 @@
 #include <net/9p/9p.h>
 #include <linux/fs.h>
 #include <linux/parser.h>
+#include <net/9p/client.h>
 #include <net/9p/transport.h>
 #include <linux/list.h>
 #include <linux/spinlock.h>
diff --git a/net/9p/protocol.c b/net/9p/protocol.c
new file mode 100644 (file)
index 0000000..29be524
--- /dev/null
@@ -0,0 +1,558 @@
+/*
+ * net/9p/protocol.c
+ *
+ * 9P Protocol Support Code
+ *
+ *  Copyright (C) 2008 by Eric Van Hensbergen <ericvh@gmail.com>
+ *
+ *  Base on code from Anthony Liguori <aliguori@us.ibm.com>
+ *  Copyright (C) 2008 by IBM, Corp.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2
+ *  as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to:
+ *  Free Software Foundation
+ *  51 Franklin Street, Fifth Floor
+ *  Boston, MA  02111-1301  USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <net/9p/9p.h>
+#include <net/9p/client.h>
+#include "protocol.h"
+
+#ifndef MIN
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#endif
+
+#ifndef MAX
+#define MAX(a, b) (((a) > (b)) ? (a) : (b))
+#endif
+
+#ifndef offset_of
+#define offset_of(type, memb) \
+       ((unsigned long)(&((type *)0)->memb))
+#endif
+#ifndef container_of
+#define container_of(obj, type, memb) \
+       ((type *)(((char *)obj) - offset_of(type, memb)))
+#endif
+
+static int
+p9pdu_writef(struct p9_fcall *pdu, int optional, const char *fmt, ...);
+
+void
+p9pdu_dump(int way, struct p9_fcall *pdu)
+{
+       int i, n;
+       u8 *data = pdu->sdata;
+       int datalen = pdu->size;
+       char buf[255];
+       int buflen = 255;
+
+       i = n = 0;
+       if (datalen > (buflen-16))
+               datalen = buflen-16;
+       while (i < datalen) {
+               n += scnprintf(buf + n, buflen - n, "%02x ", data[i]);
+               if (i%4 == 3)
+                       n += scnprintf(buf + n, buflen - n, " ");
+               if (i%32 == 31)
+                       n += scnprintf(buf + n, buflen - n, "\n");
+
+               i++;
+       }
+       n += scnprintf(buf + n, buflen - n, "\n");
+
+       if (way)
+               P9_DPRINTK(P9_DEBUG_PKT, "[[[(%d) %s\n", datalen, buf);
+       else
+               P9_DPRINTK(P9_DEBUG_PKT, "]]](%d) %s\n", datalen, buf);
+}
+EXPORT_SYMBOL(p9pdu_dump);
+
+void p9stat_free(struct p9_wstat *stbuf)
+{
+       kfree(stbuf->name);
+       kfree(stbuf->uid);
+       kfree(stbuf->gid);
+       kfree(stbuf->muid);
+       kfree(stbuf->extension);
+}
+EXPORT_SYMBOL(p9stat_free);
+
+static size_t pdu_read(struct p9_fcall *pdu, void *data, size_t size)
+{
+       size_t len = MIN(pdu->size - pdu->offset, size);
+       memcpy(data, &pdu->sdata[pdu->offset], len);
+       pdu->offset += len;
+       return size - len;
+}
+
+static size_t pdu_write(struct p9_fcall *pdu, const void *data, size_t size)
+{
+       size_t len = MIN(pdu->capacity - pdu->size, size);
+       memcpy(&pdu->sdata[pdu->size], data, len);
+       pdu->size += len;
+       return size - len;
+}
+
+static size_t
+pdu_write_u(struct p9_fcall *pdu, const char __user *udata, size_t size)
+{
+       size_t len = MIN(pdu->capacity - pdu->size, size);
+       int err = copy_from_user(&pdu->sdata[pdu->size], udata, len);
+       if (err)
+               printk(KERN_WARNING "pdu_write_u returning: %d\n", err);
+
+       pdu->size += len;
+       return size - len;
+}
+
+/*
+       b - int8_t
+       w - int16_t
+       d - int32_t
+       q - int64_t
+       s - string
+       S - stat
+       Q - qid
+       D - data blob (int32_t size followed by void *, results are not freed)
+       T - array of strings (int16_t count, followed by strings)
+       R - array of qids (int16_t count, followed by qids)
+       ? - if optional = 1, continue parsing
+*/
+
+static int
+p9pdu_vreadf(struct p9_fcall *pdu, int optional, const char *fmt, va_list ap)
+{
+       const char *ptr;
+       int errcode = 0;
+
+       for (ptr = fmt; *ptr; ptr++) {
+               switch (*ptr) {
+               case 'b':{
+                               int8_t *val = va_arg(ap, int8_t *);
+                               if (pdu_read(pdu, val, sizeof(*val))) {
+                                       errcode = -EFAULT;
+                                       break;
+                               }
+                       }
+                       break;
+               case 'w':{
+                               int16_t *val = va_arg(ap, int16_t *);
+                               if (pdu_read(pdu, val, sizeof(*val))) {
+                                       errcode = -EFAULT;
+                                       break;
+                               }
+                               *val = cpu_to_le16(*val);
+                       }
+                       break;
+               case 'd':{
+                               int32_t *val = va_arg(ap, int32_t *);
+                               if (pdu_read(pdu, val, sizeof(*val))) {
+                                       errcode = -EFAULT;
+                                       break;
+                               }
+                               *val = cpu_to_le32(*val);
+                       }
+                       break;
+               case 'q':{
+                               int64_t *val = va_arg(ap, int64_t *);
+                               if (pdu_read(pdu, val, sizeof(*val))) {
+                                       errcode = -EFAULT;
+                                       break;
+                               }
+                               *val = cpu_to_le64(*val);
+                       }
+                       break;
+               case 's':{
+                               char **ptr = va_arg(ap, char **);
+                               int16_t len;
+                               int size;
+
+                               errcode = p9pdu_readf(pdu, optional, "w", &len);
+                               if (errcode)
+                                       break;
+
+                               size = MAX(len, 0);
+
+                               *ptr = kmalloc(size + 1, GFP_KERNEL);
+                               if (*ptr == NULL) {
+                                       errcode = -EFAULT;
+                                       break;
+                               }
+                               if (pdu_read(pdu, *ptr, size)) {
+                                       errcode = -EFAULT;
+                                       kfree(*ptr);
+                                       *ptr = NULL;
+                               } else
+                                       (*ptr)[size] = 0;
+                       }
+                       break;
+               case 'Q':{
+                               struct p9_qid *qid =
+                                   va_arg(ap, struct p9_qid *);
+
+                               errcode = p9pdu_readf(pdu, optional, "bdq",
+                                                     &qid->type, &qid->version,
+                                                     &qid->path);
+                       }
+                       break;
+               case 'S':{
+                               struct p9_wstat *stbuf =
+                                   va_arg(ap, struct p9_wstat *);
+
+                               memset(stbuf, 0, sizeof(struct p9_wstat));
+                               stbuf->n_uid = stbuf->n_gid = stbuf->n_muid =
+                                                                       -1;
+                               errcode =
+                                   p9pdu_readf(pdu, optional,
+                                               "wwdQdddqssss?sddd",
+                                               &stbuf->size, &stbuf->type,
+                                               &stbuf->dev, &stbuf->qid,
+                                               &stbuf->mode, &stbuf->atime,
+                                               &stbuf->mtime, &stbuf->length,
+                                               &stbuf->name, &stbuf->uid,
+                                               &stbuf->gid, &stbuf->muid,
+                                               &stbuf->extension,
+                                               &stbuf->n_uid, &stbuf->n_gid,
+                                               &stbuf->n_muid);
+                               if (errcode)
+                                       p9stat_free(stbuf);
+                       }
+                       break;
+               case 'D':{
+                               int32_t *count = va_arg(ap, int32_t *);
+                               void **data = va_arg(ap, void **);
+
+                               errcode =
+                                   p9pdu_readf(pdu, optional, "d", count);
+                               if (!errcode) {
+                                       *count =
+                                           MIN(*count,
+                                               pdu->size - pdu->offset);
+                                       *data = &pdu->sdata[pdu->offset];
+                               }
+                       }
+                       break;
+               case 'T':{
+                               int16_t *nwname = va_arg(ap, int16_t *);
+                               char ***wnames = va_arg(ap, char ***);
+
+                               errcode =
+                                   p9pdu_readf(pdu, optional, "w", nwname);
+                               if (!errcode) {
+                                       *wnames =
+                                           kmalloc(sizeof(char *) * *nwname,
+                                                   GFP_KERNEL);
+                                       if (!*wnames)
+                                               errcode = -ENOMEM;
+                               }
+
+                               if (!errcode) {
+                                       int i;
+
+                                       for (i = 0; i < *nwname; i++) {
+                                               errcode =
+                                                   p9pdu_readf(pdu, optional,
+                                                               "s",
+                                                               &(*wnames)[i]);
+                                               if (errcode)
+                                                       break;
+                                       }
+                               }
+
+                               if (errcode) {
+                                       if (*wnames) {
+                                               int i;
+
+                                               for (i = 0; i < *nwname; i++)
+                                                       kfree((*wnames)[i]);
+                                       }
+                                       kfree(*wnames);
+                                       *wnames = NULL;
+                               }
+                       }
+                       break;
+               case 'R':{
+                               int16_t *nwqid = va_arg(ap, int16_t *);
+                               struct p9_qid **wqids =
+                                   va_arg(ap, struct p9_qid **);
+
+                               *wqids = NULL;
+
+                               errcode =
+                                   p9pdu_readf(pdu, optional, "w", nwqid);
+                               if (!errcode) {
+                                       *wqids =
+                                           kmalloc(*nwqid *
+                                                   sizeof(struct p9_qid),
+                                                   GFP_KERNEL);
+                                       if (*wqids == NULL)
+                                               errcode = -ENOMEM;
+                               }
+
+                               if (!errcode) {
+                                       int i;
+
+                                       for (i = 0; i < *nwqid; i++) {
+                                               errcode =
+                                                   p9pdu_readf(pdu, optional,
+                                                               "Q",
+                                                               &(*wqids)[i]);
+                                               if (errcode)
+                                                       break;
+                                       }
+                               }
+
+                               if (errcode) {
+                                       kfree(*wqids);
+                                       *wqids = NULL;
+                               }
+                       }
+                       break;
+               case '?':
+                       if (!optional)
+                               return 0;
+                       break;
+               default:
+                       BUG();
+                       break;
+               }
+
+               if (errcode)
+                       break;
+       }
+
+       return errcode;
+}
+
+int
+p9pdu_vwritef(struct p9_fcall *pdu, int optional, const char *fmt, va_list ap)
+{
+       const char *ptr;
+       int errcode = 0;
+
+       for (ptr = fmt; *ptr; ptr++) {
+               switch (*ptr) {
+               case 'b':{
+                               int8_t val = va_arg(ap, int);
+                               if (pdu_write(pdu, &val, sizeof(val)))
+                                       errcode = -EFAULT;
+                       }
+                       break;
+               case 'w':{
+                               int16_t val = va_arg(ap, int);
+                               if (pdu_write(pdu, &val, sizeof(val)))
+                                       errcode = -EFAULT;
+                       }
+                       break;
+               case 'd':{
+                               int32_t val = va_arg(ap, int32_t);
+                               if (pdu_write(pdu, &val, sizeof(val)))
+                                       errcode = -EFAULT;
+                       }
+                       break;
+               case 'q':{
+                               int64_t val = va_arg(ap, int64_t);
+                               if (pdu_write(pdu, &val, sizeof(val)))
+                                       errcode = -EFAULT;
+                       }
+                       break;
+               case 's':{
+                               const char *ptr = va_arg(ap, const char *);
+                               int16_t len = 0;
+                               if (ptr)
+                                       len = MIN(strlen(ptr), USHORT_MAX);
+
+                               errcode = p9pdu_writef(pdu, optional, "w", len);
+                               if (!errcode && pdu_write(pdu, ptr, len))
+                                       errcode = -EFAULT;
+                       }
+                       break;
+               case 'Q':{
+                               const struct p9_qid *qid =
+                                   va_arg(ap, const struct p9_qid *);
+                               errcode =
+                                   p9pdu_writef(pdu, optional, "bdq",
+                                                qid->type, qid->version,
+                                                qid->path);
+                       } break;
+               case 'S':{
+                               const struct p9_wstat *stbuf =
+                                   va_arg(ap, const struct p9_wstat *);
+                               errcode =
+                                   p9pdu_writef(pdu, optional,
+                                                "wwdQdddqssss?sddd",
+                                                stbuf->size, stbuf->type,
+                                                stbuf->dev, &stbuf->qid,
+                                                stbuf->mode, stbuf->atime,
+                                                stbuf->mtime, stbuf->length,
+                                                stbuf->name, stbuf->uid,
+                                                stbuf->gid, stbuf->muid,
+                                                stbuf->extension, stbuf->n_uid,
+                                                stbuf->n_gid, stbuf->n_muid);
+                       } break;
+               case 'D':{
+                               int32_t count = va_arg(ap, int32_t);
+                               const void *data = va_arg(ap, const void *);
+
+                               errcode =
+                                   p9pdu_writef(pdu, optional, "d", count);
+                               if (!errcode && pdu_write(pdu, data, count))
+                                       errcode = -EFAULT;
+                       }
+                       break;
+               case 'U':{
+                               int32_t count = va_arg(ap, int32_t);
+                               const char __user *udata =
+                                               va_arg(ap, const void *);
+                               errcode =
+                                   p9pdu_writef(pdu, optional, "d", count);
+                               if (!errcode && pdu_write_u(pdu, udata, count))
+                                       errcode = -EFAULT;
+                       }
+                       break;
+               case 'T':{
+                               int16_t nwname = va_arg(ap, int);
+                               const char **wnames = va_arg(ap, const char **);
+
+                               errcode =
+                                   p9pdu_writef(pdu, optional, "w", nwname);
+                               if (!errcode) {
+                                       int i;
+
+                                       for (i = 0; i < nwname; i++) {
+                                               errcode =
+                                                   p9pdu_writef(pdu, optional,
+                                                                "s",
+                                                                wnames[i]);
+                                               if (errcode)
+                                                       break;
+                                       }
+                               }
+                       }
+                       break;
+               case 'R':{
+                               int16_t nwqid = va_arg(ap, int);
+                               struct p9_qid *wqids =
+                                   va_arg(ap, struct p9_qid *);
+
+                               errcode =
+                                   p9pdu_writef(pdu, optional, "w", nwqid);
+                               if (!errcode) {
+                                       int i;
+
+                                       for (i = 0; i < nwqid; i++) {
+                                               errcode =
+                                                   p9pdu_writef(pdu, optional,
+                                                                "Q",
+                                                                &wqids[i]);
+                                               if (errcode)
+                                                       break;
+                                       }
+                               }
+                       }
+                       break;
+               case '?':
+                       if (!optional)
+                               return 0;
+                       break;
+               default:
+                       BUG();
+                       break;
+               }
+
+               if (errcode)
+                       break;
+       }
+
+       return errcode;
+}
+
+int p9pdu_readf(struct p9_fcall *pdu, int optional, const char *fmt, ...)
+{
+       va_list ap;
+       int ret;
+
+       va_start(ap, fmt);
+       ret = p9pdu_vreadf(pdu, optional, fmt, ap);
+       va_end(ap);
+
+       return ret;
+}
+
+static int
+p9pdu_writef(struct p9_fcall *pdu, int optional, const char *fmt, ...)
+{
+       va_list ap;
+       int ret;
+
+       va_start(ap, fmt);
+       ret = p9pdu_vwritef(pdu, optional, fmt, ap);
+       va_end(ap);
+
+       return ret;
+}
+
+int p9stat_read(char *buf, int len, struct p9_wstat *st, int dotu)
+{
+       struct p9_fcall fake_pdu;
+       int ret;
+
+       fake_pdu.size = len;
+       fake_pdu.capacity = len;
+       fake_pdu.sdata = buf;
+       fake_pdu.offset = 0;
+
+       ret = p9pdu_readf(&fake_pdu, dotu, "S", st);
+       if (ret) {
+               P9_DPRINTK(P9_DEBUG_9P, "<<< p9stat_read failed: %d\n", ret);
+               p9pdu_dump(1, &fake_pdu);
+       }
+
+       return ret;
+}
+EXPORT_SYMBOL(p9stat_read);
+
+int p9pdu_prepare(struct p9_fcall *pdu, int16_t tag, int8_t type)
+{
+       return p9pdu_writef(pdu, 0, "dbw", 0, type, tag);
+}
+
+int p9pdu_finalize(struct p9_fcall *pdu)
+{
+       int size = pdu->size;
+       int err;
+
+       pdu->size = 0;
+       err = p9pdu_writef(pdu, 0, "d", size);
+       pdu->size = size;
+
+       if ((p9_debug_level & P9_DEBUG_PKT) == P9_DEBUG_PKT)
+               p9pdu_dump(0, pdu);
+
+       P9_DPRINTK(P9_DEBUG_9P, ">>> size=%d type: %d tag: %d\n", pdu->size,
+                                                       pdu->id, pdu->tag);
+
+       return err;
+}
+
+void p9pdu_reset(struct p9_fcall *pdu)
+{
+       pdu->offset = 0;
+       pdu->size = 0;
+}
diff --git a/net/9p/protocol.h b/net/9p/protocol.h
new file mode 100644 (file)
index 0000000..ccde462
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * net/9p/protocol.h
+ *
+ * 9P Protocol Support Code
+ *
+ *  Copyright (C) 2008 by Eric Van Hensbergen <ericvh@gmail.com>
+ *
+ *  Base on code from Anthony Liguori <aliguori@us.ibm.com>
+ *  Copyright (C) 2008 by IBM, Corp.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2
+ *  as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to:
+ *  Free Software Foundation
+ *  51 Franklin Street, Fifth Floor
+ *  Boston, MA  02111-1301  USA
+ *
+ */
+
+int
+p9pdu_vwritef(struct p9_fcall *pdu, int optional, const char *fmt, va_list ap);
+int p9pdu_readf(struct p9_fcall *pdu, int optional, const char *fmt, ...);
+int p9pdu_prepare(struct p9_fcall *pdu, int16_t tag, int8_t type);
+int p9pdu_finalize(struct p9_fcall *pdu);
+void p9pdu_dump(int, struct p9_fcall *);
+void p9pdu_reset(struct p9_fcall *pdu);
index 6dabbdb666510fcfc570a67e3f24cf598429e080..be65d8242fd253ce0d9201cedfa448cfe36d6bed 100644 (file)
 #include <linux/file.h>
 #include <linux/parser.h>
 #include <net/9p/9p.h>
+#include <net/9p/client.h>
 #include <net/9p/transport.h>
 
 #define P9_PORT 564
 #define MAX_SOCK_BUF (64*1024)
-#define ERREQFLUSH     1
-#define SCHED_TIMEOUT  10
 #define MAXPOLLWADDR   2
 
 /**
@@ -61,7 +60,6 @@ struct p9_fd_opts {
        u16 port;
 };
 
-
 /**
  * struct p9_trans_fd - transport state
  * @rd: reference to file to read from
@@ -100,60 +98,22 @@ enum {
        Wpending = 8,           /* can write */
 };
 
-enum {
-       None,
-       Flushing,
-       Flushed,
-};
-
-struct p9_req;
-typedef void (*p9_conn_req_callback)(struct p9_req *req, void *a);
-
-/**
- * struct p9_req - fd mux encoding of an rpc transaction
- * @lock: protects req_list
- * @tag: numeric tag for rpc transaction
- * @tcall: request &p9_fcall structure
- * @rcall: response &p9_fcall structure
- * @err: error state
- * @cb: callback for when response is received
- * @cba: argument to pass to callback
- * @flush: flag to indicate RPC has been flushed
- * @req_list: list link for higher level objects to chain requests
- *
- */
-
-struct p9_req {
-       spinlock_t lock;
-       int tag;
-       struct p9_fcall *tcall;
-       struct p9_fcall *rcall;
-       int err;
-       p9_conn_req_callback cb;
-       void *cba;
-       int flush;
-       struct list_head req_list;
-};
-
-struct p9_mux_poll_task {
-       struct task_struct *task;
-       struct list_head mux_list;
-       int muxnum;
+struct p9_poll_wait {
+       struct p9_conn *conn;
+       wait_queue_t wait;
+       wait_queue_head_t *wait_addr;
 };
 
 /**
  * struct p9_conn - fd mux connection state information
- * @lock: protects mux_list (?)
  * @mux_list: list link for mux to manage multiple connections (?)
- * @poll_task: task polling on this connection
- * @msize: maximum size for connection (dup)
- * @extended: 9p2000.u flag (dup)
- * @trans: reference to transport instance for this connection
- * @tagpool: id accounting for transactions
+ * @client: reference to client instance for this connection
  * @err: error state
  * @req_list: accounting for requests which have been sent
  * @unsent_req_list: accounting for requests that haven't been sent
- * @rcall: current response &p9_fcall structure
+ * @req: current request being processed (if any)
+ * @tmp_buf: temporary buffer to read in header
+ * @rsize: amount to read for current frame
  * @rpos: read position in current frame
  * @rbuf: current read buffer
  * @wpos: write position for current frame
@@ -169,409 +129,300 @@ struct p9_mux_poll_task {
  */
 
 struct p9_conn {
-       spinlock_t lock; /* protect lock structure */
        struct list_head mux_list;
-       struct p9_mux_poll_task *poll_task;
-       int msize;
-       unsigned char extended;
-       struct p9_trans *trans;
-       struct p9_idpool *tagpool;
+       struct p9_client *client;
        int err;
        struct list_head req_list;
        struct list_head unsent_req_list;
-       struct p9_fcall *rcall;
+       struct p9_req_t *req;
+       char tmp_buf[7];
+       int rsize;
        int rpos;
        char *rbuf;
        int wpos;
        int wsize;
        char *wbuf;
-       wait_queue_t poll_wait[MAXPOLLWADDR];
-       wait_queue_head_t *poll_waddr[MAXPOLLWADDR];
+       struct list_head poll_pending_link;
+       struct p9_poll_wait poll_wait[MAXPOLLWADDR];
        poll_table pt;
        struct work_struct rq;
        struct work_struct wq;
        unsigned long wsched;
 };
 
-/**
- * struct p9_mux_rpc - fd mux rpc accounting structure
- * @m: connection this request was issued on
- * @err: error state
- * @tcall: request &p9_fcall
- * @rcall: response &p9_fcall
- * @wqueue: wait queue that client is blocked on for this rpc
- *
- * Bug: isn't this information duplicated elsewhere like &p9_req
- */
-
-struct p9_mux_rpc {
-       struct p9_conn *m;
-       int err;
-       struct p9_fcall *tcall;
-       struct p9_fcall *rcall;
-       wait_queue_head_t wqueue;
-};
-
-static int p9_poll_proc(void *);
-static void p9_read_work(struct work_struct *work);
-static void p9_write_work(struct work_struct *work);
-static void p9_pollwait(struct file *filp, wait_queue_head_t *wait_address,
-                                                               poll_table *p);
-static int p9_fd_write(struct p9_trans *trans, void *v, int len);
-static int p9_fd_read(struct p9_trans *trans, void *v, int len);
-
-static DEFINE_MUTEX(p9_mux_task_lock);
+static DEFINE_SPINLOCK(p9_poll_lock);
+static LIST_HEAD(p9_poll_pending_list);
 static struct workqueue_struct *p9_mux_wq;
+static struct task_struct *p9_poll_task;
 
-static int p9_mux_num;
-static int p9_mux_poll_task_num;
-static struct p9_mux_poll_task p9_mux_poll_tasks[100];
-
-static void p9_conn_destroy(struct p9_conn *);
-static unsigned int p9_fd_poll(struct p9_trans *trans,
-                                               struct poll_table_struct *pt);
-
-#ifdef P9_NONBLOCK
-static int p9_conn_rpcnb(struct p9_conn *m, struct p9_fcall *tc,
-       p9_conn_req_callback cb, void *a);
-#endif /* P9_NONBLOCK */
-
-static void p9_conn_cancel(struct p9_conn *m, int err);
-
-static u16 p9_mux_get_tag(struct p9_conn *m)
+static void p9_mux_poll_stop(struct p9_conn *m)
 {
-       int tag;
+       unsigned long flags;
+       int i;
 
-       tag = p9_idpool_get(m->tagpool);
-       if (tag < 0)
-               return P9_NOTAG;
-       else
-               return (u16) tag;
-}
+       for (i = 0; i < ARRAY_SIZE(m->poll_wait); i++) {
+               struct p9_poll_wait *pwait = &m->poll_wait[i];
 
-static void p9_mux_put_tag(struct p9_conn *m, u16 tag)
-{
-       if (tag != P9_NOTAG && p9_idpool_check(tag, m->tagpool))
-               p9_idpool_put(tag, m->tagpool);
+               if (pwait->wait_addr) {
+                       remove_wait_queue(pwait->wait_addr, &pwait->wait);
+                       pwait->wait_addr = NULL;
+               }
+       }
+
+       spin_lock_irqsave(&p9_poll_lock, flags);
+       list_del_init(&m->poll_pending_link);
+       spin_unlock_irqrestore(&p9_poll_lock, flags);
 }
 
 /**
- * p9_mux_calc_poll_procs - calculates the number of polling procs
- * @muxnum: number of mounts
+ * p9_conn_cancel - cancel all pending requests with error
+ * @m: mux data
+ * @err: error code
  *
- * Calculation is based on the number of mounted v9fs filesystems.
- * The current implementation returns sqrt of the number of mounts.
  */
 
-static int p9_mux_calc_poll_procs(int muxnum)
+static void p9_conn_cancel(struct p9_conn *m, int err)
 {
-       int n;
-
-       if (p9_mux_poll_task_num)
-               n = muxnum / p9_mux_poll_task_num +
-                   (muxnum % p9_mux_poll_task_num ? 1 : 0);
-       else
-               n = 1;
-
-       if (n > ARRAY_SIZE(p9_mux_poll_tasks))
-               n = ARRAY_SIZE(p9_mux_poll_tasks);
-
-       return n;
-}
+       struct p9_req_t *req, *rtmp;
+       unsigned long flags;
+       LIST_HEAD(cancel_list);
 
-static int p9_mux_poll_start(struct p9_conn *m)
-{
-       int i, n;
-       struct p9_mux_poll_task *vpt, *vptlast;
-       struct task_struct *pproc;
-
-       P9_DPRINTK(P9_DEBUG_MUX, "mux %p muxnum %d procnum %d\n", m, p9_mux_num,
-               p9_mux_poll_task_num);
-       mutex_lock(&p9_mux_task_lock);
-
-       n = p9_mux_calc_poll_procs(p9_mux_num + 1);
-       if (n > p9_mux_poll_task_num) {
-               for (i = 0; i < ARRAY_SIZE(p9_mux_poll_tasks); i++) {
-                       if (p9_mux_poll_tasks[i].task == NULL) {
-                               vpt = &p9_mux_poll_tasks[i];
-                               P9_DPRINTK(P9_DEBUG_MUX, "create proc %p\n",
-                                                                       vpt);
-                               pproc = kthread_create(p9_poll_proc, vpt,
-                                                               "v9fs-poll");
-
-                               if (!IS_ERR(pproc)) {
-                                       vpt->task = pproc;
-                                       INIT_LIST_HEAD(&vpt->mux_list);
-                                       vpt->muxnum = 0;
-                                       p9_mux_poll_task_num++;
-                                       wake_up_process(vpt->task);
-                               }
-                               break;
-                       }
-               }
+       P9_DPRINTK(P9_DEBUG_ERROR, "mux %p err %d\n", m, err);
 
-               if (i >= ARRAY_SIZE(p9_mux_poll_tasks))
-                       P9_DPRINTK(P9_DEBUG_ERROR,
-                                       "warning: no free poll slots\n");
-       }
+       spin_lock_irqsave(&m->client->lock, flags);
 
-       n = (p9_mux_num + 1) / p9_mux_poll_task_num +
-           ((p9_mux_num + 1) % p9_mux_poll_task_num ? 1 : 0);
-
-       vptlast = NULL;
-       for (i = 0; i < ARRAY_SIZE(p9_mux_poll_tasks); i++) {
-               vpt = &p9_mux_poll_tasks[i];
-               if (vpt->task != NULL) {
-                       vptlast = vpt;
-                       if (vpt->muxnum < n) {
-                               P9_DPRINTK(P9_DEBUG_MUX, "put in proc %d\n", i);
-                               list_add(&m->mux_list, &vpt->mux_list);
-                               vpt->muxnum++;
-                               m->poll_task = vpt;
-                               memset(&m->poll_waddr, 0,
-                                                       sizeof(m->poll_waddr));
-                               init_poll_funcptr(&m->pt, p9_pollwait);
-                               break;
-                       }
-               }
+       if (m->err) {
+               spin_unlock_irqrestore(&m->client->lock, flags);
+               return;
        }
 
-       if (i >= ARRAY_SIZE(p9_mux_poll_tasks)) {
-               if (vptlast == NULL) {
-                       mutex_unlock(&p9_mux_task_lock);
-                       return -ENOMEM;
-               }
+       m->err = err;
 
-               P9_DPRINTK(P9_DEBUG_MUX, "put in proc %d\n", i);
-               list_add(&m->mux_list, &vptlast->mux_list);
-               vptlast->muxnum++;
-               m->poll_task = vptlast;
-               memset(&m->poll_waddr, 0, sizeof(m->poll_waddr));
-               init_poll_funcptr(&m->pt, p9_pollwait);
+       list_for_each_entry_safe(req, rtmp, &m->req_list, req_list) {
+               req->status = REQ_STATUS_ERROR;
+               if (!req->t_err)
+                       req->t_err = err;
+               list_move(&req->req_list, &cancel_list);
        }
-
-       p9_mux_num++;
-       mutex_unlock(&p9_mux_task_lock);
-
-       return 0;
-}
-
-static void p9_mux_poll_stop(struct p9_conn *m)
-{
-       int i;
-       struct p9_mux_poll_task *vpt;
-
-       mutex_lock(&p9_mux_task_lock);
-       vpt = m->poll_task;
-       list_del(&m->mux_list);
-       for (i = 0; i < ARRAY_SIZE(m->poll_waddr); i++) {
-               if (m->poll_waddr[i] != NULL) {
-                       remove_wait_queue(m->poll_waddr[i], &m->poll_wait[i]);
-                       m->poll_waddr[i] = NULL;
-               }
+       list_for_each_entry_safe(req, rtmp, &m->unsent_req_list, req_list) {
+               req->status = REQ_STATUS_ERROR;
+               if (!req->t_err)
+                       req->t_err = err;
+               list_move(&req->req_list, &cancel_list);
        }
-       vpt->muxnum--;
-       if (!vpt->muxnum) {
-               P9_DPRINTK(P9_DEBUG_MUX, "destroy proc %p\n", vpt);
-               kthread_stop(vpt->task);
-               vpt->task = NULL;
-               p9_mux_poll_task_num--;
+       spin_unlock_irqrestore(&m->client->lock, flags);
+
+       list_for_each_entry_safe(req, rtmp, &cancel_list, req_list) {
+               list_del(&req->req_list);
+               P9_DPRINTK(P9_DEBUG_ERROR, "call back req %p\n", req);
+               p9_client_cb(m->client, req);
        }
-       p9_mux_num--;
-       mutex_unlock(&p9_mux_task_lock);
 }
 
-/**
- * p9_conn_create - allocate and initialize the per-session mux data
- * @trans: transport structure
- *
- * Note: Creates the polling task if this is the first session.
- */
-
-static struct p9_conn *p9_conn_create(struct p9_trans *trans)
+static unsigned int
+p9_fd_poll(struct p9_client *client, struct poll_table_struct *pt)
 {
-       int i, n;
-       struct p9_conn *m;
+       int ret, n;
+       struct p9_trans_fd *ts = NULL;
 
-       P9_DPRINTK(P9_DEBUG_MUX, "transport %p msize %d\n", trans,
-                                                               trans->msize);
-       m = kzalloc(sizeof(struct p9_conn), GFP_KERNEL);
-       if (!m)
-               return ERR_PTR(-ENOMEM);
+       if (client && client->status == Connected)
+               ts = client->trans;
 
-       spin_lock_init(&m->lock);
-       INIT_LIST_HEAD(&m->mux_list);
-       m->msize = trans->msize;
-       m->extended = trans->extended;
-       m->trans = trans;
-       m->tagpool = p9_idpool_create();
-       if (IS_ERR(m->tagpool)) {
-               kfree(m);
-               return ERR_PTR(-ENOMEM);
-       }
+       if (!ts)
+               return -EREMOTEIO;
 
-       INIT_LIST_HEAD(&m->req_list);
-       INIT_LIST_HEAD(&m->unsent_req_list);
-       INIT_WORK(&m->rq, p9_read_work);
-       INIT_WORK(&m->wq, p9_write_work);
-       n = p9_mux_poll_start(m);
-       if (n) {
-               kfree(m);
-               return ERR_PTR(n);
-       }
+       if (!ts->rd->f_op || !ts->rd->f_op->poll)
+               return -EIO;
 
-       n = p9_fd_poll(trans, &m->pt);
-       if (n & POLLIN) {
-               P9_DPRINTK(P9_DEBUG_MUX, "mux %p can read\n", m);
-               set_bit(Rpending, &m->wsched);
-       }
+       if (!ts->wr->f_op || !ts->wr->f_op->poll)
+               return -EIO;
 
-       if (n & POLLOUT) {
-               P9_DPRINTK(P9_DEBUG_MUX, "mux %p can write\n", m);
-               set_bit(Wpending, &m->wsched);
-       }
+       ret = ts->rd->f_op->poll(ts->rd, pt);
+       if (ret < 0)
+               return ret;
 
-       for (i = 0; i < ARRAY_SIZE(m->poll_waddr); i++) {
-               if (IS_ERR(m->poll_waddr[i])) {
-                       p9_mux_poll_stop(m);
-                       kfree(m);
-                       return (void *)m->poll_waddr;   /* the error code */
-               }
+       if (ts->rd != ts->wr) {
+               n = ts->wr->f_op->poll(ts->wr, pt);
+               if (n < 0)
+                       return n;
+               ret = (ret & ~POLLOUT) | (n & ~POLLIN);
        }
 
-       return m;
+       return ret;
 }
 
 /**
- * p9_mux_destroy - cancels all pending requests and frees mux resources
- * @m: mux to destroy
+ * p9_fd_read- read from a fd
+ * @client: client instance
+ * @v: buffer to receive data into
+ * @len: size of receive buffer
  *
  */
 
-static void p9_conn_destroy(struct p9_conn *m)
+static int p9_fd_read(struct p9_client *client, void *v, int len)
 {
-       P9_DPRINTK(P9_DEBUG_MUX, "mux %p prev %p next %p\n", m,
-               m->mux_list.prev, m->mux_list.next);
+       int ret;
+       struct p9_trans_fd *ts = NULL;
 
-       p9_mux_poll_stop(m);
-       cancel_work_sync(&m->rq);
-       cancel_work_sync(&m->wq);
+       if (client && client->status != Disconnected)
+               ts = client->trans;
 
-       p9_conn_cancel(m, -ECONNRESET);
+       if (!ts)
+               return -EREMOTEIO;
 
-       m->trans = NULL;
-       p9_idpool_destroy(m->tagpool);
-       kfree(m);
+       if (!(ts->rd->f_flags & O_NONBLOCK))
+               P9_DPRINTK(P9_DEBUG_ERROR, "blocking read ...\n");
+
+       ret = kernel_read(ts->rd, ts->rd->f_pos, v, len);
+       if (ret <= 0 && ret != -ERESTARTSYS && ret != -EAGAIN)
+               client->status = Disconnected;
+       return ret;
 }
 
 /**
- * p9_pollwait - add poll task to the wait queue
- * @filp: file pointer being polled
- * @wait_address: wait_q to block on
- * @p: poll state
+ * p9_read_work - called when there is some data to be read from a transport
+ * @work: container of work to be done
  *
- * called by files poll operation to add v9fs-poll task to files wait queue
  */
 
-static void
-p9_pollwait(struct file *filp, wait_queue_head_t *wait_address, poll_table *p)
+static void p9_read_work(struct work_struct *work)
 {
-       int i;
+       int n, err;
        struct p9_conn *m;
 
-       m = container_of(p, struct p9_conn, pt);
-       for (i = 0; i < ARRAY_SIZE(m->poll_waddr); i++)
-               if (m->poll_waddr[i] == NULL)
-                       break;
+       m = container_of(work, struct p9_conn, rq);
 
-       if (i >= ARRAY_SIZE(m->poll_waddr)) {
-               P9_DPRINTK(P9_DEBUG_ERROR, "not enough wait_address slots\n");
+       if (m->err < 0)
                return;
-       }
 
-       m->poll_waddr[i] = wait_address;
+       P9_DPRINTK(P9_DEBUG_TRANS, "start mux %p pos %d\n", m, m->rpos);
+
+       if (!m->rbuf) {
+               m->rbuf = m->tmp_buf;
+               m->rpos = 0;
+               m->rsize = 7; /* start by reading header */
+       }
 
-       if (!wait_address) {
-               P9_DPRINTK(P9_DEBUG_ERROR, "no wait_address\n");
-               m->poll_waddr[i] = ERR_PTR(-EIO);
+       clear_bit(Rpending, &m->wsched);
+       P9_DPRINTK(P9_DEBUG_TRANS, "read mux %p pos %d size: %d = %d\n", m,
+                                       m->rpos, m->rsize, m->rsize-m->rpos);
+       err = p9_fd_read(m->client, m->rbuf + m->rpos,
+                                               m->rsize - m->rpos);
+       P9_DPRINTK(P9_DEBUG_TRANS, "mux %p got %d bytes\n", m, err);
+       if (err == -EAGAIN) {
+               clear_bit(Rworksched, &m->wsched);
                return;
        }
 
-       init_waitqueue_entry(&m->poll_wait[i], m->poll_task->task);
-       add_wait_queue(wait_address, &m->poll_wait[i]);
-}
+       if (err <= 0)
+               goto error;
 
-/**
- * p9_poll_mux - polls a mux and schedules read or write works if necessary
- * @m: connection to poll
- *
- */
+       m->rpos += err;
 
-static void p9_poll_mux(struct p9_conn *m)
-{
-       int n;
+       if ((!m->req) && (m->rpos == m->rsize)) { /* header read in */
+               u16 tag;
+               P9_DPRINTK(P9_DEBUG_TRANS, "got new header\n");
 
-       if (m->err < 0)
-               return;
+               n = le32_to_cpu(*(__le32 *) m->rbuf); /* read packet size */
+               if (n >= m->client->msize) {
+                       P9_DPRINTK(P9_DEBUG_ERROR,
+                               "requested packet size too big: %d\n", n);
+                       err = -EIO;
+                       goto error;
+               }
 
-       n = p9_fd_poll(m->trans, NULL);
-       if (n < 0 || n & (POLLERR | POLLHUP | POLLNVAL)) {
-               P9_DPRINTK(P9_DEBUG_MUX, "error mux %p err %d\n", m, n);
-               if (n >= 0)
-                       n = -ECONNRESET;
-               p9_conn_cancel(m, n);
-       }
+               tag = le16_to_cpu(*(__le16 *) (m->rbuf+5)); /* read tag */
+               P9_DPRINTK(P9_DEBUG_TRANS,
+                       "mux %p pkt: size: %d bytes tag: %d\n", m, n, tag);
 
-       if (n & POLLIN) {
-               set_bit(Rpending, &m->wsched);
-               P9_DPRINTK(P9_DEBUG_MUX, "mux %p can read\n", m);
-               if (!test_and_set_bit(Rworksched, &m->wsched)) {
-                       P9_DPRINTK(P9_DEBUG_MUX, "schedule read work %p\n", m);
-                       queue_work(p9_mux_wq, &m->rq);
+               m->req = p9_tag_lookup(m->client, tag);
+               if (!m->req) {
+                       P9_DPRINTK(P9_DEBUG_ERROR, "Unexpected packet tag %d\n",
+                                                                tag);
+                       err = -EIO;
+                       goto error;
                }
-       }
 
-       if (n & POLLOUT) {
-               set_bit(Wpending, &m->wsched);
-               P9_DPRINTK(P9_DEBUG_MUX, "mux %p can write\n", m);
-               if ((m->wsize || !list_empty(&m->unsent_req_list))
-                   && !test_and_set_bit(Wworksched, &m->wsched)) {
-                       P9_DPRINTK(P9_DEBUG_MUX, "schedule write work %p\n", m);
-                       queue_work(p9_mux_wq, &m->wq);
+               if (m->req->rc == NULL) {
+                       m->req->rc = kmalloc(sizeof(struct p9_fcall) +
+                                               m->client->msize, GFP_KERNEL);
+                       if (!m->req->rc) {
+                               m->req = NULL;
+                               err = -ENOMEM;
+                               goto error;
+                       }
                }
+               m->rbuf = (char *)m->req->rc + sizeof(struct p9_fcall);
+               memcpy(m->rbuf, m->tmp_buf, m->rsize);
+               m->rsize = n;
        }
+
+       /* not an else because some packets (like clunk) have no payload */
+       if ((m->req) && (m->rpos == m->rsize)) { /* packet is read in */
+               P9_DPRINTK(P9_DEBUG_TRANS, "got new packet\n");
+               spin_lock(&m->client->lock);
+               list_del(&m->req->req_list);
+               spin_unlock(&m->client->lock);
+               p9_client_cb(m->client, m->req);
+
+               m->rbuf = NULL;
+               m->rpos = 0;
+               m->rsize = 0;
+               m->req = NULL;
+       }
+
+       if (!list_empty(&m->req_list)) {
+               if (test_and_clear_bit(Rpending, &m->wsched))
+                       n = POLLIN;
+               else
+                       n = p9_fd_poll(m->client, NULL);
+
+               if (n & POLLIN) {
+                       P9_DPRINTK(P9_DEBUG_TRANS, "sched read work %p\n", m);
+                       queue_work(p9_mux_wq, &m->rq);
+               } else
+                       clear_bit(Rworksched, &m->wsched);
+       } else
+               clear_bit(Rworksched, &m->wsched);
+
+       return;
+error:
+       p9_conn_cancel(m, err);
+       clear_bit(Rworksched, &m->wsched);
 }
 
 /**
- * p9_poll_proc - poll worker thread
- * @a: thread state and arguments
- *
- * polls all v9fs transports for new events and queues the appropriate
- * work to the work queue
+ * p9_fd_write - write to a socket
+ * @client: client instance
+ * @v: buffer to send data from
+ * @len: size of send buffer
  *
  */
 
-static int p9_poll_proc(void *a)
+static int p9_fd_write(struct p9_client *client, void *v, int len)
 {
-       struct p9_conn *m, *mtmp;
-       struct p9_mux_poll_task *vpt;
+       int ret;
+       mm_segment_t oldfs;
+       struct p9_trans_fd *ts = NULL;
 
-       vpt = a;
-       P9_DPRINTK(P9_DEBUG_MUX, "start %p %p\n", current, vpt);
-       while (!kthread_should_stop()) {
-               set_current_state(TASK_INTERRUPTIBLE);
+       if (client && client->status != Disconnected)
+               ts = client->trans;
 
-               list_for_each_entry_safe(m, mtmp, &vpt->mux_list, mux_list) {
-                       p9_poll_mux(m);
-               }
+       if (!ts)
+               return -EREMOTEIO;
 
-               P9_DPRINTK(P9_DEBUG_MUX, "sleeping...\n");
-               schedule_timeout(SCHED_TIMEOUT * HZ);
-       }
+       if (!(ts->wr->f_flags & O_NONBLOCK))
+               P9_DPRINTK(P9_DEBUG_ERROR, "blocking write ...\n");
 
-       __set_current_state(TASK_RUNNING);
-       P9_DPRINTK(P9_DEBUG_MUX, "finish\n");
-       return 0;
+       oldfs = get_fs();
+       set_fs(get_ds());
+       /* The cast to a user pointer is valid due to the set_fs() */
+       ret = vfs_write(ts->wr, (void __user *)v, len, &ts->wr->f_pos);
+       set_fs(oldfs);
+
+       if (ret <= 0 && ret != -ERESTARTSYS && ret != -EAGAIN)
+               client->status = Disconnected;
+       return ret;
 }
 
 /**
@@ -584,7 +435,7 @@ static void p9_write_work(struct work_struct *work)
 {
        int n, err;
        struct p9_conn *m;
-       struct p9_req *req;
+       struct p9_req_t *req;
 
        m = container_of(work, struct p9_conn, wq);
 
@@ -599,25 +450,23 @@ static void p9_write_work(struct work_struct *work)
                        return;
                }
 
-               spin_lock(&m->lock);
-again:
-               req = list_entry(m->unsent_req_list.next, struct p9_req,
+               spin_lock(&m->client->lock);
+               req = list_entry(m->unsent_req_list.next, struct p9_req_t,
                               req_list);
+               req->status = REQ_STATUS_SENT;
                list_move_tail(&req->req_list, &m->req_list);
-               if (req->err == ERREQFLUSH)
-                       goto again;
 
-               m->wbuf = req->tcall->sdata;
-               m->wsize = req->tcall->size;
+               m->wbuf = req->tc->sdata;
+               m->wsize = req->tc->size;
                m->wpos = 0;
-               spin_unlock(&m->lock);
+               spin_unlock(&m->client->lock);
        }
 
-       P9_DPRINTK(P9_DEBUG_MUX, "mux %p pos %d size %d\n", m, m->wpos,
+       P9_DPRINTK(P9_DEBUG_TRANS, "mux %p pos %d size %d\n", m, m->wpos,
                                                                m->wsize);
        clear_bit(Wpending, &m->wsched);
-       err = p9_fd_write(m->trans, m->wbuf + m->wpos, m->wsize - m->wpos);
-       P9_DPRINTK(P9_DEBUG_MUX, "mux %p sent %d bytes\n", m, err);
+       err = p9_fd_write(m->client, m->wbuf + m->wpos, m->wsize - m->wpos);
+       P9_DPRINTK(P9_DEBUG_TRANS, "mux %p sent %d bytes\n", m, err);
        if (err == -EAGAIN) {
                clear_bit(Wworksched, &m->wsched);
                return;
@@ -638,521 +487,214 @@ again:
                if (test_and_clear_bit(Wpending, &m->wsched))
                        n = POLLOUT;
                else
-                       n = p9_fd_poll(m->trans, NULL);
+                       n = p9_fd_poll(m->client, NULL);
 
                if (n & POLLOUT) {
-                       P9_DPRINTK(P9_DEBUG_MUX, "schedule write work %p\n", m);
+                       P9_DPRINTK(P9_DEBUG_TRANS, "sched write work %p\n", m);
                        queue_work(p9_mux_wq, &m->wq);
                } else
                        clear_bit(Wworksched, &m->wsched);
        } else
                clear_bit(Wworksched, &m->wsched);
 
-       return;
-
-error:
-       p9_conn_cancel(m, err);
-       clear_bit(Wworksched, &m->wsched);
-}
-
-static void process_request(struct p9_conn *m, struct p9_req *req)
-{
-       int ecode;
-       struct p9_str *ename;
-
-       if (!req->err && req->rcall->id == P9_RERROR) {
-               ecode = req->rcall->params.rerror.errno;
-               ename = &req->rcall->params.rerror.error;
-
-               P9_DPRINTK(P9_DEBUG_MUX, "Rerror %.*s\n", ename->len,
-                                                               ename->str);
-
-               if (m->extended)
-                       req->err = -ecode;
-
-               if (!req->err) {
-                       req->err = p9_errstr2errno(ename->str, ename->len);
-
-                       /* string match failed */
-                       if (!req->err) {
-                               PRINT_FCALL_ERROR("unknown error", req->rcall);
-                               req->err = -ESERVERFAULT;
-                       }
-               }
-       } else if (req->tcall && req->rcall->id != req->tcall->id + 1) {
-               P9_DPRINTK(P9_DEBUG_ERROR,
-                               "fcall mismatch: expected %d, got %d\n",
-                               req->tcall->id + 1, req->rcall->id);
-               if (!req->err)
-                       req->err = -EIO;
-       }
-}
-
-/**
- * p9_read_work - called when there is some data to be read from a transport
- * @work: container of work to be done
- *
- */
-
-static void p9_read_work(struct work_struct *work)
-{
-       int n, err;
-       struct p9_conn *m;
-       struct p9_req *req, *rptr, *rreq;
-       struct p9_fcall *rcall;
-       char *rbuf;
-
-       m = container_of(work, struct p9_conn, rq);
-
-       if (m->err < 0)
-               return;
-
-       rcall = NULL;
-       P9_DPRINTK(P9_DEBUG_MUX, "start mux %p pos %d\n", m, m->rpos);
-
-       if (!m->rcall) {
-               m->rcall =
-                   kmalloc(sizeof(struct p9_fcall) + m->msize, GFP_KERNEL);
-               if (!m->rcall) {
-                       err = -ENOMEM;
-                       goto error;
-               }
-
-               m->rbuf = (char *)m->rcall + sizeof(struct p9_fcall);
-               m->rpos = 0;
-       }
-
-       clear_bit(Rpending, &m->wsched);
-       err = p9_fd_read(m->trans, m->rbuf + m->rpos, m->msize - m->rpos);
-       P9_DPRINTK(P9_DEBUG_MUX, "mux %p got %d bytes\n", m, err);
-       if (err == -EAGAIN) {
-               clear_bit(Rworksched, &m->wsched);
-               return;
-       }
-
-       if (err <= 0)
-               goto error;
-
-       m->rpos += err;
-       while (m->rpos > 4) {
-               n = le32_to_cpu(*(__le32 *) m->rbuf);
-               if (n >= m->msize) {
-                       P9_DPRINTK(P9_DEBUG_ERROR,
-                               "requested packet size too big: %d\n", n);
-                       err = -EIO;
-                       goto error;
-               }
-
-               if (m->rpos < n)
-                       break;
-
-               err =
-                   p9_deserialize_fcall(m->rbuf, n, m->rcall, m->extended);
-               if (err < 0)
-                       goto error;
-
-#ifdef CONFIG_NET_9P_DEBUG
-               if ((p9_debug_level&P9_DEBUG_FCALL) == P9_DEBUG_FCALL) {
-                       char buf[150];
-
-                       p9_printfcall(buf, sizeof(buf), m->rcall,
-                               m->extended);
-                       printk(KERN_NOTICE ">>> %p %s\n", m, buf);
-               }
-#endif
-
-               rcall = m->rcall;
-               rbuf = m->rbuf;
-               if (m->rpos > n) {
-                       m->rcall = kmalloc(sizeof(struct p9_fcall) + m->msize,
-                                          GFP_KERNEL);
-                       if (!m->rcall) {
-                               err = -ENOMEM;
-                               goto error;
-                       }
-
-                       m->rbuf = (char *)m->rcall + sizeof(struct p9_fcall);
-                       memmove(m->rbuf, rbuf + n, m->rpos - n);
-                       m->rpos -= n;
-               } else {
-                       m->rcall = NULL;
-                       m->rbuf = NULL;
-                       m->rpos = 0;
-               }
-
-               P9_DPRINTK(P9_DEBUG_MUX, "mux %p fcall id %d tag %d\n", m,
-                                                       rcall->id, rcall->tag);
-
-               req = NULL;
-               spin_lock(&m->lock);
-               list_for_each_entry_safe(rreq, rptr, &m->req_list, req_list) {
-                       if (rreq->tag == rcall->tag) {
-                               req = rreq;
-                               if (req->flush != Flushing)
-                                       list_del(&req->req_list);
-                               break;
-                       }
-               }
-               spin_unlock(&m->lock);
-
-               if (req) {
-                       req->rcall = rcall;
-                       process_request(m, req);
-
-                       if (req->flush != Flushing) {
-                               if (req->cb)
-                                       (*req->cb) (req, req->cba);
-                               else
-                                       kfree(req->rcall);
-                       }
-               } else {
-                       if (err >= 0 && rcall->id != P9_RFLUSH)
-                               P9_DPRINTK(P9_DEBUG_ERROR,
-                                 "unexpected response mux %p id %d tag %d\n",
-                                 m, rcall->id, rcall->tag);
-                       kfree(rcall);
-               }
-       }
-
-       if (!list_empty(&m->req_list)) {
-               if (test_and_clear_bit(Rpending, &m->wsched))
-                       n = POLLIN;
-               else
-                       n = p9_fd_poll(m->trans, NULL);
-
-               if (n & POLLIN) {
-                       P9_DPRINTK(P9_DEBUG_MUX, "schedule read work %p\n", m);
-                       queue_work(p9_mux_wq, &m->rq);
-               } else
-                       clear_bit(Rworksched, &m->wsched);
-       } else
-               clear_bit(Rworksched, &m->wsched);
-
-       return;
-
-error:
-       p9_conn_cancel(m, err);
-       clear_bit(Rworksched, &m->wsched);
-}
-
-/**
- * p9_send_request - send 9P request
- * The function can sleep until the request is scheduled for sending.
- * The function can be interrupted. Return from the function is not
- * a guarantee that the request is sent successfully. Can return errors
- * that can be retrieved by PTR_ERR macros.
- *
- * @m: mux data
- * @tc: request to be sent
- * @cb: callback function to call when response is received
- * @cba: parameter to pass to the callback function
- *
- */
-
-static struct p9_req *p9_send_request(struct p9_conn *m,
-                                         struct p9_fcall *tc,
-                                         p9_conn_req_callback cb, void *cba)
-{
-       int n;
-       struct p9_req *req;
-
-       P9_DPRINTK(P9_DEBUG_MUX, "mux %p task %p tcall %p id %d\n", m, current,
-               tc, tc->id);
-       if (m->err < 0)
-               return ERR_PTR(m->err);
-
-       req = kmalloc(sizeof(struct p9_req), GFP_KERNEL);
-       if (!req)
-               return ERR_PTR(-ENOMEM);
-
-       if (tc->id == P9_TVERSION)
-               n = P9_NOTAG;
-       else
-               n = p9_mux_get_tag(m);
-
-       if (n < 0) {
-               kfree(req);
-               return ERR_PTR(-ENOMEM);
-       }
-
-       p9_set_tag(tc, n);
-
-#ifdef CONFIG_NET_9P_DEBUG
-       if ((p9_debug_level&P9_DEBUG_FCALL) == P9_DEBUG_FCALL) {
-               char buf[150];
-
-               p9_printfcall(buf, sizeof(buf), tc, m->extended);
-               printk(KERN_NOTICE "<<< %p %s\n", m, buf);
-       }
-#endif
-
-       spin_lock_init(&req->lock);
-       req->tag = n;
-       req->tcall = tc;
-       req->rcall = NULL;
-       req->err = 0;
-       req->cb = cb;
-       req->cba = cba;
-       req->flush = None;
-
-       spin_lock(&m->lock);
-       list_add_tail(&req->req_list, &m->unsent_req_list);
-       spin_unlock(&m->lock);
-
-       if (test_and_clear_bit(Wpending, &m->wsched))
-               n = POLLOUT;
-       else
-               n = p9_fd_poll(m->trans, NULL);
-
-       if (n & POLLOUT && !test_and_set_bit(Wworksched, &m->wsched))
-               queue_work(p9_mux_wq, &m->wq);
-
-       return req;
+       return;
+
+error:
+       p9_conn_cancel(m, err);
+       clear_bit(Wworksched, &m->wsched);
 }
 
-static void p9_mux_free_request(struct p9_conn *m, struct p9_req *req)
+static int p9_pollwake(wait_queue_t *wait, unsigned mode, int sync, void *key)
 {
-       p9_mux_put_tag(m, req->tag);
-       kfree(req);
+       struct p9_poll_wait *pwait =
+               container_of(wait, struct p9_poll_wait, wait);
+       struct p9_conn *m = pwait->conn;
+       unsigned long flags;
+       DECLARE_WAITQUEUE(dummy_wait, p9_poll_task);
+
+       spin_lock_irqsave(&p9_poll_lock, flags);
+       if (list_empty(&m->poll_pending_link))
+               list_add_tail(&m->poll_pending_link, &p9_poll_pending_list);
+       spin_unlock_irqrestore(&p9_poll_lock, flags);
+
+       /* perform the default wake up operation */
+       return default_wake_function(&dummy_wait, mode, sync, key);
 }
 
-static void p9_mux_flush_cb(struct p9_req *freq, void *a)
+/**
+ * p9_pollwait - add poll task to the wait queue
+ * @filp: file pointer being polled
+ * @wait_address: wait_q to block on
+ * @p: poll state
+ *
+ * called by files poll operation to add v9fs-poll task to files wait queue
+ */
+
+static void
+p9_pollwait(struct file *filp, wait_queue_head_t *wait_address, poll_table *p)
 {
-       int tag;
-       struct p9_conn *m;
-       struct p9_req *req, *rreq, *rptr;
-
-       m = a;
-       P9_DPRINTK(P9_DEBUG_MUX, "mux %p tc %p rc %p err %d oldtag %d\n", m,
-               freq->tcall, freq->rcall, freq->err,
-               freq->tcall->params.tflush.oldtag);
-
-       spin_lock(&m->lock);
-       tag = freq->tcall->params.tflush.oldtag;
-       req = NULL;
-       list_for_each_entry_safe(rreq, rptr, &m->req_list, req_list) {
-               if (rreq->tag == tag) {
-                       req = rreq;
-                       list_del(&req->req_list);
+       struct p9_conn *m = container_of(p, struct p9_conn, pt);
+       struct p9_poll_wait *pwait = NULL;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(m->poll_wait); i++) {
+               if (m->poll_wait[i].wait_addr == NULL) {
+                       pwait = &m->poll_wait[i];
                        break;
                }
        }
-       spin_unlock(&m->lock);
 
-       if (req) {
-               spin_lock(&req->lock);
-               req->flush = Flushed;
-               spin_unlock(&req->lock);
-
-               if (req->cb)
-                       (*req->cb) (req, req->cba);
-               else
-                       kfree(req->rcall);
+       if (!pwait) {
+               P9_DPRINTK(P9_DEBUG_ERROR, "not enough wait_address slots\n");
+               return;
        }
 
-       kfree(freq->tcall);
-       kfree(freq->rcall);
-       p9_mux_free_request(m, freq);
+       pwait->conn = m;
+       pwait->wait_addr = wait_address;
+       init_waitqueue_func_entry(&pwait->wait, p9_pollwake);
+       add_wait_queue(wait_address, &pwait->wait);
 }
 
-static int
-p9_mux_flush_request(struct p9_conn *m, struct p9_req *req)
-{
-       struct p9_fcall *fc;
-       struct p9_req *rreq, *rptr;
-
-       P9_DPRINTK(P9_DEBUG_MUX, "mux %p req %p tag %d\n", m, req, req->tag);
+/**
+ * p9_conn_create - allocate and initialize the per-session mux data
+ * @client: client instance
+ *
+ * Note: Creates the polling task if this is the first session.
+ */
 
-       /* if a response was received for a request, do nothing */
-       spin_lock(&req->lock);
-       if (req->rcall || req->err) {
-               spin_unlock(&req->lock);
-               P9_DPRINTK(P9_DEBUG_MUX,
-                       "mux %p req %p response already received\n", m, req);
-               return 0;
-       }
+static struct p9_conn *p9_conn_create(struct p9_client *client)
+{
+       int n;
+       struct p9_conn *m;
 
-       req->flush = Flushing;
-       spin_unlock(&req->lock);
-
-       spin_lock(&m->lock);
-       /* if the request is not sent yet, just remove it from the list */
-       list_for_each_entry_safe(rreq, rptr, &m->unsent_req_list, req_list) {
-               if (rreq->tag == req->tag) {
-                       P9_DPRINTK(P9_DEBUG_MUX,
-                          "mux %p req %p request is not sent yet\n", m, req);
-                       list_del(&rreq->req_list);
-                       req->flush = Flushed;
-                       spin_unlock(&m->lock);
-                       if (req->cb)
-                               (*req->cb) (req, req->cba);
-                       return 0;
-               }
-       }
-       spin_unlock(&m->lock);
+       P9_DPRINTK(P9_DEBUG_TRANS, "client %p msize %d\n", client,
+                                                               client->msize);
+       m = kzalloc(sizeof(struct p9_conn), GFP_KERNEL);
+       if (!m)
+               return ERR_PTR(-ENOMEM);
 
-       clear_thread_flag(TIF_SIGPENDING);
-       fc = p9_create_tflush(req->tag);
-       p9_send_request(m, fc, p9_mux_flush_cb, m);
-       return 1;
-}
+       INIT_LIST_HEAD(&m->mux_list);
+       m->client = client;
 
-static void
-p9_conn_rpc_cb(struct p9_req *req, void *a)
-{
-       struct p9_mux_rpc *r;
+       INIT_LIST_HEAD(&m->req_list);
+       INIT_LIST_HEAD(&m->unsent_req_list);
+       INIT_WORK(&m->rq, p9_read_work);
+       INIT_WORK(&m->wq, p9_write_work);
+       INIT_LIST_HEAD(&m->poll_pending_link);
+       init_poll_funcptr(&m->pt, p9_pollwait);
 
-       P9_DPRINTK(P9_DEBUG_MUX, "req %p r %p\n", req, a);
-       r = a;
-       r->rcall = req->rcall;
-       r->err = req->err;
+       n = p9_fd_poll(client, &m->pt);
+       if (n & POLLIN) {
+               P9_DPRINTK(P9_DEBUG_TRANS, "mux %p can read\n", m);
+               set_bit(Rpending, &m->wsched);
+       }
 
-       if (req->flush != None && !req->err)
-               r->err = -ERESTARTSYS;
+       if (n & POLLOUT) {
+               P9_DPRINTK(P9_DEBUG_TRANS, "mux %p can write\n", m);
+               set_bit(Wpending, &m->wsched);
+       }
 
-       wake_up(&r->wqueue);
+       return m;
 }
 
 /**
- * p9_fd_rpc- sends 9P request and waits until a response is available.
- *     The function can be interrupted.
- * @t: transport data
- * @tc: request to be sent
- * @rc: pointer where a pointer to the response is stored
+ * p9_poll_mux - polls a mux and schedules read or write works if necessary
+ * @m: connection to poll
  *
  */
 
-int
-p9_fd_rpc(struct p9_trans *t, struct p9_fcall *tc, struct p9_fcall **rc)
+static void p9_poll_mux(struct p9_conn *m)
 {
-       struct p9_trans_fd *p = t->priv;
-       struct p9_conn *m = p->conn;
-       int err, sigpending;
-       unsigned long flags;
-       struct p9_req *req;
-       struct p9_mux_rpc r;
-
-       r.err = 0;
-       r.tcall = tc;
-       r.rcall = NULL;
-       r.m = m;
-       init_waitqueue_head(&r.wqueue);
-
-       if (rc)
-               *rc = NULL;
-
-       sigpending = 0;
-       if (signal_pending(current)) {
-               sigpending = 1;
-               clear_thread_flag(TIF_SIGPENDING);
-       }
+       int n;
 
-       req = p9_send_request(m, tc, p9_conn_rpc_cb, &r);
-       if (IS_ERR(req)) {
-               err = PTR_ERR(req);
-               P9_DPRINTK(P9_DEBUG_MUX, "error %d\n", err);
-               return err;
+       if (m->err < 0)
+               return;
+
+       n = p9_fd_poll(m->client, NULL);
+       if (n < 0 || n & (POLLERR | POLLHUP | POLLNVAL)) {
+               P9_DPRINTK(P9_DEBUG_TRANS, "error mux %p err %d\n", m, n);
+               if (n >= 0)
+                       n = -ECONNRESET;
+               p9_conn_cancel(m, n);
        }
 
-       err = wait_event_interruptible(r.wqueue, r.rcall != NULL || r.err < 0);
-       if (r.err < 0)
-               err = r.err;
-
-       if (err == -ERESTARTSYS && m->trans->status == Connected
-                                                       && m->err == 0) {
-               if (p9_mux_flush_request(m, req)) {
-                       /* wait until we get response of the flush message */
-                       do {
-                               clear_thread_flag(TIF_SIGPENDING);
-                               err = wait_event_interruptible(r.wqueue,
-                                       r.rcall || r.err);
-                       } while (!r.rcall && !r.err && err == -ERESTARTSYS &&
-                               m->trans->status == Connected && !m->err);
-
-                       err = -ERESTARTSYS;
+       if (n & POLLIN) {
+               set_bit(Rpending, &m->wsched);
+               P9_DPRINTK(P9_DEBUG_TRANS, "mux %p can read\n", m);
+               if (!test_and_set_bit(Rworksched, &m->wsched)) {
+                       P9_DPRINTK(P9_DEBUG_TRANS, "sched read work %p\n", m);
+                       queue_work(p9_mux_wq, &m->rq);
                }
-               sigpending = 1;
        }
 
-       if (sigpending) {
-               spin_lock_irqsave(&current->sighand->siglock, flags);
-               recalc_sigpending();
-               spin_unlock_irqrestore(&current->sighand->siglock, flags);
+       if (n & POLLOUT) {
+               set_bit(Wpending, &m->wsched);
+               P9_DPRINTK(P9_DEBUG_TRANS, "mux %p can write\n", m);
+               if ((m->wsize || !list_empty(&m->unsent_req_list))
+                   && !test_and_set_bit(Wworksched, &m->wsched)) {
+                       P9_DPRINTK(P9_DEBUG_TRANS, "sched write work %p\n", m);
+                       queue_work(p9_mux_wq, &m->wq);
+               }
        }
-
-       if (rc)
-               *rc = r.rcall;
-       else
-               kfree(r.rcall);
-
-       p9_mux_free_request(m, req);
-       if (err > 0)
-               err = -EIO;
-
-       return err;
 }
 
-#ifdef P9_NONBLOCK
 /**
- * p9_conn_rpcnb - sends 9P request without waiting for response.
- * @m: mux data
- * @tc: request to be sent
- * @cb: callback function to be called when response arrives
- * @a: value to pass to the callback function
+ * p9_fd_request - send 9P request
+ * The function can sleep until the request is scheduled for sending.
+ * The function can be interrupted. Return from the function is not
+ * a guarantee that the request is sent successfully.
+ *
+ * @client: client instance
+ * @req: request to be sent
  *
  */
 
-int p9_conn_rpcnb(struct p9_conn *m, struct p9_fcall *tc,
-                  p9_conn_req_callback cb, void *a)
+static int p9_fd_request(struct p9_client *client, struct p9_req_t *req)
 {
-       int err;
-       struct p9_req *req;
+       int n;
+       struct p9_trans_fd *ts = client->trans;
+       struct p9_conn *m = ts->conn;
 
-       req = p9_send_request(m, tc, cb, a);
-       if (IS_ERR(req)) {
-               err = PTR_ERR(req);
-               P9_DPRINTK(P9_DEBUG_MUX, "error %d\n", err);
-               return PTR_ERR(req);
-       }
+       P9_DPRINTK(P9_DEBUG_TRANS, "mux %p task %p tcall %p id %d\n", m,
+                                               current, req->tc, req->tc->id);
+       if (m->err < 0)
+               return m->err;
+
+       spin_lock(&client->lock);
+       req->status = REQ_STATUS_UNSENT;
+       list_add_tail(&req->req_list, &m->unsent_req_list);
+       spin_unlock(&client->lock);
+
+       if (test_and_clear_bit(Wpending, &m->wsched))
+               n = POLLOUT;
+       else
+               n = p9_fd_poll(m->client, NULL);
+
+       if (n & POLLOUT && !test_and_set_bit(Wworksched, &m->wsched))
+               queue_work(p9_mux_wq, &m->wq);
 
-       P9_DPRINTK(P9_DEBUG_MUX, "mux %p tc %p tag %d\n", m, tc, req->tag);
        return 0;
 }
-#endif /* P9_NONBLOCK */
-
-/**
- * p9_conn_cancel - cancel all pending requests with error
- * @m: mux data
- * @err: error code
- *
- */
 
-void p9_conn_cancel(struct p9_conn *m, int err)
+static int p9_fd_cancel(struct p9_client *client, struct p9_req_t *req)
 {
-       struct p9_req *req, *rtmp;
-       LIST_HEAD(cancel_list);
+       struct p9_trans_fd *ts = client->trans;
+       struct p9_conn *m = ts->conn;
+       int ret = 1;
 
-       P9_DPRINTK(P9_DEBUG_ERROR, "mux %p err %d\n", m, err);
-       m->err = err;
-       spin_lock(&m->lock);
-       list_for_each_entry_safe(req, rtmp, &m->req_list, req_list) {
-               list_move(&req->req_list, &cancel_list);
-       }
-       list_for_each_entry_safe(req, rtmp, &m->unsent_req_list, req_list) {
-               list_move(&req->req_list, &cancel_list);
-       }
-       spin_unlock(&m->lock);
+       P9_DPRINTK(P9_DEBUG_TRANS, "mux %p req %p\n", m, req);
 
-       list_for_each_entry_safe(req, rtmp, &cancel_list, req_list) {
-               list_del(&req->req_list);
-               if (!req->err)
-                       req->err = err;
+       spin_lock(&client->lock);
+       list_del(&req->req_list);
 
-               if (req->cb)
-                       (*req->cb) (req, req->cba);
-               else
-                       kfree(req->rcall);
+       if (req->status == REQ_STATUS_UNSENT) {
+               req->status = REQ_STATUS_FLSHD;
+               ret = 0;
        }
+
+       spin_unlock(&client->lock);
+
+       return ret;
 }
 
 /**
@@ -1216,7 +758,7 @@ static int parse_opts(char *params, struct p9_fd_opts *opts)
        return 0;
 }
 
-static int p9_fd_open(struct p9_trans *trans, int rfd, int wfd)
+static int p9_fd_open(struct p9_client *client, int rfd, int wfd)
 {
        struct p9_trans_fd *ts = kmalloc(sizeof(struct p9_trans_fd),
                                           GFP_KERNEL);
@@ -1234,13 +776,13 @@ static int p9_fd_open(struct p9_trans *trans, int rfd, int wfd)
                return -EIO;
        }
 
-       trans->priv = ts;
-       trans->status = Connected;
+       client->trans = ts;
+       client->status = Connected;
 
        return 0;
 }
 
-static int p9_socket_open(struct p9_trans *trans, struct socket *csocket)
+static int p9_socket_open(struct p9_client *client, struct socket *csocket)
 {
        int fd, ret;
 
@@ -1251,137 +793,65 @@ static int p9_socket_open(struct p9_trans *trans, struct socket *csocket)
                return fd;
        }
 
-       ret = p9_fd_open(trans, fd, fd);
+       ret = p9_fd_open(client, fd, fd);
        if (ret < 0) {
                P9_EPRINTK(KERN_ERR, "p9_socket_open: failed to open fd\n");
                sockfd_put(csocket);
                return ret;
        }
 
-       ((struct p9_trans_fd *)trans->priv)->rd->f_flags |= O_NONBLOCK;
+       ((struct p9_trans_fd *)client->trans)->rd->f_flags |= O_NONBLOCK;
 
        return 0;
 }
 
 /**
- * p9_fd_read- read from a fd
- * @trans: transport instance state
- * @v: buffer to receive data into
- * @len: size of receive buffer
- *
- */
-
-static int p9_fd_read(struct p9_trans *trans, void *v, int len)
-{
-       int ret;
-       struct p9_trans_fd *ts = NULL;
-
-       if (trans && trans->status != Disconnected)
-               ts = trans->priv;
-
-       if (!ts)
-               return -EREMOTEIO;
-
-       if (!(ts->rd->f_flags & O_NONBLOCK))
-               P9_DPRINTK(P9_DEBUG_ERROR, "blocking read ...\n");
-
-       ret = kernel_read(ts->rd, ts->rd->f_pos, v, len);
-       if (ret <= 0 && ret != -ERESTARTSYS && ret != -EAGAIN)
-               trans->status = Disconnected;
-       return ret;
-}
-
-/**
- * p9_fd_write - write to a socket
- * @trans: transport instance state
- * @v: buffer to send data from
- * @len: size of send buffer
+ * p9_mux_destroy - cancels all pending requests and frees mux resources
+ * @m: mux to destroy
  *
  */
 
-static int p9_fd_write(struct p9_trans *trans, void *v, int len)
-{
-       int ret;
-       mm_segment_t oldfs;
-       struct p9_trans_fd *ts = NULL;
-
-       if (trans && trans->status != Disconnected)
-               ts = trans->priv;
-
-       if (!ts)
-               return -EREMOTEIO;
-
-       if (!(ts->wr->f_flags & O_NONBLOCK))
-               P9_DPRINTK(P9_DEBUG_ERROR, "blocking write ...\n");
-
-       oldfs = get_fs();
-       set_fs(get_ds());
-       /* The cast to a user pointer is valid due to the set_fs() */
-       ret = vfs_write(ts->wr, (void __user *)v, len, &ts->wr->f_pos);
-       set_fs(oldfs);
-
-       if (ret <= 0 && ret != -ERESTARTSYS && ret != -EAGAIN)
-               trans->status = Disconnected;
-       return ret;
-}
-
-static unsigned int
-p9_fd_poll(struct p9_trans *trans, struct poll_table_struct *pt)
+static void p9_conn_destroy(struct p9_conn *m)
 {
-       int ret, n;
-       struct p9_trans_fd *ts = NULL;
-
-       if (trans && trans->status == Connected)
-               ts = trans->priv;
-
-       if (!ts)
-               return -EREMOTEIO;
-
-       if (!ts->rd->f_op || !ts->rd->f_op->poll)
-               return -EIO;
-
-       if (!ts->wr->f_op || !ts->wr->f_op->poll)
-               return -EIO;
+       P9_DPRINTK(P9_DEBUG_TRANS, "mux %p prev %p next %p\n", m,
+               m->mux_list.prev, m->mux_list.next);
 
-       ret = ts->rd->f_op->poll(ts->rd, pt);
-       if (ret < 0)
-               return ret;
+       p9_mux_poll_stop(m);
+       cancel_work_sync(&m->rq);
+       cancel_work_sync(&m->wq);
 
-       if (ts->rd != ts->wr) {
-               n = ts->wr->f_op->poll(ts->wr, pt);
-               if (n < 0)
-                       return n;
-               ret = (ret & ~POLLOUT) | (n & ~POLLIN);
-       }
+       p9_conn_cancel(m, -ECONNRESET);
 
-       return ret;
+       m->client = NULL;
+       kfree(m);
 }
 
 /**
- * p9_fd_close - shutdown socket
- * @trans: private socket structure
+ * p9_fd_close - shutdown file descriptor transport
+ * @client: client instance
  *
  */
 
-static void p9_fd_close(struct p9_trans *trans)
+static void p9_fd_close(struct p9_client *client)
 {
        struct p9_trans_fd *ts;
 
-       if (!trans)
+       if (!client)
                return;
 
-       ts = xchg(&trans->priv, NULL);
-
+       ts = client->trans;
        if (!ts)
                return;
 
+       client->status = Disconnected;
+
        p9_conn_destroy(ts->conn);
 
-       trans->status = Disconnected;
        if (ts->rd)
                fput(ts->rd);
        if (ts->wr)
                fput(ts->wr);
+
        kfree(ts);
 }
 
@@ -1402,31 +872,23 @@ static inline int valid_ipaddr4(const char *buf)
        return 0;
 }
 
-static struct p9_trans *
-p9_trans_create_tcp(const char *addr, char *args, int msize, unsigned char dotu)
+static int
+p9_fd_create_tcp(struct p9_client *client, const char *addr, char *args)
 {
        int err;
-       struct p9_trans *trans;
        struct socket *csocket;
        struct sockaddr_in sin_server;
        struct p9_fd_opts opts;
-       struct p9_trans_fd *p;
+       struct p9_trans_fd *p = NULL; /* this gets allocated in p9_fd_open */
 
        err = parse_opts(args, &opts);
        if (err < 0)
-               return ERR_PTR(err);
+               return err;
 
        if (valid_ipaddr4(addr) < 0)
-               return ERR_PTR(-EINVAL);
+               return -EINVAL;
 
        csocket = NULL;
-       trans = kmalloc(sizeof(struct p9_trans), GFP_KERNEL);
-       if (!trans)
-               return ERR_PTR(-ENOMEM);
-       trans->msize = msize;
-       trans->extended = dotu;
-       trans->rpc = p9_fd_rpc;
-       trans->close = p9_fd_close;
 
        sin_server.sin_family = AF_INET;
        sin_server.sin_addr.s_addr = in_aton(addr);
@@ -1449,45 +911,38 @@ p9_trans_create_tcp(const char *addr, char *args, int msize, unsigned char dotu)
                goto error;
        }
 
-       err = p9_socket_open(trans, csocket);
+       err = p9_socket_open(client, csocket);
        if (err < 0)
                goto error;
 
-       p = (struct p9_trans_fd *) trans->priv;
-       p->conn = p9_conn_create(trans);
+       p = (struct p9_trans_fd *) client->trans;
+       p->conn = p9_conn_create(client);
        if (IS_ERR(p->conn)) {
                err = PTR_ERR(p->conn);
                p->conn = NULL;
                goto error;
        }
 
-       return trans;
+       return 0;
 
 error:
        if (csocket)
                sock_release(csocket);
 
-       kfree(trans);
-       return ERR_PTR(err);
+       kfree(p);
+
+       return err;
 }
 
-static struct p9_trans *
-p9_trans_create_unix(const char *addr, char *args, int msize,
-                                                       unsigned char dotu)
+static int
+p9_fd_create_unix(struct p9_client *client, const char *addr, char *args)
 {
        int err;
        struct socket *csocket;
        struct sockaddr_un sun_server;
-       struct p9_trans *trans;
-       struct p9_trans_fd *p;
+       struct p9_trans_fd *p = NULL; /* this gets allocated in p9_fd_open */
 
        csocket = NULL;
-       trans = kmalloc(sizeof(struct p9_trans), GFP_KERNEL);
-       if (!trans)
-               return ERR_PTR(-ENOMEM);
-
-       trans->rpc = p9_fd_rpc;
-       trans->close = p9_fd_close;
 
        if (strlen(addr) > UNIX_PATH_MAX) {
                P9_EPRINTK(KERN_ERR, "p9_trans_unix: address too long: %s\n",
@@ -1508,79 +963,69 @@ p9_trans_create_unix(const char *addr, char *args, int msize,
                goto error;
        }
 
-       err = p9_socket_open(trans, csocket);
+       err = p9_socket_open(client, csocket);
        if (err < 0)
                goto error;
 
-       trans->msize = msize;
-       trans->extended = dotu;
-       p = (struct p9_trans_fd *) trans->priv;
-       p->conn = p9_conn_create(trans);
+       p = (struct p9_trans_fd *) client->trans;
+       p->conn = p9_conn_create(client);
        if (IS_ERR(p->conn)) {
                err = PTR_ERR(p->conn);
                p->conn = NULL;
                goto error;
        }
 
-       return trans;
+       return 0;
 
 error:
        if (csocket)
                sock_release(csocket);
 
-       kfree(trans);
-       return ERR_PTR(err);
+       kfree(p);
+       return err;
 }
 
-static struct p9_trans *
-p9_trans_create_fd(const char *name, char *args, int msize,
-                                                       unsigned char extended)
+static int
+p9_fd_create(struct p9_client *client, const char *addr, char *args)
 {
        int err;
-       struct p9_trans *trans;
        struct p9_fd_opts opts;
-       struct p9_trans_fd *p;
+       struct p9_trans_fd *p = NULL; /* this get allocated in p9_fd_open */
 
        parse_opts(args, &opts);
 
        if (opts.rfd == ~0 || opts.wfd == ~0) {
                printk(KERN_ERR "v9fs: Insufficient options for proto=fd\n");
-               return ERR_PTR(-ENOPROTOOPT);
+               return -ENOPROTOOPT;
        }
 
-       trans = kmalloc(sizeof(struct p9_trans), GFP_KERNEL);
-       if (!trans)
-               return ERR_PTR(-ENOMEM);
-
-       trans->rpc = p9_fd_rpc;
-       trans->close = p9_fd_close;
-
-       err = p9_fd_open(trans, opts.rfd, opts.wfd);
+       err = p9_fd_open(client, opts.rfd, opts.wfd);
        if (err < 0)
                goto error;
 
-       trans->msize = msize;
-       trans->extended = extended;
-       p = (struct p9_trans_fd *) trans->priv;
-       p->conn = p9_conn_create(trans);
+       p = (struct p9_trans_fd *) client->trans;
+       p->conn = p9_conn_create(client);
        if (IS_ERR(p->conn)) {
                err = PTR_ERR(p->conn);
                p->conn = NULL;
                goto error;
        }
 
-       return trans;
+       return 0;
 
 error:
-       kfree(trans);
-       return ERR_PTR(err);
+       kfree(p);
+       return err;
 }
 
 static struct p9_trans_module p9_tcp_trans = {
        .name = "tcp",
        .maxsize = MAX_SOCK_BUF,
        .def = 1,
-       .create = p9_trans_create_tcp,
+       .create = p9_fd_create_tcp,
+       .close = p9_fd_close,
+       .request = p9_fd_request,
+       .cancel = p9_fd_cancel,
        .owner = THIS_MODULE,
 };
 
@@ -1588,7 +1033,10 @@ static struct p9_trans_module p9_unix_trans = {
        .name = "unix",
        .maxsize = MAX_SOCK_BUF,
        .def = 0,
-       .create = p9_trans_create_unix,
+       .create = p9_fd_create_unix,
+       .close = p9_fd_close,
+       .request = p9_fd_request,
+       .cancel = p9_fd_cancel,
        .owner = THIS_MODULE,
 };
 
@@ -1596,23 +1044,71 @@ static struct p9_trans_module p9_fd_trans = {
        .name = "fd",
        .maxsize = MAX_SOCK_BUF,
        .def = 0,
-       .create = p9_trans_create_fd,
+       .create = p9_fd_create,
+       .close = p9_fd_close,
+       .request = p9_fd_request,
+       .cancel = p9_fd_cancel,
        .owner = THIS_MODULE,
 };
 
-int p9_trans_fd_init(void)
+/**
+ * p9_poll_proc - poll worker thread
+ * @a: thread state and arguments
+ *
+ * polls all v9fs transports for new events and queues the appropriate
+ * work to the work queue
+ *
+ */
+
+static int p9_poll_proc(void *a)
 {
-       int i;
+       unsigned long flags;
+
+       P9_DPRINTK(P9_DEBUG_TRANS, "start %p\n", current);
+ repeat:
+       spin_lock_irqsave(&p9_poll_lock, flags);
+       while (!list_empty(&p9_poll_pending_list)) {
+               struct p9_conn *conn = list_first_entry(&p9_poll_pending_list,
+                                                       struct p9_conn,
+                                                       poll_pending_link);
+               list_del_init(&conn->poll_pending_link);
+               spin_unlock_irqrestore(&p9_poll_lock, flags);
+
+               p9_poll_mux(conn);
+
+               spin_lock_irqsave(&p9_poll_lock, flags);
+       }
+       spin_unlock_irqrestore(&p9_poll_lock, flags);
+
+       set_current_state(TASK_INTERRUPTIBLE);
+       if (list_empty(&p9_poll_pending_list)) {
+               P9_DPRINTK(P9_DEBUG_TRANS, "sleeping...\n");
+               schedule();
+       }
+       __set_current_state(TASK_RUNNING);
+
+       if (!kthread_should_stop())
+               goto repeat;
 
-       for (i = 0; i < ARRAY_SIZE(p9_mux_poll_tasks); i++)
-               p9_mux_poll_tasks[i].task = NULL;
+       P9_DPRINTK(P9_DEBUG_TRANS, "finish\n");
+       return 0;
+}
 
+int p9_trans_fd_init(void)
+{
        p9_mux_wq = create_workqueue("v9fs");
        if (!p9_mux_wq) {
                printk(KERN_WARNING "v9fs: mux: creating workqueue failed\n");
                return -ENOMEM;
        }
 
+       p9_poll_task = kthread_run(p9_poll_proc, NULL, "v9fs-poll");
+       if (IS_ERR(p9_poll_task)) {
+               destroy_workqueue(p9_mux_wq);
+               printk(KERN_WARNING "v9fs: mux: creating poll task failed\n");
+               return PTR_ERR(p9_poll_task);
+       }
+
        v9fs_register_trans(&p9_tcp_trans);
        v9fs_register_trans(&p9_unix_trans);
        v9fs_register_trans(&p9_fd_trans);
@@ -1622,6 +1118,7 @@ int p9_trans_fd_init(void)
 
 void p9_trans_fd_exit(void)
 {
+       kthread_stop(p9_poll_task);
        v9fs_unregister_trans(&p9_tcp_trans);
        v9fs_unregister_trans(&p9_unix_trans);
        v9fs_unregister_trans(&p9_fd_trans);
index 94912e077a55e0ecdfdd137c9a9848256edd66b3..2d7781ec663ba18425e2bbd57d6340955a3d7de4 100644 (file)
@@ -1,12 +1,10 @@
 /*
- * The Guest 9p transport driver
+ * The Virtio 9p transport driver
  *
  * This is a block based transport driver based on the lguest block driver
  * code.
  *
- */
-/*
- *  Copyright (C) 2007 Eric Van Hensbergen, IBM Corporation
+ *  Copyright (C) 2007, 2008 Eric Van Hensbergen, IBM Corporation
  *
  *  Based on virtio console driver
  *  Copyright (C) 2006, 2007 Rusty Russell, IBM Corporation
@@ -41,6 +39,7 @@
 #include <linux/file.h>
 #include <net/9p/9p.h>
 #include <linux/parser.h>
+#include <net/9p/client.h>
 #include <net/9p/transport.h>
 #include <linux/scatterlist.h>
 #include <linux/virtio.h>
@@ -53,50 +52,6 @@ static DEFINE_MUTEX(virtio_9p_lock);
 /* global which tracks highest initialized channel */
 static int chan_index;
 
-#define P9_INIT_MAXTAG 16
-
-
-/**
- * enum p9_req_status_t - virtio request status
- * @REQ_STATUS_IDLE: request slot unused
- * @REQ_STATUS_SENT: request sent to server
- * @REQ_STATUS_RCVD: response received from server
- * @REQ_STATUS_FLSH: request has been flushed
- *
- * The @REQ_STATUS_IDLE state is used to mark a request slot as unused
- * but use is actually tracked by the idpool structure which handles tag
- * id allocation.
- *
- */
-
-enum p9_req_status_t {
-       REQ_STATUS_IDLE,
-       REQ_STATUS_SENT,
-       REQ_STATUS_RCVD,
-       REQ_STATUS_FLSH,
-};
-
-/**
- * struct p9_req_t - virtio request slots
- * @status: status of this request slot
- * @wq: wait_queue for the client to block on for this request
- *
- * The virtio transport uses an array to track outstanding requests
- * instead of a list.  While this may incurr overhead during initial
- * allocation or expansion, it makes request lookup much easier as the
- * tag id is a index into an array.  (We use tag+1 so that we can accomodate
- * the -1 tag for the T_VERSION request).
- * This also has the nice effect of only having to allocate wait_queues
- * once, instead of constantly allocating and freeing them.  Its possible
- * other resources could benefit from this scheme as well.
- *
- */
-
-struct p9_req_t {
-       int status;
-       wait_queue_head_t *wq;
-};
-
 /**
  * struct virtio_chan - per-instance transport information
  * @initialized: whether the channel is initialized
@@ -121,67 +76,14 @@ static struct virtio_chan {
 
        spinlock_t lock;
 
+       struct p9_client *client;
        struct virtio_device *vdev;
        struct virtqueue *vq;
 
-       struct p9_idpool *tagpool;
-       struct p9_req_t *reqs;
-       int max_tag;
-
        /* Scatterlist: can be too big for stack. */
        struct scatterlist sg[VIRTQUEUE_NUM];
 } channels[MAX_9P_CHAN];
 
-/**
- * p9_lookup_tag - Lookup requests by tag
- * @c: virtio channel to lookup tag within
- * @tag: numeric id for transaction
- *
- * this is a simple array lookup, but will grow the
- * request_slots as necessary to accomodate transaction
- * ids which did not previously have a slot.
- *
- * Bugs: there is currently no upper limit on request slots set
- * here, but that should be constrained by the id accounting.
- */
-
-static struct p9_req_t *p9_lookup_tag(struct virtio_chan *c, u16 tag)
-{
-       /* This looks up the original request by tag so we know which
-        * buffer to read the data into */
-       tag++;
-
-       while (tag >= c->max_tag) {
-               int old_max = c->max_tag;
-               int count;
-
-               if (c->max_tag)
-                       c->max_tag *= 2;
-               else
-                       c->max_tag = P9_INIT_MAXTAG;
-
-               c->reqs = krealloc(c->reqs, sizeof(struct p9_req_t)*c->max_tag,
-                                                               GFP_ATOMIC);
-               if (!c->reqs) {
-                       printk(KERN_ERR "Couldn't grow tag array\n");
-                       BUG();
-               }
-               for (count = old_max; count < c->max_tag; count++) {
-                       c->reqs[count].status = REQ_STATUS_IDLE;
-                       c->reqs[count].wq = kmalloc(sizeof(wait_queue_head_t),
-                                                               GFP_ATOMIC);
-                       if (!c->reqs[count].wq) {
-                               printk(KERN_ERR "Couldn't grow tag array\n");
-                               BUG();
-                       }
-                       init_waitqueue_head(c->reqs[count].wq);
-               }
-       }
-
-       return &c->reqs[tag];
-}
-
-
 /* How many bytes left in this page. */
 static unsigned int rest_of_page(void *data)
 {
@@ -197,25 +99,13 @@ static unsigned int rest_of_page(void *data)
  *
  */
 
-static void p9_virtio_close(struct p9_trans *trans)
+static void p9_virtio_close(struct p9_client *client)
 {
-       struct virtio_chan *chan = trans->priv;
-       int count;
-       unsigned long flags;
-
-       spin_lock_irqsave(&chan->lock, flags);
-       p9_idpool_destroy(chan->tagpool);
-       for (count = 0; count < chan->max_tag; count++)
-               kfree(chan->reqs[count].wq);
-       kfree(chan->reqs);
-       chan->max_tag = 0;
-       spin_unlock_irqrestore(&chan->lock, flags);
+       struct virtio_chan *chan = client->trans;
 
        mutex_lock(&virtio_9p_lock);
        chan->inuse = false;
        mutex_unlock(&virtio_9p_lock);
-
-       kfree(trans);
 }
 
 /**
@@ -236,17 +126,16 @@ static void req_done(struct virtqueue *vq)
        struct virtio_chan *chan = vq->vdev->priv;
        struct p9_fcall *rc;
        unsigned int len;
-       unsigned long flags;
        struct p9_req_t *req;
 
-       spin_lock_irqsave(&chan->lock, flags);
+       P9_DPRINTK(P9_DEBUG_TRANS, ": request done\n");
+
        while ((rc = chan->vq->vq_ops->get_buf(chan->vq, &len)) != NULL) {
-               req = p9_lookup_tag(chan, rc->tag);
-               req->status = REQ_STATUS_RCVD;
-               wake_up(req->wq);
+               P9_DPRINTK(P9_DEBUG_TRANS, ": rc %p\n", rc);
+               P9_DPRINTK(P9_DEBUG_TRANS, ": lookup tag %d\n", rc->tag);
+               req = p9_tag_lookup(chan->client, rc->tag);
+               p9_client_cb(chan->client, req);
        }
-       /* In case queue is stopped waiting for more buffers. */
-       spin_unlock_irqrestore(&chan->lock, flags);
 }
 
 /**
@@ -283,8 +172,14 @@ pack_sg_list(struct scatterlist *sg, int start, int limit, char *data,
        return index-start;
 }
 
+/* We don't currently allow canceling of virtio requests */
+static int p9_virtio_cancel(struct p9_client *client, struct p9_req_t *req)
+{
+       return 1;
+}
+
 /**
- * p9_virtio_rpc - issue a request and wait for a response
+ * p9_virtio_request - issue a request
  * @t: transport state
  * @tc: &p9_fcall request to transmit
  * @rc: &p9_fcall to put reponse into
@@ -292,44 +187,22 @@ pack_sg_list(struct scatterlist *sg, int start, int limit, char *data,
  */
 
 static int
-p9_virtio_rpc(struct p9_trans *t, struct p9_fcall *tc, struct p9_fcall **rc)
+p9_virtio_request(struct p9_client *client, struct p9_req_t *req)
 {
        int in, out;
-       int n, err, size;
-       struct virtio_chan *chan = t->priv;
-       char *rdata;
-       struct p9_req_t *req;
-       unsigned long flags;
-
-       if (*rc == NULL) {
-               *rc = kmalloc(sizeof(struct p9_fcall) + t->msize, GFP_KERNEL);
-               if (!*rc)
-                       return -ENOMEM;
-       }
-
-       rdata = (char *)*rc+sizeof(struct p9_fcall);
-
-       n = P9_NOTAG;
-       if (tc->id != P9_TVERSION) {
-               n = p9_idpool_get(chan->tagpool);
-               if (n < 0)
-                       return -ENOMEM;
-       }
-
-       spin_lock_irqsave(&chan->lock, flags);
-       req = p9_lookup_tag(chan, n);
-       spin_unlock_irqrestore(&chan->lock, flags);
+       struct virtio_chan *chan = client->trans;
+       char *rdata = (char *)req->rc+sizeof(struct p9_fcall);
 
-       p9_set_tag(tc, n);
+       P9_DPRINTK(P9_DEBUG_TRANS, "9p debug: virtio request\n");
 
-       P9_DPRINTK(P9_DEBUG_TRANS, "9p debug: virtio rpc tag %d\n", n);
-
-       out = pack_sg_list(chan->sg, 0, VIRTQUEUE_NUM, tc->sdata, tc->size);
-       in = pack_sg_list(chan->sg, out, VIRTQUEUE_NUM-out, rdata, t->msize);
+       out = pack_sg_list(chan->sg, 0, VIRTQUEUE_NUM, req->tc->sdata,
+                                                               req->tc->size);
+       in = pack_sg_list(chan->sg, out, VIRTQUEUE_NUM-out, rdata,
+                                                               client->msize);
 
        req->status = REQ_STATUS_SENT;
 
-       if (chan->vq->vq_ops->add_buf(chan->vq, chan->sg, out, in, tc)) {
+       if (chan->vq->vq_ops->add_buf(chan->vq, chan->sg, out, in, req->tc)) {
                P9_DPRINTK(P9_DEBUG_TRANS,
                        "9p debug: virtio rpc add_buf returned failure");
                return -EIO;
@@ -337,31 +210,7 @@ p9_virtio_rpc(struct p9_trans *t, struct p9_fcall *tc, struct p9_fcall **rc)
 
        chan->vq->vq_ops->kick(chan->vq);
 
-       wait_event(*req->wq, req->status == REQ_STATUS_RCVD);
-
-       size = le32_to_cpu(*(__le32 *) rdata);
-
-       err = p9_deserialize_fcall(rdata, size, *rc, t->extended);
-       if (err < 0) {
-               P9_DPRINTK(P9_DEBUG_TRANS,
-                       "9p debug: virtio rpc deserialize returned %d\n", err);
-               return err;
-       }
-
-#ifdef CONFIG_NET_9P_DEBUG
-       if ((p9_debug_level&P9_DEBUG_FCALL) == P9_DEBUG_FCALL) {
-               char buf[150];
-
-               p9_printfcall(buf, sizeof(buf), *rc, t->extended);
-               printk(KERN_NOTICE ">>> %p %s\n", t, buf);
-       }
-#endif
-
-       if (n != P9_NOTAG && p9_idpool_check(n, chan->tagpool))
-               p9_idpool_put(n, chan->tagpool);
-
-       req->status = REQ_STATUS_IDLE;
-
+       P9_DPRINTK(P9_DEBUG_TRANS, "9p debug: virtio request kicked\n");
        return 0;
 }
 
@@ -422,10 +271,9 @@ fail:
 
 /**
  * p9_virtio_create - allocate a new virtio channel
+ * @client: client instance invoking this transport
  * @devname: string identifying the channel to connect to (unused)
  * @args: args passed from sys_mount() for per-transport options (unused)
- * @msize: requested maximum packet size
- * @extended: 9p2000.u enabled flag
  *
  * This sets up a transport channel for 9p communication.  Right now
  * we only match the first available channel, but eventually we couldlook up
@@ -441,11 +289,9 @@ fail:
  *
  */
 
-static struct p9_trans *
-p9_virtio_create(const char *devname, char *args, int msize,
-                                                       unsigned char extended)
+static int
+p9_virtio_create(struct p9_client *client, const char *devname, char *args)
 {
-       struct p9_trans *trans;
        struct virtio_chan *chan = channels;
        int index = 0;
 
@@ -463,30 +309,13 @@ p9_virtio_create(const char *devname, char *args, int msize,
 
        if (index >= MAX_9P_CHAN) {
                printk(KERN_ERR "9p: no channels available\n");
-               return ERR_PTR(-ENODEV);
+               return -ENODEV;
        }
 
-       chan->tagpool = p9_idpool_create();
-       if (IS_ERR(chan->tagpool)) {
-               printk(KERN_ERR "9p: couldn't allocate tagpool\n");
-               return ERR_PTR(-ENOMEM);
-       }
-       p9_idpool_get(chan->tagpool); /* reserve tag 0 */
-       chan->max_tag = 0;
-       chan->reqs = NULL;
-
-       trans = kmalloc(sizeof(struct p9_trans), GFP_KERNEL);
-       if (!trans) {
-               printk(KERN_ERR "9p: couldn't allocate transport\n");
-               return ERR_PTR(-ENOMEM);
-       }
-       trans->extended = extended;
-       trans->msize = msize;
-       trans->close = p9_virtio_close;
-       trans->rpc = p9_virtio_rpc;
-       trans->priv = chan;
+       client->trans = (void *)chan;
+       chan->client = client;
 
-       return trans;
+       return 0;
 }
 
 /**
@@ -526,6 +355,9 @@ static struct virtio_driver p9_virtio_drv = {
 static struct p9_trans_module p9_virtio_trans = {
        .name = "virtio",
        .create = p9_virtio_create,
+       .close = p9_virtio_close,
+       .request = p9_virtio_request,
+       .cancel = p9_virtio_cancel,
        .maxsize = PAGE_SIZE*16,
        .def = 0,
        .owner = THIS_MODULE,
index 958fc58cd1ffbb0723552b180302c569387adfb4..dc4ec05ad93d5324b3a7d6b81c63ae1c7815af39 100644 (file)
@@ -105,6 +105,7 @@ retry:
        else if (error)
                return -1;
 
+       P9_DPRINTK(P9_DEBUG_MUX, " id %d pool %p\n", i, p);
        return i;
 }
 EXPORT_SYMBOL(p9_idpool_get);
@@ -121,6 +122,9 @@ EXPORT_SYMBOL(p9_idpool_get);
 void p9_idpool_put(int id, struct p9_idpool *p)
 {
        unsigned long flags;
+
+       P9_DPRINTK(P9_DEBUG_MUX, " id %d pool %p\n", id, p);
+
        spin_lock_irqsave(&p->lock, flags);
        idr_remove(&p->pool, id);
        spin_unlock_irqrestore(&p->lock, flags);