mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH 0/5] fs: drain in-flight DIO before buffered writes
@ 2026-10-25 12:41 Jiale Yao
  2026-10-25 12:41 ` [PATCH 1/5] filemap: " Jiale Yao
                   ` (4 more replies)
  0 siblings, 5 replies; 9+ messages in thread
From: Jiale Yao @ 2026-10-25 12:41 UTC (permalink / raw)
  To: Jaegeuk Kim, Chao Yu, Carlos Maiolino, Damien Le Moal,
	Naohiro Aota, Johannes Thumshirn, Matthew Wilcox (Oracle),
	Jan Kara, Andrew Morton, Al Viro, Eric Biggers, Dave Chinner,
	Darrick J. Wong, linux-f2fs-devel, linux-kernel, linux-xfs,
	linux-fsdevel, linux-mm

An asynchronous direct write can remain in flight after its submitting
thread releases the filesystem's I/O submission lock. If a buffered write
dirties page cache before that direct write completes, post-I/O page-cache
invalidation can find dirty pages, report possible data corruption, and
record -EIO in the mapping error sequence.

Drain in-flight DIO before buffered writes in the generic filemap path,
F2FS, XFS, and zonefs. The filemap change also protects UDF and other
users of __generic_file_write_iter(). Keep IOCB_NOWAIT requests
nonblocking by returning -EAGAIN when a drain would be required.

The patches are separated by common code or filesystem and remain
independently buildable in series order.

Jiale Yao (5):
  filemap: drain in-flight DIO before buffered writes
  f2fs: drain in-flight DIO before buffered writes
  xfs: drain in-flight DIO before buffered writes
  xfs: drain in-flight DIO before zoned buffered writes
  zonefs: drain in-flight DIO before buffered writes

 fs/f2fs/file.c    | 13 +++++++++++--
 fs/xfs/xfs_file.c | 22 ++++++++++++++++++++++
 fs/zonefs/file.c  |  8 ++++++++
 mm/filemap.c      |  9 +++++++++
 4 files changed, 50 insertions(+), 2 deletions(-)

-- 
2.34.1


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [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

* [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

* [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

* 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

* 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

end of thread, other threads:[~2026-10-27 14:52 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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-26 13:31   ` Jan Kara
2026-10-27 14:52     ` Christoph Hellwig
2026-10-25 12:41 ` [PATCH 2/5] f2fs: " Jiale Yao
2026-10-25 12:41 ` [PATCH 3/5] xfs: " 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

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®