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? > > 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. 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
