diff options
| author | J. Bruce Fields <bfields@redhat.com> | 2019-01-11 15:36:40 -0500 |
|---|---|---|
| committer | J. Bruce Fields <bfields@redhat.com> | 2019-02-06 15:37:14 -0500 |
| commit | 95503d295ad6af20f09efff193e085481a962fd2 (patch) | |
| tree | a580f60a4a517c73575705c1745de208840f1c43 | |
| parent | 66c898caefd346a88fbef242eb7892fd959308f6 (diff) | |
svcrpc: fix unlikely races preventing queueing of sockets
In the rpc server, When something happens that might be reason to wake
up a thread to do something, what we do is
- modify xpt_flags, sk_sock->flags, xpt_reserved, or
xpt_nr_rqsts to indicate the new situation
- call svc_xprt_enqueue() to decide whether to wake up a thread.
svc_xprt_enqueue may require multiple conditions to be true before
queueing up a thread to handle the xprt. In the SMP case, one of the
other CPU's may have set another required condition, and in that case,
although both CPUs run svc_xprt_enqueue(), it's possible that neither
call sees the writes done by the other CPU in time, and neither one
recognizes that all the required conditions have been set. A socket
could therefore be ignored indefinitely.
Add memory barries to ensure that any svc_xprt_enqueue() call will
always see the conditions changed by other CPUs before deciding to
ignore a socket.
I've never seen this race reported. In the unlikely event it happens,
another event will usually come along and the problem will fix itself.
So I don't think this is worth backporting to stable.
Chuck tried this patch and said "I don't see any performance
regressions, but my server has only a single last-level CPU cache."
Tested-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: J. Bruce Fields <bfields@redhat.com>
| -rw-r--r-- | net/sunrpc/svc_xprt.c | 12 | ||||
| -rw-r--r-- | net/sunrpc/xprtrdma/svc_rdma_recvfrom.c | 3 | ||||
| -rw-r--r-- | net/sunrpc/xprtrdma/svc_rdma_rw.c | 3 |
3 files changed, 15 insertions, 3 deletions
diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c index a2435d3811a9..61530b1b7754 100644 --- a/net/sunrpc/svc_xprt.c +++ b/net/sunrpc/svc_xprt.c | |||
| @@ -357,6 +357,7 @@ static void svc_xprt_release_slot(struct svc_rqst *rqstp) | |||
| 357 | struct svc_xprt *xprt = rqstp->rq_xprt; | 357 | struct svc_xprt *xprt = rqstp->rq_xprt; |
| 358 | if (test_and_clear_bit(RQ_DATA, &rqstp->rq_flags)) { | 358 | if (test_and_clear_bit(RQ_DATA, &rqstp->rq_flags)) { |
| 359 | atomic_dec(&xprt->xpt_nr_rqsts); | 359 | atomic_dec(&xprt->xpt_nr_rqsts); |
| 360 | smp_wmb(); /* See smp_rmb() in svc_xprt_ready() */ | ||
| 360 | svc_xprt_enqueue(xprt); | 361 | svc_xprt_enqueue(xprt); |
| 361 | } | 362 | } |
| 362 | } | 363 | } |
| @@ -365,6 +366,15 @@ static bool svc_xprt_ready(struct svc_xprt *xprt) | |||
| 365 | { | 366 | { |
| 366 | unsigned long xpt_flags; | 367 | unsigned long xpt_flags; |
| 367 | 368 | ||
| 369 | /* | ||
| 370 | * If another cpu has recently updated xpt_flags, | ||
| 371 | * sk_sock->flags, xpt_reserved, or xpt_nr_rqsts, we need to | ||
| 372 | * know about it; otherwise it's possible that both that cpu and | ||
| 373 | * this one could call svc_xprt_enqueue() without either | ||
| 374 | * svc_xprt_enqueue() recognizing that the conditions below | ||
| 375 | * are satisfied, and we could stall indefinitely: | ||
| 376 | */ | ||
| 377 | smp_rmb(); | ||
| 368 | xpt_flags = READ_ONCE(xprt->xpt_flags); | 378 | xpt_flags = READ_ONCE(xprt->xpt_flags); |
| 369 | 379 | ||
| 370 | if (xpt_flags & (BIT(XPT_CONN) | BIT(XPT_CLOSE))) | 380 | if (xpt_flags & (BIT(XPT_CONN) | BIT(XPT_CLOSE))) |
| @@ -479,7 +489,7 @@ void svc_reserve(struct svc_rqst *rqstp, int space) | |||
| 479 | if (xprt && space < rqstp->rq_reserved) { | 489 | if (xprt && space < rqstp->rq_reserved) { |
| 480 | atomic_sub((rqstp->rq_reserved - space), &xprt->xpt_reserved); | 490 | atomic_sub((rqstp->rq_reserved - space), &xprt->xpt_reserved); |
| 481 | rqstp->rq_reserved = space; | 491 | rqstp->rq_reserved = space; |
| 482 | 492 | smp_wmb(); /* See smp_rmb() in svc_xprt_ready() */ | |
| 483 | svc_xprt_enqueue(xprt); | 493 | svc_xprt_enqueue(xprt); |
| 484 | } | 494 | } |
| 485 | } | 495 | } |
diff --git a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c index 828b149eaaef..3ebb158c2279 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c +++ b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c | |||
| @@ -314,8 +314,9 @@ static void svc_rdma_wc_receive(struct ib_cq *cq, struct ib_wc *wc) | |||
| 314 | 314 | ||
| 315 | spin_lock(&rdma->sc_rq_dto_lock); | 315 | spin_lock(&rdma->sc_rq_dto_lock); |
| 316 | list_add_tail(&ctxt->rc_list, &rdma->sc_rq_dto_q); | 316 | list_add_tail(&ctxt->rc_list, &rdma->sc_rq_dto_q); |
| 317 | spin_unlock(&rdma->sc_rq_dto_lock); | 317 | /* Note the unlock pairs with the smp_rmb in svc_xprt_ready: */ |
| 318 | set_bit(XPT_DATA, &rdma->sc_xprt.xpt_flags); | 318 | set_bit(XPT_DATA, &rdma->sc_xprt.xpt_flags); |
| 319 | spin_unlock(&rdma->sc_rq_dto_lock); | ||
| 319 | if (!test_bit(RDMAXPRT_CONN_PENDING, &rdma->sc_flags)) | 320 | if (!test_bit(RDMAXPRT_CONN_PENDING, &rdma->sc_flags)) |
| 320 | svc_xprt_enqueue(&rdma->sc_xprt); | 321 | svc_xprt_enqueue(&rdma->sc_xprt); |
| 321 | goto out; | 322 | goto out; |
diff --git a/net/sunrpc/xprtrdma/svc_rdma_rw.c b/net/sunrpc/xprtrdma/svc_rdma_rw.c index dc1951759a8e..c35753691960 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_rw.c +++ b/net/sunrpc/xprtrdma/svc_rdma_rw.c | |||
| @@ -287,9 +287,10 @@ static void svc_rdma_wc_read_done(struct ib_cq *cq, struct ib_wc *wc) | |||
| 287 | spin_lock(&rdma->sc_rq_dto_lock); | 287 | spin_lock(&rdma->sc_rq_dto_lock); |
| 288 | list_add_tail(&info->ri_readctxt->rc_list, | 288 | list_add_tail(&info->ri_readctxt->rc_list, |
| 289 | &rdma->sc_read_complete_q); | 289 | &rdma->sc_read_complete_q); |
| 290 | /* Note the unlock pairs with the smp_rmb in svc_xprt_ready: */ | ||
| 291 | set_bit(XPT_DATA, &rdma->sc_xprt.xpt_flags); | ||
| 290 | spin_unlock(&rdma->sc_rq_dto_lock); | 292 | spin_unlock(&rdma->sc_rq_dto_lock); |
| 291 | 293 | ||
| 292 | set_bit(XPT_DATA, &rdma->sc_xprt.xpt_flags); | ||
| 293 | svc_xprt_enqueue(&rdma->sc_xprt); | 294 | svc_xprt_enqueue(&rdma->sc_xprt); |
| 294 | } | 295 | } |
| 295 | 296 | ||
