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>,
Mike Snitzer <snitzer@kernel.org>
Cc: <linux-nfs@vger.kernel.org>, sashiko-bot <sashiko-bot@kernel.org>
Subject: [PATCH] NFSD: Refuse a direct READ whose aligned range exceeds the reply pages
Date: Wed, 11 Nov 2026 10:20:50 -0500 [thread overview]
Message-ID: <20261111152050.172047-1-cel@kernel.org> (raw)
nfsd_direct_read() expands the READ's byte range to the file's direct
I/O alignment, then maps reply pages until the range is covered or
rq_page_end is reached. A block device whose logical block size
exceeds PAGE_SIZE reports an alignment larger than PAGE_SIZE. With
16KB blocks and 4KB pages, a maximum-size READ at an offset that is
not a multiple of 16KB needs one more page than the svc_rqst has.
The loop stops at rq_page_end and passes a truncated iterator to the
filesystem. iomap rejects the unaligned length with -EINVAL, and NFSD
returns NFSERR_SERVERFAULT for a READ that a buffered read would have
satisfied.
Check the aligned range against the reply pages in
nfsd_direct_read_ok(), and fall back to a buffered read when the
range does not fit.
Reported-by: sashiko-bot <sashiko-bot@kernel.org>
Closes: https://sashiko.dev/#/patchset/20261109-xdr-page-base-v1-0-a4f4411d760d@kernel.org?part=11
Fixes: d686e64e931c ("NFSD: Implement NFSD_IO_DIRECT for NFS READ")
Signed-off-by: Chuck Lever <cel@kernel.org>
---
Documentation/filesystems/nfs/nfsd-io-modes.rst | 7 +++++++
fs/nfsd/nfs4xdr.c | 5 -----
fs/nfsd/trace.h | 1 +
fs/nfsd/vfs.c | 14 ++++++++++++--
4 files changed, 20 insertions(+), 7 deletions(-)
diff --git a/Documentation/filesystems/nfs/nfsd-io-modes.rst b/Documentation/filesystems/nfs/nfsd-io-modes.rst
index d57b7916f6c2..28235eceb0c7 100644
--- a/Documentation/filesystems/nfs/nfsd-io-modes.rst
+++ b/Documentation/filesystems/nfs/nfsd-io-modes.rst
@@ -219,6 +219,13 @@ Tracing:
nfsd_read_dio_pages_in_use an earlier operation in the same
COMPOUND already put data in the
reply's pages
+ nfsd_read_dio_range_too_large
+ the DIO read offset alignment exceeds
+ the page size and the READ, expanded
+ to that alignment, needs more pages
+ than the reply has. An NFSv4 READ
+ that also meets one of the conditions
+ below reports that condition instead.
nfsd_read_dio_head_full an NFSv4 READ whose offset is not a
multiple of four bytes, when the XDR
stream has left the reply's head or
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 0b3d77bb9941..950c67362566 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -5327,11 +5327,6 @@ static __be32 nfsd4_encode_splice_read(
return nfserr;
}
-/*
- * Reply-buffer checks that nfsd_direct_read_ok() cannot make: the
- * payload's placement depends on the alignment pad, and the pad on the
- * READ offset.
- */
static bool nfsd4_direct_read_fits(struct nfsd4_compoundres *resp,
struct nfsd4_read *read,
unsigned long count)
diff --git a/fs/nfsd/trace.h b/fs/nfsd/trace.h
index 11a6d7d66f6f..45a5db9def39 100644
--- a/fs/nfsd/trace.h
+++ b/fs/nfsd/trace.h
@@ -662,6 +662,7 @@ DEFINE_EVENT(nfsd_read_dio_class, nfsd_read_dio_##name, \
DEFINE_NFSD_READ_DIO_EVENT(no_alignment);
DEFINE_NFSD_READ_DIO_EVENT(too_small);
DEFINE_NFSD_READ_DIO_EVENT(pages_in_use);
+DEFINE_NFSD_READ_DIO_EVENT(range_too_large);
DEFINE_NFSD_READ_DIO_EVENT(head_full);
DEFINE_NFSD_READ_DIO_EVENT(reply_full);
DEFINE_NFSD_READ_DIO_EVENT(not_last_op);
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index 10ea334cfdab..123c38f3bd88 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -1250,9 +1250,11 @@ nfsd_direct_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
static bool nfsd_direct_read_ok(struct svc_rqst *rqstp, struct svc_fh *fhp,
struct nfsd_file *nf, loff_t offset,
- unsigned long count, bool dio_ok)
+ unsigned long count, struct page **page,
+ bool dio_ok)
{
u32 offset_align = nf->nf_dio_read_offset_align;
+ u64 dio_len;
/* When dio_read_offset_align is zero, dio is not supported */
if (!offset_align) {
@@ -1273,6 +1275,14 @@ static bool nfsd_direct_read_ok(struct svc_rqst *rqstp, struct svc_fh *fhp,
}
if (!dio_ok)
return false;
+ /* An alignment above PAGE_SIZE can round the range past rq_page_end */
+ dio_len = round_up((u64)offset + count, offset_align) -
+ round_down(offset, offset_align);
+ if ((s64)dio_len > (s64)PAGE_SIZE * (rqstp->rq_page_end - page)) {
+ trace_nfsd_read_dio_range_too_large(rqstp, fhp, offset, count,
+ offset_align);
+ return false;
+ }
return true;
}
@@ -1356,7 +1366,7 @@ __be32 nfsd_iter_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
break;
case NFSD_IO_DIRECT:
if (nfsd_direct_read_ok(rqstp, fhp, nf, offset, *count,
- dio_ok))
+ page, dio_ok))
return nfsd_direct_read(rqstp, fhp, nf, offset,
count, page, eof,
storage_start);
--
2.56.0
next reply other threads:[~2026-11-11 15:20 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-11 15:20 Chuck Lever [this message]
2026-11-11 17:07 ` NFSD: Refuse a direct READ whose aligned range exceeds the reply pages Mike Snitzer
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=20261111152050.172047-1-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=sashiko-bot@kernel.org \
--cc=snitzer@kernel.org \
--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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.