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

Reply via email to