* [f2fs-dev] [PATCH RESEND 1/5] f2fs: fix extent merge failure when largest extent is not in rb-tree
@ 2026-06-12 11:58 Yongpeng Yang
2026-06-12 11:58 ` [f2fs-dev] [PATCH RESEND 2/5] f2fs: only initialize largest extent without extent_node at inode init Yongpeng Yang
` (4 more replies)
0 siblings, 5 replies; 11+ messages in thread
From: Yongpeng Yang @ 2026-06-12 11:58 UTC (permalink / raw)
To: Chao Yu, Jaegeuk Kim; +Cc: Yongpeng Yang, Yongpeng Yang, linux-f2fs-devel
From: Yongpeng Yang <yangyongpeng@xiaomi.com>
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 000000000036a000 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.
Fixes: 429511cdf8b3 ("f2fs: add core functions for rb-tree extent cache")
Signed-off-by: Yongpeng Yang <yangyongpeng@xiaomi.com>
---
fs/f2fs/extent_cache.c | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c
index 61f6b9714366..aa368a01b035 100644
--- a/fs/f2fs/extent_cache.c
+++ b/fs/f2fs/extent_cache.c
@@ -702,6 +702,27 @@ static void __update_extent_tree_range(struct inode *inode,
__drop_largest_extent(et, fofs, len);
}
+ if (et->largest.len != 0 &&
+ (__is_front_mergeable(tei, &et->largest, type) ||
+ __is_back_mergeable(tei, &et->largest, type))) {
+ /* 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 (__is_back_mergeable(tei, &et->largest, type)) {
+ 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
Linux-f2fs-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [f2fs-dev] [PATCH RESEND 2/5] f2fs: only initialize largest extent without extent_node at inode init
2026-06-12 11:58 [f2fs-dev] [PATCH RESEND 1/5] f2fs: fix extent merge failure when largest extent is not in rb-tree Yongpeng Yang
@ 2026-06-12 11:58 ` Yongpeng Yang
2026-06-15 11:55 ` Chao Yu via Linux-f2fs-devel
2026-06-12 11:58 ` [f2fs-dev] [PATCH RESEND 3/5] f2fs: punch largest extent instead of dropping it entirely on overlap Yongpeng Yang
` (3 subsequent siblings)
4 siblings, 1 reply; 11+ messages in thread
From: Yongpeng Yang @ 2026-06-12 11:58 UTC (permalink / raw)
To: Chao Yu, Jaegeuk Kim; +Cc: Yongpeng Yang, Yongpeng Yang, linux-f2fs-devel
From: Yongpeng Yang <yangyongpeng@xiaomi.com>
The largest extent takes effect during both read mapping and write
mapping lookups, while read mapping does not need to access the
extent_node. For write mapping, the case where the largest extent is
not in the extent tree can already be handled by the merge logic, and
cases that cannot be merged do not require the largest extent to
participate either.
Therefore, the largest extent does not need to initialize a
corresponding extent_node, reducing memory footprint.
Signed-off-by: Yongpeng Yang <yangyongpeng@xiaomi.com>
---
fs/f2fs/extent_cache.c | 18 +-----------------
1 file changed, 1 insertion(+), 17 deletions(-)
diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c
index aa368a01b035..f8d94db60dc6 100644
--- a/fs/f2fs/extent_cache.c
+++ b/fs/f2fs/extent_cache.c
@@ -410,10 +410,8 @@ static void __drop_largest_extent(struct extent_tree *et,
void f2fs_init_read_extent_tree(struct inode *inode, struct folio *ifolio)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
- struct extent_tree_info *eti = &sbi->extent_tree[EX_READ];
struct f2fs_extent *i_ext = &F2FS_INODE(ifolio)->i_ext;
struct extent_tree *et;
- struct extent_node *en;
struct extent_info ei = {0};
if (!__may_extent_tree(inode, EX_READ)) {
@@ -435,21 +433,7 @@ void f2fs_init_read_extent_tree(struct inode *inode, struct folio *ifolio)
if (atomic_read(&et->node_cnt) || !ei.len)
goto skip;
- if (IS_DEVICE_ALIASING(inode)) {
- et->largest = ei;
- goto skip;
- }
-
- en = __attach_extent_node(sbi, et, &ei, NULL,
- &et->root.rb_root.rb_node, true);
- if (en) {
- et->largest = en->ei;
- et->cached_en = en;
-
- spin_lock(&eti->extent_lock);
- list_add_tail(&en->list, &eti->extent_list);
- spin_unlock(&eti->extent_lock);
- }
+ et->largest = ei;
skip:
/* Let's drop, if checkpoint got corrupted. */
if (f2fs_cp_error(sbi)) {
--
2.43.0
_______________________________________________
Linux-f2fs-devel mailing list
Linux-f2fs-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [f2fs-dev] [PATCH RESEND 3/5] f2fs: punch largest extent instead of dropping it entirely on overlap
2026-06-12 11:58 [f2fs-dev] [PATCH RESEND 1/5] f2fs: fix extent merge failure when largest extent is not in rb-tree Yongpeng Yang
2026-06-12 11:58 ` [f2fs-dev] [PATCH RESEND 2/5] f2fs: only initialize largest extent without extent_node at inode init Yongpeng Yang
@ 2026-06-12 11:58 ` Yongpeng Yang
2026-06-15 12:05 ` Chao Yu via Linux-f2fs-devel
2026-06-12 11:58 ` [f2fs-dev] [PATCH RESEND 4/5] f2fs: add extent_access_mode to track extent cache access patterns Yongpeng Yang
` (2 subsequent siblings)
4 siblings, 1 reply; 11+ messages in thread
From: Yongpeng Yang @ 2026-06-12 11:58 UTC (permalink / raw)
To: Chao Yu, Jaegeuk Kim; +Cc: Yongpeng Yang, Yongpeng Yang, linux-f2fs-devel
From: Yongpeng Yang <yangyongpeng@xiaomi.com>
Previously, when an extent being inserted overlaps with the largest
extent, the largest extent is dropped entirely. This was done to handle
the case where the largest extent is not in memory, avoiding
inconsistency between the largest extent and the extent tree.
This patch changes the semantics of __drop_largest_extent (renamed to
__punch_largest_extent): instead of discarding the entire largest
extent when any overlap is detected, keep the larger remaining portion
(left or right) after the punch. This preserves extent cache coverage
for truncate and overwrite operations that only partially overlap the
largest extent.
Signed-off-by: Yongpeng Yang <yangyongpeng@xiaomi.com>
---
fs/f2fs/extent_cache.c | 31 ++++++++++++++++++++++++-------
1 file changed, 24 insertions(+), 7 deletions(-)
diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c
index f8d94db60dc6..82d84c4e98b2 100644
--- a/fs/f2fs/extent_cache.c
+++ b/fs/f2fs/extent_cache.c
@@ -397,14 +397,31 @@ static unsigned int __free_extent_tree(struct f2fs_sb_info *sbi,
return count;
}
-static void __drop_largest_extent(struct extent_tree *et,
+static void __punch_largest_extent(struct extent_tree *et,
pgoff_t fofs, unsigned int len)
{
- if (fofs < (pgoff_t)et->largest.fofs + et->largest.len &&
- fofs + len > et->largest.fofs) {
- et->largest.len = 0;
- et->largest_updated = true;
+ unsigned int largest_end, punch_end;
+ unsigned int left_len, right_len;
+
+ if (fofs >= (pgoff_t)et->largest.fofs + et->largest.len ||
+ fofs + len <= et->largest.fofs)
+ return;
+
+ /* Punch [fofs, fofs + len) from largest extent. */
+ largest_end = et->largest.fofs + et->largest.len;
+ punch_end = fofs + len;
+
+ left_len = fofs > et->largest.fofs ? fofs - et->largest.fofs : 0;
+ right_len = largest_end > punch_end ? largest_end - punch_end : 0;
+
+ if (left_len >= right_len) {
+ et->largest.len = left_len;
+ } else {
+ et->largest.blk += punch_end - et->largest.fofs;
+ et->largest.fofs = punch_end;
+ et->largest.len = right_len;
}
+ et->largest_updated = true;
}
void f2fs_init_read_extent_tree(struct inode *inode, struct folio *ifolio)
@@ -680,10 +697,10 @@ static void __update_extent_tree_range(struct inode *inode,
dei.len = 0;
/*
- * drop largest extent before lookup, in case it's already
+ * punch largest extent before lookup, in case it's already
* been shrunk from extent tree
*/
- __drop_largest_extent(et, fofs, len);
+ __punch_largest_extent(et, fofs, len);
}
if (et->largest.len != 0 &&
--
2.43.0
_______________________________________________
Linux-f2fs-devel mailing list
Linux-f2fs-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [f2fs-dev] [PATCH RESEND 4/5] f2fs: add extent_access_mode to track extent cache access patterns
2026-06-12 11:58 [f2fs-dev] [PATCH RESEND 1/5] f2fs: fix extent merge failure when largest extent is not in rb-tree Yongpeng Yang
2026-06-12 11:58 ` [f2fs-dev] [PATCH RESEND 2/5] f2fs: only initialize largest extent without extent_node at inode init Yongpeng Yang
2026-06-12 11:58 ` [f2fs-dev] [PATCH RESEND 3/5] f2fs: punch largest extent instead of dropping it entirely on overlap Yongpeng Yang
@ 2026-06-12 11:58 ` Yongpeng Yang
2026-06-15 12:05 ` Chao Yu via Linux-f2fs-devel
2026-06-12 11:58 ` [f2fs-dev] [PATCH RESEND 5/5] f2fs: add ioctl to export read extent cache to userspace for debug Yongpeng Yang
2026-06-15 11:26 ` [f2fs-dev] [PATCH RESEND 1/5] f2fs: fix extent merge failure when largest extent is not in rb-tree Chao Yu via Linux-f2fs-devel
4 siblings, 1 reply; 11+ messages in thread
From: Yongpeng Yang @ 2026-06-12 11:58 UTC (permalink / raw)
To: Chao Yu, Jaegeuk Kim; +Cc: Yongpeng Yang, Yongpeng Yang, linux-f2fs-devel
From: Yongpeng Yang <yangyongpeng@xiaomi.com>
Introduce enum extent_access_mode to classify how each extent node
is accessed or created (READ, WRITE, PRECACHE, TRUNCATE, LARGEST).
This metadata optimize LRU eviction decisions:
1. Extents only accessed as the largest extent (never read-hit) are
deprioritized in the LRU list since reads can still use the largest
extent directly.
2. Sparse single-block write extents that were never merged are moved
to the head of LRU for earlier reclaim, preserving extents with
better continuity and higher read-hit probability.
Signed-off-by: Yongpeng Yang <yangyongpeng@xiaomi.com>
---
fs/f2fs/data.c | 4 ++--
fs/f2fs/extent_cache.c | 29 ++++++++++++++++++++++++++++-
fs/f2fs/f2fs.h | 14 +++++++++++++-
fs/f2fs/file.c | 6 ++++--
4 files changed, 47 insertions(+), 6 deletions(-)
diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
index 9c6440a7db0e..2d38135005fe 100644
--- a/fs/f2fs/data.c
+++ b/fs/f2fs/data.c
@@ -1873,7 +1873,7 @@ int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map, int flag)
f2fs_update_read_extent_cache_range(&dn,
start_pgofs, map->m_pblk + ofs,
- map->m_len - ofs);
+ map->m_len - ofs, EX_ACCESS_PRECACHE);
}
}
@@ -1919,7 +1919,7 @@ int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map, int flag)
if (map->m_len > ofs)
f2fs_update_read_extent_cache_range(&dn,
start_pgofs, map->m_pblk + ofs,
- map->m_len - ofs);
+ map->m_len - ofs, EX_ACCESS_PRECACHE);
}
if (map->m_next_extent)
*map->m_next_extent = is_hole ? pgofs + 1 : pgofs;
diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c
index 82d84c4e98b2..e141ffb64e5f 100644
--- a/fs/f2fs/extent_cache.c
+++ b/fs/f2fs/extent_cache.c
@@ -142,6 +142,7 @@ static void __try_update_largest_extent(struct extent_tree *et,
if (en->ei.len <= et->largest.len)
return;
+ en->ei.last_access_mode = EX_ACCESS_LARGEST;
et->largest = en->ei;
et->largest_updated = true;
}
@@ -518,6 +519,7 @@ static bool __lookup_extent_tree(struct inode *inode, pgoff_t pgofs,
stat_inc_rbtree_node_hit(sbi, type);
*ei = en->ei;
+ en->ei.last_access_mode = EX_ACCESS_READ;
spin_lock(&eti->extent_lock);
if (!list_empty(&en->list)) {
list_move_tail(&en->list, &eti->extent_list);
@@ -624,6 +626,21 @@ static struct extent_node *__insert_extent_tree(struct f2fs_sb_info *sbi,
/* update in global extent list */
spin_lock(&eti->extent_lock);
+ /*
+ * 1. For the largest extent, if subsequent writes are not merged into
+ * it, the write path will most likely not use the largest extent_node,
+ * while read requests can still access the mapping through the largest
+ * extent.
+ *
+ * 2. For sparse writes, if the extent length is 1 and no extent merging
+ * occurs, this extent should be reclaimed with higher priority to avoid
+ * evicting extents with better continuity and higher read-hit.
+ */
+ if (et->type == EX_READ && et->cached_en &&
+ (et->cached_en->ei.last_access_mode == EX_ACCESS_LARGEST ||
+ (et->cached_en->ei.len == 1 &&
+ et->cached_en->ei.last_access_mode == EX_ACCESS_WRITE)))
+ list_move(&et->cached_en->list, &eti->extent_list);
list_add_tail(&en->list, &eti->extent_list);
et->cached_en = en;
spin_unlock(&eti->extent_lock);
@@ -747,6 +764,8 @@ static void __update_extent_tree_range(struct inode *inode,
if (fofs > dei.fofs && (type != EX_READ ||
fofs - dei.fofs >= F2FS_MIN_EXTENT_LEN)) {
en->ei.len = fofs - en->ei.fofs;
+ if (type == EX_READ)
+ en->ei.last_access_mode = EX_ACCESS_TRUNCATE;
prev_en = en;
parts = 1;
}
@@ -761,6 +780,8 @@ static void __update_extent_tree_range(struct inode *inode,
end - dei.fofs + dei.blk, false,
dei.age, dei.last_blocks,
type);
+ if (type == EX_READ)
+ ei.last_access_mode = EX_ACCESS_TRUNCATE;
en1 = __insert_extent_tree(sbi, et, &ei,
NULL, NULL, true);
next_en = en1;
@@ -770,6 +791,8 @@ static void __update_extent_tree_range(struct inode *inode,
en->ei.blk + (end - dei.fofs), true,
dei.age, dei.last_blocks,
type);
+ if (type == EX_READ)
+ en->ei.last_access_mode = EX_ACCESS_TRUNCATE;
next_en = en;
}
parts++;
@@ -808,6 +831,7 @@ static void __update_extent_tree_range(struct inode *inode,
if (tei->blk) {
__set_extent_info(&ei, fofs, len, tei->blk, false,
0, 0, EX_READ);
+ ei.last_access_mode = tei->last_access_mode;
if (!__try_merge_extent_node(sbi, et, &ei, prev_en, next_en))
__insert_extent_tree(sbi, et, &ei,
insert_p, insert_parent, leftmost);
@@ -978,6 +1002,7 @@ static void __update_extent_cache(struct dnode_of_data *dn, enum extent_type typ
ei.blk = NULL_ADDR;
else
ei.blk = dn->data_blkaddr;
+ ei.last_access_mode = EX_ACCESS_WRITE;
} else if (type == EX_BLOCK_AGE) {
if (__get_new_block_age(dn->inode, &ei, dn->data_blkaddr))
return;
@@ -1091,12 +1116,14 @@ void f2fs_update_read_extent_cache(struct dnode_of_data *dn)
}
void f2fs_update_read_extent_cache_range(struct dnode_of_data *dn,
- pgoff_t fofs, block_t blkaddr, unsigned int len)
+ pgoff_t fofs, block_t blkaddr, unsigned int len,
+ enum extent_access_mode access_mode)
{
struct extent_info ei = {
.fofs = fofs,
.len = len,
.blk = blkaddr,
+ .last_access_mode = access_mode,
};
if (!__may_extent_tree(dn->inode, EX_READ))
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index fffb516b78f4..1588b64d04a3 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -763,6 +763,15 @@ enum extent_type {
NR_EXTENT_CACHES,
};
+/* extent acces mode for cache hit or extent add */
+enum extent_access_mode {
+ EX_ACCESS_READ,
+ EX_ACCESS_WRITE,
+ EX_ACCESS_PRECACHE,
+ EX_ACCESS_TRUNCATE,
+ EX_ACCESS_LARGEST,
+};
+
/*
* Reserved value to mark invalid age extents, hence valid block range
* from 0 to ULLONG_MAX-1
@@ -781,6 +790,8 @@ struct extent_info {
/* physical extent length of compressed blocks */
unsigned int c_len;
#endif
+ /* record last access mode */
+ enum extent_access_mode last_access_mode;
};
/* block age extent_cache */
struct {
@@ -4577,7 +4588,8 @@ bool f2fs_lookup_read_extent_cache_block(struct inode *inode, pgoff_t index,
block_t *blkaddr);
void f2fs_update_read_extent_cache(struct dnode_of_data *dn);
void f2fs_update_read_extent_cache_range(struct dnode_of_data *dn,
- pgoff_t fofs, block_t blkaddr, unsigned int len);
+ pgoff_t fofs, block_t blkaddr, unsigned int len,
+ enum extent_access_mode access_mode);
unsigned int f2fs_shrink_read_extent_tree(struct f2fs_sb_info *sbi,
int nr_shrink);
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index 633e9ade654f..a3a5d499eadf 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -733,7 +733,8 @@ void f2fs_truncate_data_blocks_range(struct dnode_of_data *dn, int count)
*/
fofs = f2fs_start_bidx_of_node(ofs_of_node(dn->node_folio),
dn->inode) + ofs;
- f2fs_update_read_extent_cache_range(dn, fofs, 0, len);
+ f2fs_update_read_extent_cache_range(dn, fofs, 0, len,
+ EX_ACCESS_TRUNCATE);
f2fs_update_age_extent_cache_range(dn, fofs, len);
dec_valid_block_count(sbi, dn->inode, nr_free);
}
@@ -1672,7 +1673,8 @@ static int f2fs_do_zero_range(struct dnode_of_data *dn, pgoff_t start,
if (index > start) {
f2fs_update_read_extent_cache_range(dn, start, 0,
- index - start);
+ index - start,
+ EX_ACCESS_TRUNCATE);
f2fs_update_age_extent_cache_range(dn, start, index - start);
}
--
2.43.0
_______________________________________________
Linux-f2fs-devel mailing list
Linux-f2fs-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [f2fs-dev] [PATCH RESEND 5/5] f2fs: add ioctl to export read extent cache to userspace for debug
2026-06-12 11:58 [f2fs-dev] [PATCH RESEND 1/5] f2fs: fix extent merge failure when largest extent is not in rb-tree Yongpeng Yang
` (2 preceding siblings ...)
2026-06-12 11:58 ` [f2fs-dev] [PATCH RESEND 4/5] f2fs: add extent_access_mode to track extent cache access patterns Yongpeng Yang
@ 2026-06-12 11:58 ` Yongpeng Yang
2026-06-15 12:20 ` Chao Yu via Linux-f2fs-devel
2026-06-15 11:26 ` [f2fs-dev] [PATCH RESEND 1/5] f2fs: fix extent merge failure when largest extent is not in rb-tree Chao Yu via Linux-f2fs-devel
4 siblings, 1 reply; 11+ messages in thread
From: Yongpeng Yang @ 2026-06-12 11:58 UTC (permalink / raw)
To: Chao Yu, Jaegeuk Kim; +Cc: Yongpeng Yang, Yongpeng Yang, linux-f2fs-devel
From: Yongpeng Yang <yangyongpeng@xiaomi.com>
Add F2FS_IOC_GET_READ_CACHE_EXTENTS ioctl that allows userspace to
retrieve all cached read extents for a given file. This uses a two-call
pattern similar to fiemap: the first call with ext_count=0 queries the
node_count, and the second call fetches the actual extent entries.
Signed-off-by: Yongpeng Yang <yangyongpeng@xiaomi.com>
---
fs/f2fs/extent_cache.c | 70 +++++++++++++++++++++++++++++++++++++++
fs/f2fs/f2fs.h | 3 ++
fs/f2fs/file.c | 11 ++++++
include/uapi/linux/f2fs.h | 21 ++++++++++++
4 files changed, 105 insertions(+)
diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c
index e141ffb64e5f..0c10d5639d68 100644
--- a/fs/f2fs/extent_cache.c
+++ b/fs/f2fs/extent_cache.c
@@ -14,6 +14,7 @@
#include <linux/fs.h>
#include <linux/f2fs_fs.h>
+#include <uapi/linux/f2fs.h>
#include "f2fs.h"
#include "node.h"
@@ -1267,6 +1268,75 @@ static void __init_extent_tree_info(struct extent_tree_info *eti)
atomic_set(&eti->total_ext_node, 0);
}
+int f2fs_get_read_cache_extents(struct inode *inode,
+ struct f2fs_read_cache_extent __user *uarg)
+{
+ struct extent_tree *et = F2FS_I(inode)->extent_tree[EX_READ];
+ struct f2fs_cache_extent_info *kbuf = NULL;
+ struct f2fs_cache_extent_info largest = {};
+ struct rb_node *node;
+ struct extent_node *en;
+ unsigned int capacity, count = 0;
+ __u32 flags = 0;
+ int ret = 0;
+
+ if (get_user(capacity, &uarg->ext_count))
+ return -EFAULT;
+
+ if (is_inode_flag_set(inode, FI_NO_EXTENT))
+ flags |= F2FS_EXT_FL_NO_EXTENT;
+
+ if (!et || (flags & F2FS_EXT_FL_NO_EXTENT)) {
+ if (put_user(0U, &uarg->ext_count) ||
+ put_user(flags, &uarg->flags) ||
+ put_user(0U, &uarg->node_count))
+ return -EFAULT;
+ return 0;
+ }
+
+ if (capacity) {
+ kbuf = f2fs_kvmalloc(F2FS_I_SB(inode), capacity * sizeof(*kbuf), GFP_KERNEL);
+ if (!kbuf)
+ return -ENOMEM;
+ }
+
+ read_lock(&et->lock);
+
+ largest.fofs = et->largest.fofs;
+ largest.blk = et->largest.blk;
+ largest.len = et->largest.len;
+ largest.last_access_mode = et->largest.last_access_mode;
+
+ for (node = rb_first_cached(&et->root); node; node = rb_next(node)) {
+ if (count >= capacity)
+ break;
+ en = rb_entry(node, struct extent_node, rb_node);
+
+ kbuf[count].fofs = en->ei.fofs;
+ kbuf[count].blk = en->ei.blk;
+ kbuf[count].len = en->ei.len;
+ kbuf[count].last_access_mode = en->ei.last_access_mode;
+ count++;
+ }
+
+ read_unlock(&et->lock);
+
+ if (count && copy_to_user(uarg->extents, kbuf,
+ count * sizeof(*kbuf))) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+ if (put_user(count, &uarg->ext_count) ||
+ put_user(flags, &uarg->flags) ||
+ put_user((u32)atomic_read(&et->node_cnt), &uarg->node_count) ||
+ copy_to_user(&uarg->largest, &largest, sizeof(largest)))
+ ret = -EFAULT;
+out:
+ kvfree(kbuf);
+ return ret;
+}
+
void f2fs_init_extent_cache_info(struct f2fs_sb_info *sbi)
{
__init_extent_tree_info(&sbi->extent_tree[EX_READ]);
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 1588b64d04a3..69641fc31c51 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -26,6 +26,7 @@
#include <linux/part_stat.h>
#include <linux/rw_hint.h>
+#include <uapi/linux/f2fs.h>
#include <linux/fscrypt.h>
#include <linux/fsverity.h>
@@ -4590,6 +4591,8 @@ void f2fs_update_read_extent_cache(struct dnode_of_data *dn);
void f2fs_update_read_extent_cache_range(struct dnode_of_data *dn,
pgoff_t fofs, block_t blkaddr, unsigned int len,
enum extent_access_mode access_mode);
+int f2fs_get_read_cache_extents(struct inode *inode,
+ struct f2fs_read_cache_extent __user *uarg);
unsigned int f2fs_shrink_read_extent_tree(struct f2fs_sb_info *sbi,
int nr_shrink);
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index a3a5d499eadf..66ec9927d667 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -3672,6 +3672,14 @@ static int f2fs_ioc_precache_extents(struct file *filp)
return f2fs_precache_extents(file_inode(filp));
}
+static int f2fs_ioc_get_read_cache_extents(struct file *filp, unsigned long arg)
+{
+ struct inode *inode = file_inode(filp);
+
+ return f2fs_get_read_cache_extents(inode,
+ (struct f2fs_read_cache_extent __user *)arg);
+}
+
static int f2fs_ioc_resize_fs(struct file *filp, unsigned long arg)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(file_inode(filp));
@@ -4744,6 +4752,8 @@ static long __f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
return f2fs_ioc_get_dev_alias_file(filp, arg);
case F2FS_IOC_IO_PRIO:
return f2fs_ioc_io_prio(filp, arg);
+ case F2FS_IOC_GET_READ_CACHE_EXTENTS:
+ return f2fs_ioc_get_read_cache_extents(filp, arg);
default:
return -ENOTTY;
}
@@ -5506,6 +5516,7 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
case F2FS_IOC_COMPRESS_FILE:
case F2FS_IOC_GET_DEV_ALIAS_FILE:
case F2FS_IOC_IO_PRIO:
+ case F2FS_IOC_GET_READ_CACHE_EXTENTS:
break;
default:
return -ENOIOCTLCMD;
diff --git a/include/uapi/linux/f2fs.h b/include/uapi/linux/f2fs.h
index 795e26258355..6ff9003bc030 100644
--- a/include/uapi/linux/f2fs.h
+++ b/include/uapi/linux/f2fs.h
@@ -45,6 +45,8 @@
#define F2FS_IOC_START_ATOMIC_REPLACE _IO(F2FS_IOCTL_MAGIC, 25)
#define F2FS_IOC_GET_DEV_ALIAS_FILE _IOR(F2FS_IOCTL_MAGIC, 26, __u32)
#define F2FS_IOC_IO_PRIO _IOW(F2FS_IOCTL_MAGIC, 27, __u32)
+#define F2FS_IOC_GET_READ_CACHE_EXTENTS _IOWR(F2FS_IOCTL_MAGIC, 28, \
+ struct f2fs_read_cache_extent)
/*
* should be same as XFS_IOC_GOINGDOWN.
@@ -104,4 +106,23 @@ struct f2fs_comp_option {
__u8 log_cluster_size;
};
+struct f2fs_cache_extent_info {
+ __u32 fofs; /* start file offset in blocks */
+ __u32 blk; /* start block address */
+ __u32 len; /* length in blocks */
+ __u32 last_access_mode; /* last access mode of extent_node */
+};
+
+/* flags for f2fs_read_cache_extent */
+#define F2FS_EXT_FL_NO_EXTENT 0x1 /* extent cache disabled for this inode */
+
+struct f2fs_read_cache_extent {
+ __u32 ext_count; /* in: array capacity; out: mapped extent count */
+ __u32 flags; /* out: status flags */
+ __u32 node_count; /* out: total extent nodes in tree */
+ __u32 reserved;
+ struct f2fs_cache_extent_info largest; /* out: largest extent */
+ struct f2fs_cache_extent_info extents[]; /* out: extent array */
+};
+
#endif /* _UAPI_LINUX_F2FS_H */
--
2.43.0
_______________________________________________
Linux-f2fs-devel mailing list
Linux-f2fs-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [f2fs-dev] [PATCH RESEND 1/5] f2fs: fix extent merge failure when largest extent is not in rb-tree
2026-06-12 11:58 [f2fs-dev] [PATCH RESEND 1/5] f2fs: fix extent merge failure when largest extent is not in rb-tree Yongpeng Yang
` (3 preceding siblings ...)
2026-06-12 11:58 ` [f2fs-dev] [PATCH RESEND 5/5] f2fs: add ioctl to export read extent cache to userspace for debug Yongpeng Yang
@ 2026-06-15 11:26 ` Chao Yu via Linux-f2fs-devel
4 siblings, 0 replies; 11+ messages in thread
From: Chao Yu via Linux-f2fs-devel @ 2026-06-15 11:26 UTC (permalink / raw)
To: Yongpeng Yang, Jaegeuk Kim; +Cc: Yongpeng Yang, Yongpeng Yang, linux-f2fs-devel
On 6/12/26 19:58, Yongpeng Yang wrote:
> From: Yongpeng Yang <yangyongpeng@xiaomi.com>
>
> 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 000000000036a000 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.
>
> Fixes: 429511cdf8b3 ("f2fs: add core functions for rb-tree extent cache")
> Signed-off-by: Yongpeng Yang <yangyongpeng@xiaomi.com>
> ---
> fs/f2fs/extent_cache.c | 21 +++++++++++++++++++++
> 1 file changed, 21 insertions(+)
>
> diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c
> index 61f6b9714366..aa368a01b035 100644
> --- a/fs/f2fs/extent_cache.c
> +++ b/fs/f2fs/extent_cache.c
> @@ -702,6 +702,27 @@ static void __update_extent_tree_range(struct inode *inode,
> __drop_largest_extent(et, fofs, len);
> }
>
This can only be used for EX_READ?
> + if (et->largest.len != 0 &&
> + (__is_front_mergeable(tei, &et->largest, type) ||
> + __is_back_mergeable(tei, &et->largest, type))) {
> + /* 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 (__is_back_mergeable(tei, &et->largest, type)) {
Can we reuse result of previous __is_back_mergeable()?
Thanks,
> + 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,
_______________________________________________
Linux-f2fs-devel mailing list
Linux-f2fs-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [f2fs-dev] [PATCH RESEND 2/5] f2fs: only initialize largest extent without extent_node at inode init
2026-06-12 11:58 ` [f2fs-dev] [PATCH RESEND 2/5] f2fs: only initialize largest extent without extent_node at inode init Yongpeng Yang
@ 2026-06-15 11:55 ` Chao Yu via Linux-f2fs-devel
0 siblings, 0 replies; 11+ messages in thread
From: Chao Yu via Linux-f2fs-devel @ 2026-06-15 11:55 UTC (permalink / raw)
To: Yongpeng Yang, Jaegeuk Kim; +Cc: Yongpeng Yang, Yongpeng Yang, linux-f2fs-devel
On 6/12/26 19:58, Yongpeng Yang wrote:
> From: Yongpeng Yang <yangyongpeng@xiaomi.com>
>
> The largest extent takes effect during both read mapping and write
> mapping lookups, while read mapping does not need to access the
> extent_node. For write mapping, the case where the largest extent is
> not in the extent tree can already be handled by the merge logic, and
> cases that cannot be merged do not require the largest extent to
> participate either.
>
> Therefore, the largest extent does not need to initialize a
> corresponding extent_node, reducing memory footprint.
>
> Signed-off-by: Yongpeng Yang <yangyongpeng@xiaomi.com>
> ---
> fs/f2fs/extent_cache.c | 18 +-----------------
> 1 file changed, 1 insertion(+), 17 deletions(-)
>
> diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c
> index aa368a01b035..f8d94db60dc6 100644
> --- a/fs/f2fs/extent_cache.c
> +++ b/fs/f2fs/extent_cache.c
> @@ -410,10 +410,8 @@ static void __drop_largest_extent(struct extent_tree *et,
> void f2fs_init_read_extent_tree(struct inode *inode, struct folio *ifolio)
> {
> struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
> - struct extent_tree_info *eti = &sbi->extent_tree[EX_READ];
> struct f2fs_extent *i_ext = &F2FS_INODE(ifolio)->i_ext;
> struct extent_tree *et;
> - struct extent_node *en;
> struct extent_info ei = {0};
>
> if (!__may_extent_tree(inode, EX_READ)) {
> @@ -435,21 +433,7 @@ void f2fs_init_read_extent_tree(struct inode *inode, struct folio *ifolio)
> if (atomic_read(&et->node_cnt) || !ei.len)
> goto skip;
>
> - if (IS_DEVICE_ALIASING(inode)) {
> - et->largest = ei;
> - goto skip;
> - }
> -
> - en = __attach_extent_node(sbi, et, &ei, NULL,
> - &et->root.rb_root.rb_node, true);
> - if (en) {
> - et->largest = en->ei;
> - et->cached_en = en;
> -
> - spin_lock(&eti->extent_lock);
> - list_add_tail(&en->list, &eti->extent_list);
> - spin_unlock(&eti->extent_lock);
> - }
> + et->largest = ei;
Previously, we can split largest extent node to two if we punched it, now
we can not? IIUC.
Thanks,
> skip:
> /* Let's drop, if checkpoint got corrupted. */
> if (f2fs_cp_error(sbi)) {
_______________________________________________
Linux-f2fs-devel mailing list
Linux-f2fs-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [f2fs-dev] [PATCH RESEND 3/5] f2fs: punch largest extent instead of dropping it entirely on overlap
2026-06-12 11:58 ` [f2fs-dev] [PATCH RESEND 3/5] f2fs: punch largest extent instead of dropping it entirely on overlap Yongpeng Yang
@ 2026-06-15 12:05 ` Chao Yu via Linux-f2fs-devel
0 siblings, 0 replies; 11+ messages in thread
From: Chao Yu via Linux-f2fs-devel @ 2026-06-15 12:05 UTC (permalink / raw)
To: Yongpeng Yang, Jaegeuk Kim; +Cc: Yongpeng Yang, Yongpeng Yang, linux-f2fs-devel
On 6/12/26 19:58, Yongpeng Yang wrote:
> From: Yongpeng Yang <yangyongpeng@xiaomi.com>
>
> Previously, when an extent being inserted overlaps with the largest
> extent, the largest extent is dropped entirely. This was done to handle
Please correct me if I missed anything, I remember that we will add largest
extent in below path?
- __update_extent_tree_range
- __insert_extent_tree
- __try_update_largest_extent : update largest w/ right extent
- __try_update_largest_extent : update largest w/ left extent
Thanks,
> the case where the largest extent is not in memory, avoiding
> inconsistency between the largest extent and the extent tree.
>
> This patch changes the semantics of __drop_largest_extent (renamed to
> __punch_largest_extent): instead of discarding the entire largest
> extent when any overlap is detected, keep the larger remaining portion
> (left or right) after the punch. This preserves extent cache coverage
> for truncate and overwrite operations that only partially overlap the
> largest extent.
>
> Signed-off-by: Yongpeng Yang <yangyongpeng@xiaomi.com>
> ---
> fs/f2fs/extent_cache.c | 31 ++++++++++++++++++++++++-------
> 1 file changed, 24 insertions(+), 7 deletions(-)
>
> diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c
> index f8d94db60dc6..82d84c4e98b2 100644
> --- a/fs/f2fs/extent_cache.c
> +++ b/fs/f2fs/extent_cache.c
> @@ -397,14 +397,31 @@ static unsigned int __free_extent_tree(struct f2fs_sb_info *sbi,
> return count;
> }
>
> -static void __drop_largest_extent(struct extent_tree *et,
> +static void __punch_largest_extent(struct extent_tree *et,
> pgoff_t fofs, unsigned int len)
> {
> - if (fofs < (pgoff_t)et->largest.fofs + et->largest.len &&
> - fofs + len > et->largest.fofs) {
> - et->largest.len = 0;
> - et->largest_updated = true;
> + unsigned int largest_end, punch_end;
> + unsigned int left_len, right_len;
> +
> + if (fofs >= (pgoff_t)et->largest.fofs + et->largest.len ||
> + fofs + len <= et->largest.fofs)
> + return;
> +
> + /* Punch [fofs, fofs + len) from largest extent. */
> + largest_end = et->largest.fofs + et->largest.len;
> + punch_end = fofs + len;
> +
> + left_len = fofs > et->largest.fofs ? fofs - et->largest.fofs : 0;
> + right_len = largest_end > punch_end ? largest_end - punch_end : 0;
> +
> + if (left_len >= right_len) {
> + et->largest.len = left_len;
> + } else {
> + et->largest.blk += punch_end - et->largest.fofs;
> + et->largest.fofs = punch_end;
> + et->largest.len = right_len;
> }
> + et->largest_updated = true;
> }
>
> void f2fs_init_read_extent_tree(struct inode *inode, struct folio *ifolio)
> @@ -680,10 +697,10 @@ static void __update_extent_tree_range(struct inode *inode,
> dei.len = 0;
>
> /*
> - * drop largest extent before lookup, in case it's already
> + * punch largest extent before lookup, in case it's already
> * been shrunk from extent tree
> */
> - __drop_largest_extent(et, fofs, len);
> + __punch_largest_extent(et, fofs, len);
> }
>
> if (et->largest.len != 0 &&
_______________________________________________
Linux-f2fs-devel mailing list
Linux-f2fs-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [f2fs-dev] [PATCH RESEND 4/5] f2fs: add extent_access_mode to track extent cache access patterns
2026-06-12 11:58 ` [f2fs-dev] [PATCH RESEND 4/5] f2fs: add extent_access_mode to track extent cache access patterns Yongpeng Yang
@ 2026-06-15 12:05 ` Chao Yu via Linux-f2fs-devel
0 siblings, 0 replies; 11+ messages in thread
From: Chao Yu via Linux-f2fs-devel @ 2026-06-15 12:05 UTC (permalink / raw)
To: Yongpeng Yang, Jaegeuk Kim; +Cc: Yongpeng Yang, Yongpeng Yang, linux-f2fs-devel
On 6/12/26 19:58, Yongpeng Yang wrote:
> From: Yongpeng Yang <yangyongpeng@xiaomi.com>
>
> Introduce enum extent_access_mode to classify how each extent node
> is accessed or created (READ, WRITE, PRECACHE, TRUNCATE, LARGEST).
> This metadata optimize LRU eviction decisions:
Can you please give some numbers for this change?
>
> 1. Extents only accessed as the largest extent (never read-hit) are
> deprioritized in the LRU list since reads can still use the largest
> extent directly.
>
> 2. Sparse single-block write extents that were never merged are moved
> to the head of LRU for earlier reclaim, preserving extents with
> better continuity and higher read-hit probability.
>
> Signed-off-by: Yongpeng Yang <yangyongpeng@xiaomi.com>
> ---
> fs/f2fs/data.c | 4 ++--
> fs/f2fs/extent_cache.c | 29 ++++++++++++++++++++++++++++-
> fs/f2fs/f2fs.h | 14 +++++++++++++-
> fs/f2fs/file.c | 6 ++++--
> 4 files changed, 47 insertions(+), 6 deletions(-)
>
> diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
> index 9c6440a7db0e..2d38135005fe 100644
> --- a/fs/f2fs/data.c
> +++ b/fs/f2fs/data.c
> @@ -1873,7 +1873,7 @@ int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map, int flag)
>
> f2fs_update_read_extent_cache_range(&dn,
> start_pgofs, map->m_pblk + ofs,
> - map->m_len - ofs);
> + map->m_len - ofs, EX_ACCESS_PRECACHE);
> }
> }
>
> @@ -1919,7 +1919,7 @@ int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map, int flag)
> if (map->m_len > ofs)
> f2fs_update_read_extent_cache_range(&dn,
> start_pgofs, map->m_pblk + ofs,
> - map->m_len - ofs);
> + map->m_len - ofs, EX_ACCESS_PRECACHE);
> }
> if (map->m_next_extent)
> *map->m_next_extent = is_hole ? pgofs + 1 : pgofs;
> diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c
> index 82d84c4e98b2..e141ffb64e5f 100644
> --- a/fs/f2fs/extent_cache.c
> +++ b/fs/f2fs/extent_cache.c
> @@ -142,6 +142,7 @@ static void __try_update_largest_extent(struct extent_tree *et,
> if (en->ei.len <= et->largest.len)
> return;
>
> + en->ei.last_access_mode = EX_ACCESS_LARGEST;
> et->largest = en->ei;
> et->largest_updated = true;
> }
> @@ -518,6 +519,7 @@ static bool __lookup_extent_tree(struct inode *inode, pgoff_t pgofs,
> stat_inc_rbtree_node_hit(sbi, type);
>
> *ei = en->ei;
> + en->ei.last_access_mode = EX_ACCESS_READ;
> spin_lock(&eti->extent_lock);
> if (!list_empty(&en->list)) {
> list_move_tail(&en->list, &eti->extent_list);
> @@ -624,6 +626,21 @@ static struct extent_node *__insert_extent_tree(struct f2fs_sb_info *sbi,
>
> /* update in global extent list */
> spin_lock(&eti->extent_lock);
> + /*
> + * 1. For the largest extent, if subsequent writes are not merged into
> + * it, the write path will most likely not use the largest extent_node,
> + * while read requests can still access the mapping through the largest
> + * extent.
> + *
> + * 2. For sparse writes, if the extent length is 1 and no extent merging
> + * occurs, this extent should be reclaimed with higher priority to avoid
> + * evicting extents with better continuity and higher read-hit.
> + */
> + if (et->type == EX_READ && et->cached_en &&
> + (et->cached_en->ei.last_access_mode == EX_ACCESS_LARGEST ||
> + (et->cached_en->ei.len == 1 &&
> + et->cached_en->ei.last_access_mode == EX_ACCESS_WRITE)))
> + list_move(&et->cached_en->list, &eti->extent_list);
> list_add_tail(&en->list, &eti->extent_list);
> et->cached_en = en;
> spin_unlock(&eti->extent_lock);
> @@ -747,6 +764,8 @@ static void __update_extent_tree_range(struct inode *inode,
> if (fofs > dei.fofs && (type != EX_READ ||
> fofs - dei.fofs >= F2FS_MIN_EXTENT_LEN)) {
> en->ei.len = fofs - en->ei.fofs;
> + if (type == EX_READ)
> + en->ei.last_access_mode = EX_ACCESS_TRUNCATE;
> prev_en = en;
> parts = 1;
> }
> @@ -761,6 +780,8 @@ static void __update_extent_tree_range(struct inode *inode,
> end - dei.fofs + dei.blk, false,
> dei.age, dei.last_blocks,
> type);
> + if (type == EX_READ)
> + ei.last_access_mode = EX_ACCESS_TRUNCATE;
> en1 = __insert_extent_tree(sbi, et, &ei,
> NULL, NULL, true);
> next_en = en1;
> @@ -770,6 +791,8 @@ static void __update_extent_tree_range(struct inode *inode,
> en->ei.blk + (end - dei.fofs), true,
> dei.age, dei.last_blocks,
> type);
> + if (type == EX_READ)
> + en->ei.last_access_mode = EX_ACCESS_TRUNCATE;
> next_en = en;
> }
> parts++;
> @@ -808,6 +831,7 @@ static void __update_extent_tree_range(struct inode *inode,
> if (tei->blk) {
> __set_extent_info(&ei, fofs, len, tei->blk, false,
> 0, 0, EX_READ);
> + ei.last_access_mode = tei->last_access_mode;
> if (!__try_merge_extent_node(sbi, et, &ei, prev_en, next_en))
> __insert_extent_tree(sbi, et, &ei,
> insert_p, insert_parent, leftmost);
> @@ -978,6 +1002,7 @@ static void __update_extent_cache(struct dnode_of_data *dn, enum extent_type typ
> ei.blk = NULL_ADDR;
> else
> ei.blk = dn->data_blkaddr;
> + ei.last_access_mode = EX_ACCESS_WRITE;
> } else if (type == EX_BLOCK_AGE) {
> if (__get_new_block_age(dn->inode, &ei, dn->data_blkaddr))
> return;
> @@ -1091,12 +1116,14 @@ void f2fs_update_read_extent_cache(struct dnode_of_data *dn)
> }
>
> void f2fs_update_read_extent_cache_range(struct dnode_of_data *dn,
> - pgoff_t fofs, block_t blkaddr, unsigned int len)
> + pgoff_t fofs, block_t blkaddr, unsigned int len,
> + enum extent_access_mode access_mode)
> {
> struct extent_info ei = {
> .fofs = fofs,
> .len = len,
> .blk = blkaddr,
> + .last_access_mode = access_mode,
> };
>
> if (!__may_extent_tree(dn->inode, EX_READ))
> diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
> index fffb516b78f4..1588b64d04a3 100644
> --- a/fs/f2fs/f2fs.h
> +++ b/fs/f2fs/f2fs.h
> @@ -763,6 +763,15 @@ enum extent_type {
> NR_EXTENT_CACHES,
> };
>
> +/* extent acces mode for cache hit or extent add */
> +enum extent_access_mode {
> + EX_ACCESS_READ,
> + EX_ACCESS_WRITE,
> + EX_ACCESS_PRECACHE,
> + EX_ACCESS_TRUNCATE,
> + EX_ACCESS_LARGEST,
> +};
> +
> /*
> * Reserved value to mark invalid age extents, hence valid block range
> * from 0 to ULLONG_MAX-1
> @@ -781,6 +790,8 @@ struct extent_info {
> /* physical extent length of compressed blocks */
> unsigned int c_len;
> #endif
> + /* record last access mode */
> + enum extent_access_mode last_access_mode;
As we know, memory is expensive, :P, I'd like to know if we can enable this
optionally if there is benefits.
Thanks,
> };
> /* block age extent_cache */
> struct {
> @@ -4577,7 +4588,8 @@ bool f2fs_lookup_read_extent_cache_block(struct inode *inode, pgoff_t index,
> block_t *blkaddr);
> void f2fs_update_read_extent_cache(struct dnode_of_data *dn);
> void f2fs_update_read_extent_cache_range(struct dnode_of_data *dn,
> - pgoff_t fofs, block_t blkaddr, unsigned int len);
> + pgoff_t fofs, block_t blkaddr, unsigned int len,
> + enum extent_access_mode access_mode);
> unsigned int f2fs_shrink_read_extent_tree(struct f2fs_sb_info *sbi,
> int nr_shrink);
>
> diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
> index 633e9ade654f..a3a5d499eadf 100644
> --- a/fs/f2fs/file.c
> +++ b/fs/f2fs/file.c
> @@ -733,7 +733,8 @@ void f2fs_truncate_data_blocks_range(struct dnode_of_data *dn, int count)
> */
> fofs = f2fs_start_bidx_of_node(ofs_of_node(dn->node_folio),
> dn->inode) + ofs;
> - f2fs_update_read_extent_cache_range(dn, fofs, 0, len);
> + f2fs_update_read_extent_cache_range(dn, fofs, 0, len,
> + EX_ACCESS_TRUNCATE);
> f2fs_update_age_extent_cache_range(dn, fofs, len);
> dec_valid_block_count(sbi, dn->inode, nr_free);
> }
> @@ -1672,7 +1673,8 @@ static int f2fs_do_zero_range(struct dnode_of_data *dn, pgoff_t start,
>
> if (index > start) {
> f2fs_update_read_extent_cache_range(dn, start, 0,
> - index - start);
> + index - start,
> + EX_ACCESS_TRUNCATE);
> f2fs_update_age_extent_cache_range(dn, start, index - start);
> }
>
_______________________________________________
Linux-f2fs-devel mailing list
Linux-f2fs-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [f2fs-dev] [PATCH RESEND 5/5] f2fs: add ioctl to export read extent cache to userspace for debug
2026-06-12 11:58 ` [f2fs-dev] [PATCH RESEND 5/5] f2fs: add ioctl to export read extent cache to userspace for debug Yongpeng Yang
@ 2026-06-15 12:20 ` Chao Yu via Linux-f2fs-devel
2026-06-15 15:51 ` Jaegeuk Kim via Linux-f2fs-devel
0 siblings, 1 reply; 11+ messages in thread
From: Chao Yu via Linux-f2fs-devel @ 2026-06-15 12:20 UTC (permalink / raw)
To: Yongpeng Yang, Jaegeuk Kim; +Cc: Yongpeng Yang, Yongpeng Yang, linux-f2fs-devel
On 6/12/26 19:58, Yongpeng Yang wrote:
> From: Yongpeng Yang <yangyongpeng@xiaomi.com>
>
> Add F2FS_IOC_GET_READ_CACHE_EXTENTS ioctl that allows userspace to
> retrieve all cached read extents for a given file. This uses a two-call
> pattern similar to fiemap: the first call with ext_count=0 queries the
> node_count, and the second call fetches the actual extent entries.
It looks a little bit heavy to maintain a debug purpose ioctl interface.
Maybe set ino via sysfs and dump extent cache via procfs? only enabled
if F2FS_CHECK_FS=y?
Jaegeuk, do you have any suggestion?
Thanks,
>
> Signed-off-by: Yongpeng Yang <yangyongpeng@xiaomi.com>
> ---
> fs/f2fs/extent_cache.c | 70 +++++++++++++++++++++++++++++++++++++++
> fs/f2fs/f2fs.h | 3 ++
> fs/f2fs/file.c | 11 ++++++
> include/uapi/linux/f2fs.h | 21 ++++++++++++
> 4 files changed, 105 insertions(+)
>
> diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c
> index e141ffb64e5f..0c10d5639d68 100644
> --- a/fs/f2fs/extent_cache.c
> +++ b/fs/f2fs/extent_cache.c
> @@ -14,6 +14,7 @@
>
> #include <linux/fs.h>
> #include <linux/f2fs_fs.h>
> +#include <uapi/linux/f2fs.h>
>
> #include "f2fs.h"
> #include "node.h"
> @@ -1267,6 +1268,75 @@ static void __init_extent_tree_info(struct extent_tree_info *eti)
> atomic_set(&eti->total_ext_node, 0);
> }
>
> +int f2fs_get_read_cache_extents(struct inode *inode,
> + struct f2fs_read_cache_extent __user *uarg)
> +{
> + struct extent_tree *et = F2FS_I(inode)->extent_tree[EX_READ];
> + struct f2fs_cache_extent_info *kbuf = NULL;
> + struct f2fs_cache_extent_info largest = {};
> + struct rb_node *node;
> + struct extent_node *en;
> + unsigned int capacity, count = 0;
> + __u32 flags = 0;
> + int ret = 0;
> +
> + if (get_user(capacity, &uarg->ext_count))
> + return -EFAULT;
> +
> + if (is_inode_flag_set(inode, FI_NO_EXTENT))
> + flags |= F2FS_EXT_FL_NO_EXTENT;
> +
> + if (!et || (flags & F2FS_EXT_FL_NO_EXTENT)) {
> + if (put_user(0U, &uarg->ext_count) ||
> + put_user(flags, &uarg->flags) ||
> + put_user(0U, &uarg->node_count))
> + return -EFAULT;
> + return 0;
> + }
> +
> + if (capacity) {
> + kbuf = f2fs_kvmalloc(F2FS_I_SB(inode), capacity * sizeof(*kbuf), GFP_KERNEL);
> + if (!kbuf)
> + return -ENOMEM;
> + }
> +
> + read_lock(&et->lock);
> +
> + largest.fofs = et->largest.fofs;
> + largest.blk = et->largest.blk;
> + largest.len = et->largest.len;
> + largest.last_access_mode = et->largest.last_access_mode;
> +
> + for (node = rb_first_cached(&et->root); node; node = rb_next(node)) {
> + if (count >= capacity)
> + break;
> + en = rb_entry(node, struct extent_node, rb_node);
> +
> + kbuf[count].fofs = en->ei.fofs;
> + kbuf[count].blk = en->ei.blk;
> + kbuf[count].len = en->ei.len;
> + kbuf[count].last_access_mode = en->ei.last_access_mode;
> + count++;
> + }
> +
> + read_unlock(&et->lock);
> +
> + if (count && copy_to_user(uarg->extents, kbuf,
> + count * sizeof(*kbuf))) {
> + ret = -EFAULT;
> + goto out;
> + }
> +
> + if (put_user(count, &uarg->ext_count) ||
> + put_user(flags, &uarg->flags) ||
> + put_user((u32)atomic_read(&et->node_cnt), &uarg->node_count) ||
> + copy_to_user(&uarg->largest, &largest, sizeof(largest)))
> + ret = -EFAULT;
> +out:
> + kvfree(kbuf);
> + return ret;
> +}
> +
> void f2fs_init_extent_cache_info(struct f2fs_sb_info *sbi)
> {
> __init_extent_tree_info(&sbi->extent_tree[EX_READ]);
> diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
> index 1588b64d04a3..69641fc31c51 100644
> --- a/fs/f2fs/f2fs.h
> +++ b/fs/f2fs/f2fs.h
> @@ -26,6 +26,7 @@
> #include <linux/part_stat.h>
> #include <linux/rw_hint.h>
>
> +#include <uapi/linux/f2fs.h>
> #include <linux/fscrypt.h>
> #include <linux/fsverity.h>
>
> @@ -4590,6 +4591,8 @@ void f2fs_update_read_extent_cache(struct dnode_of_data *dn);
> void f2fs_update_read_extent_cache_range(struct dnode_of_data *dn,
> pgoff_t fofs, block_t blkaddr, unsigned int len,
> enum extent_access_mode access_mode);
> +int f2fs_get_read_cache_extents(struct inode *inode,
> + struct f2fs_read_cache_extent __user *uarg);
> unsigned int f2fs_shrink_read_extent_tree(struct f2fs_sb_info *sbi,
> int nr_shrink);
>
> diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
> index a3a5d499eadf..66ec9927d667 100644
> --- a/fs/f2fs/file.c
> +++ b/fs/f2fs/file.c
> @@ -3672,6 +3672,14 @@ static int f2fs_ioc_precache_extents(struct file *filp)
> return f2fs_precache_extents(file_inode(filp));
> }
>
> +static int f2fs_ioc_get_read_cache_extents(struct file *filp, unsigned long arg)
> +{
> + struct inode *inode = file_inode(filp);
> +
> + return f2fs_get_read_cache_extents(inode,
> + (struct f2fs_read_cache_extent __user *)arg);
> +}
> +
> static int f2fs_ioc_resize_fs(struct file *filp, unsigned long arg)
> {
> struct f2fs_sb_info *sbi = F2FS_I_SB(file_inode(filp));
> @@ -4744,6 +4752,8 @@ static long __f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
> return f2fs_ioc_get_dev_alias_file(filp, arg);
> case F2FS_IOC_IO_PRIO:
> return f2fs_ioc_io_prio(filp, arg);
> + case F2FS_IOC_GET_READ_CACHE_EXTENTS:
> + return f2fs_ioc_get_read_cache_extents(filp, arg);
> default:
> return -ENOTTY;
> }
> @@ -5506,6 +5516,7 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
> case F2FS_IOC_COMPRESS_FILE:
> case F2FS_IOC_GET_DEV_ALIAS_FILE:
> case F2FS_IOC_IO_PRIO:
> + case F2FS_IOC_GET_READ_CACHE_EXTENTS:
> break;
> default:
> return -ENOIOCTLCMD;
> diff --git a/include/uapi/linux/f2fs.h b/include/uapi/linux/f2fs.h
> index 795e26258355..6ff9003bc030 100644
> --- a/include/uapi/linux/f2fs.h
> +++ b/include/uapi/linux/f2fs.h
> @@ -45,6 +45,8 @@
> #define F2FS_IOC_START_ATOMIC_REPLACE _IO(F2FS_IOCTL_MAGIC, 25)
> #define F2FS_IOC_GET_DEV_ALIAS_FILE _IOR(F2FS_IOCTL_MAGIC, 26, __u32)
> #define F2FS_IOC_IO_PRIO _IOW(F2FS_IOCTL_MAGIC, 27, __u32)
> +#define F2FS_IOC_GET_READ_CACHE_EXTENTS _IOWR(F2FS_IOCTL_MAGIC, 28, \
> + struct f2fs_read_cache_extent)
>
> /*
> * should be same as XFS_IOC_GOINGDOWN.
> @@ -104,4 +106,23 @@ struct f2fs_comp_option {
> __u8 log_cluster_size;
> };
>
> +struct f2fs_cache_extent_info {
> + __u32 fofs; /* start file offset in blocks */
> + __u32 blk; /* start block address */
> + __u32 len; /* length in blocks */
> + __u32 last_access_mode; /* last access mode of extent_node */
> +};
> +
> +/* flags for f2fs_read_cache_extent */
> +#define F2FS_EXT_FL_NO_EXTENT 0x1 /* extent cache disabled for this inode */
> +
> +struct f2fs_read_cache_extent {
> + __u32 ext_count; /* in: array capacity; out: mapped extent count */
> + __u32 flags; /* out: status flags */
> + __u32 node_count; /* out: total extent nodes in tree */
> + __u32 reserved;
> + struct f2fs_cache_extent_info largest; /* out: largest extent */
> + struct f2fs_cache_extent_info extents[]; /* out: extent array */
> +};
> +
> #endif /* _UAPI_LINUX_F2FS_H */
_______________________________________________
Linux-f2fs-devel mailing list
Linux-f2fs-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [f2fs-dev] [PATCH RESEND 5/5] f2fs: add ioctl to export read extent cache to userspace for debug
2026-06-15 12:20 ` Chao Yu via Linux-f2fs-devel
@ 2026-06-15 15:51 ` Jaegeuk Kim via Linux-f2fs-devel
0 siblings, 0 replies; 11+ messages in thread
From: Jaegeuk Kim via Linux-f2fs-devel @ 2026-06-15 15:51 UTC (permalink / raw)
To: Chao Yu; +Cc: Yongpeng Yang, Yongpeng Yang, Yongpeng Yang, linux-f2fs-devel
On 06/15, Chao Yu via Linux-f2fs-devel wrote:
> On 6/12/26 19:58, Yongpeng Yang wrote:
> > From: Yongpeng Yang <yangyongpeng@xiaomi.com>
> >
> > Add F2FS_IOC_GET_READ_CACHE_EXTENTS ioctl that allows userspace to
> > retrieve all cached read extents for a given file. This uses a two-call
> > pattern similar to fiemap: the first call with ext_count=0 queries the
> > node_count, and the second call fetches the actual extent entries.
>
> It looks a little bit heavy to maintain a debug purpose ioctl interface.
>
> Maybe set ino via sysfs and dump extent cache via procfs? only enabled
> if F2FS_CHECK_FS=y?
>
> Jaegeuk, do you have any suggestion?
Agreed, and even not sure we need sysfs or procfs. Can we dump the extents
via fsck?
>
> Thanks,
>
> >
> > Signed-off-by: Yongpeng Yang <yangyongpeng@xiaomi.com>
> > ---
> > fs/f2fs/extent_cache.c | 70 +++++++++++++++++++++++++++++++++++++++
> > fs/f2fs/f2fs.h | 3 ++
> > fs/f2fs/file.c | 11 ++++++
> > include/uapi/linux/f2fs.h | 21 ++++++++++++
> > 4 files changed, 105 insertions(+)
> >
> > diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c
> > index e141ffb64e5f..0c10d5639d68 100644
> > --- a/fs/f2fs/extent_cache.c
> > +++ b/fs/f2fs/extent_cache.c
> > @@ -14,6 +14,7 @@
> >
> > #include <linux/fs.h>
> > #include <linux/f2fs_fs.h>
> > +#include <uapi/linux/f2fs.h>
> >
> > #include "f2fs.h"
> > #include "node.h"
> > @@ -1267,6 +1268,75 @@ static void __init_extent_tree_info(struct extent_tree_info *eti)
> > atomic_set(&eti->total_ext_node, 0);
> > }
> >
> > +int f2fs_get_read_cache_extents(struct inode *inode,
> > + struct f2fs_read_cache_extent __user *uarg)
> > +{
> > + struct extent_tree *et = F2FS_I(inode)->extent_tree[EX_READ];
> > + struct f2fs_cache_extent_info *kbuf = NULL;
> > + struct f2fs_cache_extent_info largest = {};
> > + struct rb_node *node;
> > + struct extent_node *en;
> > + unsigned int capacity, count = 0;
> > + __u32 flags = 0;
> > + int ret = 0;
> > +
> > + if (get_user(capacity, &uarg->ext_count))
> > + return -EFAULT;
> > +
> > + if (is_inode_flag_set(inode, FI_NO_EXTENT))
> > + flags |= F2FS_EXT_FL_NO_EXTENT;
> > +
> > + if (!et || (flags & F2FS_EXT_FL_NO_EXTENT)) {
> > + if (put_user(0U, &uarg->ext_count) ||
> > + put_user(flags, &uarg->flags) ||
> > + put_user(0U, &uarg->node_count))
> > + return -EFAULT;
> > + return 0;
> > + }
> > +
> > + if (capacity) {
> > + kbuf = f2fs_kvmalloc(F2FS_I_SB(inode), capacity * sizeof(*kbuf), GFP_KERNEL);
> > + if (!kbuf)
> > + return -ENOMEM;
> > + }
> > +
> > + read_lock(&et->lock);
> > +
> > + largest.fofs = et->largest.fofs;
> > + largest.blk = et->largest.blk;
> > + largest.len = et->largest.len;
> > + largest.last_access_mode = et->largest.last_access_mode;
> > +
> > + for (node = rb_first_cached(&et->root); node; node = rb_next(node)) {
> > + if (count >= capacity)
> > + break;
> > + en = rb_entry(node, struct extent_node, rb_node);
> > +
> > + kbuf[count].fofs = en->ei.fofs;
> > + kbuf[count].blk = en->ei.blk;
> > + kbuf[count].len = en->ei.len;
> > + kbuf[count].last_access_mode = en->ei.last_access_mode;
> > + count++;
> > + }
> > +
> > + read_unlock(&et->lock);
> > +
> > + if (count && copy_to_user(uarg->extents, kbuf,
> > + count * sizeof(*kbuf))) {
> > + ret = -EFAULT;
> > + goto out;
> > + }
> > +
> > + if (put_user(count, &uarg->ext_count) ||
> > + put_user(flags, &uarg->flags) ||
> > + put_user((u32)atomic_read(&et->node_cnt), &uarg->node_count) ||
> > + copy_to_user(&uarg->largest, &largest, sizeof(largest)))
> > + ret = -EFAULT;
> > +out:
> > + kvfree(kbuf);
> > + return ret;
> > +}
> > +
> > void f2fs_init_extent_cache_info(struct f2fs_sb_info *sbi)
> > {
> > __init_extent_tree_info(&sbi->extent_tree[EX_READ]);
> > diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
> > index 1588b64d04a3..69641fc31c51 100644
> > --- a/fs/f2fs/f2fs.h
> > +++ b/fs/f2fs/f2fs.h
> > @@ -26,6 +26,7 @@
> > #include <linux/part_stat.h>
> > #include <linux/rw_hint.h>
> >
> > +#include <uapi/linux/f2fs.h>
> > #include <linux/fscrypt.h>
> > #include <linux/fsverity.h>
> >
> > @@ -4590,6 +4591,8 @@ void f2fs_update_read_extent_cache(struct dnode_of_data *dn);
> > void f2fs_update_read_extent_cache_range(struct dnode_of_data *dn,
> > pgoff_t fofs, block_t blkaddr, unsigned int len,
> > enum extent_access_mode access_mode);
> > +int f2fs_get_read_cache_extents(struct inode *inode,
> > + struct f2fs_read_cache_extent __user *uarg);
> > unsigned int f2fs_shrink_read_extent_tree(struct f2fs_sb_info *sbi,
> > int nr_shrink);
> >
> > diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
> > index a3a5d499eadf..66ec9927d667 100644
> > --- a/fs/f2fs/file.c
> > +++ b/fs/f2fs/file.c
> > @@ -3672,6 +3672,14 @@ static int f2fs_ioc_precache_extents(struct file *filp)
> > return f2fs_precache_extents(file_inode(filp));
> > }
> >
> > +static int f2fs_ioc_get_read_cache_extents(struct file *filp, unsigned long arg)
> > +{
> > + struct inode *inode = file_inode(filp);
> > +
> > + return f2fs_get_read_cache_extents(inode,
> > + (struct f2fs_read_cache_extent __user *)arg);
> > +}
> > +
> > static int f2fs_ioc_resize_fs(struct file *filp, unsigned long arg)
> > {
> > struct f2fs_sb_info *sbi = F2FS_I_SB(file_inode(filp));
> > @@ -4744,6 +4752,8 @@ static long __f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
> > return f2fs_ioc_get_dev_alias_file(filp, arg);
> > case F2FS_IOC_IO_PRIO:
> > return f2fs_ioc_io_prio(filp, arg);
> > + case F2FS_IOC_GET_READ_CACHE_EXTENTS:
> > + return f2fs_ioc_get_read_cache_extents(filp, arg);
> > default:
> > return -ENOTTY;
> > }
> > @@ -5506,6 +5516,7 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
> > case F2FS_IOC_COMPRESS_FILE:
> > case F2FS_IOC_GET_DEV_ALIAS_FILE:
> > case F2FS_IOC_IO_PRIO:
> > + case F2FS_IOC_GET_READ_CACHE_EXTENTS:
> > break;
> > default:
> > return -ENOIOCTLCMD;
> > diff --git a/include/uapi/linux/f2fs.h b/include/uapi/linux/f2fs.h
> > index 795e26258355..6ff9003bc030 100644
> > --- a/include/uapi/linux/f2fs.h
> > +++ b/include/uapi/linux/f2fs.h
> > @@ -45,6 +45,8 @@
> > #define F2FS_IOC_START_ATOMIC_REPLACE _IO(F2FS_IOCTL_MAGIC, 25)
> > #define F2FS_IOC_GET_DEV_ALIAS_FILE _IOR(F2FS_IOCTL_MAGIC, 26, __u32)
> > #define F2FS_IOC_IO_PRIO _IOW(F2FS_IOCTL_MAGIC, 27, __u32)
> > +#define F2FS_IOC_GET_READ_CACHE_EXTENTS _IOWR(F2FS_IOCTL_MAGIC, 28, \
> > + struct f2fs_read_cache_extent)
> >
> > /*
> > * should be same as XFS_IOC_GOINGDOWN.
> > @@ -104,4 +106,23 @@ struct f2fs_comp_option {
> > __u8 log_cluster_size;
> > };
> >
> > +struct f2fs_cache_extent_info {
> > + __u32 fofs; /* start file offset in blocks */
> > + __u32 blk; /* start block address */
> > + __u32 len; /* length in blocks */
> > + __u32 last_access_mode; /* last access mode of extent_node */
> > +};
> > +
> > +/* flags for f2fs_read_cache_extent */
> > +#define F2FS_EXT_FL_NO_EXTENT 0x1 /* extent cache disabled for this inode */
> > +
> > +struct f2fs_read_cache_extent {
> > + __u32 ext_count; /* in: array capacity; out: mapped extent count */
> > + __u32 flags; /* out: status flags */
> > + __u32 node_count; /* out: total extent nodes in tree */
> > + __u32 reserved;
> > + struct f2fs_cache_extent_info largest; /* out: largest extent */
> > + struct f2fs_cache_extent_info extents[]; /* out: extent array */
> > +};
> > +
> > #endif /* _UAPI_LINUX_F2FS_H */
>
>
>
> _______________________________________________
> Linux-f2fs-devel mailing list
> Linux-f2fs-devel@lists.sourceforge.net
> https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
_______________________________________________
Linux-f2fs-devel mailing list
Linux-f2fs-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2026-06-15 15:52 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-12 11:58 [f2fs-dev] [PATCH RESEND 1/5] f2fs: fix extent merge failure when largest extent is not in rb-tree Yongpeng Yang
2026-06-12 11:58 ` [f2fs-dev] [PATCH RESEND 2/5] f2fs: only initialize largest extent without extent_node at inode init Yongpeng Yang
2026-06-15 11:55 ` Chao Yu via Linux-f2fs-devel
2026-06-12 11:58 ` [f2fs-dev] [PATCH RESEND 3/5] f2fs: punch largest extent instead of dropping it entirely on overlap Yongpeng Yang
2026-06-15 12:05 ` Chao Yu via Linux-f2fs-devel
2026-06-12 11:58 ` [f2fs-dev] [PATCH RESEND 4/5] f2fs: add extent_access_mode to track extent cache access patterns Yongpeng Yang
2026-06-15 12:05 ` Chao Yu via Linux-f2fs-devel
2026-06-12 11:58 ` [f2fs-dev] [PATCH RESEND 5/5] f2fs: add ioctl to export read extent cache to userspace for debug Yongpeng Yang
2026-06-15 12:20 ` Chao Yu via Linux-f2fs-devel
2026-06-15 15:51 ` Jaegeuk Kim via Linux-f2fs-devel
2026-06-15 11:26 ` [f2fs-dev] [PATCH RESEND 1/5] f2fs: fix extent merge failure when largest extent is not in rb-tree Chao Yu via Linux-f2fs-devel
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.