[f2fs-dev] [PATCH 1/3] f2fs: fix extent merge failure when largest extent is not in rb-tree
Yongpeng Yang <[email protected]> Sat, 15 Aug 2026 00:49:26 +0800
| Newsgroups | net.sourceforge.lists.linux-f2fs-devel |
|---|---|
| Message-ID | <[email protected]> |
From: Yongpeng Yang <[email protected]> The following scenario can cause fiemap to report incorrect extents: $ mkfs.f2fs /dev/vdb -f $ mount -o mode=lfs /dev/vdb /mnt/f2fs/ $ dd if=/dev/urandom of=data bs=4K count=874 conv=notrunc $ f2fs_io fiemap 0 1000000 data 1 $ shrink all extent $ dd if=/dev/urandom of=data bs=4K count=150 seek=874 conv=notrunc $ f2fs_io fiemap 0 1000000 data 1 Fiemap: offset = 0 len = 1000000 logical addr. physical addr. length flags 0 0000000000000000 00000002868d4000 000000000036a000 00001000 1 0000000000000000 0000000286c3e000 0000000000096000 00001001 The root cause is that when the largest extent is not in the extent tree, mergeable extents are not merged, causing f2fs_map_blocks to misjudge and output an incorrect extent list. Fix this by allowing the extent being inserted to merge with the largest extent. When updating the extent tree range, if the new extent can be front-merged or back-merged with the largest extent and the largest extent is not in the rb-tree, merge them before the normal lookup. The merge logic only applies to the read extent cache, whose largest extent may be cached without a corresponding rb-tree node. Reuse the front/back mergeable check results instead of recomputing them. Fixes: 429511cdf8b3 ("f2fs: add core functions for rb-tree extent cache") Signed-off-by: Yongpeng Yang <[email protected]> --- v2: - Reuse the result of __is_back_mergeable(). --- fs/f2fs/extent_cache.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c index 37cf9fa8d537..72e6587f76ab 100644 --- a/fs/f2fs/extent_cache.c +++ b/fs/f2fs/extent_cache.c @@ -711,6 +711,30 @@ static void __update_extent_tree_range(struct inode *inode, __drop_largest_extent(et, fofs, len); } + if (type == EX_READ && et->largest.len != 0) { + bool front_merge = __is_front_mergeable(tei, &et->largest, type); + bool back_merge = __is_back_mergeable(tei, &et->largest, type); + + if (front_merge || back_merge) { + /* 0. try to merge with largest extent. */ + en = __lookup_extent_node_ret(&et->root, + et->cached_en, et->largest.fofs, + &prev_en, &next_en, + &insert_p, &insert_parent, + &leftmost); + if (!en) { + if (back_merge) { + tei->fofs = et->largest.fofs; + tei->blk = et->largest.blk; + fofs = tei->fofs; + } + tei->len += et->largest.len; + len = tei->len; + end = fofs + len; + } + } + } + /* 1. lookup first extent node in range [fofs, fofs + len - 1] */ en = __lookup_extent_node_ret(&et->root, et->cached_en, fofs, -- 2.43.0 _______________________________________________ Linux-f2fs-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel