Merge tag '9p-for-4.20' of git://github.com/martinetd/linux
authorLinus Torvalds <torvalds@linux-foundation.org>
Mon, 29 Oct 2018 16:09:47 +0000 (09:09 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 29 Oct 2018 16:09:47 +0000 (09:09 -0700)
Pull 9p updates from Dominique Martinet:
 "Highlights this time around are the end of Matthew's work to remove
  the custom 9p request cache and use a slab directly for requests, with
  some extra patches on my end to not degrade performance, but it's a
  very good cleanup.

  Tomas and I fixed a few more syzkaller bugs (refcount is the big one),
  and I had a go at the coverity bugs and at some of the bugzilla
  reports we had open for a while.

  I'm a bit disappointed that I couldn't get much reviews for a few of
  my own patches, but the big ones got some and it's all been soaking in
  linux-next for quite a while so I think it should be OK.

  Summary:

   - Finish removing the custom 9p request cache mechanism

   - Embed part of the fcall in the request to have better slab
     performance (msize usually is power of two aligned)

   - syzkaller fixes:
      * add a refcount to 9p requests to avoid use after free
      * a few double free issues

   - A few coverity fixes

   - Some old patches that were in the bugzilla:
      * do not trust pdu content for size header
      * mount option for lock retry interval"

* tag '9p-for-4.20' of git://github.com/martinetd/linux: (21 commits)
  9p/trans_fd: put worker reqs on destroy
  9p/trans_fd: abort p9_read_work if req status changed
  9p: potential NULL dereference
  9p locks: fix glock.client_id leak in do_lock
  9p: p9dirent_read: check network-provided name length
  9p/rdma: remove useless check in cm_event_handler
  9p: acl: fix uninitialized iattr access
  9p locks: add mount option for lock retry interval
  9p: do not trust pdu content for stat item size
  9p: Rename req to rreq in trans_fd
  9p: fix spelling mistake in fall-through annotation
  9p/rdma: do not disconnect on down_interruptible EAGAIN
  9p: Add refcount to p9_req_t
  9p: rename p9_free_req() function
  9p: add a per-client fcall kmem_cache
  9p: embed fcall in req to round down buffer allocs
  9p: Remove p9_idpool
  9p: Use a slab for allocating requests
  9p: clear dangling pointers in p9stat_free
  v9fs_dir_readdir: fix double-free on p9stat_read error
  ...

16 files changed:
fs/9p/acl.c
fs/9p/v9fs.c
fs/9p/v9fs.h
fs/9p/vfs_dir.c
fs/9p/vfs_file.c
include/net/9p/9p.h
include/net/9p/client.h
net/9p/Makefile
net/9p/client.c
net/9p/mod.c
net/9p/protocol.c
net/9p/trans_fd.c
net/9p/trans_rdma.c
net/9p/trans_virtio.c
net/9p/trans_xen.c
net/9p/util.c [deleted file]

index 082d227fa56b378772883f3a4254e7fd05c436d5..6261719f6f2a133c1f059cebaf470f3e28ef0b1c 100644 (file)
@@ -276,7 +276,7 @@ static int v9fs_xattr_set_acl(const struct xattr_handler *handler,
        switch (handler->flags) {
        case ACL_TYPE_ACCESS:
                if (acl) {
-                       struct iattr iattr;
+                       struct iattr iattr = { 0 };
                        struct posix_acl *old_acl = acl;
 
                        retval = posix_acl_update_mode(inode, &iattr.ia_mode, &acl);
index 89bac3d2f05b9a9ddc320fcc9fcc3f3115701991..619128b5583702559dae61480262644e3542b3e7 100644 (file)
@@ -61,6 +61,8 @@ enum {
        Opt_cache_loose, Opt_fscache, Opt_mmap,
        /* Access options */
        Opt_access, Opt_posixacl,
+       /* Lock timeout option */
+       Opt_locktimeout,
        /* Error token */
        Opt_err
 };
@@ -80,6 +82,7 @@ static const match_table_t tokens = {
        {Opt_cachetag, "cachetag=%s"},
        {Opt_access, "access=%s"},
        {Opt_posixacl, "posixacl"},
+       {Opt_locktimeout, "locktimeout=%u"},
        {Opt_err, NULL}
 };
 
@@ -187,6 +190,7 @@ static int v9fs_parse_options(struct v9fs_session_info *v9ses, char *opts)
 #ifdef CONFIG_9P_FSCACHE
        v9ses->cachetag = NULL;
 #endif
+       v9ses->session_lock_timeout = P9_LOCK_TIMEOUT;
 
        if (!opts)
                return 0;
@@ -359,6 +363,23 @@ static int v9fs_parse_options(struct v9fs_session_info *v9ses, char *opts)
 #endif
                        break;
 
+               case Opt_locktimeout:
+                       r = match_int(&args[0], &option);
+                       if (r < 0) {
+                               p9_debug(P9_DEBUG_ERROR,
+                                        "integer field, but no integer?\n");
+                               ret = r;
+                               continue;
+                       }
+                       if (option < 1) {
+                               p9_debug(P9_DEBUG_ERROR,
+                                        "locktimeout must be a greater than zero integer.\n");
+                               ret = -EINVAL;
+                               continue;
+                       }
+                       v9ses->session_lock_timeout = (long)option * HZ;
+                       break;
+
                default:
                        continue;
                }
index 982e017acadbc7f9efccf445876959e811d85aa4..129e5243a6bf6cfb4d7fb1793ebf5c99686b3c6b 100644 (file)
@@ -116,6 +116,7 @@ struct v9fs_session_info {
        struct p9_client *clnt; /* 9p client */
        struct list_head slist; /* list of sessions registered with v9fs */
        struct rw_semaphore rename_sem;
+       long session_lock_timeout; /* retry interval for blocking locks */
 };
 
 /* cache_validity flags */
index b0405d6aac854d4e3bb69b813f587775cbeeebe8..cb6c4031af552b010c2e8ff5469b9088c769b3b7 100644 (file)
@@ -76,15 +76,6 @@ static inline int dt_type(struct p9_wstat *mistat)
        return rettype;
 }
 
-static void p9stat_init(struct p9_wstat *stbuf)
-{
-       stbuf->name  = NULL;
-       stbuf->uid   = NULL;
-       stbuf->gid   = NULL;
-       stbuf->muid  = NULL;
-       stbuf->extension = NULL;
-}
-
 /**
  * v9fs_alloc_rdir_buf - Allocate buffer used for read and readdir
  * @filp: opened file structure
@@ -114,7 +105,6 @@ static int v9fs_dir_readdir(struct file *file, struct dir_context *ctx)
        int err = 0;
        struct p9_fid *fid;
        int buflen;
-       int reclen = 0;
        struct p9_rdir *rdir;
        struct kvec kvec;
 
@@ -145,15 +135,12 @@ static int v9fs_dir_readdir(struct file *file, struct dir_context *ctx)
                        rdir->tail = n;
                }
                while (rdir->head < rdir->tail) {
-                       p9stat_init(&st);
                        err = p9stat_read(fid->clnt, rdir->buf + rdir->head,
                                          rdir->tail - rdir->head, &st);
-                       if (err) {
+                       if (err <= 0) {
                                p9_debug(P9_DEBUG_VFS, "returned %d\n", err);
-                               p9stat_free(&st);
                                return -EIO;
                        }
-                       reclen = st.size+2;
 
                        over = !dir_emit(ctx, st.name, strlen(st.name),
                                         v9fs_qid2ino(&st.qid), dt_type(&st));
@@ -161,8 +148,8 @@ static int v9fs_dir_readdir(struct file *file, struct dir_context *ctx)
                        if (over)
                                return 0;
 
-                       rdir->head += reclen;
-                       ctx->pos += reclen;
+                       rdir->head += err;
+                       ctx->pos += err;
                }
        }
 }
index 5f2e48d41d725b40ec02c3f72ffb735733d3eaa3..a25efa782fccbab2c30a3a743a1c4f547b72e9c4 100644 (file)
@@ -154,6 +154,7 @@ static int v9fs_file_do_lock(struct file *filp, int cmd, struct file_lock *fl)
        uint8_t status = P9_LOCK_ERROR;
        int res = 0;
        unsigned char fl_type;
+       struct v9fs_session_info *v9ses;
 
        fid = filp->private_data;
        BUG_ON(fid == NULL);
@@ -189,6 +190,8 @@ static int v9fs_file_do_lock(struct file *filp, int cmd, struct file_lock *fl)
        if (IS_SETLKW(cmd))
                flock.flags = P9_LOCK_FLAGS_BLOCK;
 
+       v9ses = v9fs_inode2v9ses(file_inode(filp));
+
        /*
         * if its a blocked request and we get P9_LOCK_BLOCKED as the status
         * for lock request, keep on trying
@@ -202,8 +205,17 @@ static int v9fs_file_do_lock(struct file *filp, int cmd, struct file_lock *fl)
                        break;
                if (status == P9_LOCK_BLOCKED && !IS_SETLKW(cmd))
                        break;
-               if (schedule_timeout_interruptible(P9_LOCK_TIMEOUT) != 0)
+               if (schedule_timeout_interruptible(v9ses->session_lock_timeout)
+                               != 0)
                        break;
+               /*
+                * p9_client_lock_dotl overwrites flock.client_id with the
+                * server message, free and reuse the client name
+                */
+               if (flock.client_id != fid->clnt->name) {
+                       kfree(flock.client_id);
+                       flock.client_id = fid->clnt->name;
+               }
        }
 
        /* map 9p status to VFS status */
@@ -216,7 +228,7 @@ static int v9fs_file_do_lock(struct file *filp, int cmd, struct file_lock *fl)
                break;
        default:
                WARN_ONCE(1, "unknown lock status code: %d\n", status);
-               /* fallthough */
+               /* fall through */
        case P9_LOCK_ERROR:
        case P9_LOCK_GRACE:
                res = -ENOLCK;
@@ -235,6 +247,8 @@ out_unlock:
                locks_lock_file_wait(filp, fl);
                fl->fl_type = fl_type;
        }
+       if (flock.client_id != fid->clnt->name)
+               kfree(flock.client_id);
 out:
        return res;
 }
@@ -269,7 +283,7 @@ static int v9fs_file_getlock(struct file *filp, struct file_lock *fl)
 
        res = p9_client_getlock_dotl(fid, &glock);
        if (res < 0)
-               return res;
+               goto out;
        /* map 9p lock type to os lock type */
        switch (glock.type) {
        case P9_LOCK_TYPE_RDLCK:
@@ -290,7 +304,9 @@ static int v9fs_file_getlock(struct file *filp, struct file_lock *fl)
                        fl->fl_end = glock.start + glock.length - 1;
                fl->fl_pid = -glock.proc_id;
        }
