From: Chuck Lever <cel@kernel.org>
To: NeilBrown <neil@brown.name>, Jeff Layton <jlayton@kernel.org>,
Olga Kornievskaia <okorniev@redhat.com>,
Dai Ngo <dai.ngo@oracle.com>, Tom Talpey <tom@talpey.com>
Cc: <linux-nfs@vger.kernel.org>
Subject: [PATCH v1 3/7] nfsd: locate the READ sink page from the reply buffer
Date: Sun, 4 Oct 2026 15:40:25 -0400 [thread overview]
Message-ID: <20261004194029.10714-4-cel@kernel.org> (raw)
In-Reply-To: <20261004194029.10714-1-cel@kernel.org>
nfsd_iter_read() places file data at *rq_next_page, at an in-page
offset its caller computes from the xdr_buf. The page and the offset
come from different sources, and nothing keeps them consistent.
nfsd4_encode_readv() sets rq_next_page from the xdr_buf before each
call to keep them so, and any other caller has to do the same.
Have nfsd_iter_read() compute both the first sink page and the
in-page offset from rq_res.page_len, the accounting that
xdr_reserve_space_vec() extends once the read completes. NFSv2 and
NFSv3 hand the reply pages to READ untouched, so page_len is zero
there and the sink page is still *rq_next_page. nfsd_iter_read() and
nfsd_direct_read() now write rq_next_page and never read it.
Signed-off-by: Chuck Lever <cel@kernel.org>
---
fs/nfsd/nfs4xdr.c | 7 +------
fs/nfsd/vfs.c | 38 ++++++++++++++++++++------------------
fs/nfsd/vfs.h | 3 +--
3 files changed, 22 insertions(+), 26 deletions(-)
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 89230b3206ac..b86d181b58c3 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -5303,17 +5303,12 @@ static __be32 nfsd4_encode_readv(struct nfsd4_compoundres *resp,
unsigned long maxcount)
{
struct xdr_stream *xdr = resp->xdr;
- unsigned int base = xdr->buf->page_len & ~PAGE_MASK;
unsigned int starting_len = xdr->buf->len;
__be32 zero = xdr_zero;
__be32 nfserr;
- resp->rqstp->rq_next_page = xdr->buf->pages +
- (xdr->buf->page_len >> PAGE_SHIFT);
-
nfserr = nfsd_iter_read(resp->rqstp, read->rd_fhp, read->rd_nf,
- read->rd_offset, &maxcount, base,
- &read->rd_eof);
+ read->rd_offset, &maxcount, &read->rd_eof);
read->rd_length = maxcount;
if (nfserr)
return nfserr;
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index 3f328378c805..c7dd94697f2f 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -1097,14 +1097,13 @@ __be32 nfsd_splice_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
*
* Note that a direct read can be done only when the xdr_buf containing
* the NFS READ reply does not already have contents in its .pages array.
- * This is due to potentially restrictive alignment requirements on the
- * read buffer. When .page_len and @base are zero, the .pages array is
- * guaranteed to be page-aligned.
+ * Direct I/O alignment can be restrictive, and with .page_len zero the
+ * read buffer starts on a page boundary.
*/
static noinline_for_stack __be32
nfsd_direct_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
struct nfsd_file *nf, loff_t offset, unsigned long *count,
- u32 *eof)
+ struct page **page, u32 *eof)
{
u64 dio_start, dio_end;
unsigned long v, total;
@@ -1124,16 +1123,15 @@ nfsd_direct_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
v = 0;
total = dio_end - dio_start;
- while (total && v < rqstp->rq_maxpages &&
- rqstp->rq_next_page < rqstp->rq_page_end) {
+ while (total && v < rqstp->rq_maxpages && page < rqstp->rq_page_end) {
len = min_t(size_t, total, PAGE_SIZE);
- bvec_set_page(&rqstp->rq_bvec[v], *rqstp->rq_next_page,
- len, 0);
+ bvec_set_page(&rqstp->rq_bvec[v], *page, len, 0);
total -= len;
- ++rqstp->rq_next_page;
+ ++page;
++v;
}
+ rqstp->rq_next_page = page;
trace_nfsd_read_direct(rqstp, fhp, offset, *count - total);
iov_iter_bvec(&iter, ITER_DEST, rqstp->rq_bvec, v,
@@ -1191,19 +1189,24 @@ static bool nfsd_direct_read_ok(struct svc_rqst *rqstp, struct nfsd_file *nf)
* @nf: opened struct nfsd_file of file to be read
* @offset: starting byte offset
* @count: IN: requested number of bytes; OUT: number of bytes read
- * @base: offset in first page of read buffer
* @eof: OUT: set non-zero if operation reached the end of the file
*
* Some filesystems or situations cannot use nfsd_splice_read. This
* function is the slightly less-performant fallback for those cases.
+ * File data lands in @rqstp->rq_res.pages at the offset .page_len
+ * records. On return, @rqstp->rq_next_page points past the last page
+ * offered to the read.
*
* Returns nfs_ok on success, otherwise an nfserr stat value is
* returned.
*/
__be32 nfsd_iter_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
struct nfsd_file *nf, loff_t offset, unsigned long *count,
- unsigned int base, u32 *eof)
+ u32 *eof)
{
+ struct xdr_buf *buf = &rqstp->rq_res;
+ struct page **page = buf->pages + (buf->page_len >> PAGE_SHIFT);
+ unsigned int base = buf->page_len & ~PAGE_MASK;
struct file *file = nf->nf_file;
unsigned long v, total;
struct iov_iter iter;
@@ -1219,7 +1222,7 @@ __be32 nfsd_iter_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
case NFSD_IO_DIRECT:
if (nfsd_direct_read_ok(rqstp, nf))
return nfsd_direct_read(rqstp, fhp, nf, offset,
- count, eof);
+ count, page, eof);
fallthrough;
case NFSD_IO_DONTCACHE:
if (file->f_op->fop_flags & FOP_DONTCACHE)
@@ -1231,17 +1234,16 @@ __be32 nfsd_iter_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
v = 0;
total = *count;
- while (total && v < rqstp->rq_maxpages &&
- rqstp->rq_next_page < rqstp->rq_page_end) {
+ while (total && v < rqstp->rq_maxpages && page < rqstp->rq_page_end) {
len = min_t(size_t, total, PAGE_SIZE - base);
- bvec_set_page(&rqstp->rq_bvec[v], *rqstp->rq_next_page,
- len, base);
+ bvec_set_page(&rqstp->rq_bvec[v], *page, len, base);
total -= len;
- ++rqstp->rq_next_page;
+ ++page;
++v;
base = 0;
}
+ rqstp->rq_next_page = page;
trace_nfsd_read_vector(rqstp, fhp, offset, *count - total);
iov_iter_bvec(&iter, ITER_DEST, rqstp->rq_bvec, v, *count - total);
@@ -1598,7 +1600,7 @@ __be32 nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
if (file->f_op->splice_read && nfsd_read_splice_ok(rqstp))
err = nfsd_splice_read(rqstp, fhp, file, offset, count, eof);
else
- err = nfsd_iter_read(rqstp, fhp, nf, offset, count, 0, eof);
+ err = nfsd_iter_read(rqstp, fhp, nf, offset, count, eof);
nfsd_file_put(nf);
trace_nfsd_read_done(rqstp, fhp, offset, *count);
diff --git a/fs/nfsd/vfs.h b/fs/nfsd/vfs.h
index 38f7d36bd4da..8c130473aa79 100644
--- a/fs/nfsd/vfs.h
+++ b/fs/nfsd/vfs.h
@@ -181,8 +181,7 @@ __be32 nfsd_splice_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
u32 *eof);
__be32 nfsd_iter_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
struct nfsd_file *nf, loff_t offset,
- unsigned long *count, unsigned int base,
- u32 *eof);
+ unsigned long *count, u32 *eof);
bool nfsd_read_splice_ok(struct svc_rqst *rqstp);
__be32 nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
loff_t offset, unsigned long *count,
--
2.55.0
next prev parent reply other threads:[~2026-10-04 19:40 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-04 19:40 [PATCH v1 0/7] NFSD short subjects Chuck Lever
2026-10-04 19:40 ` [PATCH v1 1/7] nfsd: use direct I/O only for the last operation in a COMPOUND Chuck Lever
2026-10-04 19:40 ` [PATCH v1 2/7] nfsd: account for page_base when advancing rq_next_page Chuck Lever
2026-10-04 19:40 ` Chuck Lever [this message]
2026-10-04 19:40 ` [PATCH v1 4/7] nfsd: keep spliced pages below rq_next_page when a READ fails Chuck Lever
2026-10-04 19:40 ` [PATCH v1 5/7] nfsd: remove unreachable cancel of the layout fence work Chuck Lever
2026-10-04 19:40 ` [PATCH v1 6/7] sunrpc: preserve rq_daddrlen across request deferral Chuck Lever
2026-10-04 19:40 ` [PATCH v1 7/7] sunrpc: assign RQ_LOCAL from the transport on every receive Chuck Lever
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261004194029.10714-4-cel@kernel.org \
--to=cel@kernel.org \
--cc=dai.ngo@oracle.com \
--cc=jlayton@kernel.org \
--cc=linux-nfs@vger.kernel.org \
--cc=neil@brown.name \
--cc=okorniev@redhat.com \
--cc=tom@talpey.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox