[PATCH v1] f2fs: Improve large section GC by locating valid block segments

yohan.joung posted 1 patch 8 months, 3 weeks ago
There is a newer version of this series
fs/f2fs/f2fs.h  |  2 ++
fs/f2fs/gc.c    | 92 +++++++++++++++++++++++++++++++++++--------------
fs/f2fs/gc.h    |  6 ++++
fs/f2fs/super.c |  8 ++++-
4 files changed, 82 insertions(+), 26 deletions(-)
[PATCH v1] f2fs: Improve large section GC by locating valid block segments
Posted by yohan.joung 8 months, 3 weeks ago
Change the large section GC to locate valid block segments instead of
performing a sequential search. This modification enhances performance
by reducing unnecessary block scanning in large storage sections.

example :
[invalid seg 0] [invalid seg 1] [invalid seg 2]
[  valid seg 3] [  valid seg 4] [  valid seg 5]

Current: In the first GC, nothing is moved,
but in the second GC, segments 3, 4, and 5 are moved.
Change: In the first GC, segments 3, 4, and 5 are moved.

Signed-off-by: yohan.joung <yohan.joung@sk.com>
---
 fs/f2fs/f2fs.h  |  2 ++
 fs/f2fs/gc.c    | 92 +++++++++++++++++++++++++++++++++++--------------
 fs/f2fs/gc.h    |  6 ++++
 fs/f2fs/super.c |  8 ++++-
 4 files changed, 82 insertions(+), 26 deletions(-)

diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index f1576dc6ec67..348417edac25 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -4008,6 +4008,8 @@ int f2fs_gc_range(struct f2fs_sb_info *sbi,
 int f2fs_resize_fs(struct file *filp, __u64 block_count);
 int __init f2fs_create_garbage_collection_cache(void);
 void f2fs_destroy_garbage_collection_cache(void);
+int __init f2fs_init_garbage_collection_summary_cache(void);
+void f2fs_destroy_garbage_collection_summary_cache(void);
 /* victim selection function for cleaning and SSR */
 int f2fs_get_victim(struct f2fs_sb_info *sbi, unsigned int *result,
 			int gc_type, int type, char alloc_mode,
diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c
index 2b8f9239bede..3b63e85fa038 100644
--- a/fs/f2fs/gc.c
+++ b/fs/f2fs/gc.c
@@ -24,6 +24,7 @@
 #include <trace/events/f2fs.h>
 
 static struct kmem_cache *victim_entry_slab;
+static struct kmem_cache *gc_page_entry_slab;
 
 static unsigned int count_bits(const unsigned long *addr,
 				unsigned int offset, unsigned int len);
@@ -711,6 +712,30 @@ static void release_victim_entry(struct f2fs_sb_info *sbi)
 	f2fs_bug_on(sbi, !list_empty(&am->victim_list));
 }
 
+static struct gc_page_entry *add_gc_page_entry(struct list_head *gc_page_list,
+					struct page *sum_page, unsigned int segno)
+{
+	struct gc_page_entry *gpe;
+
+	gpe = f2fs_kmem_cache_alloc(gc_page_entry_slab, GFP_NOFS, true, NULL);
+	gpe->segno = segno;
+	gpe->sum_page = sum_page;
+	list_add_tail(&gpe->list, gc_page_list);
+	return gpe;
+}
+
+static void release_gc_page_entry(struct list_head *gc_page_list, bool putpage)
+{
+	struct gc_page_entry *gpe, *tmp;
+
+	list_for_each_entry_safe(gpe, tmp, gc_page_list, list) {
+		if (putpage)
+			f2fs_put_page(gpe->sum_page, 0);
+		list_del(&gpe->list);
+		kmem_cache_free(gc_page_entry_slab, gpe);
+	}
+}
+
 static bool f2fs_pin_section(struct f2fs_sb_info *sbi, unsigned int segno)
 {
 	struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
@@ -1721,14 +1746,18 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi,
 	struct page *sum_page;
 	struct f2fs_summary_block *sum;
 	struct blk_plug plug;
+	struct gc_page_entry *gpe;
 	unsigned int segno = start_segno;
 	unsigned int end_segno = start_segno + SEGS_PER_SEC(sbi);
 	unsigned int sec_end_segno;
+	unsigned int window_granularity = 1;
 	int seg_freed = 0, migrated = 0;
 	unsigned char type = IS_DATASEG(get_seg_entry(sbi, segno)->type) ?
 						SUM_TYPE_DATA : SUM_TYPE_NODE;
 	unsigned char data_type = (type == SUM_TYPE_DATA) ? DATA : NODE;
 	int submitted = 0;
+	int gc_list_count = 0;
+	LIST_HEAD(gc_page_list);
 
 	if (__is_large_section(sbi)) {
 		sec_end_segno = rounddown(end_segno, SEGS_PER_SEC(sbi));
@@ -1744,7 +1773,7 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi,
 					f2fs_usable_segs_in_sec(sbi);
 
 		if (gc_type == BG_GC || one_time) {
-			unsigned int window_granularity =
+			window_granularity =
 				sbi->migration_window_granularity;
 
 			if (f2fs_sb_has_blkzoned(sbi) &&
@@ -1752,8 +1781,6 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi,
 					sbi->gc_thread->boost_zoned_gc_percent))
 				window_granularity *=
 					BOOST_GC_MULTIPLE;
-
-			end_segno = start_segno + window_granularity;
 		}
 
 		if (end_segno > sec_end_segno)
@@ -1762,37 +1789,38 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi,
 
 	sanity_check_seg_type(sbi, get_seg_entry(sbi, segno)->type);
 
-	/* readahead multi ssa blocks those have contiguous address */
-	if (__is_large_section(sbi))
+	for (segno = start_segno; segno < end_segno; segno++) {
+		if (!get_valid_blocks(sbi, segno, false))
+			continue;
+
+		/* readahead multi ssa blocks those have contiguous address */
 		f2fs_ra_meta_pages(sbi, GET_SUM_BLOCK(sbi, segno),
-					end_segno - segno, META_SSA, true);
+				window_granularity, META_SSA, true);
 
-	/* reference all summary page */
-	while (segno < end_segno) {
-		sum_page = f2fs_get_sum_page(sbi, segno++);
+		/* reference all summary page */
+		sum_page = f2fs_get_sum_page(sbi, segno);
 		if (IS_ERR(sum_page)) {
 			int err = PTR_ERR(sum_page);
-
-			end_segno = segno - 1;
-			for (segno = start_segno; segno < end_segno; segno++) {
-				sum_page = find_get_page(META_MAPPING(sbi),
-						GET_SUM_BLOCK(sbi, segno));
-				f2fs_put_page(sum_page, 0);
-				f2fs_put_page(sum_page, 0);
-			}
+			release_gc_page_entry(&gc_page_list, true);
 			return err;
 		}
+		add_gc_page_entry(&gc_page_list, sum_page, segno);
 		unlock_page(sum_page);
+		if (++gc_list_count >= window_granularity)
+			break;
 	}
 
 	blk_start_plug(&plug);
 
-	for (segno = start_segno; segno < end_segno; segno++) {
+	list_for_each_entry(gpe, &gc_page_list, list) {
 
 		/* find segment summary of victim */
-		sum_page = find_get_page(META_MAPPING(sbi),
-					GET_SUM_BLOCK(sbi, segno));
-		f2fs_put_page(sum_page, 0);
+		sum_page = gpe->sum_page;
+		segno = gpe->segno;
+		if (!sum_page) {
+			f2fs_err(sbi, "Failed to get summary page for segment %u", segno);
+			goto skip;
+		}
 
 		if (get_valid_blocks(sbi, segno, false) == 0)
 			goto freed;
@@ -1835,18 +1863,20 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi,
 				get_valid_blocks(sbi, segno, false) == 0)
 			seg_freed++;
 
-		if (__is_large_section(sbi))
-			sbi->next_victim_seg[gc_type] =
-				(segno + 1 < sec_end_segno) ?
-					segno + 1 : NULL_SEGNO;
 skip:
 		f2fs_put_page(sum_page, 0);
 	}
 
+	if (__is_large_section(sbi) && !list_empty(&gc_page_list))
+		sbi->next_victim_seg[gc_type] =
+			(segno + 1 < sec_end_segno) ?
+				segno + 1 : NULL_SEGNO;
+
 	if (submitted)
 		f2fs_submit_merged_write(sbi, data_type);
 
 	blk_finish_plug(&plug);
+	release_gc_page_entry(&gc_page_list, false);
 
 	if (migrated)
 		stat_inc_gc_sec_count(sbi, data_type, gc_type);
@@ -2008,6 +2038,18 @@ int f2fs_gc(struct f2fs_sb_info *sbi, struct f2fs_gc_control *gc_control)
 	return ret;
 }
 
+int __init f2fs_init_garbage_collection_summary_cache(void)
+{
+	gc_page_entry_slab = f2fs_kmem_cache_create("f2fs_gc_page_entry",
+					sizeof(struct gc_page_entry));
+	return gc_page_entry_slab ? 0 : -ENOMEM;
+}
+
+void f2fs_destroy_garbage_collection_summary_cache(void)
+{
+	kmem_cache_destroy(gc_page_entry_slab);
+}
+
 int __init f2fs_create_garbage_collection_cache(void)
 {
 	victim_entry_slab = f2fs_kmem_cache_create("f2fs_victim_entry",
diff --git a/fs/f2fs/gc.h b/fs/f2fs/gc.h
index 5c1eaf55e127..9c8695efe394 100644
--- a/fs/f2fs/gc.h
+++ b/fs/f2fs/gc.h
@@ -82,6 +82,12 @@ struct victim_entry {
 	struct list_head list;
 };
 
+struct gc_page_entry {
+	unsigned int segno;
+	struct page *sum_page;
+	struct list_head list;
+};
+
 /*
  * inline functions
  */
diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index f087b2b71c89..a3241730fe4f 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -5090,9 +5090,12 @@ static int __init init_f2fs_fs(void)
 	err = f2fs_create_garbage_collection_cache();
 	if (err)
 		goto free_extent_cache;
-	err = f2fs_init_sysfs();
+	err = f2fs_init_garbage_collection_summary_cache();
 	if (err)
 		goto free_garbage_collection_cache;
+	err = f2fs_init_sysfs();
+	if (err)
+		goto free_garbage_collection_summary_cache;
 	err = f2fs_init_shrinker();
 	if (err)
 		goto free_sysfs;
@@ -5141,6 +5144,8 @@ static int __init init_f2fs_fs(void)
 	f2fs_exit_shrinker();
 free_sysfs:
 	f2fs_exit_sysfs();
+free_garbage_collection_summary_cache:
+	f2fs_destroy_garbage_collection_summary_cache();
 free_garbage_collection_cache:
 	f2fs_destroy_garbage_collection_cache();
 free_extent_cache:
@@ -5172,6 +5177,7 @@ static void __exit exit_f2fs_fs(void)
 	f2fs_destroy_root_stats();
 	f2fs_exit_shrinker();
 	f2fs_exit_sysfs();
+	f2fs_destroy_garbage_collection_summary_cache();
 	f2fs_destroy_garbage_collection_cache();
 	f2fs_destroy_extent_cache();
 	f2fs_destroy_recovery_cache();
-- 
2.33.0
Re: [f2fs-dev] [PATCH v1] f2fs: Improve large section GC by locating valid block segments
Posted by yohan.joung 8 months, 1 week ago
hi jeageuk, chao 
How about changing the large section gc in this direction?
Thanks

> Change the large section GC to locate valid block segments instead of
> performing a sequential search. This modification enhances performance
> by reducing unnecessary block scanning in large storage sections.
> 
> example :
> [invalid seg 0] [invalid seg 1] [invalid seg 2]
> [  valid seg 3] [  valid seg 4] [  valid seg 5]
> 
> Current: In the first GC, nothing is moved,
> but in the second GC, segments 3, 4, and 5 are moved.
> Change: In the first GC, segments 3, 4, and 5 are moved.
> 
> Signed-off-by: yohan.joung <yohan.joung@sk.com>
> ---
>  fs/f2fs/f2fs.h  |  2 ++
>  fs/f2fs/gc.c    | 92 +++++++++++++++++++++++++++++++++++--------------
>  fs/f2fs/gc.h    |  6 ++++
>  fs/f2fs/super.c |  8 ++++-
>  4 files changed, 82 insertions(+), 26 deletions(-)
> 
> diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
> index f1576dc6ec67..348417edac25 100644
> --- a/fs/f2fs/f2fs.h
> +++ b/fs/f2fs/f2fs.h
> @@ -4008,6 +4008,8 @@ int f2fs_gc_range(struct f2fs_sb_info *sbi,
>  int f2fs_resize_fs(struct file *filp, __u64 block_count);
>  int __init f2fs_create_garbage_collection_cache(void);
>  void f2fs_destroy_garbage_collection_cache(void);
> +int __init f2fs_init_garbage_collection_summary_cache(void);
> +void f2fs_destroy_garbage_collection_summary_cache(void);
>  /* victim selection function for cleaning and SSR */
>  int f2fs_get_victim(struct f2fs_sb_info *sbi, unsigned int *result,
>  			int gc_type, int type, char alloc_mode,
> diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c
> index 2b8f9239bede..3b63e85fa038 100644
> --- a/fs/f2fs/gc.c
> +++ b/fs/f2fs/gc.c
> @@ -24,6 +24,7 @@
>  #include <trace/events/f2fs.h>
>  
>  static struct kmem_cache *victim_entry_slab;
> +static struct kmem_cache *gc_page_entry_slab;
>  
>  static unsigned int count_bits(const unsigned long *addr,
>  				unsigned int offset, unsigned int len);
> @@ -711,6 +712,30 @@ static void release_victim_entry(struct f2fs_sb_info *sbi)
>  	f2fs_bug_on(sbi, !list_empty(&am->victim_list));
>  }
>  
> +static struct gc_page_entry *add_gc_page_entry(struct list_head *gc_page_list,
> +					struct page *sum_page, unsigned int segno)
> +{
> +	struct gc_page_entry *gpe;
> +
> +	gpe = f2fs_kmem_cache_alloc(gc_page_entry_slab, GFP_NOFS, true, NULL);
> +	gpe->segno = segno;
> +	gpe->sum_page = sum_page;
> +	list_add_tail(&gpe->list, gc_page_list);
> +	return gpe;
> +}
> +
> +static void release_gc_page_entry(struct list_head *gc_page_list, bool putpage)
> +{
> +	struct gc_page_entry *gpe, *tmp;
> +
> +	list_for_each_entry_safe(gpe, tmp, gc_page_list, list) {
> +		if (putpage)
> +			f2fs_put_page(gpe->sum_page, 0);
> +		list_del(&gpe->list);
> +		kmem_cache_free(gc_page_entry_slab, gpe);
> +	}
> +}
> +
>  static bool f2fs_pin_section(struct f2fs_sb_info *sbi, unsigned int segno)
>  {
>  	struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
> @@ -1721,14 +1746,18 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi,
>  	struct page *sum_page;
>  	struct f2fs_summary_block *sum;
>  	struct blk_plug plug;
> +	struct gc_page_entry *gpe;
>  	unsigned int segno = start_segno;
>  	unsigned int end_segno = start_segno + SEGS_PER_SEC(sbi);
>  	unsigned int sec_end_segno;
> +	unsigned int window_granularity = 1;
>  	int seg_freed = 0, migrated = 0;
>  	unsigned char type = IS_DATASEG(get_seg_entry(sbi, segno)->type) ?
>  						SUM_TYPE_DATA : SUM_TYPE_NODE;
>  	unsigned char data_type = (type == SUM_TYPE_DATA) ? DATA : NODE;
>  	int submitted = 0;
> +	int gc_list_count = 0;
> +	LIST_HEAD(gc_page_list);
>  
>  	if (__is_large_section(sbi)) {
>  		sec_end_segno = rounddown(end_segno, SEGS_PER_SEC(sbi));
> @@ -1744,7 +1773,7 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi,
>  					f2fs_usable_segs_in_sec(sbi);
>  
>  		if (gc_type == BG_GC || one_time) {
> -			unsigned int window_granularity =
> +			window_granularity =
>  				sbi->migration_window_granularity;
>  
>  			if (f2fs_sb_has_blkzoned(sbi) &&
> @@ -1752,8 +1781,6 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi,
>  					sbi->gc_thread->boost_zoned_gc_percent))
>  				window_granularity *=
>  					BOOST_GC_MULTIPLE;
> -
> -			end_segno = start_segno + window_granularity;
>  		}
>  
>  		if (end_segno > sec_end_segno)
> @@ -1762,37 +1789,38 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi,
>  
>  	sanity_check_seg_type(sbi, get_seg_entry(sbi, segno)->type);
>  
> -	/* readahead multi ssa blocks those have contiguous address */
> -	if (__is_large_section(sbi))
> +	for (segno = start_segno; segno < end_segno; segno++) {
> +		if (!get_valid_blocks(sbi, segno, false))
> +			continue;
> +
> +		/* readahead multi ssa blocks those have contiguous address */
>  		f2fs_ra_meta_pages(sbi, GET_SUM_BLOCK(sbi, segno),
> -					end_segno - segno, META_SSA, true);
> +				window_granularity, META_SSA, true);
>  
> -	/* reference all summary page */
> -	while (segno < end_segno) {
> -		sum_page = f2fs_get_sum_page(sbi, segno++);
> +		/* reference all summary page */
> +		sum_page = f2fs_get_sum_page(sbi, segno);
>  		if (IS_ERR(sum_page)) {
>  			int err = PTR_ERR(sum_page);
> -
> -			end_segno = segno - 1;
> -			for (segno = start_segno; segno < end_segno; segno++) {
> -				sum_page = find_get_page(META_MAPPING(sbi),
> -						GET_SUM_BLOCK(sbi, segno));
> -				f2fs_put_page(sum_page, 0);
> -				f2fs_put_page(sum_page, 0);
> -			}
> +			release_gc_page_entry(&gc_page_list, true);
>  			return err;
>  		}
> +		add_gc_page_entry(&gc_page_list, sum_page, segno);
>  		unlock_page(sum_page);
> +		if (++gc_list_count >= window_granularity)
> +			break;
>  	}
>  
>  	blk_start_plug(&plug);
>  
> -	for (segno = start_segno; segno < end_segno; segno++) {
> +	list_for_each_entry(gpe, &gc_page_list, list) {
>  
>  		/* find segment summary of victim */
> -		sum_page = find_get_page(META_MAPPING(sbi),
> -					GET_SUM_BLOCK(sbi, segno));
> -		f2fs_put_page(sum_page, 0);
> +		sum_page = gpe->sum_page;
> +		segno = gpe->segno;
> +		if (!sum_page) {
> +			f2fs_err(sbi, "Failed to get summary page for segment %u", segno);
> +			goto skip;
> +		}
>  
>  		if (get_valid_blocks(sbi, segno, false) == 0)
>  			goto freed;
> @@ -1835,18 +1863,20 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi,
>  				get_valid_blocks(sbi, segno, false) == 0)
>  			seg_freed++;
>  
> -		if (__is_large_section(sbi))
> -			sbi->next_victim_seg[gc_type] =
> -				(segno + 1 < sec_end_segno) ?
> -					segno + 1 : NULL_SEGNO;
>  skip:
>  		f2fs_put_page(sum_page, 0);
>  	}
>  
> +	if (__is_large_section(sbi) && !list_empty(&gc_page_list))
> +		sbi->next_victim_seg[gc_type] =
> +			(segno + 1 < sec_end_segno) ?
> +				segno + 1 : NULL_SEGNO;
> +
>  	if (submitted)
>  		f2fs_submit_merged_write(sbi, data_type);
>  
>  	blk_finish_plug(&plug);
> +	release_gc_page_entry(&gc_page_list, false);
>  
>  	if (migrated)
>  		stat_inc_gc_sec_count(sbi, data_type, gc_type);
> @@ -2008,6 +2038,18 @@ int f2fs_gc(struct f2fs_sb_info *sbi, struct f2fs_gc_control *gc_control)
>  	return ret;
>  }
>  
> +int __init f2fs_init_garbage_collection_summary_cache(void)
> +{
> +	gc_page_entry_slab = f2fs_kmem_cache_create("f2fs_gc_page_entry",
> +					sizeof(struct gc_page_entry));
> +	return gc_page_entry_slab ? 0 : -ENOMEM;
> +}
> +
> +void f2fs_destroy_garbage_collection_summary_cache(void)
> +{
> +	kmem_cache_destroy(gc_page_entry_slab);
> +}
> +
>  int __init f2fs_create_garbage_collection_cache(void)
>  {
>  	victim_entry_slab = f2fs_kmem_cache_create("f2fs_victim_entry",
> diff --git a/fs/f2fs/gc.h b/fs/f2fs/gc.h
> index 5c1eaf55e127..9c8695efe394 100644
> --- a/fs/f2fs/gc.h
> +++ b/fs/f2fs/gc.h
> @@ -82,6 +82,12 @@ struct victim_entry {
>  	struct list_head list;
>  };
>  
> +struct gc_page_entry {
> +	unsigned int segno;
> +	struct page *sum_page;
> +	struct list_head list;
> +};
> +
>  /*
>   * inline functions
>   */
> diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
> index f087b2b71c89..a3241730fe4f 100644
> --- a/fs/f2fs/super.c
> +++ b/fs/f2fs/super.c
> @@ -5090,9 +5090,12 @@ static int __init init_f2fs_fs(void)
>  	err = f2fs_create_garbage_collection_cache();
>  	if (err)
>  		goto free_extent_cache;
> -	err = f2fs_init_sysfs();
> +	err = f2fs_init_garbage_collection_summary_cache();
>  	if (err)
>  		goto free_garbage_collection_cache;
> +	err = f2fs_init_sysfs();
> +	if (err)
> +		goto free_garbage_collection_summary_cache;
>  	err = f2fs_init_shrinker();
>  	if (err)
>  		goto free_sysfs;
> @@ -5141,6 +5144,8 @@ static int __init init_f2fs_fs(void)
>  	f2fs_exit_shrinker();
>  free_sysfs:
>  	f2fs_exit_sysfs();
> +free_garbage_collection_summary_cache:
> +	f2fs_destroy_garbage_collection_summary_cache();
>  free_garbage_collection_cache:
>  	f2fs_destroy_garbage_collection_cache();
>  free_extent_cache:
> @@ -5172,6 +5177,7 @@ static void __exit exit_f2fs_fs(void)
>  	f2fs_destroy_root_stats();
>  	f2fs_exit_shrinker();
>  	f2fs_exit_sysfs();
> +	f2fs_destroy_garbage_collection_summary_cache();
>  	f2fs_destroy_garbage_collection_cache();
>  	f2fs_destroy_extent_cache();
>  	f2fs_destroy_recovery_cache();
> -- 
> 2.33.0
> 
> 
> 
> _______________________________________________
> Linux-f2fs-devel mailing list
> Linux-f2fs-devel@lists.sourceforge.net
Re: [f2fs-dev] [PATCH v1] f2fs: Improve large section GC by locating valid block segments
Posted by Chao Yu 8 months, 1 week ago
Yohan,

Sorry for the delay, will catch up this a little bit latter.

On 2025/4/10 8:17, yohan.joung wrote:
> hi jeageuk, chao
> How about changing the large section gc in this direction?
> Thanks
> 
>> Change the large section GC to locate valid block segments instead of
>> performing a sequential search. This modification enhances performance
>> by reducing unnecessary block scanning in large storage sections.
>>
>> example :
>> [invalid seg 0] [invalid seg 1] [invalid seg 2]
>> [  valid seg 3] [  valid seg 4] [  valid seg 5]
>>
>> Current: In the first GC, nothing is moved,
>> but in the second GC, segments 3, 4, and 5 are moved.
>> Change: In the first GC, segments 3, 4, and 5 are moved.
>>
>> Signed-off-by: yohan.joung <yohan.joung@sk.com>
>> ---
>>   fs/f2fs/f2fs.h  |  2 ++
>>   fs/f2fs/gc.c    | 92 +++++++++++++++++++++++++++++++++++--------------
>>   fs/f2fs/gc.h    |  6 ++++
>>   fs/f2fs/super.c |  8 ++++-
>>   4 files changed, 82 insertions(+), 26 deletions(-)
>>
>> diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
>> index f1576dc6ec67..348417edac25 100644
>> --- a/fs/f2fs/f2fs.h
>> +++ b/fs/f2fs/f2fs.h
>> @@ -4008,6 +4008,8 @@ int f2fs_gc_range(struct f2fs_sb_info *sbi,
>>   int f2fs_resize_fs(struct file *filp, __u64 block_count);
>>   int __init f2fs_create_garbage_collection_cache(void);
>>   void f2fs_destroy_garbage_collection_cache(void);
>> +int __init f2fs_init_garbage_collection_summary_cache(void);
>> +void f2fs_destroy_garbage_collection_summary_cache(void);
>>   /* victim selection function for cleaning and SSR */
>>   int f2fs_get_victim(struct f2fs_sb_info *sbi, unsigned int *result,
>>   			int gc_type, int type, char alloc_mode,
>> diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c
>> index 2b8f9239bede..3b63e85fa038 100644
>> --- a/fs/f2fs/gc.c
>> +++ b/fs/f2fs/gc.c
>> @@ -24,6 +24,7 @@
>>   #include <trace/events/f2fs.h>
>>   
>>   static struct kmem_cache *victim_entry_slab;
>> +static struct kmem_cache *gc_page_entry_slab;
>>   
>>   static unsigned int count_bits(const unsigned long *addr,
>>   				unsigned int offset, unsigned int len);
>> @@ -711,6 +712,30 @@ static void release_victim_entry(struct f2fs_sb_info *sbi)
>>   	f2fs_bug_on(sbi, !list_empty(&am->victim_list));
>>   }
>>   
>> +static struct gc_page_entry *add_gc_page_entry(struct list_head *gc_page_list,
>> +					struct page *sum_page, unsigned int segno)
>> +{
>> +	struct gc_page_entry *gpe;
>> +
>> +	gpe = f2fs_kmem_cache_alloc(gc_page_entry_slab, GFP_NOFS, true, NULL);
>> +	gpe->segno = segno;
>> +	gpe->sum_page = sum_page;
>> +	list_add_tail(&gpe->list, gc_page_list);
>> +	return gpe;
>> +}
>> +
>> +static void release_gc_page_entry(struct list_head *gc_page_list, bool putpage)
>> +{
>> +	struct gc_page_entry *gpe, *tmp;
>> +
>> +	list_for_each_entry_safe(gpe, tmp, gc_page_list, list) {
>> +		if (putpage)
>> +			f2fs_put_page(gpe->sum_page, 0);
>> +		list_del(&gpe->list);
>> +		kmem_cache_free(gc_page_entry_slab, gpe);
>> +	}
>> +}
>> +
>>   static bool f2fs_pin_section(struct f2fs_sb_info *sbi, unsigned int segno)
>>   {
>>   	struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
>> @@ -1721,14 +1746,18 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi,
>>   	struct page *sum_page;
>>   	struct f2fs_summary_block *sum;
>>   	struct blk_plug plug;
>> +	struct gc_page_entry *gpe;
>>   	unsigned int segno = start_segno;
>>   	unsigned int end_segno = start_segno + SEGS_PER_SEC(sbi);
>>   	unsigned int sec_end_segno;
>> +	unsigned int window_granularity = 1;
>>   	int seg_freed = 0, migrated = 0;
>>   	unsigned char type = IS_DATASEG(get_seg_entry(sbi, segno)->type) ?
>>   						SUM_TYPE_DATA : SUM_TYPE_NODE;
>>   	unsigned char data_type = (type == SUM_TYPE_DATA) ? DATA : NODE;
>>   	int submitted = 0;
>> +	int gc_list_count = 0;
>> +	LIST_HEAD(gc_page_list);
>>   
>>   	if (__is_large_section(sbi)) {
>>   		sec_end_segno = rounddown(end_segno, SEGS_PER_SEC(sbi));
>> @@ -1744,7 +1773,7 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi,
>>   					f2fs_usable_segs_in_sec(sbi);
>>   
>>   		if (gc_type == BG_GC || one_time) {
>> -			unsigned int window_granularity =
>> +			window_granularity =
>>   				sbi->migration_window_granularity;
>>   
>>   			if (f2fs_sb_has_blkzoned(sbi) &&
>> @@ -1752,8 +1781,6 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi,
>>   					sbi->gc_thread->boost_zoned_gc_percent))
>>   				window_granularity *=
>>   					BOOST_GC_MULTIPLE;
>> -
>> -			end_segno = start_segno + window_granularity;
>>   		}
>>   
>>   		if (end_segno > sec_end_segno)
>> @@ -1762,37 +1789,38 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi,
>>   
>>   	sanity_check_seg_type(sbi, get_seg_entry(sbi, segno)->type);
>>   
>> -	/* readahead multi ssa blocks those have contiguous address */
>> -	if (__is_large_section(sbi))
>> +	for (segno = start_segno; segno < end_segno; segno++) {
>> +		if (!get_valid_blocks(sbi, segno, false))
>> +			continue;
>> +
>> +		/* readahead multi ssa blocks those have contiguous address */
>>   		f2fs_ra_meta_pages(sbi, GET_SUM_BLOCK(sbi, segno),
>> -					end_segno - segno, META_SSA, true);
>> +				window_granularity, META_SSA, true);
>>   
>> -	/* reference all summary page */
>> -	while (segno < end_segno) {
>> -		sum_page = f2fs_get_sum_page(sbi, segno++);
>> +		/* reference all summary page */
>> +		sum_page = f2fs_get_sum_page(sbi, segno);
>>   		if (IS_ERR(sum_page)) {
>>   			int err = PTR_ERR(sum_page);
>> -
>> -			end_segno = segno - 1;
>> -			for (segno = start_segno; segno < end_segno; segno++) {
>> -				sum_page = find_get_page(META_MAPPING(sbi),
>> -						GET_SUM_BLOCK(sbi, segno));
>> -				f2fs_put_page(sum_page, 0);
>> -				f2fs_put_page(sum_page, 0);
>> -			}
>> +			release_gc_page_entry(&gc_page_list, true);
>>   			return err;
>>   		}
>> +		add_gc_page_entry(&gc_page_list, sum_page, segno);
>>   		unlock_page(sum_page);
>> +		if (++gc_list_count >= window_granularity)
>> +			break;
>>   	}
>>   
>>   	blk_start_plug(&plug);
>>   
>> -	for (segno = start_segno; segno < end_segno; segno++) {
>> +	list_for_each_entry(gpe, &gc_page_list, list) {
>>   
>>   		/* find segment summary of victim */
>> -		sum_page = find_get_page(META_MAPPING(sbi),
>> -					GET_SUM_BLOCK(sbi, segno));
>> -		f2fs_put_page(sum_page, 0);
>> +		sum_page = gpe->sum_page;
>> +		segno = gpe->segno;
>> +		if (!sum_page) {
>> +			f2fs_err(sbi, "Failed to get summary page for segment %u", segno);
>> +			goto skip;
>> +		}
>>   
>>   		if (get_valid_blocks(sbi, segno, false) == 0)
>>   			goto freed;
>> @@ -1835,18 +1863,20 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi,
>>   				get_valid_blocks(sbi, segno, false) == 0)
>>   			seg_freed++;
>>   
>> -		if (__is_large_section(sbi))
>> -			sbi->next_victim_seg[gc_type] =
>> -				(segno + 1 < sec_end_segno) ?
>> -					segno + 1 : NULL_SEGNO;
>>   skip:
>>   		f2fs_put_page(sum_page, 0);
>>   	}
>>   
>> +	if (__is_large_section(sbi) && !list_empty(&gc_page_list))
>> +		sbi->next_victim_seg[gc_type] =
>> +			(segno + 1 < sec_end_segno) ?
>> +				segno + 1 : NULL_SEGNO;
>> +
>>   	if (submitted)
>>   		f2fs_submit_merged_write(sbi, data_type);
>>   
>>   	blk_finish_plug(&plug);
>> +	release_gc_page_entry(&gc_page_list, false);
>>   
>>   	if (migrated)
>>   		stat_inc_gc_sec_count(sbi, data_type, gc_type);
>> @@ -2008,6 +2038,18 @@ int f2fs_gc(struct f2fs_sb_info *sbi, struct f2fs_gc_control *gc_control)
>>   	return ret;
>>   }
>>   
>> +int __init f2fs_init_garbage_collection_summary_cache(void)
>> +{
>> +	gc_page_entry_slab = f2fs_kmem_cache_create("f2fs_gc_page_entry",
>> +					sizeof(struct gc_page_entry));
>> +	return gc_page_entry_slab ? 0 : -ENOMEM;
>> +}
>> +
>> +void f2fs_destroy_garbage_collection_summary_cache(void)
>> +{
>> +	kmem_cache_destroy(gc_page_entry_slab);
>> +}
>> +
>>   int __init f2fs_create_garbage_collection_cache(void)
>>   {
>>   	victim_entry_slab = f2fs_kmem_cache_create("f2fs_victim_entry",
>> diff --git a/fs/f2fs/gc.h b/fs/f2fs/gc.h
>> index 5c1eaf55e127..9c8695efe394 100644
>> --- a/fs/f2fs/gc.h
>> +++ b/fs/f2fs/gc.h
>> @@ -82,6 +82,12 @@ struct victim_entry {
>>   	struct list_head list;
>>   };
>>   
>> +struct gc_page_entry {
>> +	unsigned int segno;
>> +	struct page *sum_page;
>> +	struct list_head list;
>> +};
>> +
>>   /*
>>    * inline functions
>>    */
>> diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
>> index f087b2b71c89..a3241730fe4f 100644
>> --- a/fs/f2fs/super.c
>> +++ b/fs/f2fs/super.c
>> @@ -5090,9 +5090,12 @@ static int __init init_f2fs_fs(void)
>>   	err = f2fs_create_garbage_collection_cache();
>>   	if (err)
>>   		goto free_extent_cache;
>> -	err = f2fs_init_sysfs();
>> +	err = f2fs_init_garbage_collection_summary_cache();
>>   	if (err)
>>   		goto free_garbage_collection_cache;
>> +	err = f2fs_init_sysfs();
>> +	if (err)
>> +		goto free_garbage_collection_summary_cache;
>>   	err = f2fs_init_shrinker();
>>   	if (err)
>>   		goto free_sysfs;
>> @@ -5141,6 +5144,8 @@ static int __init init_f2fs_fs(void)
>>   	f2fs_exit_shrinker();
>>   free_sysfs:
>>   	f2fs_exit_sysfs();
>> +free_garbage_collection_summary_cache:
>> +	f2fs_destroy_garbage_collection_summary_cache();
>>   free_garbage_collection_cache:
>>   	f2fs_destroy_garbage_collection_cache();
>>   free_extent_cache:
>> @@ -5172,6 +5177,7 @@ static void __exit exit_f2fs_fs(void)
>>   	f2fs_destroy_root_stats();
>>   	f2fs_exit_shrinker();
>>   	f2fs_exit_sysfs();
>> +	f2fs_destroy_garbage_collection_summary_cache();
>>   	f2fs_destroy_garbage_collection_cache();
>>   	f2fs_destroy_extent_cache();
>>   	f2fs_destroy_recovery_cache();
>> -- 
>> 2.33.0
>>
>>
>>
>> _______________________________________________
>> Linux-f2fs-devel mailing list
>> Linux-f2fs-devel@lists.sourceforge.net
Re: [f2fs-dev] [PATCH v1] f2fs: Improve large section GC by locating valid block segments
Posted by Daeho Jeong 8 months ago
On Fri, Apr 11, 2025 at 3:07 AM Chao Yu <chao@kernel.org> wrote:
>
> Yohan,
>
> Sorry for the delay, will catch up this a little bit latter.
>
> On 2025/4/10 8:17, yohan.joung wrote:
> > hi jeageuk, chao
> > How about changing the large section gc in this direction?
> > Thanks
> >
> >> Change the large section GC to locate valid block segments instead of
> >> performing a sequential search. This modification enhances performance
> >> by reducing unnecessary block scanning in large storage sections.
> >>
> >> example :
> >> [invalid seg 0] [invalid seg 1] [invalid seg 2]
> >> [  valid seg 3] [  valid seg 4] [  valid seg 5]
> >>
> >> Current: In the first GC, nothing is moved,
> >> but in the second GC, segments 3, 4, and 5 are moved.
> >> Change: In the first GC, segments 3, 4, and 5 are moved.
> >>
> >> Signed-off-by: yohan.joung <yohan.joung@sk.com>
> >> ---
> >>   fs/f2fs/f2fs.h  |  2 ++
> >>   fs/f2fs/gc.c    | 92 +++++++++++++++++++++++++++++++++++--------------
> >>   fs/f2fs/gc.h    |  6 ++++
> >>   fs/f2fs/super.c |  8 ++++-
> >>   4 files changed, 82 insertions(+), 26 deletions(-)
> >>
> >> diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
> >> index f1576dc6ec67..348417edac25 100644
> >> --- a/fs/f2fs/f2fs.h
> >> +++ b/fs/f2fs/f2fs.h
> >> @@ -4008,6 +4008,8 @@ int f2fs_gc_range(struct f2fs_sb_info *sbi,
> >>   int f2fs_resize_fs(struct file *filp, __u64 block_count);
> >>   int __init f2fs_create_garbage_collection_cache(void);
> >>   void f2fs_destroy_garbage_collection_cache(void);
> >> +int __init f2fs_init_garbage_collection_summary_cache(void);
> >> +void f2fs_destroy_garbage_collection_summary_cache(void);
> >>   /* victim selection function for cleaning and SSR */
> >>   int f2fs_get_victim(struct f2fs_sb_info *sbi, unsigned int *result,
> >>                      int gc_type, int type, char alloc_mode,
> >> diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c
> >> index 2b8f9239bede..3b63e85fa038 100644
> >> --- a/fs/f2fs/gc.c
> >> +++ b/fs/f2fs/gc.c
> >> @@ -24,6 +24,7 @@
> >>   #include <trace/events/f2fs.h>
> >>
> >>   static struct kmem_cache *victim_entry_slab;
> >> +static struct kmem_cache *gc_page_entry_slab;
> >>
> >>   static unsigned int count_bits(const unsigned long *addr,
> >>                              unsigned int offset, unsigned int len);
> >> @@ -711,6 +712,30 @@ static void release_victim_entry(struct f2fs_sb_info *sbi)
> >>      f2fs_bug_on(sbi, !list_empty(&am->victim_list));
> >>   }
> >>
> >> +static struct gc_page_entry *add_gc_page_entry(struct list_head *gc_page_list,
> >> +                                    struct page *sum_page, unsigned int segno)
> >> +{
> >> +    struct gc_page_entry *gpe;
> >> +
> >> +    gpe = f2fs_kmem_cache_alloc(gc_page_entry_slab, GFP_NOFS, true, NULL);
> >> +    gpe->segno = segno;
> >> +    gpe->sum_page = sum_page;
> >> +    list_add_tail(&gpe->list, gc_page_list);
> >> +    return gpe;
> >> +}
> >> +
> >> +static void release_gc_page_entry(struct list_head *gc_page_list, bool putpage)
> >> +{
> >> +    struct gc_page_entry *gpe, *tmp;
> >> +
> >> +    list_for_each_entry_safe(gpe, tmp, gc_page_list, list) {
> >> +            if (putpage)
> >> +                    f2fs_put_page(gpe->sum_page, 0);
> >> +            list_del(&gpe->list);
> >> +            kmem_cache_free(gc_page_entry_slab, gpe);
> >> +    }
> >> +}
> >> +
> >>   static bool f2fs_pin_section(struct f2fs_sb_info *sbi, unsigned int segno)
> >>   {
> >>      struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
> >> @@ -1721,14 +1746,18 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi,
> >>      struct page *sum_page;
> >>      struct f2fs_summary_block *sum;
> >>      struct blk_plug plug;
> >> +    struct gc_page_entry *gpe;
> >>      unsigned int segno = start_segno;
> >>      unsigned int end_segno = start_segno + SEGS_PER_SEC(sbi);
> >>      unsigned int sec_end_segno;
> >> +    unsigned int window_granularity = 1;
> >>      int seg_freed = 0, migrated = 0;
> >>      unsigned char type = IS_DATASEG(get_seg_entry(sbi, segno)->type) ?
> >>                                              SUM_TYPE_DATA : SUM_TYPE_NODE;
> >>      unsigned char data_type = (type == SUM_TYPE_DATA) ? DATA : NODE;
> >>      int submitted = 0;
> >> +    int gc_list_count = 0;
> >> +    LIST_HEAD(gc_page_list);
> >>
> >>      if (__is_large_section(sbi)) {
> >>              sec_end_segno = rounddown(end_segno, SEGS_PER_SEC(sbi));
> >> @@ -1744,7 +1773,7 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi,
> >>                                      f2fs_usable_segs_in_sec(sbi);
> >>
> >>              if (gc_type == BG_GC || one_time) {
> >> -                    unsigned int window_granularity =
> >> +                    window_granularity =
> >>                              sbi->migration_window_granularity;

Plz, refer to the below described in sysfs-fs-f2fs.

What:           /sys/fs/f2fs/<disk>/migration_window_granularity
Date:           September 2024
Contact:        "Daeho Jeong" <daehojeong@google.com>
Description:    Controls migration window granularity of garbage
collection on large
                section. it can control the scanning window
granularity for GC migration
                in a unit of segment, while migration_granularity
controls the number
                of segments which can be migrated at the same turn.

In your patch, it's more like migration_granularity.
Plus, I think we don't need migration_window_granularity anymore with
your patch.

> >>
> >>                      if (f2fs_sb_has_blkzoned(sbi) &&
> >> @@ -1752,8 +1781,6 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi,
> >>                                      sbi->gc_thread->boost_zoned_gc_percent))
> >>                              window_granularity *=
> >>                                      BOOST_GC_MULTIPLE;
> >> -
> >> -                    end_segno = start_segno + window_granularity;
> >>              }
> >>
> >>              if (end_segno > sec_end_segno)
> >> @@ -1762,37 +1789,38 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi,
> >>
> >>      sanity_check_seg_type(sbi, get_seg_entry(sbi, segno)->type);
> >>
> >> -    /* readahead multi ssa blocks those have contiguous address */
> >> -    if (__is_large_section(sbi))
> >> +    for (segno = start_segno; segno < end_segno; segno++) {
> >> +            if (!get_valid_blocks(sbi, segno, false))
> >> +                    continue;
> >> +
> >> +            /* readahead multi ssa blocks those have contiguous address */
> >>              f2fs_ra_meta_pages(sbi, GET_SUM_BLOCK(sbi, segno),
> >> -                                    end_segno - segno, META_SSA, true);
> >> +                            window_granularity, META_SSA, true);
> >>
> >> -    /* reference all summary page */
> >> -    while (segno < end_segno) {
> >> -            sum_page = f2fs_get_sum_page(sbi, segno++);
> >> +            /* reference all summary page */
> >> +            sum_page = f2fs_get_sum_page(sbi, segno);
> >>              if (IS_ERR(sum_page)) {
> >>                      int err = PTR_ERR(sum_page);
> >> -
> >> -                    end_segno = segno - 1;
> >> -                    for (segno = start_segno; segno < end_segno; segno++) {
> >> -                            sum_page = find_get_page(META_MAPPING(sbi),
> >> -                                            GET_SUM_BLOCK(sbi, segno));
> >> -                            f2fs_put_page(sum_page, 0);
> >> -                            f2fs_put_page(sum_page, 0);
> >> -                    }
> >> +                    release_gc_page_entry(&gc_page_list, true);
> >>                      return err;
> >>              }
> >> +            add_gc_page_entry(&gc_page_list, sum_page, segno);
> >>              unlock_page(sum_page);
> >> +            if (++gc_list_count >= window_granularity)
> >> +                    break;
> >>      }
> >>
> >>      blk_start_plug(&plug);
> >>
> >> -    for (segno = start_segno; segno < end_segno; segno++) {
> >> +    list_for_each_entry(gpe, &gc_page_list, list) {
> >>
> >>              /* find segment summary of victim */
> >> -            sum_page = find_get_page(META_MAPPING(sbi),
> >> -                                    GET_SUM_BLOCK(sbi, segno));
> >> -            f2fs_put_page(sum_page, 0);
> >> +            sum_page = gpe->sum_page;
> >> +            segno = gpe->segno;
> >> +            if (!sum_page) {
> >> +                    f2fs_err(sbi, "Failed to get summary page for segment %u", segno);
> >> +                    goto skip;
> >> +            }
> >>
> >>              if (get_valid_blocks(sbi, segno, false) == 0)
> >>                      goto freed;
> >> @@ -1835,18 +1863,20 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi,
> >>                              get_valid_blocks(sbi, segno, false) == 0)
> >>                      seg_freed++;
> >>
> >> -            if (__is_large_section(sbi))
> >> -                    sbi->next_victim_seg[gc_type] =
> >> -                            (segno + 1 < sec_end_segno) ?
> >> -                                    segno + 1 : NULL_SEGNO;
> >>   skip:
> >>              f2fs_put_page(sum_page, 0);
> >>      }
> >>
> >> +    if (__is_large_section(sbi) && !list_empty(&gc_page_list))
> >> +            sbi->next_victim_seg[gc_type] =
> >> +                    (segno + 1 < sec_end_segno) ?
> >> +                            segno + 1 : NULL_SEGNO;
> >> +
> >>      if (submitted)
> >>              f2fs_submit_merged_write(sbi, data_type);
> >>
> >>      blk_finish_plug(&plug);
> >> +    release_gc_page_entry(&gc_page_list, false);
> >>
> >>      if (migrated)
> >>              stat_inc_gc_sec_count(sbi, data_type, gc_type);
> >> @@ -2008,6 +2038,18 @@ int f2fs_gc(struct f2fs_sb_info *sbi, struct f2fs_gc_control *gc_control)
> >>      return ret;
> >>   }
> >>
> >> +int __init f2fs_init_garbage_collection_summary_cache(void)
> >> +{
> >> +    gc_page_entry_slab = f2fs_kmem_cache_create("f2fs_gc_page_entry",
> >> +                                    sizeof(struct gc_page_entry));
> >> +    return gc_page_entry_slab ? 0 : -ENOMEM;
> >> +}
> >> +
> >> +void f2fs_destroy_garbage_collection_summary_cache(void)
> >> +{
> >> +    kmem_cache_destroy(gc_page_entry_slab);
> >> +}
> >> +
> >>   int __init f2fs_create_garbage_collection_cache(void)
> >>   {
> >>      victim_entry_slab = f2fs_kmem_cache_create("f2fs_victim_entry",
> >> diff --git a/fs/f2fs/gc.h b/fs/f2fs/gc.h
> >> index 5c1eaf55e127..9c8695efe394 100644
> >> --- a/fs/f2fs/gc.h
> >> +++ b/fs/f2fs/gc.h
> >> @@ -82,6 +82,12 @@ struct victim_entry {
> >>      struct list_head list;
> >>   };
> >>
> >> +struct gc_page_entry {
> >> +    unsigned int segno;
> >> +    struct page *sum_page;
> >> +    struct list_head list;
> >> +};
> >> +
> >>   /*
> >>    * inline functions
> >>    */
> >> diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
> >> index f087b2b71c89..a3241730fe4f 100644
> >> --- a/fs/f2fs/super.c
> >> +++ b/fs/f2fs/super.c
> >> @@ -5090,9 +5090,12 @@ static int __init init_f2fs_fs(void)
> >>      err = f2fs_create_garbage_collection_cache();
> >>      if (err)
> >>              goto free_extent_cache;
> >> -    err = f2fs_init_sysfs();
> >> +    err = f2fs_init_garbage_collection_summary_cache();
> >>      if (err)
> >>              goto free_garbage_collection_cache;
> >> +    err = f2fs_init_sysfs();
> >> +    if (err)
> >> +            goto free_garbage_collection_summary_cache;
> >>      err = f2fs_init_shrinker();
> >>      if (err)
> >>              goto free_sysfs;
> >> @@ -5141,6 +5144,8 @@ static int __init init_f2fs_fs(void)
> >>      f2fs_exit_shrinker();
> >>   free_sysfs:
> >>      f2fs_exit_sysfs();
> >> +free_garbage_collection_summary_cache:
> >> +    f2fs_destroy_garbage_collection_summary_cache();
> >>   free_garbage_collection_cache:
> >>      f2fs_destroy_garbage_collection_cache();
> >>   free_extent_cache:
> >> @@ -5172,6 +5177,7 @@ static void __exit exit_f2fs_fs(void)
> >>      f2fs_destroy_root_stats();
> >>      f2fs_exit_shrinker();
> >>      f2fs_exit_sysfs();
> >> +    f2fs_destroy_garbage_collection_summary_cache();
> >>      f2fs_destroy_garbage_collection_cache();
> >>      f2fs_destroy_extent_cache();
> >>      f2fs_destroy_recovery_cache();
> >> --
> >> 2.33.0
> >>

Do you have any numbers showing how much the performance is enhanced?

Thanks.

> >>
> >>
> >> _______________________________________________
> >> Linux-f2fs-devel mailing list
> >> Linux-f2fs-devel@lists.sourceforge.net
>
Re: [f2fs-dev] [PATCH v1] f2fs: Improve large section GC by locating valid block segments
Posted by yohan.joung 8 months ago
>On Fri, Apr 11, 2025 at 3:07 AM Chao Yu <chao@kernel.org> wrote:
>>
>> Yohan,
>>
>> Sorry for the delay, will catch up this a little bit latter.
>>
>> On 2025/4/10 8:17, yohan.joung wrote:
>> > hi jeageuk, chao
>> > How about changing the large section gc in this direction?
>> > Thanks
>> >
>> >> Change the large section GC to locate valid block segments instead of
>> >> performing a sequential search. This modification enhances performance
>> >> by reducing unnecessary block scanning in large storage sections.
>> >>
>> >> example :
>> >> [invalid seg 0] [invalid seg 1] [invalid seg 2]
>> >> [  valid seg 3] [  valid seg 4] [  valid seg 5]
>> >>
>> >> Current: In the first GC, nothing is moved,
>> >> but in the second GC, segments 3, 4, and 5 are moved.
>> >> Change: In the first GC, segments 3, 4, and 5 are moved.
>> >>
>> >> Signed-off-by: yohan.joung <yohan.joung@sk.com>
>> >> ---
>> >>   fs/f2fs/f2fs.h  |  2 ++
>> >>   fs/f2fs/gc.c    | 92 +++++++++++++++++++++++++++++++++++--------------
>> >>   fs/f2fs/gc.h    |  6 ++++
>> >>   fs/f2fs/super.c |  8 ++++-
>> >>   4 files changed, 82 insertions(+), 26 deletions(-)
>> >>
>> >> diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
>> >> index f1576dc6ec67..348417edac25 100644
>> >> --- a/fs/f2fs/f2fs.h
>> >> +++ b/fs/f2fs/f2fs.h
>> >> @@ -4008,6 +4008,8 @@ int f2fs_gc_range(struct f2fs_sb_info *sbi,
>> >>   int f2fs_resize_fs(struct file *filp, __u64 block_count);
>> >>   int __init f2fs_create_garbage_collection_cache(void);
>> >>   void f2fs_destroy_garbage_collection_cache(void);
>> >> +int __init f2fs_init_garbage_collection_summary_cache(void);
>> >> +void f2fs_destroy_garbage_collection_summary_cache(void);
>> >>   /* victim selection function for cleaning and SSR */
>> >>   int f2fs_get_victim(struct f2fs_sb_info *sbi, unsigned int *result,
>> >>                      int gc_type, int type, char alloc_mode,
>> >> diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c
>> >> index 2b8f9239bede..3b63e85fa038 100644
>> >> --- a/fs/f2fs/gc.c
>> >> +++ b/fs/f2fs/gc.c
>> >> @@ -24,6 +24,7 @@
>> >>   #include <trace/events/f2fs.h>
>> >>
>> >>   static struct kmem_cache *victim_entry_slab;
>> >> +static struct kmem_cache *gc_page_entry_slab;
>> >>
>> >>   static unsigned int count_bits(const unsigned long *addr,
>> >>                              unsigned int offset, unsigned int len);
>> >> @@ -711,6 +712,30 @@ static void release_victim_entry(struct f2fs_sb_info *sbi)
>> >>      f2fs_bug_on(sbi, !list_empty(&am->victim_list));
>> >>   }
>> >>
>> >> +static struct gc_page_entry *add_gc_page_entry(struct list_head *gc_page_list,
>> >> +                                    struct page *sum_page, unsigned int segno)
>> >> +{
>> >> +    struct gc_page_entry *gpe;
>> >> +
>> >> +    gpe = f2fs_kmem_cache_alloc(gc_page_entry_slab, GFP_NOFS, true, NULL);
>> >> +    gpe->segno = segno;
>> >> +    gpe->sum_page = sum_page;
>> >> +    list_add_tail(&gpe->list, gc_page_list);
>> >> +    return gpe;
>> >> +}
>> >> +
>> >> +static void release_gc_page_entry(struct list_head *gc_page_list, bool putpage)
>> >> +{
>> >> +    struct gc_page_entry *gpe, *tmp;
>> >> +
>> >> +    list_for_each_entry_safe(gpe, tmp, gc_page_list, list) {
>> >> +            if (putpage)
>> >> +                    f2fs_put_page(gpe->sum_page, 0);
>> >> +            list_del(&gpe->list);
>> >> +            kmem_cache_free(gc_page_entry_slab, gpe);
>> >> +    }
>> >> +}
>> >> +
>> >>   static bool f2fs_pin_section(struct f2fs_sb_info *sbi, unsigned int segno)
>> >>   {
>> >>      struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
>> >> @@ -1721,14 +1746,18 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi,
>> >>      struct page *sum_page;
>> >>      struct f2fs_summary_block *sum;
>> >>      struct blk_plug plug;
>> >> +    struct gc_page_entry *gpe;
>> >>      unsigned int segno = start_segno;
>> >>      unsigned int end_segno = start_segno + SEGS_PER_SEC(sbi);
>> >>      unsigned int sec_end_segno;
>> >> +    unsigned int window_granularity = 1;
>> >>      int seg_freed = 0, migrated = 0;
>> >>      unsigned char type = IS_DATASEG(get_seg_entry(sbi, segno)->type) ?
>> >>                                              SUM_TYPE_DATA : SUM_TYPE_NODE;
>> >>      unsigned char data_type = (type == SUM_TYPE_DATA) ? DATA : NODE;
>> >>      int submitted = 0;
>> >> +    int gc_list_count = 0;
>> >> +    LIST_HEAD(gc_page_list);
>> >>
>> >>      if (__is_large_section(sbi)) {
>> >>              sec_end_segno = rounddown(end_segno, SEGS_PER_SEC(sbi));
>> >> @@ -1744,7 +1773,7 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi,
>> >>                                      f2fs_usable_segs_in_sec(sbi);
>> >>
>> >>              if (gc_type == BG_GC || one_time) {
>> >> -                    unsigned int window_granularity =
>> >> +                    window_granularity =
>> >>                              sbi->migration_window_granularity;
>
>Plz, refer to the below described in sysfs-fs-f2fs.
Thanks, I'll take a look at this.
>
>What:           /sys/fs/f2fs/<disk>/migration_window_granularity
>Date:           September 2024
>Contact:        "Daeho Jeong" <daehojeong@google.com>
>Description:    Controls migration window granularity of garbage
>collection on large
>                section. it can control the scanning window
>granularity for GC migration
>                in a unit of segment, while migration_granularity
>controls the number
>                of segments which can be migrated at the same turn.
>
>In your patch, it's more like migration_granularity.
>Plus, I think we don't need migration_window_granularity anymore with
>your patch.
>
>> >>
>> >>                      if (f2fs_sb_has_blkzoned(sbi) &&
>> >> @@ -1752,8 +1781,6 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi,
>> >>                                      sbi->gc_thread->boost_zoned_gc_percent))
>> >>                              window_granularity *=
>> >>                                      BOOST_GC_MULTIPLE;
>> >> -
>> >> -                    end_segno = start_segno + window_granularity;
>> >>              }
>> >>
>> >>              if (end_segno > sec_end_segno)
>> >> @@ -1762,37 +1789,38 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi,
>> >>
>> >>      sanity_check_seg_type(sbi, get_seg_entry(sbi, segno)->type);
>> >>
>> >> -    /* readahead multi ssa blocks those have contiguous address */
>> >> -    if (__is_large_section(sbi))
>> >> +    for (segno = start_segno; segno < end_segno; segno++) {
>> >> +            if (!get_valid_blocks(sbi, segno, false))
>> >> +                    continue;
>> >> +
>> >> +            /* readahead multi ssa blocks those have contiguous address */
>> >>              f2fs_ra_meta_pages(sbi, GET_SUM_BLOCK(sbi, segno),
>> >> -                                    end_segno - segno, META_SSA, true);
>> >> +                            window_granularity, META_SSA, true);
>> >>
>> >> -    /* reference all summary page */
>> >> -    while (segno < end_segno) {
>> >> -            sum_page = f2fs_get_sum_page(sbi, segno++);
>> >> +            /* reference all summary page */
>> >> +            sum_page = f2fs_get_sum_page(sbi, segno);
>> >>              if (IS_ERR(sum_page)) {
>> >>                      int err = PTR_ERR(sum_page);
>> >> -
>> >> -                    end_segno = segno - 1;
>> >> -                    for (segno = start_segno; segno < end_segno; segno++) {
>> >> -                            sum_page = find_get_page(META_MAPPING(sbi),
>> >> -                                            GET_SUM_BLOCK(sbi, segno));
>> >> -                            f2fs_put_page(sum_page, 0);
>> >> -                            f2fs_put_page(sum_page, 0);
>> >> -                    }
>> >> +                    release_gc_page_entry(&gc_page_list, true);
>> >>                      return err;
>> >>              }
>> >> +            add_gc_page_entry(&gc_page_list, sum_page, segno);
>> >>              unlock_page(sum_page);
>> >> +            if (++gc_list_count >= window_granularity)
>> >> +                    break;
>> >>      }
>> >>
>> >>      blk_start_plug(&plug);
>> >>
>> >> -    for (segno = start_segno; segno < end_segno; segno++) {
>> >> +    list_for_each_entry(gpe, &gc_page_list, list) {
>> >>
>> >>              /* find segment summary of victim */
>> >> -            sum_page = find_get_page(META_MAPPING(sbi),
>> >> -                                    GET_SUM_BLOCK(sbi, segno));
>> >> -            f2fs_put_page(sum_page, 0);
>> >> +            sum_page = gpe->sum_page;
>> >> +            segno = gpe->segno;
>> >> +            if (!sum_page) {
>> >> +                    f2fs_err(sbi, "Failed to get summary page for segment %u", segno);
>> >> +                    goto skip;
>> >> +            }
>> >>
>> >>              if (get_valid_blocks(sbi, segno, false) == 0)
>> >>                      goto freed;
>> >> @@ -1835,18 +1863,20 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi,
>> >>                              get_valid_blocks(sbi, segno, false) == 0)
>> >>                      seg_freed++;
>> >>
>> >> -            if (__is_large_section(sbi))
>> >> -                    sbi->next_victim_seg[gc_type] =
>> >> -                            (segno + 1 < sec_end_segno) ?
>> >> -                                    segno + 1 : NULL_SEGNO;
>> >>   skip:
>> >>              f2fs_put_page(sum_page, 0);
>> >>      }
>> >>
>> >> +    if (__is_large_section(sbi) && !list_empty(&gc_page_list))
>> >> +            sbi->next_victim_seg[gc_type] =
>> >> +                    (segno + 1 < sec_end_segno) ?
>> >> +                            segno + 1 : NULL_SEGNO;
>> >> +
>> >>      if (submitted)
>> >>              f2fs_submit_merged_write(sbi, data_type);
>> >>
>> >>      blk_finish_plug(&plug);
>> >> +    release_gc_page_entry(&gc_page_list, false);
>> >>
>> >>      if (migrated)
>> >>              stat_inc_gc_sec_count(sbi, data_type, gc_type);
>> >> @@ -2008,6 +2038,18 @@ int f2fs_gc(struct f2fs_sb_info *sbi, struct f2fs_gc_control *gc_control)
>> >>      return ret;
>> >>   }
>> >>
>> >> +int __init f2fs_init_garbage_collection_summary_cache(void)
>> >> +{
>> >> +    gc_page_entry_slab = f2fs_kmem_cache_create("f2fs_gc_page_entry",
>> >> +                                    sizeof(struct gc_page_entry));
>> >> +    return gc_page_entry_slab ? 0 : -ENOMEM;
>> >> +}
>> >> +
>> >> +void f2fs_destroy_garbage_collection_summary_cache(void)
>> >> +{
>> >> +    kmem_cache_destroy(gc_page_entry_slab);
>> >> +}
>> >> +
>> >>   int __init f2fs_create_garbage_collection_cache(void)
>> >>   {
>> >>      victim_entry_slab = f2fs_kmem_cache_create("f2fs_victim_entry",
>> >> diff --git a/fs/f2fs/gc.h b/fs/f2fs/gc.h
>> >> index 5c1eaf55e127..9c8695efe394 100644
>> >> --- a/fs/f2fs/gc.h
>> >> +++ b/fs/f2fs/gc.h
>> >> @@ -82,6 +82,12 @@ struct victim_entry {
>> >>      struct list_head list;
>> >>   };
>> >>
>> >> +struct gc_page_entry {
>> >> +    unsigned int segno;
>> >> +    struct page *sum_page;
>> >> +    struct list_head list;
>> >> +};
>> >> +
>> >>   /*
>> >>    * inline functions
>> >>    */
>> >> diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
>> >> index f087b2b71c89..a3241730fe4f 100644
>> >> --- a/fs/f2fs/super.c
>> >> +++ b/fs/f2fs/super.c
>> >> @@ -5090,9 +5090,12 @@ static int __init init_f2fs_fs(void)
>> >>      err = f2fs_create_garbage_collection_cache();
>> >>      if (err)
>> >>              goto free_extent_cache;
>> >> -    err = f2fs_init_sysfs();
>> >> +    err = f2fs_init_garbage_collection_summary_cache();
>> >>      if (err)
>> >>              goto free_garbage_collection_cache;
>> >> +    err = f2fs_init_sysfs();
>> >> +    if (err)
>> >> +            goto free_garbage_collection_summary_cache;
>> >>      err = f2fs_init_shrinker();
>> >>      if (err)
>> >>              goto free_sysfs;
>> >> @@ -5141,6 +5144,8 @@ static int __init init_f2fs_fs(void)
>> >>      f2fs_exit_shrinker();
>> >>   free_sysfs:
>> >>      f2fs_exit_sysfs();
>> >> +free_garbage_collection_summary_cache:
>> >> +    f2fs_destroy_garbage_collection_summary_cache();
>> >>   free_garbage_collection_cache:
>> >>      f2fs_destroy_garbage_collection_cache();
>> >>   free_extent_cache:
>> >> @@ -5172,6 +5177,7 @@ static void __exit exit_f2fs_fs(void)
>> >>      f2fs_destroy_root_stats();
>> >>      f2fs_exit_shrinker();
>> >>      f2fs_exit_sysfs();
>> >> +    f2fs_destroy_garbage_collection_summary_cache();
>> >>      f2fs_destroy_garbage_collection_cache();
>> >>      f2fs_destroy_extent_cache();
>> >>      f2fs_destroy_recovery_cache();
>> >> --
>> >> 2.33.0
>> >>
>
>Do you have any numbers showing how much the performance is enhanced?
Depending on the valid block count within a section, 
in the worst-case scenario, it is assumed that only 
the last 3 segments in the section are valid (SEGS_PER_SEC 512)
#define DEF_GC_THREAD_MAX_SLEEP_TIME_ZONED	20
#define DEF_MIGRATION_WINDOW_GRANULARITY_ZONED	3
The time required to move a single section
before : 
170 * 20ms + migrate 3segments
after : 
migrate 3segments
Thanks.
>
>Thanks.

>
>> >>
>> >>
>> >> _______________________________________________
>> >> Linux-f2fs-devel mailing list
>> >> Linux-f2fs-devel@lists.sourceforge.net