| Message ID | 20260926072612.3022680-2-yaojiale02@163.com |
|---|---|
| State | New |
| Headers | show |
| Series | fs: drain in-flight DIO before buffered write fallback | expand |
> An asynchronous direct write can remain in flight after the inode lock is > released. If another direct write falls back to buffered I/O while the > first write is still pending, generic_perform_write() can dirty pages > before the first write completes its post-I/O page cache invalidation. > The invalidation then finds dirty pages, reports a page cache invalidation > failure, and records -EIO in the mapping error sequence. A later fsync() > therefore returns -EIO. > > Commit 15cdefd0c0522f9d5e12d947fa04f4c11649b699 ("ext4: drain > in-flight DIO before buffered write fallback") fixed the same race in > ext4. Ext2 has an equivalent fallback after iomap_dio_rw() returns > -ENOTBLK or a short write, but does not drain other in-flight DIO before > dirtying the page cache. > > Wait for in-flight DIO before calling generic_perform_write() in the > fallback path. > > A reproducer using concurrent AIO direct writes and buffered fallback > triggered the following warning and made a subsequent fsync() return > -EIO: > > Page cache invalidation failure on direct I/O. Possible data corruption > due to collision with buffered I/O! > > Fixes: fb5de4358e1a ("ext2: Move direct-io to use iomap") > Link: https://lore.kernel.org/r/20260629113827.4074335-3-libaokun@linux.alibaba.com > Signed-off-by: Jiale Yao <yaojiale02@163.com> Sashiko has reviewed this patch and found no issues. It looks great!
On Sat 26-09-26 15:26:10, Jiale Yao wrote: > An asynchronous direct write can remain in flight after the inode lock is > released. If another direct write falls back to buffered I/O while the > first write is still pending, generic_perform_write() can dirty pages > before the first write completes its post-I/O page cache invalidation. > The invalidation then finds dirty pages, reports a page cache invalidation > failure, and records -EIO in the mapping error sequence. A later fsync() > therefore returns -EIO. > > Commit 15cdefd0c0522f9d5e12d947fa04f4c11649b699 ("ext4: drain > in-flight DIO before buffered write fallback") fixed the same race in > ext4. Ext2 has an equivalent fallback after iomap_dio_rw() returns > -ENOTBLK or a short write, but does not drain other in-flight DIO before > dirtying the page cache. > > Wait for in-flight DIO before calling generic_perform_write() in the > fallback path. > > A reproducer using concurrent AIO direct writes and buffered fallback > triggered the following warning and made a subsequent fsync() return > -EIO: > > Page cache invalidation failure on direct I/O. Possible data corruption > due to collision with buffered I/O! > > Fixes: fb5de4358e1a ("ext2: Move direct-io to use iomap") > Link: https://lore.kernel.org/r/20260629113827.4074335-3-libaokun@linux.alibaba.com > Signed-off-by: Jiale Yao <yaojiale02@163.com> This patch is already merged in my tree. You can drop it from the series. Honza > --- > fs/ext2/file.c | 7 +++++++ > 1 file changed, 7 insertions(+) > > diff --git a/fs/ext2/file.c b/fs/ext2/file.c > index b9020df7d89e..67fe423c3828 100644 > --- a/fs/ext2/file.c > +++ b/fs/ext2/file.c > @@ -135,6 +135,13 @@ static ssize_t ext2_dio_write_iter(struct kiocb *iocb, struct iov_iter *from) > int ret2; > > iocb->ki_flags &= ~IOCB_DIRECT; > + > + /* > + * Prevent concurrent direct I/O and buffered I/O to the same file > + * range. Wait for in-flight DIO to finish before dirtying pages. > + */ > + inode_dio_wait(inode); > + > pos = iocb->ki_pos; > status = generic_perform_write(iocb, from); > if (unlikely(status < 0)) { > -- > 2.34.1 >
diff --git a/fs/ext2/file.c b/fs/ext2/file.c index b9020df7d89e..67fe423c3828 100644 --- a/fs/ext2/file.c +++ b/fs/ext2/file.c @@ -135,6 +135,13 @@ static ssize_t ext2_dio_write_iter(struct kiocb *iocb, struct iov_iter *from) int ret2; iocb->ki_flags &= ~IOCB_DIRECT; + + /* + * Prevent concurrent direct I/O and buffered I/O to the same file + * range. Wait for in-flight DIO to finish before dirtying pages. + */ + inode_dio_wait(inode); + pos = iocb->ki_pos; status = generic_perform_write(iocb, from); if (unlikely(status < 0)) {
An asynchronous direct write can remain in flight after the inode lock is released. If another direct write falls back to buffered I/O while the first write is still pending, generic_perform_write() can dirty pages before the first write completes its post-I/O page cache invalidation. The invalidation then finds dirty pages, reports a page cache invalidation failure, and records -EIO in the mapping error sequence. A later fsync() therefore returns -EIO. Commit 15cdefd0c0522f9d5e12d947fa04f4c11649b699 ("ext4: drain in-flight DIO before buffered write fallback") fixed the same race in ext4. Ext2 has an equivalent fallback after iomap_dio_rw() returns -ENOTBLK or a short write, but does not drain other in-flight DIO before dirtying the page cache. Wait for in-flight DIO before calling generic_perform_write() in the fallback path. A reproducer using concurrent AIO direct writes and buffered fallback triggered the following warning and made a subsequent fsync() return -EIO: Page cache invalidation failure on direct I/O. Possible data corruption due to collision with buffered I/O! Fixes: fb5de4358e1a ("ext2: Move direct-io to use iomap") Link: https://lore.kernel.org/r/20260629113827.4074335-3-libaokun@linux.alibaba.com Signed-off-by: Jiale Yao <yaojiale02@163.com> --- fs/ext2/file.c | 7 +++++++ 1 file changed, 7 insertions(+)