@@ -72,7 +72,8 @@ enum p9_req_status_t {
* @wq: wait_queue for the client to block on for this request
* @tc: the request fcall structure
* @rc: the response fcall structure
- * @req_list: link for higher level objects to chain requests
+ * @req_list: link for transports to chain requests (used by trans_fd)
+ * @async_list: link used to check on async requests
*/
struct p9_req_t {
int status;
@@ -82,6 +83,7 @@ struct p9_req_t {
struct p9_fcall tc;
struct p9_fcall rc;
struct list_head req_list;
+ struct list_head async_list;
};
/**
@@ -92,6 +94,9 @@ struct p9_req_t {
* @trans_mod: module API instantiated with this client
* @status: connection state
* @trans: tranport instance state and API
+ * @fcall_cache: kmem cache for client request data (msize-specific)
+ * @async_req_lock: protects @async_req_list
+ * @async_req_list: list of requests waiting a reply
* @fids: All active FID handles
* @reqs: All active requests.
* @name: node name used as client id
@@ -107,6 +112,8 @@ struct p9_client {
enum p9_trans_status status;
void *trans;
struct kmem_cache *fcall_cache;
+ spinlock_t async_req_lock;
+ struct list_head async_req_list;
union {
struct {
@@ -311,6 +311,7 @@ p9_tag_alloc(struct p9_client *c, int8_t type, uint t_size, uint r_size,
refcount_set(&req->refcount, 0);
init_waitqueue_head(&req->wq);
INIT_LIST_HEAD(&req->req_list);
+ INIT_LIST_HEAD(&req->async_list);
idr_preload(GFP_NOFS);
spin_lock_irq(&c->lock);
@@ -495,6 +496,27 @@ void do_trace_9p_fid_put(struct p9_fid *fid)
}
EXPORT_SYMBOL(do_trace_9p_fid_put);
+/**
+ * p9_client_async_cb -- handle async requests in cb
+ * @c: client state
+ * @req: request received
+ */
+static void p9_client_async_cb(struct p9_client *c, struct p9_req_t *req)
+{
+ unsigned long flags;
+
+ /* Nothing to do for non-async requests */
+ if (likely(list_empty(&req->async_list)))
+ return;
+
+ WARN(1, "Async request received with tc.id %d\n", req->tc.id);
+
+ spin_lock_irqsave(&c->async_req_lock, flags);
+ list_del(&req->async_list);
+ spin_unlock_irqrestore(&c->async_req_lock, flags);
+ p9_tag_remove(c, req);
+}
+
/**
* p9_client_cb - call back from transport to client
* @c: client state
@@ -506,6 +528,8 @@ 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_client_async_cb(c, req);
+
/* This barrier is needed to make sure any change made to req before
* the status change is visible to another thread
*/
@@ -676,6 +700,54 @@ static struct p9_req_t *p9_client_prepare_req(struct p9_client *c,
return ERR_PTR(err);
}
+static struct p9_req_t *
+p9_client_async_rpc(struct p9_client *c, int8_t type, const char *fmt, ...)
+{
+ va_list ap;
+ int err;
+ struct p9_req_t *req;
+
+ va_start(ap, fmt);
+ /* auto determine an appropriate request/response size */
+ req = p9_client_prepare_req(c, type, 0, 0, fmt, ap);
+ va_end(ap);
+ if (IS_ERR(req))
+ return req;
+
+ err = c->trans_mod->request(c, req);
+ if (err < 0) {
+ /* bail out without flushing... */
+ p9_req_put(c, req);
+ p9_tag_remove(c, req);
+ if (err != -ERESTARTSYS && err != -EFAULT)
+ c->status = Disconnected;
+ return ERR_PTR(safe_errno(err));
+ }
+
+ return req;
+}
+
+static void p9_client_cleanup_async(struct p9_client *c)
+{
+ struct p9_req_t *req, *nreq;
+ unsigned long flags;
+
+ spin_lock_irqsave(&c->async_req_lock, flags);
+ list_for_each_entry_safe(req, nreq, &c->async_req_list, async_list) {
+ if (req->status < REQ_STATUS_RCVD) {
+ p9_debug(P9_DEBUG_ERROR,
+ "final async handler found req tag %d type %d status %d\n",
+ req->tc.tag, req->tc.id, req->status);
+ if (c->trans_mod->cancelled)
+ c->trans_mod->cancelled(c, req);
+ /* won't receive reply now */
+ p9_req_put(c, req);
+ }
+ p9_client_async_cb(c, req);
+ }
+ spin_unlock_irqrestore(&c->async_req_lock, flags);
+}
+
static struct p9_req_t *
p9_client_rpc(struct p9_client *c, int8_t type, const char *fmt, ...);
@@ -983,6 +1055,8 @@ struct p9_client *p9_client_create(const char *dev_name, char *options)
memcpy(clnt->name, client_id, strlen(client_id) + 1);
spin_lock_init(&clnt->lock);
+ spin_lock_init(&clnt->async_req_lock);
+ INIT_LIST_HEAD(&clnt->async_req_list);
idr_init(&clnt->fids);
idr_init(&clnt->reqs);
@@ -1059,6 +1133,8 @@ void p9_client_destroy(struct p9_client *clnt)
v9fs_put_trans(clnt->trans_mod);
+ p9_client_cleanup_async(clnt);
+
idr_for_each_entry(&clnt->fids, fid, id) {
pr_info("Found fid %d not clunked\n", fid->fid);
p9_fid_destroy(fid);