diff options
| author | Tom Tucker <tom@opengridcomputing.com> | 2008-08-13 12:05:41 -0400 |
|---|---|---|
| committer | J. Bruce Fields <bfields@citi.umich.edu> | 2008-08-13 16:57:31 -0400 |
| commit | 24b8b44780a2c53ecb738f4a1c08d114f5eda27c (patch) | |
| tree | f772e59438783c6230158ea65bd5aeb3f84f4202 | |
| parent | 30a2f3c60a84092c8084dfe788b710f8d0768cd4 (diff) | |
svcrdma: Fix race between svc_rdma_recvfrom thread and the dto_tasklet
RDMA_READ completions are kept on a separate queue from the general
I/O request queue. Since a separate lock is used to protect the RDMA_READ
completion queue, a race exists between the dto_tasklet and the
svc_rdma_recvfrom thread where the dto_tasklet sets the XPT_DATA
bit and adds I/O to the read-completion queue. Concurrently, the
recvfrom thread checks the generic queue, finds it empty and resets
the XPT_DATA bit. A subsequent svc_xprt_enqueue will fail to enqueue
the transport for I/O and cause the transport to "stall".
The fix is to protect both lists with the same lock and set the XPT_DATA
bit with this lock held.
Signed-off-by: Tom Tucker <tom@opengridcomputing.com>
Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>
| -rw-r--r-- | include/linux/sunrpc/svc_rdma.h | 1 | ||||
| -rw-r--r-- | net/sunrpc/xprtrdma/svc_rdma_recvfrom.c | 8 | ||||
| -rw-r--r-- | net/sunrpc/xprtrdma/svc_rdma_transport.c | 5 |
3 files changed, 6 insertions, 8 deletions
diff --git a/include/linux/sunrpc/svc_rdma.h b/include/linux/sunrpc/svc_rdma.h index ef2e3a20bf3b..dc05b54bd3a3 100644 --- a/include/linux/sunrpc/svc_rdma.h +++ b/include/linux/sunrpc/svc_rdma.h | |||
| @@ -143,7 +143,6 @@ struct svcxprt_rdma { | |||
| 143 | unsigned long sc_flags; | 143 | unsigned long sc_flags; |
| 144 | struct list_head sc_dto_q; /* DTO tasklet I/O pending Q */ | 144 | struct list_head sc_dto_q; /* DTO tasklet I/O pending Q */ |
| 145 | struct list_head sc_read_complete_q; | 145 | struct list_head sc_read_complete_q; |
| 146 | spinlock_t sc_read_complete_lock; | ||
| 147 | struct work_struct sc_work; | 146 | struct work_struct sc_work; |
| 148 | }; | 147 | }; |
| 149 | /* sc_flags */ | 148 | /* sc_flags */ |
diff --git a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c index b4b17f44cb29..74de31a06616 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c +++ b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c | |||
| @@ -443,18 +443,18 @@ int svc_rdma_recvfrom(struct svc_rqst *rqstp) | |||
| 443 | 443 | ||
| 444 | dprintk("svcrdma: rqstp=%p\n", rqstp); | 444 | dprintk("svcrdma: rqstp=%p\n", rqstp); |
| 445 | 445 | ||
| 446 | spin_lock_bh(&rdma_xprt->sc_read_complete_lock); | 446 | spin_lock_bh(&rdma_xprt->sc_rq_dto_lock); |
| 447 | if (!list_empty(&rdma_xprt->sc_read_complete_q)) { | 447 | if (!list_empty(&rdma_xprt->sc_read_complete_q)) { |
| 448 | ctxt = list_entry(rdma_xprt->sc_read_complete_q.next, | 448 | ctxt = list_entry(rdma_xprt->sc_read_complete_q.next, |
| 449 | struct svc_rdma_op_ctxt, | 449 | struct svc_rdma_op_ctxt, |
| 450 | dto_q); | 450 | dto_q); |
| 451 | list_del_init(&ctxt->dto_q); | 451 | list_del_init(&ctxt->dto_q); |
| 452 | } | 452 | } |
| 453 | spin_unlock_bh(&rdma_xprt->sc_read_complete_lock); | 453 | if (ctxt) { |
| 454 | if (ctxt) | 454 | spin_unlock_bh(&rdma_xprt->sc_rq_dto_lock); |
| 455 | return rdma_read_complete(rqstp, ctxt); | 455 | return rdma_read_complete(rqstp, ctxt); |
| 456 | } | ||
| 456 | 457 | ||
| 457 | spin_lock_bh(&rdma_xprt->sc_rq_dto_lock); | ||
| 458 | if (!list_empty(&rdma_xprt->sc_rq_dto_q)) { | 458 | if (!list_empty(&rdma_xprt->sc_rq_dto_q)) { |
| 459 | ctxt = list_entry(rdma_xprt->sc_rq_dto_q.next, | 459 | ctxt = list_entry(rdma_xprt->sc_rq_dto_q.next, |
| 460 | struct svc_rdma_op_ctxt, | 460 | struct svc_rdma_op_ctxt, |
diff --git a/net/sunrpc/xprtrdma/svc_rdma_transport.c b/net/sunrpc/xprtrdma/svc_rdma_transport.c index 19ddc382b777..900cb69728c6 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_transport.c +++ b/net/sunrpc/xprtrdma/svc_rdma_transport.c | |||
| @@ -359,11 +359,11 @@ static void sq_cq_reap(struct svcxprt_rdma *xprt) | |||
| 359 | if (test_bit(RDMACTXT_F_LAST_CTXT, &ctxt->flags)) { | 359 | if (test_bit(RDMACTXT_F_LAST_CTXT, &ctxt->flags)) { |
| 360 | struct svc_rdma_op_ctxt *read_hdr = ctxt->read_hdr; | 360 | struct svc_rdma_op_ctxt *read_hdr = ctxt->read_hdr; |
| 361 | BUG_ON(!read_hdr); | 361 | BUG_ON(!read_hdr); |
| 362 | spin_lock_bh(&xprt->sc_rq_dto_lock); | ||
| 362 | set_bit(XPT_DATA, &xprt->sc_xprt.xpt_flags); | 363 | set_bit(XPT_DATA, &xprt->sc_xprt.xpt_flags); |
| 363 | spin_lock_bh(&xprt->sc_read_complete_lock); | ||
| 364 | list_add_tail(&read_hdr->dto_q, | 364 | list_add_tail(&read_hdr->dto_q, |
| 365 | &xprt->sc_read_complete_q); | 365 | &xprt->sc_read_complete_q); |
| 366 | spin_unlock_bh(&xprt->sc_read_complete_lock); | 366 | spin_unlock_bh(&xprt->sc_rq_dto_lock); |
| 367 | svc_xprt_enqueue(&xprt->sc_xprt); | 367 | svc_xprt_enqueue(&xprt->sc_xprt); |
| 368 | } | 368 | } |
| 369 | svc_rdma_put_context(ctxt, 0); | 369 | svc_rdma_put_context(ctxt, 0); |
| @@ -428,7 +428,6 @@ static struct svcxprt_rdma *rdma_create_xprt(struct svc_serv *serv, | |||
| 428 | init_waitqueue_head(&cma_xprt->sc_send_wait); | 428 | init_waitqueue_head(&cma_xprt->sc_send_wait); |
| 429 | 429 | ||
| 430 | spin_lock_init(&cma_xprt->sc_lock); | 430 | spin_lock_init(&cma_xprt->sc_lock); |
| 431 | spin_lock_init(&cma_xprt->sc_read_complete_lock); | ||
| 432 | spin_lock_init(&cma_xprt->sc_rq_dto_lock); | 431 | spin_lock_init(&cma_xprt->sc_rq_dto_lock); |
| 433 | 432 | ||
| 434 | cma_xprt->sc_ord = svcrdma_ord; | 433 | cma_xprt->sc_ord = svcrdma_ord; |
