* [PATCH 1/5] filemap: drain in-flight DIO before buffered writes
2026-10-25 12:41 [PATCH 0/5] fs: drain in-flight DIO before buffered writes Jiale Yao
@ 2026-10-25 12:41 ` Jiale Yao
2026-10-26 13:31 ` Jan Kara
2026-10-25 12:41 ` [PATCH 2/5] f2fs: " Jiale Yao
` (3 subsequent siblings)
4 siblings, 1 reply; 9+ messages in thread
From: Jiale Yao @ 2026-10-25 12:41 UTC (permalink / raw)
To: Matthew Wilcox (Oracle),
Jan Kara, Andrew Morton, Al Viro, linux-fsdevel, linux-mm,
linux-kernel
Cc: stable
An asynchronous direct write can remain in flight after its submitting
thread releases the inode lock. A buffered write can then dirty page
cache before the direct write completes its post-I/O invalidation. The
invalidation finds dirty pages, reports a possible data corruption, and
records -EIO in the mapping error sequence.
Drain in-flight DIO before both regular buffered writes and direct I/O
fallbacks in __generic_file_write_iter(). Preserve IOCB_NOWAIT semantics
by returning -EAGAIN when waiting would be required, while retaining a
positive partial direct write result.
Fixes: 8174202b34c3 ("write_iter variants of {__,}generic_file_aio_write()")
Cc: stable@vger.kernel.org
Signed-off-by: Jiale Yao <yaojiale02@163.com>
---
mm/filemap.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/mm/filemap.c b/mm/filemap.c
index b74bc1e5015c..289b8ca44ada 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -4502,10 +4502,19 @@ ssize_t __generic_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
*/
if (ret < 0 || !iov_iter_count(from) || IS_DAX(inode))
return ret;
+ if ((iocb->ki_flags & IOCB_NOWAIT) &&
+ atomic_read(&inode->i_dio_count))
+ return ret ?: -EAGAIN;
+ inode_dio_wait(inode);
return direct_write_fallback(iocb, from, ret,
generic_perform_write(iocb, from));
}
+ if ((iocb->ki_flags & IOCB_NOWAIT) &&
+ atomic_read(&inode->i_dio_count))
+ return -EAGAIN;
+ inode_dio_wait(inode);
+
return generic_perform_write(iocb, from);
}
EXPORT_SYMBOL(__generic_file_write_iter);
--
2.34.1
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [PATCH 1/5] filemap: drain in-flight DIO before buffered writes
2026-10-25 12:41 ` [PATCH 1/5] filemap: " Jiale Yao
@ 2026-10-26 13:31 ` Jan Kara
2026-10-27 14:52 ` Christoph Hellwig
0 siblings, 1 reply; 9+ messages in thread
From: Jan Kara @ 2026-10-26 13:31 UTC (permalink / raw)
To: Jiale Yao
Cc: Matthew Wilcox (Oracle),
Jan Kara, Andrew Morton, Al Viro, linux-fsdevel, linux-mm,
linux-kernel, stable
On Sun 25-10-26 20:41:10, Jiale Yao wrote:
> An asynchronous direct write can remain in flight after its submitting
> thread releases the inode lock. A buffered write can then dirty page
> cache before the direct write completes its post-I/O invalidation. The
> invalidation finds dirty pages, reports a possible data corruption, and
> records -EIO in the mapping error sequence.
Well, when you do stupid things, you get stupid results. We were always
saying mixing buffered and direct IO in the same file isn't a good idea and
we try to not screw users too badly but give no promises. This falls into
that cathegory... So I don't think there's much to fix here. The only
sensible exception here are simple filesystems which have only simple
support for direct IO and fallback to buffered IO for the rest. But I think
we should handle these on a case-by-case basis and either drop the support
for direct IO altogether or convert them to iomap and properly handle the
waiting in their .write_iter methods (like e.g. ext2 does it).
Honza
> Drain in-flight DIO before both regular buffered writes and direct I/O
> fallbacks in __generic_file_write_iter(). Preserve IOCB_NOWAIT semantics
> by returning -EAGAIN when waiting would be required, while retaining a
> positive partial direct write result.
>
> Fixes: 8174202b34c3 ("write_iter variants of {__,}generic_file_aio_write()")
> Cc: stable@vger.kernel.org
> Signed-off-by: Jiale Yao <yaojiale02@163.com>
> ---
> mm/filemap.c | 9 +++++++++
> 1 file changed, 9 insertions(+)
>
> diff --git a/mm/filemap.c b/mm/filemap.c
> index b74bc1e5015c..289b8ca44ada 100644
> --- a/mm/filemap.c
> +++ b/mm/filemap.c
> @@ -4502,10 +4502,19 @@ ssize_t __generic_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
> */
> if (ret < 0 || !iov_iter_count(from) || IS_DAX(inode))
> return ret;
> + if ((iocb->ki_flags & IOCB_NOWAIT) &&
> + atomic_read(&inode->i_dio_count))
> + return ret ?: -EAGAIN;
> + inode_dio_wait(inode);
> return direct_write_fallback(iocb, from, ret,
> generic_perform_write(iocb, from));
> }
>
> + if ((iocb->ki_flags & IOCB_NOWAIT) &&
> + atomic_read(&inode->i_dio_count))
> + return -EAGAIN;
> + inode_dio_wait(inode);
> +
> return generic_perform_write(iocb, from);
> }
> EXPORT_SYMBOL(__generic_file_write_iter);
> --
> 2.34.1
>
--
Jan Kara <jack@suse.com>
SUSE Labs, CR
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [PATCH 1/5] filemap: drain in-flight DIO before buffered writes
2026-10-26 13:31 ` Jan Kara
@ 2026-10-27 14:52 ` Christoph Hellwig
0 siblings, 0 replies; 9+ messages in thread
From: Christoph Hellwig @ 2026-10-27 14:52 UTC (permalink / raw)
To: Jan Kara
Cc: Jiale Yao, Matthew Wilcox (Oracle),
Andrew Morton, Al Viro, linux-fsdevel, linux-mm, linux-kernel,
stable
On Mon, Oct 26, 2026 at 02:31:31PM +0100, Jan Kara wrote:
> > An asynchronous direct write can remain in flight after its submitting
> > thread releases the inode lock. A buffered write can then dirty page
> > cache before the direct write completes its post-I/O invalidation. The
> > invalidation finds dirty pages, reports a possible data corruption, and
> > records -EIO in the mapping error sequence.
>
> Well, when you do stupid things, you get stupid results. We were always
> saying mixing buffered and direct IO in the same file isn't a good idea and
> we try to not screw users too badly but give no promises. This falls into
> that cathegory... So I don't think there's much to fix here.
We have declared it stupid, but people do it anyway. See all the nfsd
patches floating around for in-kernel consumers doing this heavy
and fine grained mixing. So I think we should get this right, and we
should also have test in xfstests to verify the beavior instead of just
shooting into the sky. (related: I only see patches 1 and 5 in my
fsdevel inbox, something is messed up with the series)
> The only
> sensible exception here are simple filesystems which have only simple
> support for direct IO and fallback to buffered IO for the rest. But I think
> we should handle these on a case-by-case basis and either drop the support
> for direct IO altogether or convert them to iomap and properly handle the
> waiting in their .write_iter methods (like e.g. ext2 does it).
That's a bit too easy. There actually is a much better option, which
unfortunately is much more invasive: kill i_dio_wait and move to
non-owner unlocking of i_rwsem from the asynchronous completion handler.
That gets us sane locking behavior without any footguns. I really
regret adding i_dio_wait in hindsight - yes, the PREEMPT_RT folks
hat locks that aren't bound to tasks, but we ended up with a few of
those anyway and thus have the primitives, and file read/write is a
case where they really fit.
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 2/5] f2fs: drain in-flight DIO before buffered writes
2026-10-25 12:41 [PATCH 0/5] fs: drain in-flight DIO before buffered writes Jiale Yao
2026-10-25 12:41 ` [PATCH 1/5] filemap: " Jiale Yao
@ 2026-10-25 12:41 ` Jiale Yao
2026-10-25 12:41 ` [PATCH 3/5] xfs: " Jiale Yao
` (2 subsequent siblings)
4 siblings, 0 replies; 9+ messages in thread
From: Jiale Yao @ 2026-10-25 12:41 UTC (permalink / raw)
To: Jaegeuk Kim, Chao Yu, Eric Biggers, linux-f2fs-devel, linux-kernel; +Cc: stable
An asynchronous iomap direct write can remain in flight after the inode
lock is released. A buffered write can then dirty page cache before the
direct write completes its post-I/O invalidation, causing the
invalidation to fail and record -EIO in the mapping error sequence.
The pre-write checks can also zero post-EOF page cache while an earlier
direct write is still active.
Wait for in-flight DIO before post-EOF page-cache operations and before
buffered writes. Return -EAGAIN for a NOWAIT write that would need the
blocking post-EOF operation.
Fixes: a1e09b03e6f5 ("f2fs: use iomap for direct I/O")
Cc: stable@vger.kernel.org
Signed-off-by: Jiale Yao <yaojiale02@163.com>
---
fs/f2fs/file.c | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index 0c759cc99054..d3d0ebf2f6c3 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -5328,6 +5328,7 @@ static ssize_t f2fs_write_checks(struct kiocb *iocb, struct iov_iter *from)
{
struct file *file = iocb->ki_filp;
struct inode *inode = file_inode(file);
+ loff_t end_pos;
ssize_t count;
int err;
@@ -5345,8 +5346,14 @@ static ssize_t f2fs_write_checks(struct kiocb *iocb, struct iov_iter *from)
if (err)
return err;
- err = f2fs_zero_post_eof_page(inode,
- iocb->ki_pos + iov_iter_count(from), true, true);
+ end_pos = iocb->ki_pos + iov_iter_count(from);
+ if (end_pos > i_size_read(inode)) {
+ if (iocb->ki_flags & IOCB_NOWAIT)
+ return -EAGAIN;
+ inode_dio_wait(inode);
+ }
+
+ err = f2fs_zero_post_eof_page(inode, end_pos, true, true);
if (err)
return err;
return count;
@@ -5436,6 +5443,8 @@ static ssize_t f2fs_buffered_write_iter(struct kiocb *iocb,
if (iocb->ki_flags & IOCB_NOWAIT)
return -EOPNOTSUPP;
+ inode_dio_wait(inode);
+
ret = generic_perform_write(iocb, from);
if (ret > 0) {
--
2.34.1
^ permalink raw reply [flat|nested] 9+ messages in thread* [PATCH 3/5] xfs: drain in-flight DIO before buffered writes
2026-10-25 12:41 [PATCH 0/5] fs: drain in-flight DIO before buffered writes Jiale Yao
2026-10-25 12:41 ` [PATCH 1/5] filemap: " Jiale Yao
2026-10-25 12:41 ` [PATCH 2/5] f2fs: " Jiale Yao
@ 2026-10-25 12:41 ` Jiale Yao
2026-10-26 0:52 ` Dave Chinner
2026-10-25 12:41 ` [PATCH 4/5] xfs: drain in-flight DIO before zoned " Jiale Yao
2026-10-25 12:41 ` [PATCH 5/5] zonefs: drain in-flight DIO before " Jiale Yao
4 siblings, 1 reply; 9+ messages in thread
From: Jiale Yao @ 2026-10-25 12:41 UTC (permalink / raw)
To: Carlos Maiolino, Darrick J. Wong, Dave Chinner, linux-xfs, linux-kernel
Cc: stable
XFS_IOLOCK_EXCL prevents new direct I/O submissions, but asynchronous
direct writes submitted while the lock was shared can remain active after
their submitting threads release the lock. A buffered write can then dirty
page cache before direct I/O completion performs post-write invalidation.
Wait for active DIO after acquiring XFS_IOLOCK_EXCL and before buffered
write checks can modify page cache. Return -EAGAIN instead of waiting for
an IOCB_NOWAIT request.
Fixes: acdda3aae146 ("xfs: use iomap_dio_rw")
Cc: stable@vger.kernel.org
Signed-off-by: Jiale Yao <yaojiale02@163.com>
---
fs/xfs/xfs_file.c | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c
index 7d0c8a197575..aa7cf51b5843 100644
--- a/fs/xfs/xfs_file.c
+++ b/fs/xfs/xfs_file.c
@@ -1018,6 +1018,20 @@ xfs_file_dax_write(
return ret;
}
+STATIC int
+xfs_file_buffered_write_dio_wait(
+ struct kiocb *iocb)
+{
+ struct inode *inode = file_inode(iocb->ki_filp);
+
+ if (!atomic_read(&inode->i_dio_count))
+ return 0;
+ if (iocb->ki_flags & IOCB_NOWAIT)
+ return -EAGAIN;
+ inode_dio_wait(inode);
+ return 0;
+}
+
STATIC ssize_t
xfs_file_buffered_write(
struct kiocb *iocb,
@@ -1035,6 +1049,10 @@ xfs_file_buffered_write(
if (ret)
return ret;
+ ret = xfs_file_buffered_write_dio_wait(iocb);
+ if (ret)
+ goto out;
+
ret = xfs_file_write_checks(iocb, from, &iolock, NULL);
if (ret)
goto out;
--
2.34.1
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [PATCH 3/5] xfs: drain in-flight DIO before buffered writes
2026-10-25 12:41 ` [PATCH 3/5] xfs: " Jiale Yao
@ 2026-10-26 0:52 ` Dave Chinner
0 siblings, 0 replies; 9+ messages in thread
From: Dave Chinner @ 2026-10-26 0:52 UTC (permalink / raw)
To: Jiale Yao
Cc: Carlos Maiolino, Darrick J. Wong, Dave Chinner, linux-xfs,
linux-kernel, stable
On Sun, Oct 25, 2026 at 08:41:12PM +0800, Jiale Yao wrote:
> XFS_IOLOCK_EXCL prevents new direct I/O submissions, but asynchronous
> direct writes submitted while the lock was shared can remain active after
> their submitting threads release the lock. A buffered write can then dirty
> page cache before direct I/O completion performs post-write invalidation.
>
> Wait for active DIO after acquiring XFS_IOLOCK_EXCL and before buffered
> write checks can modify page cache. Return -EAGAIN instead of waiting for
> an IOCB_NOWAIT request.
>
> Fixes: acdda3aae146 ("xfs: use iomap_dio_rw")
> Cc: stable@vger.kernel.org
> Signed-off-by: Jiale Yao <yaojiale02@163.com>
> ---
> fs/xfs/xfs_file.c | 18 ++++++++++++++++++
> 1 file changed, 18 insertions(+)
This doesn't fix the page cache dirtying race condition. Write to
the file with mmap() instead of write() and the same thing happens.
Because ->page-mkwrite() doesn't use the IOLOCK, it can't be
serialised against DIO submission or in flight DIO.
Regardless, overlapping buffered and direct writes to the same file
indicate an application bug that needs fixing because the result of
such IO patterns is not deterministic (even with this "fix").
Sometimes the DIO write will win, sometimes the buffered write will
win. Either way, one of the application writes gets lost and so we
still have data corruption from an application perspective...
IOWs, all this patch does is shut up the warning that tells us that
the application is doing stupid things that could result in data
corruption.
-Dave.
--
Dave Chinner
dgc@kernel.org
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 4/5] xfs: drain in-flight DIO before zoned buffered writes
2026-10-25 12:41 [PATCH 0/5] fs: drain in-flight DIO before buffered writes Jiale Yao
` (2 preceding siblings ...)
2026-10-25 12:41 ` [PATCH 3/5] xfs: " Jiale Yao
@ 2026-10-25 12:41 ` Jiale Yao
2026-10-25 12:41 ` [PATCH 5/5] zonefs: drain in-flight DIO before " Jiale Yao
4 siblings, 0 replies; 9+ messages in thread
From: Jiale Yao @ 2026-10-25 12:41 UTC (permalink / raw)
To: Carlos Maiolino, Darrick J. Wong, linux-xfs, linux-kernel; +Cc: stable
Buffered writes to zoned realtime files hold XFS_IOLOCK_EXCL, but direct
I/O submitted earlier while holding the lock shared can still be active.
The buffered write can dirty page cache before the asynchronous direct
write completes its post-I/O invalidation, causing an invalidation failure
and a mapping error.
Use the buffered-write DIO drain after acquiring XFS_IOLOCK_EXCL and
before the zoned write checks can touch page cache. Preserve NOWAIT
semantics by returning -EAGAIN when a wait would be needed.
Fixes: 2e2383405824 ("xfs: implement direct writes to zoned RT devices")
Cc: stable@vger.kernel.org
Signed-off-by: Jiale Yao <yaojiale02@163.com>
---
fs/xfs/xfs_file.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c
index aa7cf51b5843..96a11af8e71a 100644
--- a/fs/xfs/xfs_file.c
+++ b/fs/xfs/xfs_file.c
@@ -1121,6 +1121,10 @@ xfs_file_buffered_write_zoned(
if (ret)
goto out_unreserve;
+ ret = xfs_file_buffered_write_dio_wait(iocb);
+ if (ret)
+ goto out_unlock;
+
ret = xfs_file_write_checks(iocb, from, &iolock, &ac);
if (ret)
goto out_unlock;
--
2.34.1
^ permalink raw reply [flat|nested] 9+ messages in thread* [PATCH 5/5] zonefs: drain in-flight DIO before buffered writes
2026-10-25 12:41 [PATCH 0/5] fs: drain in-flight DIO before buffered writes Jiale Yao
` (3 preceding siblings ...)
2026-10-25 12:41 ` [PATCH 4/5] xfs: drain in-flight DIO before zoned " Jiale Yao
@ 2026-10-25 12:41 ` Jiale Yao
4 siblings, 0 replies; 9+ messages in thread
From: Jiale Yao @ 2026-10-25 12:41 UTC (permalink / raw)
To: Damien Le Moal, Naohiro Aota, Johannes Thumshirn, linux-fsdevel,
linux-kernel
Cc: stable
Conventional zone files allow both direct and buffered writes. An
asynchronous direct write can remain active after its submitting thread
releases the inode lock. A buffered write can then dirty page cache before
direct I/O completion performs post-write invalidation, causing the
invalidation to fail and record -EIO in the mapping error sequence.
After taking the exclusive inode lock, wait for active DIO before running
buffered write checks. Return -EAGAIN when an IOCB_NOWAIT request would
otherwise block.
Fixes: 4008e2a0b01a ("zonefs: Reorganize code")
Cc: stable@vger.kernel.org
Signed-off-by: Jiale Yao <yaojiale02@163.com>
---
fs/zonefs/file.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/fs/zonefs/file.c b/fs/zonefs/file.c
index 5b34849be7a2..8635a795c24c 100644
--- a/fs/zonefs/file.c
+++ b/fs/zonefs/file.c
@@ -576,6 +576,14 @@ static ssize_t zonefs_file_buffered_write(struct kiocb *iocb,
inode_lock(inode);
}
+ if (atomic_read(&inode->i_dio_count)) {
+ if (iocb->ki_flags & IOCB_NOWAIT) {
+ ret = -EAGAIN;
+ goto inode_unlock;
+ }
+ inode_dio_wait(inode);
+ }
+
ret = zonefs_write_checks(iocb, from);
if (ret <= 0)
goto inode_unlock;
--
2.34.1
^ permalink raw reply [flat|nested] 9+ messages in thread