diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c index b16e710926c1..b8f3ac29e718 100644 --- a/net/sunrpc/svc_xprt.c +++ b/net/sunrpc/svc_xprt.c @@ -425,13 +425,28 @@ static bool svc_xprt_reserve_slot(struct svc_rqst *rqstp, struct svc_xprt *xprt) return true; } +/* + * After a caller releases write-space or a request slot, + * re-enqueue the transport only when there is pending + * work that a thread could act on. The smp_mb() pairs + * with the smp_rmb() in svc_xprt_ready() and orders the + * preceding counter update before the flags read so a + * concurrent set_bit(XPT_DATA) is visible here. + */ +static void svc_xprt_resource_released(struct svc_xprt *xprt) +{ + smp_mb(); + if (READ_ONCE(xprt->xpt_flags) & + (BIT(XPT_DATA) | BIT(XPT_DEFERRED))) + svc_xprt_enqueue(xprt); +} + static void svc_xprt_release_slot(struct svc_rqst *rqstp) { struct svc_xprt *xprt = rqstp->rq_xprt; if (test_and_clear_bit(RQ_DATA, &rqstp->rq_flags)) { atomic_dec(&xprt->xpt_nr_rqsts); - smp_wmb(); /* See smp_rmb() in svc_xprt_ready() */ - svc_xprt_enqueue(xprt); + svc_xprt_resource_released(xprt); } } @@ -525,10 +540,10 @@ 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); + atomic_sub((rqstp->rq_reserved - space), + &xprt->xpt_reserved); rqstp->rq_reserved = space; - smp_wmb(); /* See smp_rmb() in svc_xprt_ready() */ - svc_xprt_enqueue(xprt); + svc_xprt_resource_released(xprt); } } EXPORT_SYMBOL_GPL(svc_reserve);