[PATCH] f2fs: skip preallocation for mapped buffered overwrites

From: Wenjie Qi

Date: Mon Jun 29 2026 - 07:39:26 EST


For a full-block buffered overwrite, PRE_AIO does not reserve a block if
the target range is already mapped. It still walks the dnode path under
the node_change read lock.

Skip PRE_AIO when the write is within i_size and the read extent cache
covers the whole range with valid data blocks.

Assisted-by: Codex:gpt-5.5
Signed-off-by: Wenjie Qi <qiwenjie@xxxxxxxxxx>
---
QEMU test on a 1GiB F2FS scratch image:
- mapped 4K overwrite: PRE_AIO traces 20000 -> 0, about
10.2 -> 2.0 us/write.
- sparse in-i_size hole write miss case: PRE_AIO traces stayed
8192 -> 8192, median 7.71 -> 7.95 us/write.

fs/f2fs/file.c | 29 +++++++++++++++++++++++++++++
1 file changed, 29 insertions(+)

diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index 4b52c56d71f0..cb03df086ff3 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -5007,6 +5007,32 @@ static ssize_t f2fs_write_checks(struct kiocb *iocb, struct iov_iter *from)
return count;
}

+static bool f2fs_prealloc_mapped_overwrite(struct inode *inode,
+ loff_t pos, size_t count,
+ const struct f2fs_map_blocks *map)
+{
+ struct extent_info ei = {};
+ loff_t size = i_size_read(inode);
+ pgoff_t start = map->m_lblk;
+ pgoff_t end = start + map->m_len;
+ pgoff_t ei_end;
+
+ if (pos >= size || count > size - pos)
+ return false;
+ if (f2fs_has_inline_data(inode) || f2fs_compressed_file(inode) ||
+ f2fs_is_atomic_file(inode) || IS_DEVICE_ALIASING(inode))
+ return false;
+ if (end < start)
+ return false;
+ if (!f2fs_lookup_read_extent_cache(inode, start, &ei))
+ return false;
+ if (!__is_valid_data_blkaddr(ei.blk))
+ return false;
+
+ ei_end = (pgoff_t)ei.fofs + ei.len;
+ return start >= ei.fofs && end <= ei_end;
+}
+
/*
* Preallocate blocks for a write request, if it is possible and helpful to do
* so. Returns a positive number if blocks may have been preallocated, 0 if no
@@ -5061,6 +5087,9 @@ static int f2fs_preallocate_blocks(struct kiocb *iocb, struct iov_iter *iter,
else
return 0;

+ if (!dio && f2fs_prealloc_mapped_overwrite(inode, pos, count, &map))
+ return 0;
+
if (!IS_DEVICE_ALIASING(inode))
map.m_may_create = true;
if (dio) {