Re: [f2fs-dev] [PATCH RESEND 4/5] f2fs: add extent_access_mode to track extent cache access patterns
Chao Yu via Linux-f2fs-devel <[email protected]>
| Newsgroups | net.sourceforge.lists.linux-f2fs-devel |
|---|---|
| Message-ID | <[email protected]> |
On 6/19/26 22:45, Yongpeng Yang wrote: > On 6/15/26 8:05 PM, Chao Yu via Linux-f2fs-devel wrote: >> On 6/12/26 19:58, Yongpeng Yang wrote: >>> From: Yongpeng Yang <[email protected]> >>> >>> 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? > > This is a qualitative analysis. This patch aims to prioritize shrinking > extents that are not read-hit and the largest extent. I think it needs to give some numbers to show benefits. > > >> >>> >>> 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 <[email protected]> >>> --- >>> 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. > > extent_access_mode consumes only padding holes within the union and > brings no additional memory overhead. The union is sized to 16 bytes in > total, whereas blk, c_len and extent_access_mode together take up just > 12 bytes. Well, seems fine now, but I just notice block age extent costs more space than read extent. Thanks, > > Thanks > Yongpeng, > > >> >> 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 >> [email protected] >> https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel > _______________________________________________ Linux-f2fs-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel