summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChuck Lever <cel@kernel.org>2026-08-28 09:50:36 -0400
committerChuck Lever <cel@kernel.org>2026-10-04 21:15:29 -0400
commitbde744fe9f2ede72b38985a24b459f649e8ed5fa (patch)
tree434ab018c30c289985210c04c9ab8083727097ea
parent46913d461a351e0dd09ab4344c73c73f4d34ff64 (diff)
SUNRPC: Skip xpt_reserved accounting for non-UDP transports
The xpt_reserved counter exists for UDP socket-buffer back-pressure. svc_udp_has_wspace() is the only has_wspace implementation that consults it, so on TCP and RDMA the counter is maintained and never read. svc_handle_xprt() adds to it once per RPC. svc_reserve() shrinks it again on each call from svc_process_common(), from svc_xprt_release(), and from each proc function that calls svc_reserve_auth(). Every shrinking call also runs svc_xprt_resource_released(), which issues an smp_mb() and can enqueue the transport. Add an xcl_flags field to svc_xprt_class and set SVC_XPRT_FLAG_WSPACE_RESERVE on the UDP class. Gate the xpt_reserved accounting on that flag. After the change, svc_reserve() no longer calls svc_xprt_resource_released() on TCP and RDMA. Two paths still cover that enqueue. svc_xprt_release() reaches the helper through svc_xprt_release_slot(), and svc_xprt_received() enqueues a transport whose XPT_DATA remains set. Link: https://patch.msgid.link/20260828135036.796842-4-cel@kernel.org Signed-off-by: Chuck Lever <cel@kernel.org>
-rw-r--r--include/linux/sunrpc/svc_xprt.h5
-rw-r--r--net/sunrpc/svc_xprt.c25
-rw-r--r--net/sunrpc/svcsock.c1
3 files changed, 21 insertions, 10 deletions
diff --git a/include/linux/sunrpc/svc_xprt.h b/include/linux/sunrpc/svc_xprt.h
index da2a2531e110..2af222f3ea2c 100644
--- a/include/linux/sunrpc/svc_xprt.h
+++ b/include/linux/sunrpc/svc_xprt.h
@@ -37,6 +37,9 @@ struct svc_xprt_class {
struct list_head xcl_list;
u32 xcl_max_payload;
int xcl_ident;
+ u32 xcl_flags;
+/* Set only on classes whose xpo_has_wspace() reads xpt_reserved */
+#define SVC_XPRT_FLAG_WSPACE_RESERVE BIT(0)
};
/*
@@ -59,7 +62,7 @@ struct svc_xprt {
unsigned long xpt_flags;
struct svc_serv *xpt_server; /* service for transport */
- atomic_t xpt_reserved; /* space on outq that is rsvd */
+ atomic_t xpt_reserved; /* outq space rsvd, UDP only */
atomic_t xpt_nr_rqsts; /* Number of requests */
struct mutex xpt_mutex; /* to serialize sending data */
spinlock_t xpt_lock; /* protects sk_deferred
diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c
index a60e972490ec..77e28dcc4d2a 100644
--- a/net/sunrpc/svc_xprt.c
+++ b/net/sunrpc/svc_xprt.c
@@ -478,11 +478,11 @@ static bool svc_xprt_ready(struct svc_xprt *xprt)
/*
* If another cpu has recently updated xpt_flags,
- * sk_sock->flags, xpt_reserved, or xpt_nr_rqsts, we need to
- * know about it; otherwise it's possible that both that cpu and
- * this one could call svc_xprt_enqueue() without either
- * svc_xprt_enqueue() recognizing that the conditions below
- * are satisfied, and we could stall indefinitely:
+ * sk_sock->flags, xpt_reserved (UDP only), or xpt_nr_rqsts,
+ * we need to know about it; otherwise it's possible that both
+ * that cpu and this one could call svc_xprt_enqueue() without
+ * either svc_xprt_enqueue() recognizing that the conditions
+ * below are satisfied, and we could stall indefinitely:
*/
smp_rmb();
xpt_flags = READ_ONCE(xprt->xpt_flags);
@@ -554,6 +554,10 @@ static struct svc_xprt *svc_xprt_dequeue(struct svc_pool *pool)
* to make sure the reply fits. This function reduces that reserved
* space to be the amount of space used already, plus @space.
*
+ * The transport's reservation is tracked only on classes that set
+ * SVC_XPRT_FLAG_WSPACE_RESERVE. On the others, only @rqstp's
+ * reservation is updated.
+ *
*/
void svc_reserve(struct svc_rqst *rqstp, int space)
{
@@ -562,10 +566,12 @@ void svc_reserve(struct svc_rqst *rqstp, int space)
space += rqstp->rq_res.head[0].iov_len;
if (xprt && space < rqstp->rq_reserved) {
- atomic_sub((rqstp->rq_reserved - space),
- &xprt->xpt_reserved);
+ if (xprt->xpt_class->xcl_flags & SVC_XPRT_FLAG_WSPACE_RESERVE) {
+ atomic_sub((rqstp->rq_reserved - space),
+ &xprt->xpt_reserved);
+ svc_xprt_resource_released(xprt);
+ }
rqstp->rq_reserved = space;
- svc_xprt_resource_released(xprt);
}
}
EXPORT_SYMBOL_GPL(svc_reserve);
@@ -872,7 +878,8 @@ static void svc_handle_xprt(struct svc_rqst *rqstp, struct svc_xprt *xprt)
else
len = xprt->xpt_ops->xpo_recvfrom(rqstp);
rqstp->rq_reserved = serv->sv_max_mesg;
- atomic_add(rqstp->rq_reserved, &xprt->xpt_reserved);
+ if (xprt->xpt_class->xcl_flags & SVC_XPRT_FLAG_WSPACE_RESERVE)
+ atomic_add(rqstp->rq_reserved, &xprt->xpt_reserved);
if (len <= 0)
goto out;
diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c
index ef7ac080fcd3..e5459d504b6a 100644
--- a/net/sunrpc/svcsock.c
+++ b/net/sunrpc/svcsock.c
@@ -800,6 +800,7 @@ static struct svc_xprt_class svc_udp_class = {
.xcl_ops = &svc_udp_ops,
.xcl_max_payload = RPCSVC_MAXPAYLOAD_UDP,
.xcl_ident = XPRT_TRANSPORT_UDP,
+ .xcl_flags = SVC_XPRT_FLAG_WSPACE_RESERVE,
};
static void svc_udp_init(struct svc_sock *svsk, struct svc_serv *serv)