f2fs: add a sysfs node to limit max read extent count per-inode

Quoted:
"at this time, there are still 1086911 extent nodes in this zombie
extent tree that need to be cleaned up.

crash_arm64_sprd_v8.0.3++> extent_tree.node_cnt ffffff80896cc500
  node_cnt = {
    counter = 1086911
  },
"

As reported by Xiuhong, there will be a huge number of extent nodes
in extent tree, it may potentially cause:
- slab memory fragments
- extreme long time shrink on extent tree
- low mapping efficiency

Let's add a sysfs node to limit max read extent count for each inode,
by default, value of this threshold is 10240, it can be updated
according to user's requirement.

Reported-by: Xiuhong Wang <xiuhong.wang@unisoc.com>
Closes: https://lore.kernel.org/linux-f2fs-devel/20241112110627.1314632-1-xiuhong.wang@unisoc.com/
Signed-off-by: Xiuhong Wang <xiuhong.wang@unisoc.com>
Signed-off-by: Zhiguo Niu <zhiguo.niu@unisoc.com>
Signed-off-by: Chao Yu <chao@kernel.org>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
This commit is contained in:
Chao Yu 2024-11-22 14:50:05 +08:00 committed by Jaegeuk Kim
parent 3fc5d5a182
commit 009a8241a8
4 changed files with 24 additions and 1 deletions

View file

@ -822,3 +822,9 @@ Description: It controls the valid block ratio threshold not to trigger excessiv
for zoned deivces. The initial value of it is 95(%). F2FS will stop the for zoned deivces. The initial value of it is 95(%). F2FS will stop the
background GC thread from intiating GC for sections having valid blocks background GC thread from intiating GC for sections having valid blocks
exceeding the ratio. exceeding the ratio.
What: /sys/fs/f2fs/<disk>/max_read_extent_count
Date: November 2024
Contact: "Chao Yu" <chao@kernel.org>
Description: It controls max read extent count for per-inode, the value of threshold
is 10240 by default.

View file

@ -717,7 +717,9 @@ static void __update_extent_tree_range(struct inode *inode,
} }
if (end < org_end && (type != EX_READ || if (end < org_end && (type != EX_READ ||
org_end - end >= F2FS_MIN_EXTENT_LEN)) { (org_end - end >= F2FS_MIN_EXTENT_LEN &&
atomic_read(&et->node_cnt) <
sbi->max_read_extent_count))) {
if (parts) { if (parts) {
__set_extent_info(&ei, __set_extent_info(&ei,
end, org_end - end, end, org_end - end,
@ -1212,6 +1214,7 @@ void f2fs_init_extent_cache_info(struct f2fs_sb_info *sbi)
sbi->hot_data_age_threshold = DEF_HOT_DATA_AGE_THRESHOLD; sbi->hot_data_age_threshold = DEF_HOT_DATA_AGE_THRESHOLD;
sbi->warm_data_age_threshold = DEF_WARM_DATA_AGE_THRESHOLD; sbi->warm_data_age_threshold = DEF_WARM_DATA_AGE_THRESHOLD;
sbi->last_age_weight = LAST_AGE_WEIGHT; sbi->last_age_weight = LAST_AGE_WEIGHT;
sbi->max_read_extent_count = DEF_MAX_READ_EXTENT_COUNT;
} }
int __init f2fs_create_extent_cache(void) int __init f2fs_create_extent_cache(void)

View file

@ -635,6 +635,9 @@ enum {
#define DEF_HOT_DATA_AGE_THRESHOLD 262144 #define DEF_HOT_DATA_AGE_THRESHOLD 262144
#define DEF_WARM_DATA_AGE_THRESHOLD 2621440 #define DEF_WARM_DATA_AGE_THRESHOLD 2621440
/* default max read extent count per inode */
#define DEF_MAX_READ_EXTENT_COUNT 10240
/* extent cache type */ /* extent cache type */
enum extent_type { enum extent_type {
EX_READ, EX_READ,
@ -1619,6 +1622,7 @@ struct f2fs_sb_info {
/* for extent tree cache */ /* for extent tree cache */
struct extent_tree_info extent_tree[NR_EXTENT_CACHES]; struct extent_tree_info extent_tree[NR_EXTENT_CACHES];
atomic64_t allocated_data_blocks; /* for block age extent_cache */ atomic64_t allocated_data_blocks; /* for block age extent_cache */
unsigned int max_read_extent_count; /* max read extent count per inode */
/* The threshold used for hot and warm data seperation*/ /* The threshold used for hot and warm data seperation*/
unsigned int hot_data_age_threshold; unsigned int hot_data_age_threshold;

View file

@ -787,6 +787,13 @@ out:
return count; return count;
} }
if (!strcmp(a->attr.name, "max_read_extent_count")) {
if (t > UINT_MAX)
return -EINVAL;
*ui = (unsigned int)t;
return count;
}
if (!strcmp(a->attr.name, "ipu_policy")) { if (!strcmp(a->attr.name, "ipu_policy")) {
if (t >= BIT(F2FS_IPU_MAX)) if (t >= BIT(F2FS_IPU_MAX))
return -EINVAL; return -EINVAL;
@ -1052,6 +1059,8 @@ F2FS_SBI_GENERAL_RW_ATTR(revoked_atomic_block);
F2FS_SBI_GENERAL_RW_ATTR(hot_data_age_threshold); F2FS_SBI_GENERAL_RW_ATTR(hot_data_age_threshold);
F2FS_SBI_GENERAL_RW_ATTR(warm_data_age_threshold); F2FS_SBI_GENERAL_RW_ATTR(warm_data_age_threshold);
F2FS_SBI_GENERAL_RW_ATTR(last_age_weight); F2FS_SBI_GENERAL_RW_ATTR(last_age_weight);
/* read extent cache */
F2FS_SBI_GENERAL_RW_ATTR(max_read_extent_count);
#ifdef CONFIG_BLK_DEV_ZONED #ifdef CONFIG_BLK_DEV_ZONED
F2FS_SBI_GENERAL_RO_ATTR(unusable_blocks_per_sec); F2FS_SBI_GENERAL_RO_ATTR(unusable_blocks_per_sec);
F2FS_SBI_GENERAL_RW_ATTR(blkzone_alloc_policy); F2FS_SBI_GENERAL_RW_ATTR(blkzone_alloc_policy);
@ -1242,6 +1251,7 @@ static struct attribute *f2fs_attrs[] = {
ATTR_LIST(hot_data_age_threshold), ATTR_LIST(hot_data_age_threshold),
ATTR_LIST(warm_data_age_threshold), ATTR_LIST(warm_data_age_threshold),
ATTR_LIST(last_age_weight), ATTR_LIST(last_age_weight),
ATTR_LIST(max_read_extent_count),
NULL, NULL,
}; };
ATTRIBUTE_GROUPS(f2fs); ATTRIBUTE_GROUPS(f2fs);