|
|
@@ -248,132 +248,102 @@ static struct p9_fcall *p9_fcall_alloc(int alloc_msize)
|
|
|
return fc;
|
|
|
}
|
|
|
|
|
|
+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;
|
|
|
+ if (!req)
|
|
|
+ return NULL;
|
|
|
|
|
|
- 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);
|
|
|
+ req->tc = p9_fcall_alloc(alloc_msize);
|
|
|
+ req->rc = p9_fcall_alloc(alloc_msize);
|
|
|
if (!req->tc || !req->rc)
|
|
|
- goto grow_failed;
|
|
|
+ goto free;
|
|
|
|
|
|
p9pdu_reset(req->tc);
|
|
|
p9pdu_reset(req->rc);
|
|
|
-
|
|
|
- req->tc->tag = tag-1;
|
|
|
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;
|
|
|
|
|
|
return req;
|
|
|
|
|
|
-grow_failed:
|
|
|
- pr_err("Couldn't grow tag array\n");
|
|
|
+free:
|
|
|
kfree(req->tc);
|
|
|
kfree(req->rc);
|
|
|
- req->tc = req->rc = NULL;
|
|
|
+ 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();
|
|
|
+ req = idr_find(&c->reqs, tag);
|
|
|
+ /* There's no refcount on the req; a malicious server could cause
|
|
|
+ * us to dereference a NULL pointer
|
|
|
+ */
|
|
|
+ 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_free_req - Free a request.
|
|
|
+ * @c: Client session.
|
|
|
+ * @r: Request to free.
|
|
|
*
|
|
|
+ * Context: Any context.
|
|
|
*/
|
|
|
-
|
|
|
-static int p9_tag_init(struct p9_client *c)
|
|
|
+static void p9_free_req(struct p9_client *c, struct p9_req_t *r)
|
|
|
{
|
|
|
- int err = 0;
|
|
|
+ unsigned long flags;
|
|
|
+ u16 tag = r->tc->tag;
|
|
|
|
|
|
- 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;
|
|
|
+ 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);
|
|
|
+ kfree(r->tc);
|
|
|
+ kfree(r->rc);
|
|
|
+ kmem_cache_free(p9_req_cache, r);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -385,52 +355,15 @@ 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);
|
|
|
+ p9_free_req(c, req);
|
|
|
}
|
|
|
- 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();
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -704,7 +637,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);
|
|
|
@@ -717,24 +650,17 @@ 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);
|
|
|
+ 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);
|
|
|
+ trace_9p_client_req(c, type, req->tc->tag);
|
|
|
return req;
|
|
|
reterr:
|
|
|
p9_free_req(c, req);
|
|
|
@@ -1040,14 +966,11 @@ struct p9_client *p9_client_create(const char *dev_name, char *options)
|
|
|
|
|
|
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();
|
|
|
@@ -1056,7 +979,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",
|
|
|
@@ -1086,8 +1009,6 @@ 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);
|
|
|
@@ -2303,3 +2224,14 @@ error:
|
|
|
return err;
|
|
|
}
|
|
|
EXPORT_SYMBOL(p9_client_readlink);
|
|
|
+
|
|
|
+int __init p9_client_init(void)
|
|
|
+{
|
|
|
+ p9_req_cache = KMEM_CACHE(p9_req_t, 0);
|
|
|
+ return p9_req_cache ? 0 : -ENOMEM;
|
|
|
+}
|
|
|
+
|
|
|
+void __exit p9_client_exit(void)
|
|
|
+{
|
|
|
+ kmem_cache_destroy(p9_req_cache);
|
|
|
+}
|