* FAILED: patch "[PATCH] NFS/localio: detect a short read or write before the iterator" failed to apply to 6.18-stable tree
@ 2026-11-04 18:47 gregkh
2026-11-06 1:53 ` [PATCH 6.18.y] NFS/localio: detect a short read or write before the iterator has moved Sasha Levin
0 siblings, 1 reply; 2+ messages in thread
From: gregkh @ 2026-11-04 18:47 UTC (permalink / raw)
To: snitzer, anna.schumaker; +Cc: stable
The patch below does not apply to the 6.18-stable tree.
If someone wants it applied there, or to any other stable or longterm
tree, then please email the backport, including the original git commit
id to <stable@vger.kernel.org>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.18.y
git checkout FETCH_HEAD
git cherry-pick -x db3ca539c1142d2c5380c5dbd3154ab7bb1c7d77
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<stable@vger.kernel.org>' --in-reply-to '2026110458-dreamt-steering-e6da@gregkh' --subject-prefix 'PATCH 6.18.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From db3ca539c1142d2c5380c5dbd3154ab7bb1c7d77 Mon Sep 17 00:00:00 2001
From: Mike Snitzer <snitzer@kernel.org>
Date: Mon, 28 Sep 2026 11:54:26 -0400
Subject: [PATCH] NFS/localio: detect a short read or write before the iterator
has moved
nfs_local_call_read() and nfs_local_call_write() issue a misaligned
DIO as up to three segments and must stop at the first short one, or
the next segment lands at the wrong file offset. They test for that
by comparing the bytes transferred against iov_iter_count() of the
segment, read after read_iter() or write_iter() has advanced the
iterator, when the count is the residual rather than the request:
before the call: count == expected
after the call: count == expected - status
The test is therefore status < expected - status, and it fires only
when less than half of the segment was transferred. A short I/O that
covers between 50% and 99% of a segment slips through: the loop moves
on with ki_pos advanced by the short amount, the next segment is
issued at the wrong offset, and the header's byte count is
over-reported to the caller. A write that slips through also never
sets NFS_CONTEXT_WRITE_SYNC, which is what makes the writes that
follow a short one synchronous.
Take the segment's count before issuing it and compare against that.
This is the same defect that commit 250ec14932d5 ("nfsd: fix
partial-write detection in nfsd_direct_write") fixed in NFSD's version
of this loop.
Fixes: c817248fc831 ("nfs/localio: add proper O_DIRECT support for READ and WRITE")
Fixes: d0497dd27452 ("nfs/localio: backfill missing partial read support for misaligned DIO")
Cc: stable@vger.kernel.org
Assisted-by: Claude:claude-fable-5-1
Signed-off-by: Mike Snitzer <snitzer@kernel.org>
Signed-off-by: Anna Schumaker <anna.schumaker@hammerspace.com>
diff --git a/fs/nfs/localio.c b/fs/nfs/localio.c
index 63c38dea50cc..9fcae2391b72 100644
--- a/fs/nfs/localio.c
+++ b/fs/nfs/localio.c
@@ -674,6 +674,8 @@ static void nfs_local_call_read(struct work_struct *work)
n_iters = atomic_read(&iocb->n_iters);
for (int i = 0; i < n_iters ; i++) {
+ size_t expected;
+
if (iocb->iter_is_dio_aligned[i]) {
iocb->kiocb.ki_flags |= IOCB_DIRECT;
/* Only use AIO completion if DIO-aligned segment is last */
@@ -684,6 +686,8 @@ static void nfs_local_call_read(struct work_struct *work)
} else
iocb->kiocb.ki_flags &= ~IOCB_DIRECT;
+ /* read_iter() advances the iterator: measure it beforehand */
+ expected = iov_iter_count(&iocb->iters[i]);
scoped_with_creds(filp->f_cred)
status = filp->f_op->read_iter(&iocb->kiocb, &iocb->iters[i]);
@@ -691,7 +695,7 @@ static void nfs_local_call_read(struct work_struct *work)
continue;
/* Break on completion, errors, or short reads */
if (nfs_local_pgio_done(iocb, status) || status < 0 ||
- (size_t)status < iov_iter_count(&iocb->iters[i])) {
+ (size_t)status < expected) {
nfs_local_read_iocb_done(iocb);
break;
}
@@ -890,7 +894,7 @@ static void nfs_local_call_write(struct work_struct *work)
file_start_write(filp);
n_iters = atomic_read(&iocb->n_iters);
for (int i = 0; i < n_iters ; i++) {
- size_t icount;
+ size_t expected;
if (iocb->iter_is_dio_aligned[i]) {
iocb->kiocb.ki_flags |= IOCB_DIRECT;
@@ -902,16 +906,17 @@ static void nfs_local_call_write(struct work_struct *work)
} else
iocb->kiocb.ki_flags &= ~IOCB_DIRECT;
+ /* write_iter() advances the iterator: measure it beforehand */
+ expected = iov_iter_count(&iocb->iters[i]);
scoped_with_creds(filp->f_cred)
status = filp->f_op->write_iter(&iocb->kiocb, &iocb->iters[i]);
if (status == -EIOCBQUEUED)
continue;
/* Break on completion, errors, or short writes */
- icount = iov_iter_count(&iocb->iters[i]);
if (nfs_local_pgio_done(iocb, status) || status < 0 ||
- (size_t)status < icount) {
- if ((size_t)status < icount) {
+ (size_t)status < expected) {
+ if ((size_t)status < expected) {
struct nfs_lock_context *ctx =
iocb->hdr->req->wb_lock_context;
^ permalink raw reply related [flat|nested] 2+ messages in thread
* [PATCH 6.18.y] NFS/localio: detect a short read or write before the iterator has moved
2026-11-04 18:47 FAILED: patch "[PATCH] NFS/localio: detect a short read or write before the iterator" failed to apply to 6.18-stable tree gregkh
@ 2026-11-06 1:53 ` Sasha Levin
0 siblings, 0 replies; 2+ messages in thread
From: Sasha Levin @ 2026-11-06 1:53 UTC (permalink / raw)
To: stable; +Cc: Mike Snitzer, Anna Schumaker, Sasha Levin
From: Mike Snitzer <snitzer@kernel.org>
[ Upstream commit db3ca539c1142d2c5380c5dbd3154ab7bb1c7d77 ]
nfs_local_call_read() and nfs_local_call_write() issue a misaligned
DIO as up to three segments and must stop at the first short one, or
the next segment lands at the wrong file offset. They test for that
by comparing the bytes transferred against iov_iter_count() of the
segment, read after read_iter() or write_iter() has advanced the
iterator, when the count is the residual rather than the request:
before the call: count == expected
after the call: count == expected - status
The test is therefore status < expected - status, and it fires only
when less than half of the segment was transferred. A short I/O that
covers between 50% and 99% of a segment slips through: the loop moves
on with ki_pos advanced by the short amount, the next segment is
issued at the wrong offset, and the header's byte count is
over-reported to the caller. A write that slips through also never
sets NFS_CONTEXT_WRITE_SYNC, which is what makes the writes that
follow a short one synchronous.
Take the segment's count before issuing it and compare against that.
This is the same defect that commit 250ec14932d5 ("nfsd: fix
partial-write detection in nfsd_direct_write") fixed in NFSD's version
of this loop.
Fixes: c817248fc831 ("nfs/localio: add proper O_DIRECT support for READ and WRITE")
Fixes: d0497dd27452 ("nfs/localio: backfill missing partial read support for misaligned DIO")
Cc: stable@vger.kernel.org
Assisted-by: Claude:claude-fable-5-1
Signed-off-by: Mike Snitzer <snitzer@kernel.org>
Signed-off-by: Anna Schumaker <anna.schumaker@hammerspace.com>
[ sashal: adapted completion handling to the three-argument nfs_local_pgio_done() helper using force_done ]
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
fs/nfs/localio.c | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/fs/nfs/localio.c b/fs/nfs/localio.c
index 3c5891d3ab897..3138439d19109 100644
--- a/fs/nfs/localio.c
+++ b/fs/nfs/localio.c
@@ -682,6 +682,8 @@ static void nfs_local_call_read(struct work_struct *work)
n_iters = atomic_read(&iocb->n_iters);
for (int i = 0; i < n_iters ; i++) {
+ size_t expected;
+
if (iocb->iter_is_dio_aligned[i]) {
iocb->kiocb.ki_flags |= IOCB_DIRECT;
/* Only use AIO completion if DIO-aligned segment is last */
@@ -692,12 +694,14 @@ static void nfs_local_call_read(struct work_struct *work)
} else
iocb->kiocb.ki_flags &= ~IOCB_DIRECT;
+ /* read_iter() advances the iterator: measure it beforehand */
+ expected = iov_iter_count(&iocb->iters[i]);
save_cred = override_creds(filp->f_cred);
status = filp->f_op->read_iter(&iocb->kiocb, &iocb->iters[i]);
revert_creds(save_cred);
if (status != -EIOCBQUEUED) {
- if (unlikely(status >= 0 && status < iocb->iters[i].count))
+ if (unlikely(status >= 0 && (size_t)status < expected))
force_done = true; /* Partial read */
if (nfs_local_pgio_done(iocb, status, force_done)) {
nfs_local_read_iocb_done(iocb);
@@ -878,6 +882,8 @@ static void nfs_local_call_write(struct work_struct *work)
file_start_write(filp);
n_iters = atomic_read(&iocb->n_iters);
for (int i = 0; i < n_iters ; i++) {
+ size_t expected;
+
if (iocb->iter_is_dio_aligned[i]) {
iocb->kiocb.ki_flags |= IOCB_DIRECT;
/* Only use AIO completion if DIO-aligned segment is last */
@@ -888,12 +894,14 @@ static void nfs_local_call_write(struct work_struct *work)
} else
iocb->kiocb.ki_flags &= ~IOCB_DIRECT;
+ /* write_iter() advances the iterator: measure it beforehand */
+ expected = iov_iter_count(&iocb->iters[i]);
save_cred = override_creds(filp->f_cred);
status = filp->f_op->write_iter(&iocb->kiocb, &iocb->iters[i]);
revert_creds(save_cred);
if (status != -EIOCBQUEUED) {
- if (unlikely(status >= 0 && status < iocb->iters[i].count))
+ if (unlikely(status >= 0 && (size_t)status < expected))
force_done = true; /* Partial write */
if (nfs_local_pgio_done(iocb, status, force_done)) {
nfs_local_write_iocb_done(iocb);
--
2.53.0
^ permalink raw reply related [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-11-06 1:53 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-04 18:47 FAILED: patch "[PATCH] NFS/localio: detect a short read or write before the iterator" failed to apply to 6.18-stable tree gregkh
2026-11-06 1:53 ` [PATCH 6.18.y] NFS/localio: detect a short read or write before the iterator has moved Sasha Levin
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox