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 /net/sunrpc/xprtrdma/svc_rdma_recvfrom.c | |
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>
Diffstat (limited to 'net/sunrpc/xprtrdma/svc_rdma_recvfrom.c')
-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) { |