Commit a1f26739 authored by Frank van der Linden's avatar Frank van der Linden Committed by Trond Myklebust

NFSv4.2: improve page handling for GETXATTR

XDRBUF_SPARSE_PAGES can cause problems for the RDMA transport,
and it's easy enough to allocate enough pages for the request
up front, so do that.

Also, since we've allocated the pages anyway, use the full
page aligned length for the receive buffer. This will allow
caching of valid replies that are too large for the caller,
but that still fit in the allocated pages.
Signed-off-by: default avatarFrank van der Linden <fllinden@amazon.com>
Signed-off-by: default avatarTrond Myklebust <trond.myklebust@hammerspace.com>
parent ac9645c8
...@@ -1173,14 +1173,12 @@ static int _nfs42_proc_setxattr(struct inode *inode, const char *name, ...@@ -1173,14 +1173,12 @@ static int _nfs42_proc_setxattr(struct inode *inode, const char *name,
} }
static ssize_t _nfs42_proc_getxattr(struct inode *inode, const char *name, static ssize_t _nfs42_proc_getxattr(struct inode *inode, const char *name,
void *buf, size_t buflen) void *buf, size_t buflen, struct page **pages,
size_t plen)
{ {
struct nfs_server *server = NFS_SERVER(inode); struct nfs_server *server = NFS_SERVER(inode);
struct page *pages[NFS4XATTR_MAXPAGES] = {};
struct nfs42_getxattrargs arg = { struct nfs42_getxattrargs arg = {
.fh = NFS_FH(inode), .fh = NFS_FH(inode),
.xattr_pages = pages,
.xattr_len = buflen,
.xattr_name = name, .xattr_name = name,
}; };
struct nfs42_getxattrres res; struct nfs42_getxattrres res;
...@@ -1189,7 +1187,10 @@ static ssize_t _nfs42_proc_getxattr(struct inode *inode, const char *name, ...@@ -1189,7 +1187,10 @@ static ssize_t _nfs42_proc_getxattr(struct inode *inode, const char *name,
.rpc_argp = &arg, .rpc_argp = &arg,
.rpc_resp = &res, .rpc_resp = &res,
}; };
int ret, np; ssize_t ret;
arg.xattr_len = plen;
arg.xattr_pages = pages;
ret = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, ret = nfs4_call_sync(server->client, server, &msg, &arg.seq_args,
&res.seq_res, 0); &res.seq_res, 0);
...@@ -1214,10 +1215,6 @@ static ssize_t _nfs42_proc_getxattr(struct inode *inode, const char *name, ...@@ -1214,10 +1215,6 @@ static ssize_t _nfs42_proc_getxattr(struct inode *inode, const char *name,
_copy_from_pages(buf, pages, 0, res.xattr_len); _copy_from_pages(buf, pages, 0, res.xattr_len);
} }
np = DIV_ROUND_UP(res.xattr_len, PAGE_SIZE);
while (--np >= 0)
__free_page(pages[np]);
return res.xattr_len; return res.xattr_len;
} }
...@@ -1292,16 +1289,44 @@ ssize_t nfs42_proc_getxattr(struct inode *inode, const char *name, ...@@ -1292,16 +1289,44 @@ ssize_t nfs42_proc_getxattr(struct inode *inode, const char *name,
void *buf, size_t buflen) void *buf, size_t buflen)
{ {
struct nfs4_exception exception = { }; struct nfs4_exception exception = { };
ssize_t err; ssize_t err, np, i;
struct page **pages;
np = nfs_page_array_len(0, buflen ?: XATTR_SIZE_MAX);
pages = kmalloc_array(np, sizeof(*pages), GFP_KERNEL);
if (!pages)
return -ENOMEM;
for (i = 0; i < np; i++) {
pages[i] = alloc_page(GFP_KERNEL);
if (!pages[i]) {
np = i + 1;
goto out;
}
}
/*
* The GETXATTR op has no length field in the call, and the
* xattr data is at the end of the reply.
*
* There is no downside in using the page-aligned length. It will
* allow receiving and caching xattrs that are too large for the
* caller but still fit in the page-rounded value.
*/
do { do {
err = _nfs42_proc_getxattr(inode, name, buf, buflen); err = _nfs42_proc_getxattr(inode, name, buf, buflen,
pages, np * PAGE_SIZE);
if (err >= 0) if (err >= 0)
break; break;
err = nfs4_handle_exception(NFS_SERVER(inode), err, err = nfs4_handle_exception(NFS_SERVER(inode), err,
&exception); &exception);
} while (exception.retry); } while (exception.retry);
out:
while (--np >= 0)
__free_page(pages[np]);
kfree(pages);
return err; return err;
} }
......
...@@ -489,6 +489,12 @@ static int decode_getxattr(struct xdr_stream *xdr, ...@@ -489,6 +489,12 @@ static int decode_getxattr(struct xdr_stream *xdr,
return -EIO; return -EIO;
len = be32_to_cpup(p); len = be32_to_cpup(p);
/*
* Only check against the page length here. The actual
* requested length may be smaller, but that is only
* checked against after possibly caching a valid reply.
*/
if (len > req->rq_rcv_buf.page_len) if (len > req->rq_rcv_buf.page_len)
return -ERANGE; return -ERANGE;
...@@ -1477,7 +1483,6 @@ static void nfs4_xdr_enc_getxattr(struct rpc_rqst *req, struct xdr_stream *xdr, ...@@ -1477,7 +1483,6 @@ static void nfs4_xdr_enc_getxattr(struct rpc_rqst *req, struct xdr_stream *xdr,
.minorversion = nfs4_xdr_minorversion(&args->seq_args), .minorversion = nfs4_xdr_minorversion(&args->seq_args),
}; };
uint32_t replen; uint32_t replen;
size_t plen;
encode_compound_hdr(xdr, req, &hdr); encode_compound_hdr(xdr, req, &hdr);
encode_sequence(xdr, &args->seq_args, &hdr); encode_sequence(xdr, &args->seq_args, &hdr);
...@@ -1485,10 +1490,8 @@ static void nfs4_xdr_enc_getxattr(struct rpc_rqst *req, struct xdr_stream *xdr, ...@@ -1485,10 +1490,8 @@ static void nfs4_xdr_enc_getxattr(struct rpc_rqst *req, struct xdr_stream *xdr,
replen = hdr.replen + op_decode_hdr_maxsz + 1; replen = hdr.replen + op_decode_hdr_maxsz + 1;
encode_getxattr(xdr, args->xattr_name, &hdr); encode_getxattr(xdr, args->xattr_name, &hdr);
plen = args->xattr_len ? args->xattr_len : XATTR_SIZE_MAX; rpc_prepare_reply_pages(req, args->xattr_pages, 0, args->xattr_len,
replen);
rpc_prepare_reply_pages(req, args->xattr_pages, 0, plen, replen);
req->rq_rcv_buf.flags |= XDRBUF_SPARSE_PAGES;
encode_nops(&hdr); encode_nops(&hdr);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment