[PATCH 2/3] f2fs: fix in-place direct I/O overwrites in LFS mode
From: Jeuk Kim
Date: Fri Aug 28 2026 - 03:21:41 EST
Commit 351bc761338d ("f2fs: optimize f2fs DIO overwrites") stopped
setting m_may_create for direct I/O overwrites of allocated blocks. In
LFS mode, this causes the existing physical blocks to be reused instead
of allocating new ones.
The physical block address of logical block 0 after each overwrite was:
initial buffered O_DIRECT
mode=lfs pre-fix 6596096 88066 88066
post-fix 6596096 88066 6596352
mode=adaptive pre-fix 6596096 6596096 6596096
post-fix 6596096 6596096 6596096
Set m_may_create when a direct I/O write must use OPU. Since allocation
may sleep, reject NOWAIT writes with -EAGAIN. Keep pinned files on the
IPU path because their block addresses must not change.
Add f2fs_should_opu() to keep these decisions consistent.
Fixes: 351bc761338d ("f2fs: optimize f2fs DIO overwrites")
Signed-off-by: Jeuk Kim <jeuk20.kim@xxxxxxxxxxx>
---
fs/f2fs/data.c | 17 ++++++++++-------
fs/f2fs/f2fs.h | 9 +++++++++
fs/f2fs/file.c | 11 +++--------
3 files changed, 22 insertions(+), 15 deletions(-)
diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
index dad5c2bd5888..618e6fc2da0c 100644
--- a/fs/f2fs/data.c
+++ b/fs/f2fs/data.c
@@ -1644,7 +1644,7 @@ int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map, int flag)
if (!maxblocks)
return 0;
- lfs_dio_write = (flag == F2FS_GET_BLOCK_DIO && f2fs_lfs_mode(sbi) &&
+ lfs_dio_write = (flag == F2FS_GET_BLOCK_DIO && f2fs_should_opu(inode) &&
map->m_may_create);
if (!map->m_may_create && f2fs_map_blocks_cached(inode, map, flag)) {
@@ -1717,8 +1717,8 @@ int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map, int flag)
/* use out-place-update for direct IO under LFS mode */
if (map->m_may_create && (is_hole ||
- (flag == F2FS_GET_BLOCK_DIO && f2fs_lfs_mode(sbi) &&
- !f2fs_is_pinned_file(inode) && map->m_last_pblk != blkaddr))) {
+ (flag == F2FS_GET_BLOCK_DIO && f2fs_should_opu(inode) &&
+ map->m_last_pblk != blkaddr))) {
if (unlikely(f2fs_cp_error(sbi))) {
err = -EIO;
goto sync_out;
@@ -1810,7 +1810,7 @@ int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map, int flag)
ofs++;
map->m_len++;
} else {
- if (lfs_dio_write && !f2fs_is_pinned_file(inode))
+ if (lfs_dio_write)
map->m_last_pblk = blkaddr;
goto sync_out;
}
@@ -4513,11 +4513,14 @@ static int f2fs_iomap_begin(struct inode *inode, loff_t offset, loff_t length,
}
/*
- * If the blocks being overwritten are already allocated,
- * f2fs_map_lock and f2fs_balance_fs are not necessary.
+ * Out-place-update must not reuse the existing block, so ask for an
+ * allocation even when the range is already mapped. Otherwise, if the
+ * blocks being overwritten are already allocated, f2fs_map_lock and
+ * f2fs_balance_fs are not necessary.
*/
if ((flags & IOMAP_WRITE) &&
- !__f2fs_overwrite_io(inode, offset, length, true))
+ (f2fs_should_opu(inode) ||
+ !__f2fs_overwrite_io(inode, offset, length, true)))
map.m_may_create = true;
err = f2fs_map_blocks(inode, &map, F2FS_GET_BLOCK_DIO);
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 8376bbe58ee3..e85d4db3a401 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -4938,6 +4938,15 @@ static inline bool f2fs_lfs_mode(struct f2fs_sb_info *sbi)
return F2FS_OPTION(sbi).fs_mode == FS_MODE_LFS;
}
+static inline bool f2fs_should_opu(struct inode *inode)
+{
+ /* pinned files must keep their block addresses */
+ if (f2fs_is_pinned_file(inode))
+ return false;
+
+ return f2fs_lfs_mode(F2FS_I_SB(inode));
+}
+
static inline bool f2fs_is_sequential_zone_area(struct f2fs_sb_info *sbi,
block_t blkaddr)
{
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index 51518caee460..37d4e5c8dbcb 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -5022,7 +5022,7 @@ static int f2fs_preallocate_blocks(struct kiocb *iocb, struct iov_iter *iter,
int ret;
/* If it will be an out-of-place direct write, don't bother. */
- if (dio && f2fs_lfs_mode(sbi))
+ if (dio && f2fs_should_opu(inode))
return 0;
/*
* Don't preallocate holes aligned to DIO_SKIP_HOLES which turns into
@@ -5166,7 +5166,7 @@ static ssize_t f2fs_dio_write_iter(struct kiocb *iocb, struct iov_iter *from,
struct inode *inode = file_inode(file);
struct f2fs_inode_info *fi = F2FS_I(inode);
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
- const bool do_opu = f2fs_lfs_mode(sbi) && !f2fs_is_pinned_file(inode);
+ const bool do_opu = f2fs_should_opu(inode);
const loff_t pos = iocb->ki_pos;
const ssize_t count = iov_iter_count(from);
unsigned int dio_flags;
@@ -5182,7 +5182,7 @@ static ssize_t f2fs_dio_write_iter(struct kiocb *iocb, struct iov_iter *from,
if (iocb->ki_flags & IOCB_NOWAIT) {
/* f2fs_convert_inline_inode() and block allocation can block */
- if (f2fs_has_inline_data(inode) ||
+ if (do_opu || f2fs_has_inline_data(inode) ||
!f2fs_overwrite_io(inode, pos, count)) {
ret = -EAGAIN;
goto out;
@@ -5192,11 +5192,6 @@ static ssize_t f2fs_dio_write_iter(struct kiocb *iocb, struct iov_iter *from,
ret = -EAGAIN;
goto out;
}
- if (do_opu && !f2fs_down_read_trylock(&fi->i_gc_rwsem[READ])) {
- f2fs_up_read(&fi->i_gc_rwsem[WRITE]);
- ret = -EAGAIN;
- goto out;
- }
} else {
ret = f2fs_convert_inline_inode(inode);
if (ret)
--
2.43.0