* [PATCH v2 0/2] SUNRPC/NFS: limit transport disconnects when cancelling flexfiles I/O
@ 2026-11-05 2:02 tmenninger
2026-11-05 2:02 ` [PATCH v2 1/2] SUNRPC: add request-scoped disconnects when cancelling tasks tmenninger
2026-11-05 2:02 ` [PATCH v2 2/2] NFSv4/flexfiles: limit transport disconnects when cancelling I/O tmenninger
0 siblings, 2 replies; 5+ messages in thread
From: tmenninger @ 2026-11-05 2:02 UTC (permalink / raw)
To: anna, trondmy
Cc: cel, ebadger, jcurley, jlayton, linux-kernel, linux-nfs, netdev,
slingappa, Tim Menninger
From: Tim Menninger <tmenninger@everpuredata.com>
This series narrows the transport disconnects performed when flexfiles
cancels I/O for a recalled or revoked layout segment.
Today ff_layout_cancel_io() first cancels matching RPC tasks and then
calls rpc_clnt_disconnect() if any tasks matched. rpc_clnt_disconnect()
operates on the entire rpc_clnt, so for a data server client using
multiple transports it disconnects transports that may have had no I/O
associated with the affected layout segment.
I encountered this while testing pNFS over RPC/RDMA during data server
recovery. Restarting the NFS service on a data server while sustained
direct-read I/O was active could cause repeated reconnect activity and a
large throughput drop. The client-wide disconnect from
ff_layout_cancel_io() amplified the disruption by tearing down otherwise
unaffected transports.
The first patch adds a SUNRPC helper that combines task cancellation with
request-scoped conditional disconnects. Matching tasks are cancelled as
before. A subsequent transport-queue pass marks matching requests
belonging to the same rpc_clnt that remain on the transmit or receive
queues so that, when released, xprt_conditional_disconnect() is called
using the request's saved connection cookie.
The disconnect state is stored on struct rpc_rqst rather than struct
rpc_task. A task can release one request and later acquire another, so a
task-scoped indication could otherwise be consumed by a successor
request and applied to the wrong transport generation.
The request-marking pass also preserves the rpc_clnt selection used by
rpc_cancel_tasks(). An rpc_xprt may be shared by multiple rpc_clnt
instances, so requests belonging to other clients sharing the same
transport are not marked.
The second patch switches flexfiles to the new helper, preserving its
I/O cancellation behavior while avoiding client-wide transport
disconnects.
Testing:
The code in this series does not depend on:
https://lore.kernel.org/all/20261105000131.2512336-1-tmenninger@everpuredata.com/
However, I used that change while collecting the results below because,
without it, the client enters the earlier recovery livelock before the
transport-disconnect behavior can be observed cleanly.
As a control experiment, I also tested skipping the
ff_layout_cancel_io() task-cancellation and client-disconnect path
entirely for RPC/RDMA. Doing so eliminates the reconnect pathology. This
shows that the pathology depends on the cancellation/disconnect path
introduced by commit b739a5bd9d9f ("NFSv4/flexfiles: Cancel I/O if the
layout is recalled or revoked"), but simply opting RPC/RDMA out of that
behavior would also remove the cancellation that is intended to drain
I/O associated with a recalled or revoked layout segment.
This series instead preserves that cancellation while limiting transport
disconnects to matching requests that remain in flight.
I have repeated the data-server restart test many times and the behavior
is consistent. The counters below are intentionally collected after the
restart event, once throughput has partially recovered and the client has
entered the post-recovery state in which it remains until the workload
completes.
This distinction matters because some transport activity during the
data-server restart itself is expected. The problem addressed here is
the continued reconnect activity after that transition has completed:
once recovery has settled, the client should not continue cycling
otherwise healthy RPC/RDMA connections.
Over a 30-second post-recovery sampling window, this series reduces
sunrpc events from 4,526 to 289, a roughly
16-fold reduction. RPC/RDMA disconnect and connect events fall by about
6.6-fold. During the same interval, read throughput improves from
approximately 0.4-1.1 GB/s to 1.9-2.2 GB/s.
Before (throughput: approximately 0.4-1.1 GB/s):
Performance counter stats for 'system wide':
4,526 sunrpc:xprt_disconnect_force
1,911 rpcrdma:xprtrdma_disconnect
1,909 rpcrdma:xprtrdma_connect
30.021267692 seconds time elapsed
After (throughput: approximately 1.9-2.2 GB/s):
Performance counter stats for 'system wide':
289 sunrpc:xprt_disconnect_force
289 rpcrdma:xprtrdma_disconnect
291 rpcrdma:xprtrdma_connect
30.080790959 seconds time elapsed
---
Changes in v2:
- Rebase onto v7.4-rc1.
- Adapt to the device-ID-aware ff_layout_cancel_io() interface,
preserving device-node reference cleanup.
- No functional changes to the SUNRPC implementation.
v1: https://lore.kernel.org/all/20260924204525.3381590-1-tmenninger@everpuredata.com/
Tim Menninger (2):
SUNRPC: add request-scoped disconnects when cancelling tasks
NFSv4/flexfiles: limit transport disconnects when cancelling I/O
fs/nfs/flexfilelayout/flexfilelayout.c | 7 +-
include/linux/sunrpc/sched.h | 4 ++
include/linux/sunrpc/xprt.h | 5 ++
net/sunrpc/clnt.c | 94 +++++++++++++++++++++-----
net/sunrpc/sunrpc.h | 5 ++
net/sunrpc/xprt.c | 76 +++++++++++++++++++++
6 files changed, 170 insertions(+), 21 deletions(-)
--
2.53.0
^ permalink raw reply [flat|nested] 5+ messages in thread* [PATCH v2 1/2] SUNRPC: add request-scoped disconnects when cancelling tasks 2026-11-05 2:02 [PATCH v2 0/2] SUNRPC/NFS: limit transport disconnects when cancelling flexfiles I/O tmenninger @ 2026-11-05 2:02 ` tmenninger 2026-11-05 3:14 ` Trond Myklebust 2026-11-05 2:02 ` [PATCH v2 2/2] NFSv4/flexfiles: limit transport disconnects when cancelling I/O tmenninger 1 sibling, 1 reply; 5+ messages in thread From: tmenninger @ 2026-11-05 2:02 UTC (permalink / raw) To: anna, trondmy Cc: cel, ebadger, jcurley, jlayton, linux-kernel, linux-nfs, netdev, slingappa, Tim Menninger From: Tim Menninger <tmenninger@everpuredata.com> rpc_cancel_tasks() allows callers to cancel a selected set of RPC tasks, but callers that also need to tear down the connection currently have to disconnect at the rpc_clnt level. That is broader than necessary for clients with multiple transports. A cancelled request is associated with a specific rpc_xprt and records the connection generation on which it was transmitted in rq_connect_cookie. Add rpc_cancel_tasks_and_disconnect() to cancel matching tasks and mark matching requests that remain on the transport transmit or receive queues for conditional transport disconnect when the request is released. Store the disconnect indication in struct rpc_rqst rather than struct rpc_task. An RPC task can release one request and later acquire another, so task-scoped state could otherwise be consumed by a successor request and disconnect the wrong transport generation. After cancelling matching tasks, walk the client's transports and mark matching requests belonging to that client that remain on the transmit or receive queues. An rpc_xprt may be shared by multiple rpc_clnt instances, so requests belonging to other clients are left untouched. When a marked request is released, use xprt_conditional_disconnect() with its saved rq_connect_cookie. This disconnects the transport only if it is still using the same connection generation on which the request was sent, and leaves a replacement connection untouched. Requests that were never transmitted do not require a disconnect. Signed-off-by: Tim Menninger <tmenninger@everpuredata.com> --- include/linux/sunrpc/sched.h | 4 ++ include/linux/sunrpc/xprt.h | 5 ++ net/sunrpc/clnt.c | 94 +++++++++++++++++++++++++++++------- net/sunrpc/sunrpc.h | 5 ++ net/sunrpc/xprt.c | 76 +++++++++++++++++++++++++++++ 5 files changed, 167 insertions(+), 17 deletions(-) diff --git a/include/linux/sunrpc/sched.h b/include/linux/sunrpc/sched.h index 0dbdf3722537f..dbaad8b5f181c 100644 --- a/include/linux/sunrpc/sched.h +++ b/include/linux/sunrpc/sched.h @@ -230,6 +230,10 @@ unsigned long rpc_cancel_tasks(struct rpc_clnt *clnt, int error, bool (*fnmatch)(const struct rpc_task *, const void *), const void *data); +unsigned long rpc_cancel_tasks_and_disconnect(struct rpc_clnt *clnt, int error, + bool (*fnmatch)(const struct rpc_task *, + const void *), + const void *data); void rpc_execute(struct rpc_task *); void rpc_init_priority_wait_queue(struct rpc_wait_queue *, const char *); void rpc_init_wait_queue(struct rpc_wait_queue *, const char *); diff --git a/include/linux/sunrpc/xprt.h b/include/linux/sunrpc/xprt.h index 005730623f3be..ad8ecf8ccfb48 100644 --- a/include/linux/sunrpc/xprt.h +++ b/include/linux/sunrpc/xprt.h @@ -112,6 +112,11 @@ struct rpc_rqst { ktime_t rq_xtime; /* transmit time stamp */ int rq_ntrans; + bool rq_disconnect_on_release; + /* + * protected by rq_xprt->queue_lock + * while the request is active + */ #if defined(CONFIG_SUNRPC_BACKCHANNEL) struct lwq_node rq_bc_list; /* Callback service list */ diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 6cedc824cf826..5c33bf3fb4c34 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -901,29 +901,36 @@ void rpc_killall_tasks(struct rpc_clnt *clnt) } EXPORT_SYMBOL_GPL(rpc_killall_tasks); -/** - * rpc_cancel_tasks - try to cancel a set of RPC tasks - * @clnt: Pointer to RPC client - * @error: RPC task error value to set - * @fnmatch: Pointer to selector function - * @data: User data - * - * Uses @fnmatch to define a set of RPC tasks that are to be cancelled. - * The argument @error must be a negative error value. - */ -unsigned long rpc_cancel_tasks(struct rpc_clnt *clnt, int error, - bool (*fnmatch)(const struct rpc_task *, - const void *), - const void *data) +struct rpc_cancel_disconnect_ctx { + bool (*fnmatch)(const struct rpc_task *task, const void *data); + const void *data; +}; + +static int rpc_mark_xprt_disconnect_on_release(struct rpc_clnt *clnt, + struct rpc_xprt *xprt, + void *arg) +{ + struct rpc_cancel_disconnect_ctx *ctx = arg; + + xprt_mark_matching_reqs_disconnect_on_release(xprt, clnt, + ctx->fnmatch, + ctx->data); + return 0; +} + +static unsigned long +__rpc_cancel_tasks(struct rpc_clnt *clnt, int error, + bool (*fnmatch)(const struct rpc_task *, + const void *), + const void *data, + bool disconnect) { struct rpc_task *task; unsigned long count = 0; if (list_empty(&clnt->cl_tasks)) return 0; - /* - * Spin lock all_tasks to prevent changes... - */ + spin_lock(&clnt->cl_lock); list_for_each_entry(task, &clnt->cl_tasks, tk_task) { if (!RPC_IS_ACTIVATED(task)) @@ -934,10 +941,63 @@ unsigned long rpc_cancel_tasks(struct rpc_clnt *clnt, int error, count++; } spin_unlock(&clnt->cl_lock); + + if (disconnect && count) { + struct rpc_cancel_disconnect_ctx ctx = { + .fnmatch = fnmatch, + .data = data, + }; + rpc_clnt_iterate_for_each_xprt(clnt, + rpc_mark_xprt_disconnect_on_release, + &ctx); + } return count; } + +/** + * rpc_cancel_tasks - try to cancel a set of RPC tasks + * @clnt: Pointer to RPC client + * @error: RPC task error value to set + * @fnmatch: Pointer to selector function + * @data: User data + * + * Uses @fnmatch to define a set of RPC tasks that are to be cancelled. + * The argument @error must be a negative error value. + */ +unsigned long rpc_cancel_tasks(struct rpc_clnt *clnt, int error, + bool (*fnmatch)(const struct rpc_task *, + const void *), + const void *data) +{ + return __rpc_cancel_tasks(clnt, error, fnmatch, data, false); +} EXPORT_SYMBOL_GPL(rpc_cancel_tasks); +/** + * rpc_cancel_tasks_and_disconnect - cancel matching RPC tasks + * @clnt: Pointer to RPC client + * @error: RPC task error value to set + * @fnmatch: Pointer to selector function + * @data: User data + * + * Like rpc_cancel_tasks(), but after cancelling matching tasks, marks + * matching in-flight requests so that xprt_release() conditionally + * disconnects the connection generation on which each request was sent. + * + * @fnmatch is called while holding spinlocks and must not sleep. + * + * Returns the number of active tasks matched by @fnmatch. + */ +unsigned long +rpc_cancel_tasks_and_disconnect(struct rpc_clnt *clnt, int error, + bool (*fnmatch)(const struct rpc_task *, + const void *), + const void *data) +{ + return __rpc_cancel_tasks(clnt, error, fnmatch, data, true); +} +EXPORT_SYMBOL_GPL(rpc_cancel_tasks_and_disconnect); + static int rpc_clnt_disconnect_xprt(struct rpc_clnt *clnt, struct rpc_xprt *xprt, void *dummy) { diff --git a/net/sunrpc/sunrpc.h b/net/sunrpc/sunrpc.h index e3c6e3b63f0ba..2c0a4532891f0 100644 --- a/net/sunrpc/sunrpc.h +++ b/net/sunrpc/sunrpc.h @@ -43,4 +43,9 @@ void rpc_clients_notifier_unregister(void); void auth_domain_cleanup(void); void svc_sock_update_bufs(struct svc_serv *serv); enum svc_auth_status svc_authenticate(struct svc_rqst *rqstp); +void xprt_mark_matching_reqs_disconnect_on_release(struct rpc_xprt *xprt, + const struct rpc_clnt *clnt, + bool (*fnmatch)(const struct rpc_task *, + const void *), + const void *data); #endif /* _NET_SUNRPC_SUNRPC_H */ diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index 48a3618cbb29f..66eca8e4690b6 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c @@ -1434,6 +1434,69 @@ xprt_request_dequeue_transmit(struct rpc_task *task) spin_unlock(&xprt->queue_lock); } +static bool +xprt_request_matches(const struct rpc_rqst *req, + const struct rpc_clnt *clnt, + bool (*fnmatch)(const struct rpc_task *, const void *), + const void *data) +{ + const struct rpc_task *task = req->rq_task; + + return task && task->tk_client == clnt && fnmatch(task, data); +} + +/** + * xprt_mark_matching_reqs_disconnect_on_release - mark matching in-flight + * requests for connection-generation + * teardown at release + * @xprt: transport whose queues to walk + * @clnt: RPC client whose requests are eligible + * @fnmatch: selector called with the request's owning rpc_task + * @data: caller data forwarded to @fnmatch + * + * @fnmatch must not sleep and must be safe to call while xprt->queue_lock + * is held. + * + * Walks the transmit queue (including per-owner rq_xmit2 chains) and the + * receive tree under xprt->queue_lock and marks each matching rpc_rqst so + * that xprt_release() will call xprt_conditional_disconnect() against that + * request's snapshotted rq_connect_cookie. Requests remain eligible for marking + * while they are present on a transport queue. xprt_release() removes queue + * membership under xprt->queue_lock before consuming the marker, so a marker + * installed before dequeue remains associated with that rpc_rqst and cannot be + * inherited by a later request using the same rpc_task. + * + * The @clnt qualification preserves the selection domain of + * rpc_cancel_tasks(). An rpc_xprt may be shared by multiple rpc_clnt + * instances, so requests belonging to other clients must not be marked. + */ +void +xprt_mark_matching_reqs_disconnect_on_release(struct rpc_xprt *xprt, + const struct rpc_clnt *clnt, + bool (*fnmatch)(const struct rpc_task *, + const void *), + const void *data) +{ + struct rpc_rqst *req, *pos; + struct rb_node *n; + + spin_lock(&xprt->queue_lock); + list_for_each_entry(req, &xprt->xmit_queue, rq_xmit) { + if (xprt_request_matches(req, clnt, fnmatch, data)) + req->rq_disconnect_on_release = true; + list_for_each_entry(pos, &req->rq_xmit2, rq_xmit2) { + if (xprt_request_matches(pos, clnt, fnmatch, data)) + pos->rq_disconnect_on_release = true; + } + } + for (n = rb_first(&xprt->recv_queue); n; n = rb_next(n)) { + req = rb_entry(n, struct rpc_rqst, rq_recv); + if (xprt_request_matches(req, clnt, fnmatch, data)) + req->rq_disconnect_on_release = true; + } + spin_unlock(&xprt->queue_lock); +} + /** * xprt_request_dequeue_xprt - remove a task from the transmit+receive queue * @task: pointer to rpc_task @@ -1915,6 +1978,7 @@ xprt_request_init(struct rpc_task *task) req->rq_rcv_buf.bvec = NULL; req->rq_release_snd_buf = NULL; req->rq_seqno_count = 0; + req->rq_disconnect_on_release = false; xprt_init_majortimeo(task, req, task->tk_client->cl_timeout); trace_xprt_reserve(req); @@ -1979,6 +2043,7 @@ void xprt_release(struct rpc_task *task) { struct rpc_xprt *xprt; struct rpc_rqst *req = task->tk_rqstp; + bool disconnect; if (req == NULL) { if (task->tk_client) { @@ -1990,12 +2055,22 @@ void xprt_release(struct rpc_task *task) xprt = req->rq_xprt; xprt_request_dequeue_xprt(task); + + spin_lock(&xprt->queue_lock); + disconnect = req->rq_disconnect_on_release; + req->rq_disconnect_on_release = false; + spin_unlock(&xprt->queue_lock); + + if (unlikely(disconnect && req->rq_ntrans > 0)) + xprt_conditional_disconnect(xprt, req->rq_connect_cookie); + spin_lock(&xprt->transport_lock); xprt->ops->release_xprt(xprt, task); if (xprt->ops->release_request) xprt->ops->release_request(task); xprt_schedule_autodisconnect(xprt); spin_unlock(&xprt->transport_lock); + if (req->rq_buffer) xprt->ops->buf_free(task); if (req->rq_cred != NULL) @@ -2020,6 +2095,7 @@ xprt_init_bc_request(struct rpc_rqst *req, struct rpc_task *task, task->tk_rqstp = req; req->rq_task = task; xprt_init_connect_cookie(req, req->rq_xprt); + req->rq_disconnect_on_release = false; /* * Set up the xdr_buf length. * This also indicates that the buffer is XDR encoded already. -- 2.53.0 ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH v2 1/2] SUNRPC: add request-scoped disconnects when cancelling tasks 2026-11-05 2:02 ` [PATCH v2 1/2] SUNRPC: add request-scoped disconnects when cancelling tasks tmenninger @ 2026-11-05 3:14 ` Trond Myklebust 2026-11-05 3:20 ` Trond Myklebust 0 siblings, 1 reply; 5+ messages in thread From: Trond Myklebust @ 2026-11-05 3:14 UTC (permalink / raw) To: tmenninger, anna Cc: cel, ebadger, jcurley, jlayton, linux-kernel, linux-nfs, netdev, slingappa, Tim Menninger On Thu, 2026-11-05 at 02:02 +0000, tmenninger@purestorage.com wrote: > From: Tim Menninger <tmenninger@everpuredata.com> > > rpc_cancel_tasks() allows callers to cancel a selected set of RPC > tasks, > but callers that also need to tear down the connection currently have > to > disconnect at the rpc_clnt level. > > That is broader than necessary for clients with multiple transports. > A > cancelled request is associated with a specific rpc_xprt and records > the > connection generation on which it was transmitted in > rq_connect_cookie. > > Add rpc_cancel_tasks_and_disconnect() to cancel matching tasks and > mark > matching requests that remain on the transport transmit or receive > queues > for conditional transport disconnect when the request is released. > > Store the disconnect indication in struct rpc_rqst rather than struct > rpc_task. An RPC task can release one request and later acquire > another, > so task-scoped state could otherwise be consumed by a successor > request > and disconnect the wrong transport generation. > > After cancelling matching tasks, walk the client's transports and > mark > matching requests belonging to that client that remain on the > transmit > or receive queues. An rpc_xprt may be shared by multiple rpc_clnt > instances, so requests belonging to other clients are left untouched. > > When a marked request is released, use xprt_conditional_disconnect() > with > its saved rq_connect_cookie. This disconnects the transport only if > it is > still using the same connection generation on which the request was > sent, > and leaves a replacement connection untouched. > > Requests that were never transmitted do not require a disconnect. > I'm all for optimising where it makes sense, however this is adding another spinlock protected section in xprt_release() for something that is clearly a rare corner case. So how about something like the following change instead? 8<--------------------------------------------------------------- diff --git a/fs/nfs/flexfilelayout/flexfilelayout.c b/fs/nfs/flexfilelayout/flexfilelayout.c index 7fe8b91fa47c..42b0b04de659 100644 --- a/fs/nfs/flexfilelayout/flexfilelayout.c +++ b/fs/nfs/flexfilelayout/flexfilelayout.c @@ -2459,6 +2459,12 @@ static bool ff_layout_match_io(const struct rpc_task *task, const void *data) return false; } +static bool ff_layout_match_untransmitted_io(const struct rpc_task *task, + const void *data) +{ + return !RPC_WAS_SENT(task) && ff_layout_match_io(task, data); +} + static void ff_layout_cancel_io(struct pnfs_layout_segment *lseg) { struct nfs4_ff_layout_segment *flseg = FF_LAYOUT_LSEG(lseg); @@ -2484,6 +2490,9 @@ static void ff_layout_cancel_io(struct pnfs_layout_segment *lseg) clnt = ds_clp->cl_rpcclient; if (!clnt) continue; + rpc_cancel_tasks(clnt, -ECANCELED, + ff_layout_match_untransmitted_io, + lseg); if (!rpc_cancel_tasks(clnt, -ECANCELED, ff_layout_match_io, lseg)) continue; -- Trond Myklebust Linux NFS client maintainer, Hammerspace trondmy@kernel.org, trond.myklebust@hammerspace.com ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH v2 1/2] SUNRPC: add request-scoped disconnects when cancelling tasks 2026-11-05 3:14 ` Trond Myklebust @ 2026-11-05 3:20 ` Trond Myklebust 0 siblings, 0 replies; 5+ messages in thread From: Trond Myklebust @ 2026-11-05 3:20 UTC (permalink / raw) To: tmenninger, anna Cc: cel, ebadger, jcurley, jlayton, linux-kernel, linux-nfs, netdev, slingappa, Tim Menninger On Wed, 2026-11-04 at 22:14 -0500, Trond Myklebust wrote: > On Thu, 2026-11-05 at 02:02 +0000, tmenninger@purestorage.com wrote: > > From: Tim Menninger <tmenninger@everpuredata.com> > > > > rpc_cancel_tasks() allows callers to cancel a selected set of RPC > > tasks, > > but callers that also need to tear down the connection currently > > have > > to > > disconnect at the rpc_clnt level. > > > > That is broader than necessary for clients with multiple > > transports. > > A > > cancelled request is associated with a specific rpc_xprt and > > records > > the > > connection generation on which it was transmitted in > > rq_connect_cookie. > > > > Add rpc_cancel_tasks_and_disconnect() to cancel matching tasks and > > mark > > matching requests that remain on the transport transmit or receive > > queues > > for conditional transport disconnect when the request is released. > > > > Store the disconnect indication in struct rpc_rqst rather than > > struct > > rpc_task. An RPC task can release one request and later acquire > > another, > > so task-scoped state could otherwise be consumed by a successor > > request > > and disconnect the wrong transport generation. > > > > After cancelling matching tasks, walk the client's transports and > > mark > > matching requests belonging to that client that remain on the > > transmit > > or receive queues. An rpc_xprt may be shared by multiple rpc_clnt > > instances, so requests belonging to other clients are left > > untouched. > > > > When a marked request is released, use > > xprt_conditional_disconnect() > > with > > its saved rq_connect_cookie. This disconnects the transport only if > > it is > > still using the same connection generation on which the request was > > sent, > > and leaves a replacement connection untouched. > > > > Requests that were never transmitted do not require a disconnect. > > > > I'm all for optimising where it makes sense, however this is adding > another spinlock protected section in xprt_release() for something > that > is clearly a rare corner case. > > So how about something like the following change instead? > Sorry, I oversimplified. Something like this: 8<------------------------------------------------------ diff --git a/fs/nfs/flexfilelayout/flexfilelayout.c b/fs/nfs/flexfilelayout/flexfilelayout.c index 7fe8b91fa47c..42b0b04de659 100644 --- a/fs/nfs/flexfilelayout/flexfilelayout.c +++ b/fs/nfs/flexfilelayout/flexfilelayout.c @@ -2459,6 +2459,12 @@ static bool ff_layout_match_io(const struct rpc_task *task, const void *data) return false; } +static bool ff_layout_match_untransmitted_io(const struct rpc_task *task, + const void *data) +{ + return !RPC_WAS_SENT(task) && ff_layout_match_io(task, data); +} + static void ff_layout_cancel_io(struct pnfs_layout_segment *lseg) { struct nfs4_ff_layout_segment *flseg = FF_LAYOUT_LSEG(lseg); @@ -2484,6 +2490,9 @@ static void ff_layout_cancel_io(struct pnfs_layout_segment *lseg) clnt = ds_clp->cl_rpcclient; if (!clnt) continue; + rpc_cancel_tasks(clnt, -ECANCELED, + ff_layout_match_untransmitted_io, + lseg); if (!rpc_cancel_tasks(clnt, -ECANCELED, ff_layout_match_io, lseg)) continue; diff --git a/include/linux/sunrpc/sched.h b/include/linux/sunrpc/sched.h index 0dbdf3722537..28f5bacb59f9 100644 --- a/include/linux/sunrpc/sched.h +++ b/include/linux/sunrpc/sched.h @@ -219,8 +219,8 @@ struct rpc_task *rpc_run_bc_task(struct rpc_rqst *req, struct rpc_timeout *timeout); void rpc_put_task(struct rpc_task *); void rpc_put_task_async(struct rpc_task *); -bool rpc_task_set_rpc_status(struct rpc_task *task, int rpc_status); -void rpc_task_try_cancel(struct rpc_task *task, int error); +bool rpc_task_set_rpc_status(struct rpc_task *task, int rpc_status); +bool rpc_task_try_cancel(struct rpc_task *task, int error); void rpc_signal_task(struct rpc_task *); void rpc_exit_task(struct rpc_task *); void rpc_exit(struct rpc_task *, int); diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 6cedc824cf82..503e95647270 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -930,7 +930,8 @@ unsigned long rpc_cancel_tasks(struct rpc_clnt *clnt, int error, continue; if (!fnmatch(task, data)) continue; - rpc_task_try_cancel(task, error); + if (!rpc_task_try_cancel(task, error)) + continue; count++; } spin_unlock(&clnt->cl_lock); diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index 016f16ca5779..97727732400b 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -869,15 +869,16 @@ void rpc_signal_task(struct rpc_task *task) rpc_wake_up_queued_task(queue, task); } -void rpc_task_try_cancel(struct rpc_task *task, int error) +bool rpc_task_try_cancel(struct rpc_task *task, int error) { struct rpc_wait_queue *queue; if (!rpc_task_set_rpc_status(task, error)) - return; + return false; queue = READ_ONCE(task->tk_waitqueue); if (queue) rpc_wake_up_queued_task(queue, task); + return true; } void rpc_exit(struct rpc_task *task, int status) -- Trond Myklebust Linux NFS client maintainer, Hammerspace trondmy@kernel.org, trond.myklebust@hammerspace.com ^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v2 2/2] NFSv4/flexfiles: limit transport disconnects when cancelling I/O 2026-11-05 2:02 [PATCH v2 0/2] SUNRPC/NFS: limit transport disconnects when cancelling flexfiles I/O tmenninger 2026-11-05 2:02 ` [PATCH v2 1/2] SUNRPC: add request-scoped disconnects when cancelling tasks tmenninger @ 2026-11-05 2:02 ` tmenninger 1 sibling, 0 replies; 5+ messages in thread From: tmenninger @ 2026-11-05 2:02 UTC (permalink / raw) To: anna, trondmy Cc: cel, ebadger, jcurley, jlayton, linux-kernel, linux-nfs, netdev, slingappa, Tim Menninger From: Tim Menninger <tmenninger@everpuredata.com> ff_layout_cancel_io() cancels RPC tasks associated with a recalled or revoked layout segment. If any matching tasks are found, it then calls rpc_clnt_disconnect(). rpc_clnt_disconnect() disconnects every transport attached to the RPC client. A data server client can have multiple transports, while the cancelled requests may have used only a subset of them. Cancelling I/O for one layout segment can therefore disrupt unrelated I/O using other transports belonging to the same data server client. This is particularly disruptive with RPC/RDMA during data server recovery, where unnecessary transport disconnects can result in repeated reconnect activity on otherwise unaffected connections. Use rpc_cancel_tasks_and_disconnect() instead. Matching tasks are still cancelled, but only matching requests belonging to that client that remain on the transport transmit or receive queues are marked for disconnect. When such a request is released, SUNRPC conditionally disconnects the connection generation on which that request was sent. This preserves cancellation and draining of I/O associated with the layout segment without disconnecting unrelated transports attached to the same RPC client. Fixes: b739a5bd9d9f ("NFSv4/flexfiles: Cancel I/O if the layout is recalled or revoked") Signed-off-by: Tim Menninger <tmenninger@everpuredata.com> --- fs/nfs/flexfilelayout/flexfilelayout.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/fs/nfs/flexfilelayout/flexfilelayout.c b/fs/nfs/flexfilelayout/flexfilelayout.c index 9492d6bc1d1a7..700f0e4606e8f 100644 --- a/fs/nfs/flexfilelayout/flexfilelayout.c +++ b/fs/nfs/flexfilelayout/flexfilelayout.c @@ -2599,10 +2599,9 @@ static void ff_layout_cancel_io(struct pnfs_layout_segment *lseg, clnt = ds_clp->cl_rpcclient; if (!clnt) goto next; - if (!rpc_cancel_tasks(clnt, -ECANCELED, - ff_layout_match_io, lseg)) - goto next; - rpc_clnt_disconnect(clnt); + rpc_cancel_tasks_and_disconnect(clnt, -ECANCELED, + ff_layout_match_io, + lseg); next: nfs4_ff_layout_put_deviceid(mirror_ds); } -- 2.53.0 ^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-11-05 3:20 UTC | newest] Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2026-11-05 2:02 [PATCH v2 0/2] SUNRPC/NFS: limit transport disconnects when cancelling flexfiles I/O tmenninger 2026-11-05 2:02 ` [PATCH v2 1/2] SUNRPC: add request-scoped disconnects when cancelling tasks tmenninger 2026-11-05 3:14 ` Trond Myklebust 2026-11-05 3:20 ` Trond Myklebust 2026-11-05 2:02 ` [PATCH v2 2/2] NFSv4/flexfiles: limit transport disconnects when cancelling I/O tmenninger
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®