Merge branch 'work.afs' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
[platform/kernel/linux-starfive.git] / net / 9p / client.c
index a9cd140..2c9a17b 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);
+}