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:
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; }; /* 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 [email protected] https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
