Re: [f2fs-dev] [PATCH RESEND 4/5] f2fs: add extent_access_mode to track extent cache access patterns
Yongpeng Yang <[email protected]>
| Newsgroups | net.sourceforge.lists.linux-f2fs-devel |
|---|---|
| Message-ID | <SEZPR02MB56625194F24957F19418C62A99E22@SEZPR02MB5662.apcprd02.prod.outlook.com> |
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. > >> >> 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. 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