[PATCH 3/5] 9p/net: implement asynchronous rpc skeleton

From: Dominique Martinet
Date: Sat Feb 11 2023 - 02:51:15 EST


Add p9_client_async_rpc that will let us send an rpc without waiting
for the reply, as well as an async callback hook.

The callback is called, but nothing is ever put in the list at this
point and p9_client_async_rpc() is unused.

Previous version of this patch here[1] used to implement the async check
separately from the callback, but we will want to be notified when flush
has been processed.

Link: http://lkml.kernel.org/r/1544532108-21689-1-git-send-email-asmadeus@xxxxxxxxxxxxx (v1)
Signed-off-by: Dominique Martinet <asmadeus@xxxxxxxxxxxxx>
---
include/net/9p/client.h | 9 ++++-
net/9p/client.c | 76 +++++++++++++++++++++++++++++++++++++++++
2 files changed, 84 insertions(+), 1 deletion(-)

diff --git a/include/net/9p/client.h b/include/net/9p/client.h
index 78ebcf782ce5..348ea191ac66 100644
--- a/include/net/9p/client.h
+++ b/include/net/9p/client.h
@@ -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 {
diff --git a/net/9p/client.c b/net/9p/client.c
index 4e5238db4a7a..3235543c1884 100644
--- a/net/9p/client.c
+++ b/net/9p/client.c
@@ -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);
--
2.39.1