mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-05 02:21:52 +09:00
xprtrdma: Fix XDRBUF_SPARSE_PAGES support
commit 15261b9126 upstream.
Olga K. observed that rpcrdma_marsh_req() allocates sparse pages
only when it has determined that a Reply chunk is necessary. There
are plenty of cases where no Reply chunk is needed, but the
XDRBUF_SPARSE_PAGES flag is set. The result would be a crash in
rpcrdma_inline_fixup() when it tries to copy parts of the received
Reply into a missing page.
To avoid crashing, handle sparse page allocation up front.
Until XATTR support was added, this issue did not appear often
because the only SPARSE_PAGES consumer always expected a reply large
enough to always require a Reply chunk.
Reported-by: Olga Kornievskaia <kolga@netapp.com>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
6a9a98fdd4
commit
35f71f3cbd
@@ -179,6 +179,31 @@ rpcrdma_nonpayload_inline(const struct rpcrdma_xprt *r_xprt,
|
||||
r_xprt->rx_ep->re_max_inline_recv;
|
||||
}
|
||||
|
||||
/* ACL likes to be lazy in allocating pages. For TCP, these
|
||||
* pages can be allocated during receive processing. Not true
|
||||
* for RDMA, which must always provision receive buffers
|
||||
* up front.
|
||||
*/
|
||||
static noinline int
|
||||
rpcrdma_alloc_sparse_pages(struct xdr_buf *buf)
|
||||
{
|
||||
struct page **ppages;
|
||||
int len;
|
||||
|
||||
len = buf->page_len;
|
||||
ppages = buf->pages + (buf->page_base >> PAGE_SHIFT);
|
||||
while (len > 0) {
|
||||
if (!*ppages)
|
||||
*ppages = alloc_page(GFP_NOWAIT | __GFP_NOWARN);
|
||||
if (!*ppages)
|
||||
return -ENOBUFS;
|
||||
ppages++;
|
||||
len -= PAGE_SIZE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Split @vec on page boundaries into SGEs. FMR registers pages, not
|
||||
* a byte range. Other modes coalesce these SGEs into a single MR
|
||||
* when they can.
|
||||
@@ -233,15 +258,6 @@ rpcrdma_convert_iovs(struct rpcrdma_xprt *r_xprt, struct xdr_buf *xdrbuf,
|
||||
ppages = xdrbuf->pages + (xdrbuf->page_base >> PAGE_SHIFT);
|
||||
page_base = offset_in_page(xdrbuf->page_base);
|
||||
while (len) {
|
||||
/* ACL likes to be lazy in allocating pages - ACLs
|
||||
* are small by default but can get huge.
|
||||
*/
|
||||
if (unlikely(xdrbuf->flags & XDRBUF_SPARSE_PAGES)) {
|
||||
if (!*ppages)
|
||||
*ppages = alloc_page(GFP_NOWAIT | __GFP_NOWARN);
|
||||
if (!*ppages)
|
||||
return -ENOBUFS;
|
||||
}
|
||||
seg->mr_page = *ppages;
|
||||
seg->mr_offset = (char *)page_base;
|
||||
seg->mr_len = min_t(u32, PAGE_SIZE - page_base, len);
|
||||
@@ -867,6 +883,12 @@ rpcrdma_marshal_req(struct rpcrdma_xprt *r_xprt, struct rpc_rqst *rqst)
|
||||
__be32 *p;
|
||||
int ret;
|
||||
|
||||
if (unlikely(rqst->rq_rcv_buf.flags & XDRBUF_SPARSE_PAGES)) {
|
||||
ret = rpcrdma_alloc_sparse_pages(&rqst->rq_rcv_buf);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
rpcrdma_set_xdrlen(&req->rl_hdrbuf, 0);
|
||||
xdr_init_encode(xdr, &req->rl_hdrbuf, rdmab_data(req->rl_rdmabuf),
|
||||
rqst);
|
||||
|
||||
Reference in New Issue
Block a user