diff options
| author | Tom Tucker <tom@opengridcomputing.com> | 2008-03-25 22:27:19 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-03-26 14:24:19 -0400 |
| commit | c8237a5fcea9d49a73275b4c8f541dd42f8da1a4 (patch) | |
| tree | b93287513360a027baf3a8936c457ab2e21aa532 | |
| parent | 12c22d6ef299ccf0955e5756eb57d90d7577ac68 (diff) | |
SVCRDMA: Check num_sge when setting LAST_CTXT bit
The RDMACTXT_F_LAST_CTXT bit was getting set incorrectly
when the last chunk in the read-list spanned multiple pages. This
resulted in a kernel panic when the wrong context was used to
build the RPC iovec page list.
RDMA_READ is used to fetch RPC data from the client for
NFS_WRITE requests. A scatter-gather is used to map the
advertised client side buffer to the server-side iovec and
associated page list.
WR contexts are used to convey which scatter-gather entries are
handled by each WR. When the write data is large, a single RPC may
require multiple RDMA_READ requests so the contexts for a single RPC
are chained together in a linked list. The last context in this list
is marked with a bit RDMACTXT_F_LAST_CTXT so that when this WR completes,
the CQ handler code can enqueue the RPC for processing.
The code in rdma_read_xdr was setting this bit on the last two
contexts on this list when the last read-list chunk spanned multiple
pages. This caused the svc_rdma_recvfrom logic to incorrectly build
the RPC and caused the kernel to crash because the second-to-last
context doesn't contain the iovec page list.
Modified the condition that sets this bit so that it correctly detects
the last context for the RPC.
Signed-off-by: Tom Tucker <tom@opengridcomputing.com>
Tested-by: Roland Dreier <rolandd@cisco.com>
Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
| -rw-r--r-- | net/sunrpc/xprtrdma/svc_rdma_recvfrom.c | 21 |
1 files changed, 11 insertions, 10 deletions
diff --git a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c index 971271602dd0..c22d6b6f2db4 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c +++ b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c | |||
| @@ -322,15 +322,6 @@ next_sge: | |||
| 322 | ctxt->direction = DMA_FROM_DEVICE; | 322 | ctxt->direction = DMA_FROM_DEVICE; |
| 323 | clear_bit(RDMACTXT_F_READ_DONE, &ctxt->flags); | 323 | clear_bit(RDMACTXT_F_READ_DONE, &ctxt->flags); |
| 324 | clear_bit(RDMACTXT_F_LAST_CTXT, &ctxt->flags); | 324 | clear_bit(RDMACTXT_F_LAST_CTXT, &ctxt->flags); |
| 325 | if ((ch+1)->rc_discrim == 0) { | ||
| 326 | /* | ||
| 327 | * Checked in sq_cq_reap to see if we need to | ||
| 328 | * be enqueued | ||
| 329 | */ | ||
| 330 | set_bit(RDMACTXT_F_LAST_CTXT, &ctxt->flags); | ||
| 331 | ctxt->next = hdr_ctxt; | ||
| 332 | hdr_ctxt->next = head; | ||
| 333 | } | ||
| 334 | 325 | ||
| 335 | /* Prepare READ WR */ | 326 | /* Prepare READ WR */ |
| 336 | memset(&read_wr, 0, sizeof read_wr); | 327 | memset(&read_wr, 0, sizeof read_wr); |
| @@ -348,7 +339,17 @@ next_sge: | |||
| 348 | rdma_set_ctxt_sge(ctxt, &sge[ch_sge_ary[ch_no].start], | 339 | rdma_set_ctxt_sge(ctxt, &sge[ch_sge_ary[ch_no].start], |
| 349 | &sgl_offset, | 340 | &sgl_offset, |
| 350 | read_wr.num_sge); | 341 | read_wr.num_sge); |
| 351 | 342 | if (((ch+1)->rc_discrim == 0) && | |
| 343 | (read_wr.num_sge == ch_sge_ary[ch_no].count)) { | ||
| 344 | /* | ||
| 345 | * Mark the last RDMA_READ with a bit to | ||
| 346 | * indicate all RPC data has been fetched from | ||
| 347 | * the client and the RPC needs to be enqueued. | ||
| 348 | */ | ||
| 349 | set_bit(RDMACTXT_F_LAST_CTXT, &ctxt->flags); | ||
| 350 | ctxt->next = hdr_ctxt; | ||
| 351 | hdr_ctxt->next = head; | ||
| 352 | } | ||
| 352 | /* Post the read */ | 353 | /* Post the read */ |
| 353 | err = svc_rdma_send(xprt, &read_wr); | 354 | err = svc_rdma_send(xprt, &read_wr); |
| 354 | if (err) { | 355 | if (err) { |