-       kfree(glock.client_id);
+out:
+       if (glock.client_id != fid->clnt->name)
+               kfree(glock.client_id);
        return res;
 }
 
index b8eb51a661e5606b41a18fb04456ed86d2ba8fc7..beede1e1a919a7ac52e6df1c54df9e15844e30e9 100644 (file)
@@ -336,6 +336,9 @@ enum p9_qid_t {
 #define P9_NOFID       (u32)(~0)
 #define P9_MAXWELEM    16
 
+/* Minimal header size: size[4] type[1] tag[2] */
+#define P9_HDRSZ       7
+
 /* ample room for Twrite/Rread header */
 #define P9_IOHDRSZ     24
 
@@ -558,19 +561,12 @@ struct p9_fcall {
        size_t offset;
        size_t capacity;
 
+       struct kmem_cache *cache;
        u8 *sdata;
 };
 
-struct p9_idpool;
-
 int p9_errstr2errno(char *errstr, int len);
 
-struct p9_idpool *p9_idpool_create(void);
-void p9_idpool_destroy(struct p9_idpool *);
-int p9_idpool_get(struct p9_idpool *p);
-void p9_idpool_put(int id, struct p9_idpool *p);
-int p9_idpool_check(int id, struct p9_idpool *p);
-
 int p9_error_init(void);
 int p9_trans_fd_init(void);
 void p9_trans_fd_exit(void);
index 0fa0fbab33b0a71a6e2dacf15c217c3a18568385..947a570307a6fb5def03e111f12cefebb93a842c 100644 (file)
@@ -64,22 +64,15 @@ enum p9_trans_status {
 
 /**
  * enum p9_req_status_t - status of a request
- * @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_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,
@@ -92,70 +85,46 @@ enum p9_req_status_t {
  * 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 accommodate
- * 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;
+       struct kref refcount;
        wait_queue_head_t wq;
-       struct p9_fcall *tc;
-       struct p9_fcall *rc;
+       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
+ * @lock: protect @fids and @reqs
  * @msize: maximum data size negotiated by protocol
- * @dotu: extension flags negotiated by protocol
  * @proto_version: 9P protocol version to use
  * @trans_mod: module API instantiated with this client
+ * @status: connection state
  * @trans: tranport instance state and API
  * @fids: All active FID handles
- * @tagpool - transaction id accounting for session
- * @reqs - 2D array of requests
- * @max_tag - current maximum tag id allocated
- * @name - node name used as client id
+ * @reqs: All active requests.
+ * @name: node name used as client id
  *
  * 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.
  */
-
 struct p9_client {
-       spinlock_t lock; /* protect client structure */
+       spinlock_t lock;
        unsigned int msize;
        unsigned char proto_version;
        struct p9_trans_module *trans_mod;
        enum p9_trans_status status;
        void *trans;
+       struct kmem_cache *fcall_cache;
 
        union {
                struct {
@@ -170,10 +139,7 @@ struct p9_client {
        } trans_opts;
 
        struct idr fids;
-
-       struct p9_idpool *tagpool;
-       struct p9_req_t *reqs[P9_ROW_MAXTAG];
-       int max_tag;
+       struct idr reqs;
 
        char name[__NEW_UTS_LEN + 1];
 };
@@ -266,7 +232,21 @@ int p9_client_mkdir_dotl(struct p9_fid *fid, const char *name, int mode,
                                kgid_t gid, struct p9_qid *);
 int p9_client_lock_dotl(struct p9_fid *fid, struct p9_flock *flock, u8 *status);
 int p9_client_getlock_dotl(struct p9_fid *fid, struct p9_getlock *fl);
+void p9_fcall_fini(struct p9_fcall *fc);
 struct p9_req_t *p9_tag_lookup(struct p9_client *, u16);
+
+static inline void p9_req_get(struct p9_req_t *r)
+{
+       kref_get(&r->refcount);
+}
+
+static inline int p9_req_try_get(struct p9_req_t *r)
+{
+       return kref_get_unless_zero(&r->refcount);
+}
+
+int p9_req_put(struct p9_req_t *r);
+
 void p9_client_cb(struct p9_client *c, struct p9_req_t *req, int status);
 
 int p9_parse_header(struct p9_fcall *, int32_t *, int8_t *, int16_t *, int);
@@ -279,4 +259,7 @@ struct p9_fid *p9_client_xattrwalk(struct p9_fid *, const char *, u64 *);
 int p9_client_xattrcreate(struct p9_fid *, const char *, u64, int);
 int p9_client_readlink(struct p9_fid *fid, char **target);
 
+int p9_client_init(void);
+void p9_client_exit(void);
+
 #endif /* NET_9P_CLIENT_H */
index c0486cfc85d93b8ebb8db58e89e6fbecfffdf279..aa0a5641e5d010a29860313b7cd2bf6fb60bc722 100644 (file)
@@ -8,7 +8,6 @@ obj-$(CONFIG_NET_9P_RDMA) += 9pnet_rdma.o
        mod.o \
        client.o \
        error.o \
-       util.o \
        protocol.o \
        trans_fd.o \
        trans_common.o \
index deae53a7dffc868d66efbcc01bba141b97d97128..5f23e18eecc02f32ac80ddd85658006f9ad15430 100644 (file)
@@ -231,144 +231,170 @@ free_and_return:
        return ret;
 }
 
-static struct p9_fcall *p9_fcall_alloc(int alloc_msize)
+static int p9_fcall_init(struct p9_client *c, struct p9_fcall *fc,
+                        int alloc_msize)
 {
-       struct p9_fcall *fc;
-       fc = kmalloc(sizeof(struct p9_fcall) + alloc_msize, GFP_NOFS);
-       if (!fc)
-               return NULL;
+       if (likely(c->fcall_cache) && alloc_msize == c->msize) {
+               fc->sdata = kmem_cache_alloc(c->fcall_cache, GFP_NOFS);
+               fc->cache = c->fcall_cache;
+       } else {
+               fc->sdata = kmalloc(alloc_msize, GFP_NOFS);
+               fc->cache = NULL;
+       }
+       if (!fc->sdata)
+               return -ENOMEM;
        fc->capacity = alloc_msize;
-       fc->sdata = (char *) fc + sizeof(struct p9_fcall);
-       return fc;
+       return 0;
+}
+
+void p9_fcall_fini(struct p9_fcall *fc)
+{
+       /* sdata can be NULL for interrupted requests in trans_rdma,
+        * and kmem_cache_free does not do NULL-check for us
+        */
+       if (unlikely(!fc->sdata))
+               return;
+
+       if (fc->cache)
+               kmem_cache_free(fc->cache, fc->sdata);
+       else
+               kfree(fc->sdata);
 }
+EXPORT_SYMBOL(p9_fcall_fini);
+
+static struct kmem_cache *p9_req_cache;
 
 /**
- * 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 accommodate 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.
+ * p9_req_alloc - Allocate a new request.
+ * @c: Client session.
+ * @type: Transaction type.
+ * @max_size: Maximum packet size for this request.
  *
+ * Context: Process context.
+ * Return: Pointer to new request.
  */
-
 static struct p9_req_t *
-p9_tag_alloc(struct p9_client *c, u16 tag, unsigned int max_size)
+p9_tag_alloc(struct p9_client *c, int8_t type, unsigned int max_size)
 {
-       unsigned long flags;
-       int row, col;
-       struct p9_req_t *req;
+       struct p9_req_t *req = kmem_cache_alloc(p9_req_cache, GFP_NOFS);
        int alloc_msize = min(c->msize, max_size);
+       int tag;
 
-       /* 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]) {
-                               pr_err("Couldn't grow tag array\n");
-                               spin_unlock_irqrestore(&c->lock, flags);
-                               return ERR_PTR(-ENOMEM);
-                       }
-                       for (col = 0; col < P9_ROW_MAXTAG; col++) {
-                               req = &c->reqs[row][col];
-                               req->status = REQ_STATUS_IDLE;
-                               init_waitqueue_head(&req->wq);
-                       }
-                       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->tc = p9_fcall_alloc(alloc_msize);
-       if (!req->rc)
-               req->rc = p9_fcall_alloc(alloc_msize);
-       if (!req->tc || !req->rc)
-               goto grow_failed;
+       if (!req)
+               return ERR_PTR(-ENOMEM);
 
-       p9pdu_reset(req->tc);
-       p9pdu_reset(req->rc);
+       if (p9_fcall_init(c, &req->tc, alloc_msize))
+               goto free_req;
+       if (p9_fcall_init(c, &req->rc, alloc_msize))
+               goto free;
 
-       req->tc->tag = tag-1;
+       p9pdu_reset(&req->tc);
+       p9pdu_reset(&req->rc);
        req->status = REQ_STATUS_ALLOC;
+       init_waitqueue_head(&req->wq);
+       INIT_LIST_HEAD(&req->req_list);
+
+       idr_preload(GFP_NOFS);
+       spin_lock_irq(&c->lock);
+       if (type == P9_TVERSION)
+               tag = idr_alloc(&c->reqs, req, P9_NOTAG, P9_NOTAG + 1,
+                               GFP_NOWAIT);
+       else
+               tag = idr_alloc(&c->reqs, req, 0, P9_NOTAG, GFP_NOWAIT);
+       req->tc.tag = tag;
+       spin_unlock_irq(&c->lock);
+       idr_preload_end();
+       if (tag < 0)
+               goto free;
+
+       /* Init ref to two because in the general case there is one ref
+        * that is put asynchronously by a writer thread, one ref
+        * temporarily given by p9_tag_lookup and put by p9_client_cb
+        * in the recv thread, and one ref put by p9_tag_remove in the
+        * main thread. The only exception is virtio that does not use
+        * p9_tag_lookup but does not have a writer thread either
+        * (the write happens synchronously in the request/zc_request
+        * callback), so p9_client_cb eats the second ref there
+        * as the pointer is duplicated directly by virtqueue_add_sgs()
+        */
+       refcount_set(&req->refcount.refcount, 2);
 
        return req;
 
-grow_failed:
-       pr_err("Couldn't grow tag array\n");
-       kfree(req->tc);
-       kfree(req->rc);
-       req->tc = req->rc = NULL;
+free:
+       p9_fcall_fini(&req->tc);
+       p9_fcall_fini(&req->rc);
+free_req:
+       kmem_cache_free(p9_req_cache, req);
        return ERR_PTR(-ENOMEM);
 }
 
 /**
- * p9_tag_lookup - lookup a request by tag
- * @c: client session to lookup tag within
- * @tag: numeric id for transaction
+ * p9_tag_lookup - Look up a request by tag.
+ * @c: Client session.
+ * @tag: Transaction ID.
  *
+ * Context: Any context.
+ * Return: A request, or %NULL if there is no request with that tag.
  */
-
 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++;
-
-       if (tag >= c->max_tag)
-               return NULL;
+       struct p9_req_t *req;
 
-       row = tag / P9_ROW_MAXTAG;
-       col = tag % P9_ROW_MAXTAG;
+       rcu_read_lock();
+again:
+       req = idr_find(&c->reqs, tag);
+       if (req) {
+               /* We have to be careful with the req found under rcu_read_lock
+                * Thanks to SLAB_TYPESAFE_BY_RCU we can safely try to get the
+                * ref again without corrupting other data, then check again
+                * that the tag matches once we have the ref
+                */
+               if (!p9_req_try_get(req))
+                       goto again;
+               if (req->tc.tag != tag) {
+                       p9_req_put(req);
+                       goto again;
+               }
+       }
+       rcu_read_unlock();
 
-       return &c->reqs[row][col];
+       return req;
 }
 EXPORT_SYMBOL(p9_tag_lookup);
 
 /**
- * p9_tag_init - setup tags structure and contents
- * @c:  v9fs client struct
- *
- * This initializes the tags structure for each client instance.
+ * p9_tag_remove - Remove a tag.
+ * @c: Client session.
+ * @r: Request of reference.
  *
+ * Context: Any context.
  */
+static int p9_tag_remove(struct p9_client *c, struct p9_req_t *r)
+{
+       unsigned long flags;
+       u16 tag = r->tc.tag;
+
+       p9_debug(P9_DEBUG_MUX, "clnt %p req %p tag: %d\n", c, r, tag);
+       spin_lock_irqsave(&c->lock, flags);
+       idr_remove(&c->reqs, tag);
+       spin_unlock_irqrestore(&c->lock, flags);
+       return p9_req_put(r);
+}
 
-static int p9_tag_init(struct p9_client *c)
+static void p9_req_free(struct kref *ref)
 {
-       int err = 0;
+       struct p9_req_t *r = container_of(ref, struct p9_req_t, refcount);
+       p9_fcall_fini(&r->tc);
+       p9_fcall_fini(&r->rc);
+       kmem_cache_free(p9_req_cache, r);
+}
 
-       c->tagpool = p9_idpool_create();
-       if (IS_ERR(c->tagpool)) {
-               err = PTR_ERR(c->tagpool);
-               goto error;
-       }
-       err = p9_idpool_get(c->tagpool); /* reserve tag 0 */
-       if (err < 0) {
-               p9_idpool_destroy(c->tagpool);
-               goto error;
-       }
-       c->max_tag = 0;
-error:
-       return err;
+int p9_req_put(struct p9_req_t *r)
+{
+       return kref_put(&r->refcount, p9_req_free);
 }
+EXPORT_SYMBOL(p9_req_put);
 
 /**
  * p9_tag_cleanup - cleans up tags structure and reclaims resources
@@ -379,52 +405,17 @@ error:
  */
 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_debug(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_put(0, c->tagpool); /* free reserved tag 0 */
-               p9_idpool_destroy(c->tagpool);
-       }
+       struct p9_req_t *req;
+       int id;
 
-       /* 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].tc);
-                       kfree(c->reqs[row][col].rc);
-               }
-               kfree(c->reqs[row]);
+       rcu_read_lock();
+       idr_for_each_entry(&c->reqs, req, id) {
+               pr_info("Tag %d still in use\n", id);
+               if (p9_tag_remove(c, req) == 0)
+                       pr_warn("Packet with tag %d has still references",
+                               req->tc.tag);
        }
-       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_debug(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);
+       rcu_read_unlock();
 }
 
 /**
@@ -435,7 +426,7 @@ static void p9_free_req(struct p9_client *c, struct p9_req_t *r)
  */
 void p9_client_cb(struct p9_client *c, struct p9_req_t *req, int status)
 {
-       p9_debug(P9_DEBUG_MUX, " tag %d\n", req->tc->tag);
+       p9_debug(P9_DEBUG_MUX, " tag %d\n", req->tc.tag);
 
        /*
         * This barrier is needed to make sure any change made to req before
@@ -445,7 +436,8 @@ void p9_client_cb(struct p9_client *c, struct p9_req_t *req, int status)
        req->status = status;
 
        wake_up(&req->wq);
-       p9_debug(P9_DEBUG_MUX, "wakeup: %d\n", req->tc->tag);
+       p9_debug(P9_DEBUG_MUX, "wakeup: %d\n", req->tc.tag);
+       p9_req_put(req);
 }
 EXPORT_SYMBOL(p9_client_cb);
 
@@ -516,18 +508,18 @@ static int p9_check_errors(struct p9_client *c, struct p9_req_t *req)
        int err;
        int ecode;
 
-       err = p9_parse_header(req->rc, NULL, &type, NULL, 0);
-       if (req->rc->size >= c->msize) {
+       err = p9_parse_header(&req->rc, NULL, &type, NULL, 0);
+       if (req->rc.size >= c->msize) {
                p9_debug(P9_DEBUG_ERROR,
                         "requested packet size too big: %d\n",
-                        req->rc->size);
+                        req->rc.size);
                return -EIO;
        }
        /*
         * dump the response from server
         * This should be after check errors which poplulate pdu_fcall.
         */
-       trace_9p_protocol_dump(c, req->rc);
+       trace_9p_protocol_dump(c, &req->rc);
        if (err) {
                p9_debug(P9_DEBUG_ERROR, "couldn't parse header %d\n", err);
                return err;
@@ -537,7 +529,7 @@ static int p9_check_errors(struct p9_client *c, struct p9_req_t *req)
 
        if (!p9_is_proto_dotl(c)) {
                char *ename;
-               err = p9pdu_readf(req->rc, c->proto_version, "s?d",
+               err = p9pdu_readf(&req->rc, c->proto_version, "s?d",
                                  &ename, &ecode);
                if (err)
                        goto out_err;
@@ -553,7 +545,7 @@ static int p9_check_errors(struct p9_client *c, struct p9_req_t *req)
                }
                kfree(ename);
        } else {
-               err = p9pdu_readf(req->rc, c->proto_version, "d", &ecode);
+               err = p9pdu_readf(&req->rc, c->proto_version, "d", &ecode);
                err = -ecode;
 
                p9_debug(P9_DEBUG_9P, "<<< RLERROR (%d)\n", -ecode);
@@ -587,12 +579,12 @@ static int p9_check_zc_errors(struct p9_client *c, struct p9_req_t *req,
        int8_t type;
        char *ename = NULL;
 
-       err = p9_parse_header(req->rc, NULL, &type, NULL, 0);
+       err = p9_parse_header(&req->rc, NULL, &type, NULL, 0);
        /*
         * dump the response from server
         * This should be after parse_header which poplulate pdu_fcall.
         */
-       trace_9p_protocol_dump(c, req->rc);
+       trace_9p_protocol_dump(c, &req->rc);
        if (err) {
                p9_debug(P9_DEBUG_ERROR, "couldn't parse header %d\n", err);
                return err;
@@ -607,13 +599,13 @@ static int p9_check_zc_errors(struct p9_client *c, struct p9_req_t *req,
                /* 7 = header size for RERROR; */
                int inline_len = in_hdrlen - 7;
 
-               len =  req->rc->size - req->rc->offset;
+               len = req->rc.size - req->rc.offset;
                if (len > (P9_ZC_HDR_SZ - 7)) {
                        err = -EFAULT;
                        goto out_err;
                }
 
-               ename = &req->rc->sdata[req->rc->offset];
+               ename = &req->rc.sdata[req->rc.offset];
                if (len > inline_len) {
                        /* We have error in external buffer */
                        if (!copy_from_iter_full(ename + inline_len,
@@ -623,7 +615,7 @@ static int p9_check_zc_errors(struct p9_client *c, struct p9_req_t *req,
                        }
                }
                ename = NULL;
-               err = p9pdu_readf(req->rc, c->proto_version, "s?d",
+               err = p9pdu_readf(&req->rc, c->proto_version, "s?d",
                                  &ename, &ecode);
                if (err)
                        goto out_err;
@@ -639,7 +631,7 @@ static int p9_check_zc_errors(struct p9_client *c, struct p9_req_t *req,
                }
                kfree(ename);
        } else {
-               err = p9pdu_readf(req->rc, c->proto_version, "d", &ecode);
+               err = p9pdu_readf(&req->rc, c->proto_version, "d", &ecode);
                err = -ecode;
 
                p9_debug(P9_DEBUG_9P, "<<< RLERROR (%d)\n", -ecode);
@@ -672,7 +664,7 @@ static int p9_client_flush(struct p9_client *c, struct p9_req_t *oldreq)
        int16_t oldtag;
        int err;
 
-       err = p9_parse_header(oldreq->tc, NULL, NULL, &oldtag, 1);
+       err = p9_parse_header(&oldreq->tc, NULL, NULL, &oldtag, 1);
        if (err)
                return err;
 
@@ -686,11 +678,12 @@ static int p9_client_flush(struct p9_client *c, struct p9_req_t *oldreq)
         * if we haven't received a response for oldreq,
         * remove it from the list
         */
-       if (oldreq->status == REQ_STATUS_SENT)
+       if (oldreq->status == REQ_STATUS_SENT) {
                if (c->trans_mod->cancelled)
                        c->trans_mod->cancelled(c, oldreq);
+       }
 
-       p9_free_req(c, req);
+       p9_tag_remove(c, req);
        return 0;
 }
 
@@ -698,7 +691,7 @@ static struct p9_req_t *p9_client_prepare_req(struct p9_client *c,
                                              int8_t type, int req_size,
                                              const char *fmt, va_list ap)
 {
-       int tag, err;
+       int err;
        struct p9_req_t *req;
 
        p9_debug(P9_DEBUG_MUX, "client %p op %d\n", c, type);
@@ -711,27 +704,22 @@ static struct p9_req_t *p9_client_prepare_req(struct p9_client *c,
        if ((c->status == BeginDisconnect) && (type != P9_TCLUNK))
                return ERR_PTR(-EIO);
 
-       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, req_size);
+       req = p9_tag_alloc(c, type, req_size);
        if (IS_ERR(req))
                return req;
 
        /* marshall the data */
-       p9pdu_prepare(req->tc, tag, type);
-       err = p9pdu_vwritef(req->tc, c->proto_version, fmt, ap);
+       p9pdu_prepare(&req->tc, req->tc.tag, type);
+       err = p9pdu_vwritef(&req->tc, c->proto_version, fmt, ap);
        if (err)
                goto reterr;
-       p9pdu_finalize(c, req->tc);
-       trace_9p_client_req(c, type, tag);
+       p9pdu_finalize(c, &req->tc);
+       trace_9p_client_req(c, type, req->tc.tag);
        return req;
 reterr:
-       p9_free_req(c, req);
+       p9_tag_remove(c, req);
+       /* We have to put also the 2nd reference as it won't be used */
+       p9_req_put(req);
        return ERR_PTR(err);
 }
 
@@ -741,7 +729,7 @@ reterr:
  * @type: type of request
  * @fmt: protocol format string (see protocol.c)
  *
- * Returns request structure (which client must free using p9_free_req)
+ * Returns request structure (which client must free using p9_tag_remove)
  */
 
 static struct p9_req_t *
@@ -766,6 +754,8 @@ p9_client_rpc(struct p9_client *c, int8_t type, const char *fmt, ...)
 
        err = c->trans_mod->request(c, req);
        if (err < 0) {
+               /* write won't happen */
+               p9_req_put(req);
                if (err != -ERESTARTSYS && err != -EFAULT)
                        c->status = Disconnected;
                goto recalc_sigpending;
@@ -813,11 +803,11 @@ recalc_sigpending:
                goto reterr;
 
        err = p9_check_errors(c, req);
-       trace_9p_client_res(c, type, req->rc->tag, err);
+       trace_9p_client_res(c, type, req->rc.tag, err);
        if (!err)
                return req;
 reterr:
-       p9_free_req(c, req);
+       p9_tag_remove(c, req);
        return ERR_PTR(safe_errno(err));
 }
 
@@ -832,7 +822,7 @@ reterr:
  * @hdrlen: reader header size, This is the size of response protocol data
  * @fmt: protocol format string (see protocol.c)
  *
- * Returns request structure (which client must free using p9_free_req)
+ * Returns request structure (which client must free using p9_tag_remove)
  */
 static struct p9_req_t *p9_client_zc_rpc(struct p9_client *c, int8_t type,
                                         struct iov_iter *uidata,
@@ -895,11 +885,11 @@ recalc_sigpending:
                goto reterr;
 
        err = p9_check_zc_errors(c, req, uidata, in_hdrlen);
-       trace_9p_client_res(c, type, req->rc->tag, err);
+       trace_9p_client_res(c, type, req->rc.tag, err);
        if (!err)
                return req;
 reterr:
-       p9_free_req(c, req);
+       p9_tag_remove(c, req);
        return ERR_PTR(safe_errno(err));
 }
 
@@ -978,10 +968,10 @@ static int p9_client_version(struct p9_client *c)
        if (IS_ERR(req))
                return PTR_ERR(req);
 
-       err = p9pdu_readf(req->rc, c->proto_version, "ds", &msize, &version);
+       err = p9pdu_readf(&req->rc, c->proto_version, "ds", &msize, &version);
        if (err) {
                p9_debug(P9_DEBUG_9P, "version error %d\n", err);
-               trace_9p_protocol_dump(c, req->rc);
+               trace_9p_protocol_dump(c, &req->rc);
                goto error;
        }
 
@@ -1002,7 +992,7 @@ static int p9_client_version(struct p9_client *c)
 
 error:
        kfree(version);
-       p9_free_req(c, req);
+       p9_tag_remove(c, req);
 
        return err;
 }
@@ -1020,20 +1010,18 @@ struct p9_client *p9_client_create(const char *dev_name, char *options)
 
        clnt->trans_mod = NULL;
        clnt->trans = NULL;
+       clnt->fcall_cache = NULL;
 
        client_id = utsname()->nodename;
        memcpy(clnt->name, client_id, strlen(client_id) + 1);
 
        spin_lock_init(&clnt->lock);
        idr_init(&clnt->fids);
-
-       err = p9_tag_init(clnt);
-       if (err < 0)
-               goto free_client;
+       idr_init(&clnt->reqs);
 
        err = parse_opts(options, clnt);
        if (err < 0)
-               goto destroy_tagpool;
+               goto free_client;
 
        if (!clnt->trans_mod)
                clnt->trans_mod = v9fs_get_default_trans();
@@ -1042,7 +1030,7 @@ struct p9_client *p9_client_create(const char *dev_name, char *options)
                err = -EPROTONOSUPPORT;
                p9_debug(P9_DEBUG_ERROR,
                         "No transport defined or default transport\n");
-               goto destroy_tagpool;
+               goto free_client;
        }
 
        p9_debug(P9_DEBUG_MUX, "clnt %p trans %p msize %d protocol %d\n",
@@ -1059,14 +1047,21 @@ struct p9_client *p9_client_create(const char *dev_name, char *options)
        if (err)
                goto close_trans;
 
+       /* P9_HDRSZ + 4 is the smallest packet header we can have that is
+        * followed by data accessed from userspace by read
+        */
+       clnt->fcall_cache =
+               kmem_cache_create_usercopy("9p-fcall-cache", clnt->msize,
+                                          0, 0, P9_HDRSZ + 4,
+                                          clnt->msize - (P9_HDRSZ + 4),
+                                          NULL);
+
        return clnt;
 
 close_trans:
        clnt->trans_mod->close(clnt);
 put_trans:
        v9fs_put_trans(clnt->trans_mod);
-destroy_tagpool:
-       p9_idpool_destroy(clnt->tagpool);
 free_client:
        kfree(clnt);
        return ERR_PTR(err);
@@ -1092,6 +1087,7 @@ void p9_client_destroy(struct p9_client *clnt)
 
        p9_tag_cleanup(clnt);
 
+       kmem_cache_destroy(clnt->fcall_cache);
        kfree(clnt);
 }
 EXPORT_SYMBOL(p9_client_destroy);
@@ -1135,10 +1131,10 @@ struct p9_fid *p9_client_attach(struct p9_client *clnt, struct p9_fid *afid,
                goto error;
        }
 
-       err = p9pdu_readf(req->rc, clnt->proto_version, "Q", &qid);
+       err = p9pdu_readf(&req->rc, clnt->proto_version, "Q", &qid);
        if (err) {
-               trace_9p_protocol_dump(clnt, req->rc);
-               p9_free_req(clnt, req);
+               trace_9p_protocol_dump(clnt, &req->rc);
+               p9_tag_remove(clnt, req);
                goto error;
        }
 
@@ -1147,7 +1143,7 @@ struct p9_fid *p9_client_attach(struct p9_client *clnt, struct p9_fid *afid,
 
        memmove(&fid->qid, &qid, sizeof(struct p9_qid));
 
-       p9_free_req(clnt, req);
+       p9_tag_remove(clnt, req);
        return fid;
 
 error:
@@ -1192,13 +1188,13 @@ struct p9_fid *p9_client_walk(struct p9_fid *oldfid, uint16_t nwname,
                goto error;
        }
 
-       err = p9pdu_readf(req->rc, clnt->proto_version, "R", &nwqids, &wqids);
+       err = p9pdu_readf(&req->rc, clnt->proto_version, "R", &nwqids, &wqids);
        if (err) {
-               trace_9p_protocol_dump(clnt, req->rc);
-               p9_free_req(clnt, req);
+               trace_9p_protocol_dump(clnt, &req->rc);
+               p9_tag_remove(clnt, req);
                goto clunk_fid;
        }
-       p9_free_req(clnt, req);
+       p9_tag_remove(clnt, req);
 
        p9_debug(P9_DEBUG_9P, "<<< RWALK nwqid %d:\n", nwqids);
 
@@ -1259,9 +1255,9 @@ int p9_client_open(struct p9_fid *fid, int mode)
                goto error;
        }
 
-       err = p9pdu_readf(req->rc, clnt->proto_version, "Qd", &qid, &iounit);
+       err = p9pdu_readf(&req->rc, clnt->proto_version, "Qd", &qid, &iounit);
        if (err) {
-               trace_9p_protocol_dump(clnt, req->rc);
+               trace_9p_protocol_dump(clnt, &req->rc);
                goto free_and_error;
        }
 
@@ -1273,7 +1269,7 @@ int p9_client_open(struct p9_fid *fid, int mode)
        fid->iounit = iounit;
 
 free_and_error:
-       p9_free_req(clnt, req);
+       p9_tag_remove(clnt, req);
 error:
        return err;
 }
@@ -1303,9 +1299,9 @@ int p9_client_create_dotl(struct p9_fid *ofid, const char *name, u32 flags, u32
                goto error;
        }
 
-       err = p9pdu_readf(req->rc, clnt->proto_version, "Qd", qid, &iounit);
+       err = p9pdu_readf(&req->rc, clnt->proto_version, "Qd", qid, &iounit);
        if (err) {
-               trace_9p_protocol_dump(clnt, req->rc);
+               trace_9p_protocol_dump(clnt, &req->rc);
                goto free_and_error;
        }
 
@@ -1318,7 +1314,7 @@ int p9_client_create_dotl(struct p9_fid *ofid, const char *name, u32 flags, u32
        ofid->iounit = iounit;
 
 free_and_error:
-       p9_free_req(clnt, req);
+       p9_tag_remove(clnt, req);
 error:
        return err;
 }
@@ -1348,9 +1344,9 @@ int p9_client_fcreate(struct p9_fid *fid, const char *name, u32 perm, int mode,
                goto error;
        }
 
-       err = p9pdu_readf(req->rc, clnt->proto_version, "Qd", &qid, &iounit);
+       err = p9pdu_readf(&req->rc, clnt->proto_version, "Qd", &qid, &iounit);
        if (err) {
-               trace_9p_protocol_dump(clnt, req->rc);
+               trace_9p_protocol_dump(clnt, &req->rc);
                goto free_and_error;
        }
 
@@ -1363,7 +1359,7 @@ int p9_client_fcreate(struct p9_fid *fid, const char *name, u32 perm, int mode,
        fid->iounit = iounit;
 
 free_and_error:
-       p9_free_req(clnt, req);
+       p9_tag_remove(clnt, req);
 error:
        return err;
 }
@@ -1387,9 +1383,9 @@ int p9_client_symlink(struct p9_fid *dfid, const char *name,
                goto error;
        }
 
-       err = p9pdu_readf(req->rc, clnt->proto_version, "Q", qid);
+       err = p9pdu_readf(&req->rc, clnt->proto_version, "Q", qid);
        if (err) {
-               trace_9p_protocol_dump(clnt, req->rc);
+               trace_9p_protocol_dump(clnt, &req->rc);
                goto free_and_error;
        }
 
@@ -1397,7 +1393,7 @@ int p9_client_symlink(struct p9_fid *dfid, const char *name,
                        qid->type, (unsigned long long)qid->path, qid->version);
 
 free_and_error:
-       p9_free_req(clnt, req);
+       p9_tag_remove(clnt, req);
 error:
        return err;
 }
@@ -1417,7 +1413,7 @@ int p9_client_link(struct p9_fid *dfid, struct p9_fid *oldfid, const char *newna
                return PTR_ERR(req);
 
        p9_debug(P9_DEBUG_9P, "<<< RLINK\n");
-       p9_free_req(clnt, req);
+       p9_tag_remove(clnt, req);
        return 0;
 }
 EXPORT_SYMBOL(p9_client_link);
@@ -1441,7 +1437,7 @@ int p9_client_fsync(struct p9_fid *fid, int datasync)
 
        p9_debug(P9_DEBUG_9P, "<<< RFSYNC fid %d\n", fid->fid);
 
-       p9_free_req(clnt, req);
+       p9_tag_remove(clnt, req);
 
 error:
        return err;
@@ -1476,7 +1472,7 @@ again:
 
        p9_debug(P9_DEBUG_9P, "<<< RCLUNK fid %d\n", fid->fid);
 
-       p9_free_req(clnt, req);
+       p9_tag_remove(clnt, req);
 error:
        /*
         * Fid is not valid even after a failed clunk
@@ -1510,7 +1506,7 @@ int p9_client_remove(struct p9_fid *fid)
 
        p9_debug(P9_DEBUG_9P, "<<< RREMOVE fid %d\n", fid->fid);
 
-       p9_free_req(clnt, req);
+       p9_tag_remove(clnt, req);
 error:
        if (err == -ERESTARTSYS)
                p9_client_clunk(fid);
@@ -1537,7 +1533,7 @@ int p9_client_unlinkat(struct p9_fid *dfid, const char *name, int flags)
        }
        p9_debug(P9_DEBUG_9P, "<<< RUNLINKAT fid %d %s\n", dfid->fid, name);
 
-       p9_free_req(clnt, req);
+       p9_tag_remove(clnt, req);
 error:
        return err;
 }
@@ -1585,11 +1581,11 @@ p9_client_read(struct p9_fid *fid, u64 offset, struct iov_iter *to, int *err)
                        break;
                }
 
-               *err = p9pdu_readf(req->rc, clnt->proto_version,
+               *err = p9pdu_readf(&req->rc, clnt->proto_version,
                                   "D", &count, &dataptr);
                if (*err) {
-                       trace_9p_protocol_dump(clnt, req->rc);
-                       p9_free_req(clnt, req);
+                       trace_9p_protocol_dump(clnt, &req->rc);
+                       p9_tag_remove(clnt, req);
                        break;
                }
                if (rsize < count) {
@@ -1599,7 +1595,7 @@ p9_client_read(struct p9_fid *fid, u64 offset, struct iov_iter *to, int *err)
 
                p9_debug(P9_DEBUG_9P, "<<< RREAD count %d\n", count);
                if (!count) {
-                       p9_free_req(clnt, req);
+                       p9_tag_remove(clnt, req);
                        break;
                }
 
@@ -1609,7 +1605,7 @@ p9_client_read(struct p9_fid *fid, u64 offset, struct iov_iter *to, int *err)
                        offset += n;
                        if (n != count) {
                                *err = -EFAULT;
-                               p9_free_req(clnt, req);
+                               p9_tag_remove(clnt, req);
                                break;
                        }
                } else {
@@ -1617,7 +1613,7 @@ p9_client_read(struct p9_fid *fid, u64 offset, struct iov_iter *to, int *err)
                        total += count;
                        offset += count;
                }
-               p9_free_req(clnt, req);
+               p9_tag_remove(clnt, req);
        }
        return total;
 }
@@ -1658,10 +1654,10 @@ p9_client_write(struct p9_fid *fid, u64 offset, struct iov_iter *from, int *err)
                        break;
                }
 
-               *err = p9pdu_readf(req->rc, clnt->proto_version, "d", &count);
+               *err = p9pdu_readf(&req->rc, clnt->proto_version, "d", &count);
                if (*err) {
-                       trace_9p_protocol_dump(clnt, req->rc);
-                       p9_free_req(clnt, req);
+                       trace_9p_protocol_dump(clnt, &req->rc);
+                       p9_tag_remove(clnt, req);
                        break;
                }
                if (rsize < count) {
@@ -1671,7 +1667,7 @@ p9_client_write(struct p9_fid *fid, u64 offset, struct iov_iter *from, int *err)
 
                p9_debug(P9_DEBUG_9P, "<<< RWRITE count %d\n", count);
 
-               p9_free_req(clnt, req);
+               p9_tag_remove(clnt, req);
                iov_iter_advance(from, count);
                total += count;
                offset += count;
@@ -1702,10 +1698,10 @@ struct p9_wstat *p9_client_stat(struct p9_fid *fid)
                goto error;
        }
 
-       err = p9pdu_readf(req->rc, clnt->proto_version, "wS", &ignored, ret);
+       err = p9pdu_readf(&req->rc, clnt->proto_version, "wS", &ignored, ret);
        if (err) {
-               trace_9p_protocol_dump(clnt, req->rc);
-               p9_free_req(clnt, req);
+               trace_9p_protocol_dump(clnt, &req->rc);
+               p9_tag_remove(clnt, req);
                goto error;
        }
 
@@ -1722,7 +1718,7 @@ struct p9_wstat *p9_client_stat(struct p9_fid *fid)
                from_kgid(&init_user_ns, ret->n_gid),
                from_kuid(&init_user_ns, ret->n_muid));
 
-       p9_free_req(clnt, req);
+       p9_tag_remove(clnt, req);
        return ret;
 
 error:
@@ -1755,10 +1751,10 @@ struct p9_stat_dotl *p9_client_getattr_dotl(struct p9_fid *fid,
                goto error;
        }
 
-       err = p9pdu_readf(req->rc, clnt->proto_version, "A", ret);
+       err = p9pdu_readf(&req->rc, clnt->proto_version, "A", ret);
        if (err) {
-               trace_9p_protocol_dump(clnt, req->rc);
-               p9_free_req(clnt, req);
+               trace_9p_protocol_dump(clnt, &req->rc);
+               p9_tag_remove(clnt, req);
                goto error;
        }
 
@@ -1783,7 +1779,7 @@ struct p9_stat_dotl *p9_client_getattr_dotl(struct p9_fid *fid,
                ret->st_ctime_nsec, ret->st_btime_sec, ret->st_btime_nsec,
                ret->st_gen, ret->st_data_version);
 
-       p9_free_req(clnt, req);
+       p9_tag_remove(clnt, req);
        return ret;
 
 error:
@@ -1852,7 +1848,7 @@ int p9_client_wstat(struct p9_fid *fid, struct p9_wstat *wst)
 
        p9_debug(P9_DEBUG_9P, "<<< RWSTAT fid %d\n", fid->fid);
 
-       p9_free_req(clnt, req);
+       p9_tag_remove(clnt, req);
 error:
        return err;
 }
@@ -1884,7 +1880,7 @@ int p9_client_setattr(struct p9_fid *fid, struct p9_iattr_dotl *p9attr)
                goto error;
        }
        p9_debug(P9_DEBUG_9P, "<<< RSETATTR fid %d\n", fid->fid);
-       p9_free_req(clnt, req);
+       p9_tag_remove(clnt, req);
 error:
        return err;
 }
@@ -1907,12 +1903,12 @@ int p9_client_statfs(struct p9_fid *fid, struct p9_rstatfs *sb)
                goto error;
        }
 
-       err = p9pdu_readf(req->rc, clnt->proto_version, "ddqqqqqqd", &sb->type,
-               &sb->bsize, &sb->blocks, &sb->bfree, &sb->bavail,
-               &sb->files, &sb->ffree, &sb->fsid, &sb->namelen);
+       err = p9pdu_readf(&req->rc, clnt->proto_version, "ddqqqqqqd", &sb->type,
+                         &sb->bsize, &sb->blocks, &sb->bfree, &sb->bavail,
+                         &sb->files, &sb->ffree, &sb->fsid, &sb->namelen);
        if (err) {
-               trace_9p_protocol_dump(clnt, req->rc);
-               p9_free_req(clnt, req);
+               trace_9p_protocol_dump(clnt, &req->rc);
+               p9_tag_remove(clnt, req);
                goto error;
        }
 
@@ -1923,7 +1919,7 @@ int p9_client_statfs(struct p9_fid *fid, struct p9_rstatfs *sb)
                sb->blocks, sb->bfree, sb->bavail, sb->files,  sb->ffree,
                sb->fsid, (long int)sb->namelen);
 
-       p9_free_req(clnt, req);
+       p9_tag_remove(clnt, req);
 error:
        return err;
 }
@@ -1951,7 +1947,7 @@ int p9_client_rename(struct p9_fid *fid,
 
        p9_debug(P9_DEBUG_9P, "<<< RRENAME fid %d\n", fid->fid);
 
-       p9_free_req(clnt, req);
+       p9_tag_remove(clnt, req);
 error:
        return err;
 }
@@ -1981,7 +1977,7 @@ int p9_client_renameat(struct p9_fid *olddirfid, const char *old_name,
        p9_debug(P9_DEBUG_9P, "<<< RRENAMEAT newdirfid %d new name %s\n",
                   newdirfid->fid, new_name);
 
-       p9_free_req(clnt, req);
+       p9_tag_remove(clnt, req);
 error:
        return err;
 }
@@ -2015,13 +2011,13 @@ struct p9_fid *p9_client_xattrwalk(struct p9_fid *file_fid,
                err = PTR_ERR(req);
                goto error;
        }
-       err = p9pdu_readf(req->rc, clnt->proto_version, "q", attr_size);
+       err = p9pdu_readf(&req->rc, clnt->proto_version, "q", attr_size);
        if (err) {
-               trace_9p_protocol_dump(clnt, req->rc);
-               p9_free_req(clnt, req);
+               trace_9p_protocol_dump(clnt, &req->rc);
+               p9_tag_remove(clnt, req);
                goto clunk_fid;
        }
-       p9_free_req(clnt, req);
+       p9_tag_remove(clnt, req);
        p9_debug(P9_DEBUG_9P, "<<<  RXATTRWALK fid %d size %llu\n",
                attr_fid->fid, *attr_size);
        return attr_fid;
@@ -2055,7 +2051,7 @@ int p9_client_xattrcreate(struct p9_fid *fid, const char *name,
                goto error;
        }
        p9_debug(P9_DEBUG_9P, "<<< RXATTRCREATE fid %d\n", fid->fid);
-       p9_free_req(clnt, req);
+       p9_tag_remove(clnt, req);
 error:
        return err;
 }
@@ -2103,9 +2099,9 @@ int p9_client_readdir(struct p9_fid *fid, char *data, u32 count, u64 offset)
                goto error;
        }
 
-       err = p9pdu_readf(req->rc, clnt->proto_version, "D", &count, &dataptr);
+       err = p9pdu_readf(&req->rc, clnt->proto_version, "D", &count, &dataptr);
        if (err) {
-               trace_9p_protocol_dump(clnt, req->rc);
+               trace_9p_protocol_dump(clnt, &req->rc);
                goto free_and_error;
        }
        if (rsize < count) {
@@ -2118,11 +2114,11 @@ int p9_client_readdir(struct p9_fid *fid, char *data, u32 count, u64 offset)
        if (non_zc)
                memmove(data, dataptr, count);
 
-       p9_free_req(clnt, req);
+       p9_tag_remove(clnt, req);
        return count;
 
 free_and_error:
-       p9_free_req(clnt, req);
+       p9_tag_remove(clnt, req);
 error:
        return err;
 }
@@ -2144,16 +2140,16 @@ int p9_client_mknod_dotl(struct p9_fid *fid, const char *name, int mode,
        if (IS_ERR(req))
                return PTR_ERR(req);
 
-       err = p9pdu_readf(req->rc, clnt->proto_version, "Q", qid);
+       err = p9pdu_readf(&req->rc, clnt->proto_version, "Q", qid);
        if (err) {
-               trace_9p_protocol_dump(clnt, req->rc);
+               trace_9p_protocol_dump(clnt, &req->rc);
                goto error;
        }
        p9_debug(P9_DEBUG_9P, "<<< RMKNOD qid %x.%llx.%x\n", qid->type,
                                (unsigned long long)qid->path, qid->version);
 
 error:
-       p9_free_req(clnt, req);
+       p9_tag_remove(clnt, req);
        return err;
 
 }
@@ -2175,16 +2171,16 @@ int p9_client_mkdir_dotl(struct p9_fid *fid, const char *name, int mode,
        if (IS_ERR(req))
                return PTR_ERR(req);
 
-       err = p9pdu_readf(req->rc, clnt->proto_version, "Q", qid);
+       err = p9pdu_readf(&req->rc, clnt->proto_version, "Q", qid);
        if (err) {
-               trace_9p_protocol_dump(clnt, req->rc);
+               trace_9p_protocol_dump(clnt, &req->rc);
                goto error;
        }
        p9_debug(P9_DEBUG_9P, "<<< RMKDIR qid %x.%llx.%x\n", qid->type,
                                (unsigned long long)qid->path, qid->version);
 
 error:
-       p9_free_req(clnt, req);
+       p9_tag_remove(clnt, req);
        return err;
 
 }
@@ -2210,14 +2206,14 @@ int p9_client_lock_dotl(struct p9_fid *fid, struct p9_flock *flock, u8 *status)
        if (IS_ERR(req))
                return PTR_ERR(req);
 
-       err = p9pdu_readf(req->rc, clnt->proto_version, "b", status);
+       err = p9pdu_readf(&req->rc, clnt->proto_version, "b", status);
        if (err) {
-               trace_9p_protocol_dump(clnt, req->rc);
+               trace_9p_protocol_dump(clnt, &req->rc);
                goto error;
        }
        p9_debug(P9_DEBUG_9P, "<<< RLOCK status %i\n", *status);
 error:
-       p9_free_req(clnt, req);
+       p9_tag_remove(clnt, req);
        return err;
 
 }
@@ -2241,18 +2237,18 @@ int p9_client_getlock_dotl(struct p9_fid *fid, struct p9_getlock *glock)
        if (IS_ERR(req))
                return PTR_ERR(req);
 
-       err = p9pdu_readf(req->rc, clnt->proto_version, "bqqds", &glock->type,
-                       &glock->start, &glock->length, &glock->proc_id,
-                       &glock->client_id);
+       err = p9pdu_readf(&req->rc, clnt->proto_version, "bqqds", &glock->type,
+                         &glock->start, &glock->length, &glock->proc_id,
+                         &glock->client_id);
        if (err) {
-               trace_9p_protocol_dump(clnt, req->rc);
+               trace_9p_protocol_dump(clnt, &req->rc);
                goto error;
        }
        p9_debug(P9_DEBUG_9P, "<<< RGETLOCK type %i start %lld length %lld "
                "proc_id %d client_id %s\n", glock->type, glock->start,
                glock->length, glock->proc_id, glock->client_id);
 error:
-       p9_free_req(clnt, req);
+       p9_tag_remove(clnt, req);
        return err;
 }
 EXPORT_SYMBOL(p9_client_getlock_dotl);
@@ -2271,14 +2267,25 @@ int p9_client_readlink(struct p9_fid *fid, char **target)
        if (IS_ERR(req))
                return PTR_ERR(req);
 
-       err = p9pdu_readf(req->rc, clnt->proto_version, "s", target);
+       err = p9pdu_readf(&req->rc, clnt->proto_version, "s", target);
        if (err) {
-               trace_9p_protocol_dump(clnt, req->rc);
+               trace_9p_protocol_dump(clnt, &req->rc);
                goto error;
        }
        p9_debug(P9_DEBUG_9P, "<<< RREADLINK target %s\n", *target);
 error:
-       p9_free_req(clnt, req);
+       p9_tag_remove(clnt, req);
        return err;
 }
 EXPORT_SYMBOL(p9_client_readlink);
+
+int __init p9_client_init(void)
+{
+       p9_req_cache = KMEM_CACHE(p9_req_t, SLAB_TYPESAFE_BY_RCU);
+       return p9_req_cache ? 0 : -ENOMEM;
+}
+
+void __exit p9_client_exit(void)
+{
+       kmem_cache_destroy(p9_req_cache);
+}
index 253ba824a325d903931d522b997ad9ab07708e41..0da56d6af73bdf6ef1708a2cb8059d392c835951 100644 (file)
@@ -171,11 +171,17 @@ void v9fs_put_trans(struct p9_trans_module *m)
  */
 static int __init init_p9(void)
 {
+       int ret;
+
+       ret = p9_client_init();
+       if (ret)
+               return ret;
+
        p9_error_init();
        pr_info("Installing 9P2000 support\n");
        p9_trans_fd_init();
 
-       return 0;
+       return ret;
 }
 
 /**
@@ -188,6 +194,7 @@ static void __exit exit_p9(void)
        pr_info("Unloading 9P2000 support\n");
 
        p9_trans_fd_exit();
+       p9_client_exit();
 }
 
 module_init(init_p9)
index 4a1e1dd30b52fd04abef83272567a1975a840e0f..462ba144cb3934d6761a66c84f7d332f0559c804 100644 (file)
@@ -46,10 +46,15 @@ p9pdu_writef(struct p9_fcall *pdu, int proto_version, const char *fmt, ...);
 void p9stat_free(struct p9_wstat *stbuf)
 {
        kfree(stbuf->name);
+       stbuf->name = NULL;
        kfree(stbuf->uid);
+       stbuf->uid = NULL;
        kfree(stbuf->gid);
+       stbuf->gid = NULL;
        kfree(stbuf->muid);
+       stbuf->muid = NULL;
        kfree(stbuf->extension);
+       stbuf->extension = NULL;
 }
 EXPORT_SYMBOL(p9stat_free);
 
@@ -566,9 +571,10 @@ int p9stat_read(struct p9_client *clnt, char *buf, int len, struct p9_wstat *st)
        if (ret) {
                p9_debug(P9_DEBUG_9P, "<<< p9stat_read failed: %d\n", ret);
                trace_9p_protocol_dump(clnt, &fake_pdu);
+               return ret;
        }
 
-       return ret;
+       return fake_pdu.offset;
 }
 EXPORT_SYMBOL(p9stat_read);
 
@@ -617,13 +623,19 @@ int p9dirent_read(struct p9_client *clnt, char *buf, int len,
        if (ret) {
                p9_debug(P9_DEBUG_9P, "<<< p9dirent_read failed: %d\n", ret);
                trace_9p_protocol_dump(clnt, &fake_pdu);
-               goto out;
+               return ret;
        }
 
-       strcpy(dirent->d_name, nameptr);
+       ret = strscpy(dirent->d_name, nameptr, sizeof(dirent->d_name));
+       if (ret < 0) {
+               p9_debug(P9_DEBUG_ERROR,
+                        "On the wire dirent name too long: %s\n",
+                        nameptr);
+               kfree(nameptr);
+               return ret;
+       }
        kfree(nameptr);
 
-out:
        return fake_pdu.offset;
 }
 EXPORT_SYMBOL(p9dirent_read);
index e2ef3c782c53869ba2b7748dbee67b1c55bfb217..f868cf6fba7946c87d0bb152dbd35ba61e3eb0fb 100644 (file)
@@ -131,7 +131,8 @@ struct p9_conn {
        int err;
        struct list_head req_list;
        struct list_head unsent_req_list;
-       struct p9_req_t *req;
+       struct p9_req_t *rreq;
+       struct p9_req_t *wreq;
        char tmp_buf[7];
        struct p9_fcall rc;
        int wpos;
@@ -291,7 +292,6 @@ static void p9_read_work(struct work_struct *work)
        __poll_t n;
        int err;
        struct p9_conn *m;
-       int status = REQ_STATUS_ERROR;
 
        m = container_of(work, struct p9_conn, rq);
 
@@ -322,7 +322,7 @@ static void p9_read_work(struct work_struct *work)
        m->rc.offset += err;
 
        /* header read in */
-       if ((!m->req) && (m->rc.offset == m->rc.capacity)) {
+       if ((!m->rreq) && (m->rc.offset == m->rc.capacity)) {
                p9_debug(P9_DEBUG_TRANS, "got new header\n");
 
                /* Header size */
@@ -346,23 +346,23 @@ static void p9_read_work(struct work_struct *work)
                         "mux %p pkt: size: %d bytes tag: %d\n",
                         m, m->rc.size, m->rc.tag);
 
-               m->req = p9_tag_lookup(m->client, m->rc.tag);
-               if (!m->req || (m->req->status != REQ_STATUS_SENT)) {
+               m->rreq = p9_tag_lookup(m->client, m->rc.tag);
+               if (!m->rreq || (m->rreq->status != REQ_STATUS_SENT)) {
                        p9_debug(P9_DEBUG_ERROR, "Unexpected packet tag %d\n",
                                 m->rc.tag);
                        err = -EIO;
                        goto error;
                }
 
-               if (m->req->rc == NULL) {
+               if (!m->rreq->rc.sdata) {
                        p9_debug(P9_DEBUG_ERROR,
                                 "No recv fcall for tag %d (req %p), disconnecting!\n",
-                                m->rc.tag, m->req);
-                       m->req = NULL;
+                                m->rc.tag, m->rreq);
+                       m->rreq = NULL;
                        err = -EIO;
                        goto error;
                }
-               m->rc.sdata = (char *)m->req->rc + sizeof(struct p9_fcall);
+               m->rc.sdata = m->rreq->rc.sdata;
                memcpy(m->rc.sdata, m->tmp_buf, m->rc.capacity);
                m->rc.capacity = m->rc.size;
        }
@@ -370,20 +370,27 @@ static void p9_read_work(struct work_struct *work)
        /* packet is read in
         * not an else because some packets (like clunk) have no payload
         */
-       if ((m->req) && (m->rc.offset == m->rc.capacity)) {
+       if ((m->rreq) && (m->rc.offset == m->rc.capacity)) {
                p9_debug(P9_DEBUG_TRANS, "got new packet\n");
-               m->req->rc->size = m->rc.offset;
+               m->rreq->rc.size = m->rc.offset;
                spin_lock(&m->client->lock);
-               if (m->req->status != REQ_STATUS_ERROR)
-                       status = REQ_STATUS_RCVD;
-               list_del(&m->req->req_list);
-               /* update req->status while holding client->lock  */
-               p9_client_cb(m->client, m->req, status);
+               if (m->rreq->status == REQ_STATUS_SENT) {
+                       list_del(&m->rreq->req_list);
+                       p9_client_cb(m->client, m->rreq, REQ_STATUS_RCVD);
+               } else {
+                       spin_unlock(&m->client->lock);
+                       p9_debug(P9_DEBUG_ERROR,
+                                "Request tag %d errored out while we were reading the reply\n",
+                                m->rc.tag);
+                       err = -EIO;
+                       goto error;
+               }
                spin_unlock(&m->client->lock);
                m->rc.sdata = NULL;
                m->rc.offset = 0;
                m->rc.capacity = 0;
-               m->req = NULL;
+               p9_req_put(m->rreq);
+               m->rreq = NULL;
        }
 
 end_clear:
@@ -469,9 +476,11 @@ static void p9_write_work(struct work_struct *work)
                p9_debug(P9_DEBUG_TRANS, "move req %p\n", req);
                list_move_tail(&req->req_list, &m->req_list);
 
-               m->wbuf = req->tc->sdata;
-               m->wsize = req->tc->size;
+               m->wbuf = req->tc.sdata;
+               m->wsize = req->tc.size;
                m->wpos = 0;
+               p9_req_get(req);
+               m->wreq = req;
                spin_unlock(&m->client->lock);
        }
 
@@ -492,8 +501,11 @@ static void p9_write_work(struct work_struct *work)
        }
 
        m->wpos += err;
-       if (m->wpos == m->wsize)
+       if (m->wpos == m->wsize) {
                m->wpos = m->wsize = 0;
+               p9_req_put(m->wreq);
+               m->wreq = NULL;
+       }
 
 end_clear:
        clear_bit(Wworksched, &m->wsched);
@@ -663,7 +675,7 @@ static int p9_fd_request(struct p9_client *client, struct p9_req_t *req)
        struct p9_conn *m = &ts->conn;
 
        p9_debug(P9_DEBUG_TRANS, "mux %p task %p tcall %p id %d\n",
-                m, current, req->tc, req->tc->id);
+                m, current, &req->tc, req->tc.id);
        if (m->err < 0)
                return m->err;
 
@@ -694,6 +706,7 @@ static int p9_fd_cancel(struct p9_client *client, struct p9_req_t *req)
        if (req->status == REQ_STATUS_UNSENT) {
                list_del(&req->req_list);
                req->status = REQ_STATUS_FLSHD;
+               p9_req_put(req);
                ret = 0;
        }
        spin_unlock(&client->lock);
@@ -711,6 +724,7 @@ static int p9_fd_cancelled(struct p9_client *client, struct p9_req_t *req)
        spin_lock(&client->lock);
        list_del(&req->req_list);
        spin_unlock(&client->lock);
+       p9_req_put(req);
 
        return 0;
 }
@@ -862,7 +876,15 @@ static void p9_conn_destroy(struct p9_conn *m)
 
        p9_mux_poll_stop(m);
        cancel_work_sync(&m->rq);
+       if (m->rreq) {
+               p9_req_put(m->rreq);
+               m->rreq = NULL;
+       }
        cancel_work_sync(&m->wq);
+       if (m->wreq) {
+               p9_req_put(m->wreq);
+               m->wreq = NULL;
+       }
 
        p9_conn_cancel(m, -ECONNRESET);
 
index b513cffeeb3c05d721ac2e203fd5687ad5b2b037..119103bfa82eef579e53e60908812b33c1fea52f 100644 (file)
@@ -122,7 +122,7 @@ struct p9_rdma_context {
        dma_addr_t busa;
        union {
                struct p9_req_t *req;
-               struct p9_fcall *rc;
+               struct p9_fcall rc;
        };
 };
 
@@ -274,8 +274,7 @@ p9_cm_event_handler(struct rdma_cm_id *id, struct rdma_cm_event *event)
        case RDMA_CM_EVENT_DISCONNECTED:
                if (rdma)
                        rdma->state = P9_RDMA_CLOSED;
-               if (c)
-                       c->status = Disconnected;
+               c->status = Disconnected;
                break;
 
        case RDMA_CM_EVENT_TIMEWAIT_EXIT:
@@ -320,8 +319,8 @@ recv_done(struct ib_cq *cq, struct ib_wc *wc)
        if (wc->status != IB_WC_SUCCESS)
                goto err_out;
 
-       c->rc->size = wc->byte_len;
-       err = p9_parse_header(c->rc, NULL, NULL, &tag, 1);
+       c->rc.size = wc->byte_len;
+       err = p9_parse_header(&c->rc, NULL, NULL, &tag, 1);
        if (err)
                goto err_out;
 
@@ -331,12 +330,13 @@ recv_done(struct ib_cq *cq, struct ib_wc *wc)
 
        /* Check that we have not yet received a reply for this request.
         */
-       if (unlikely(req->rc)) {
+       if (unlikely(req->rc.sdata)) {
                pr_err("Duplicate reply for request %d", tag);
                goto err_out;
        }
 
-       req->rc = c->rc;
+       req->rc.size = c->rc.size;
+       req->rc.sdata = c->rc.sdata;
        p9_client_cb(client, req, REQ_STATUS_RCVD);
 
  out:
@@ -361,9 +361,10 @@ send_done(struct ib_cq *cq, struct ib_wc *wc)
                container_of(wc->wr_cqe, struct p9_rdma_context, cqe);
 
        ib_dma_unmap_single(rdma->cm_id->device,
-                           c->busa, c->req->tc->size,
+                           c->busa, c->req->tc.size,
                            DMA_TO_DEVICE);
        up(&rdma->sq_sem);
+       p9_req_put(c->req);
        kfree(c);
 }
 
@@ -401,7 +402,7 @@ post_recv(struct p9_client *client, struct p9_rdma_context *c)
        struct ib_sge sge;
 
        c->busa = ib_dma_map_single(rdma->cm_id->device,
-                                   c->rc->sdata, client->msize,
+                                   c->rc.sdata, client->msize,
                                    DMA_FROM_DEVICE);
        if (ib_dma_mapping_error(rdma->cm_id->device, c->busa))
                goto error;
@@ -443,9 +444,9 @@ static int rdma_request(struct p9_client *client, struct p9_req_t *req)
         **/
        if (unlikely(atomic_read(&rdma->excess_rc) > 0)) {
                if ((atomic_sub_return(1, &rdma->excess_rc) >= 0)) {
-                       /* Got one ! */
-                       kfree(req->rc);
-                       req->rc = NULL;
+                       /* Got one! */
+                       p9_fcall_fini(&req->rc);
+                       req->rc.sdata = NULL;
                        goto dont_need_post_recv;
                } else {
                        /* We raced and lost. */
@@ -459,7 +460,7 @@ static int rdma_request(struct p9_client *client, struct p9_req_t *req)
                err = -ENOMEM;
                goto recv_error;
        }
-       rpl_context->rc = req->rc;
+       rpl_context->rc.sdata = req->rc.sdata;
 
        /*
         * Post a receive buffer for this request. We need to ensure
@@ -475,11 +476,11 @@ static int rdma_request(struct p9_client *client, struct p9_req_t *req)
 
        err = post_recv(client, rpl_context);
        if (err) {
-               p9_debug(P9_DEBUG_FCALL, "POST RECV failed\n");
+               p9_debug(P9_DEBUG_ERROR, "POST RECV failed: %d\n", err);
                goto recv_error;
        }
        /* remove posted receive buffer from request structure */
-       req->rc = NULL;
+       req->rc.sdata = NULL;
 
 dont_need_post_recv:
        /* Post the request */
@@ -491,7 +492,7 @@ dont_need_post_recv:
        c->req = req;
 
        c->busa = ib_dma_map_single(rdma->cm_id->device,
-                                   c->req->tc->sdata, c->req->tc->size,
+                                   c->req->tc.sdata, c->req->tc.size,
                                    DMA_TO_DEVICE);
        if (ib_dma_mapping_error(rdma->cm_id->device, c->busa)) {
                err = -EIO;
@@ -501,7 +502,7 @@ dont_need_post_recv:
        c->cqe.done = send_done;
 
        sge.addr = c->busa;
-       sge.length = c->req->tc->size;
+       sge.length = c->req->tc.size;
        sge.lkey = rdma->pd->local_dma_lkey;
 
        wr.next = NULL;
@@ -544,7 +545,7 @@ dont_need_post_recv:
  recv_error:
        kfree(rpl_context);
        spin_lock_irqsave(&rdma->req_lock, flags);
-       if (rdma->state < P9_RDMA_CLOSING) {
+       if (err != -EINTR && rdma->state < P9_RDMA_CLOSING) {
                rdma->state = P9_RDMA_CLOSING;
                spin_unlock_irqrestore(&rdma->req_lock, flags);
                rdma_disconnect(rdma->cm_id);
index 7728b0acde09aa904f4cc564de23f264abd8d2b6..eb596c2ed546ca5555a2100426eebb67205d99b1 100644 (file)
@@ -155,7 +155,7 @@ static void req_done(struct virtqueue *vq)
                }
 
                if (len) {
-                       req->rc->size = len;
+                       req->rc.size = len;
                        p9_client_cb(chan->client, req, REQ_STATUS_RCVD);
                }
        }
@@ -207,6 +207,13 @@ static int p9_virtio_cancel(struct p9_client *client, struct p9_req_t *req)
        return 1;
 }
 
+/* Reply won't come, so drop req ref */
+static int p9_virtio_cancelled(struct p9_client *client, struct p9_req_t *req)
+{
+       p9_req_put(req);
+       return 0;
+}
+
 /**
  * pack_sg_list_p - Just like pack_sg_list. Instead of taking a buffer,
  * this takes a list of pages.
@@ -273,12 +280,12 @@ req_retry:
        out_sgs = in_sgs = 0;
        /* Handle out VirtIO ring buffers */
        out = pack_sg_list(chan->sg, 0,
-                          VIRTQUEUE_NUM, req->tc->sdata, req->tc->size);
+                          VIRTQUEUE_NUM, req->tc.sdata, req->tc.size);
        if (out)
                sgs[out_sgs++] = chan->sg;
 
        in = pack_sg_list(chan->sg, out,
-                         VIRTQUEUE_NUM, req->rc->sdata, req->rc->capacity);
+                         VIRTQUEUE_NUM, req->rc.sdata, req->rc.capacity);
        if (in)
                sgs[out_sgs + in_sgs++] = chan->sg + out;
 
@@ -404,6 +411,7 @@ p9_virtio_zc_request(struct p9_client *client, struct p9_req_t *req,
        struct scatterlist *sgs[4];
        size_t offs;
        int need_drop = 0;
+       int kicked = 0;
 
        p9_debug(P9_DEBUG_TRANS, "virtio request\n");
 
@@ -411,29 +419,33 @@ p9_virtio_zc_request(struct p9_client *client, struct p9_req_t *req,
                __le32 sz;
                int n = p9_get_mapped_pages(chan, &out_pages, uodata,
                                            outlen, &offs, &need_drop);
-               if (n < 0)
-                       return n;
+               if (n < 0) {
+                       err = n;
+                       goto err_out;
+               }
                out_nr_pages = DIV_ROUND_UP(n + offs, PAGE_SIZE);
                if (n != outlen) {
                        __le32 v = cpu_to_le32(n);
-                       memcpy(&req->tc->sdata[req->tc->size - 4], &v, 4);
+                       memcpy(&req->tc.sdata[req->tc.size - 4], &v, 4);
                        outlen = n;
                }
                /* The size field of the message must include the length of the
                 * header and the length of the data.  We didn't actually know
                 * the length of the data until this point so add it in now.
                 */
-               sz = cpu_to_le32(req->tc->size + outlen);
-               memcpy(&req->tc->sdata[0], &sz, sizeof(sz));
+               sz = cpu_to_le32(req->tc.size + outlen);
+               memcpy(&req->tc.sdata[0], &sz, sizeof(sz));
        } else if (uidata) {
                int n = p9_get_mapped_pages(chan, &in_pages, uidata,
                                            inlen, &offs, &need_drop);
-               if (n < 0)
-                       return n;
+               if (n < 0) {
+                       err = n;
+                       goto err_out;
+               }
                in_nr_pages = DIV_ROUND_UP(n + offs, PAGE_SIZE);
                if (n != inlen) {
                        __le32 v = cpu_to_le32(n);
-                       memcpy(&req->tc->sdata[req->tc->size - 4], &v, 4);
+                       memcpy(&req->tc.sdata[req->tc.size - 4], &v, 4);
                        inlen = n;
                }
        }
@@ -445,7 +457,7 @@ req_retry_pinned:
 
        /* out data */
        out = pack_sg_list(chan->sg, 0,
-                          VIRTQUEUE_NUM, req->tc->sdata, req->tc->size);
+                          VIRTQUEUE_NUM, req->tc.sdata, req->tc.size);
 
        if (out)
                sgs[out_sgs++] = chan->sg;
@@ -464,7 +476,7 @@ req_retry_pinned:
         * alloced memory and payload onto the user buffer.
         */
        in = pack_sg_list(chan->sg, out,
-                         VIRTQUEUE_NUM, req->rc->sdata, in_hdr_len);
+                         VIRTQUEUE_NUM, req->rc.sdata, in_hdr_len);
        if (in)
                sgs[out_sgs + in_sgs++] = chan->sg + out;
 
@@ -498,6 +510,7 @@ req_retry_pinned:
        }
        virtqueue_kick(chan->vq);
        spin_unlock_irqrestore(&chan->lock, flags);
+       kicked = 1;
        p9_debug(P9_DEBUG_TRANS, "virtio request kicked\n");
        err = wait_event_killable(req->wq, req->status >= REQ_STATUS_RCVD);
        /*
@@ -518,6 +531,10 @@ err_out:
        }
        kvfree(in_pages);
        kvfree(out_pages);
+       if (!kicked) {
+               /* reply won't come */
+               p9_req_put(req);
+       }
        return err;
 }
 
@@ -750,6 +767,7 @@ static struct p9_trans_module p9_virtio_trans = {
        .request = p9_virtio_request,
        .zc_request = p9_virtio_zc_request,
        .cancel = p9_virtio_cancel,
+       .cancelled = p9_virtio_cancelled,
        /*
         * We leave one entry for input and one entry for response
         * headers. We also skip one more entry to accomodate, address
index c2d54ac76bfdbb72fa7ad24d63f7379afdc5433d..e2fbf3677b9baf3fa99ba98485f73caf0118f249 100644 (file)
@@ -141,7 +141,7 @@ static int p9_xen_request(struct p9_client *client, struct p9_req_t *p9_req)
        struct xen_9pfs_front_priv *priv = NULL;
        RING_IDX cons, prod, masked_cons, masked_prod;
        unsigned long flags;
-       u32 size = p9_req->tc->size;
+       u32 size = p9_req->tc.size;
        struct xen_9pfs_dataring *ring;
        int num;
 
@@ -154,7 +154,7 @@ static int p9_xen_request(struct p9_client *client, struct p9_req_t *p9_req)
        if (!priv || priv->client != client)
                return -EINVAL;
 
-       num = p9_req->tc->tag % priv->num_rings;
+       num = p9_req->tc.tag % priv->num_rings;
        ring = &priv->rings[num];
 
 again:
@@ -176,7 +176,7 @@ again:
        masked_prod = xen_9pfs_mask(prod, XEN_9PFS_RING_SIZE);
        masked_cons = xen_9pfs_mask(cons, XEN_9PFS_RING_SIZE);
 
-       xen_9pfs_write_packet(ring->data.out, p9_req->tc->sdata, size,
+       xen_9pfs_write_packet(ring->data.out, p9_req->tc.sdata, size,
                              &masked_prod, masked_cons, XEN_9PFS_RING_SIZE);
 
        p9_req->status = REQ_STATUS_SENT;
@@ -185,6 +185,7 @@ again:
        ring->intf->out_prod = prod;
        spin_unlock_irqrestore(&ring->lock, flags);
        notify_remote_via_irq(ring->irq);
+       p9_req_put(p9_req);
 
        return 0;
 }
@@ -229,12 +230,12 @@ static void p9_xen_response(struct work_struct *work)
                        continue;
                }
 
-               memcpy(req->rc, &h, sizeof(h));
-               req->rc->offset = 0;
+               memcpy(&req->rc, &h, sizeof(h));
+               req->rc.offset = 0;
 
                masked_cons = xen_9pfs_mask(cons, XEN_9PFS_RING_SIZE);
                /* Then, read the whole packet (including the header) */
-               xen_9pfs_read_packet(req->rc->sdata, ring->data.in, h.size,
+               xen_9pfs_read_packet(req->rc.sdata, ring->data.in, h.size,
                                     masked_prod, &masked_cons,
                                     XEN_9PFS_RING_SIZE);
 
@@ -391,8 +392,8 @@ static int xen_9pfs_front_probe(struct xenbus_device *dev,
        unsigned int max_rings, max_ring_order, len = 0;
 
        versions = xenbus_read(XBT_NIL, dev->otherend, "versions", &len);
-       if (!len)
-               return -EINVAL;
+       if (IS_ERR(versions))
+               return PTR_ERR(versions);
        if (strcmp(versions, "1")) {
                kfree(versions);
                return -EINVAL;
diff --git a/net/9p/util.c b/net/9p/util.c
deleted file mode 100644 (file)
index 55ad982..0000000
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- *  net/9p/util.c
- *
- *  This file contains some helper functions
- *
- *  Copyright (C) 2007 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/parser.h>
-#include <linux/idr.h>
-#include <linux/slab.h>
-#include <net/9p/9p.h>
-
-/**
- * struct p9_idpool - per-connection accounting for tag idpool
- * @lock: protects the pool
- * @pool: idr to allocate tag id from
- *
- */
-
-struct p9_idpool {
-       spinlock_t lock;
-       struct idr pool;
-};
-
-/**
- * p9_idpool_create - create a new per-connection id pool
- *
- */
-
-struct p9_idpool *p9_idpool_create(void)
-{
-       struct p9_idpool *p;
-
-       p = kmalloc(sizeof(struct p9_idpool), GFP_KERNEL);
-       if (!p)
-               return ERR_PTR(-ENOMEM);
-
-       spin_lock_init(&p->lock);
-       idr_init(&p->pool);
-
-       return p;
-}
-EXPORT_SYMBOL(p9_idpool_create);
-
-/**
- * p9_idpool_destroy - create a new per-connection id pool
- * @p: idpool to destroy
- */
-
-void p9_idpool_destroy(struct p9_idpool *p)
-{
-       idr_destroy(&p->pool);
-       kfree(p);
-}
-EXPORT_SYMBOL(p9_idpool_destroy);
-
-/**
- * p9_idpool_get - allocate numeric id from pool
- * @p: pool to allocate from
- *
- * Bugs: This seems to be an awful generic function, should it be in idr.c with
- *            the lock included in struct idr?
- */
-
-int p9_idpool_get(struct p9_idpool *p)
-{
-       int i;
-       unsigned long flags;
-
-       idr_preload(GFP_NOFS);
-       spin_lock_irqsave(&p->lock, flags);
-
-       /* no need to store exactly p, we just need something non-null */
-       i = idr_alloc(&p->pool, p, 0, 0, GFP_NOWAIT);
-
-       spin_unlock_irqrestore(&p->lock, flags);
-       idr_preload_end();
-       if (i < 0)
-               return -1;
-
-       p9_debug(P9_DEBUG_MUX, " id %d pool %p\n", i, p);
-       return i;
-}
-EXPORT_SYMBOL(p9_idpool_get);
-
-/**
- * p9_idpool_put - release numeric id from pool
- * @id: numeric id which is being released
- * @p: pool to release id into
- *
- * Bugs: This seems to be an awful generic function, should it be in idr.c with
- *            the lock included in struct idr?
- */
-
-void p9_idpool_put(int id, struct p9_idpool *p)
-{
-       unsigned long flags;
-
-       p9_debug(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);
-}
-EXPORT_SYMBOL(p9_idpool_put);
-
-/**
- * p9_idpool_check - check if the specified id is available
- * @id: id to check
- * @p: pool to check
- */
-
-int p9_idpool_check(int id, struct p9_idpool *p)
-{
-       return idr_find(&p->pool, id) != NULL;
-}
-EXPORT_SYMBOL(p9_idpool_check);