[PATCH] f2fs: introduce hot_data_age_threshold and warm_data_age_threshold mount opt

From: Yangtao Li
Date: Tue Dec 06 2022 - 01:36:40 EST


This patch supports parsing these two parameters from mount opt,
so that we don't have to dynamically modify the parameters through
the sysfs node after the system starts.

Signed-off-by: Yangtao Li <frank.li@xxxxxxxx>
---
Documentation/filesystems/f2fs.rst | 6 +++++
fs/f2fs/debug.c | 3 ++-
fs/f2fs/extent_cache.c | 14 +++++++----
fs/f2fs/f2fs.h | 14 +++++++----
fs/f2fs/segment.c | 8 ++++---
fs/f2fs/super.c | 38 +++++++++++++++++++++++++++++-
fs/f2fs/sysfs.c | 16 +++++++++----
7 files changed, 81 insertions(+), 18 deletions(-)

diff --git a/Documentation/filesystems/f2fs.rst b/Documentation/filesystems/f2fs.rst
index 220f3e0d3f55..12a04d7cd634 100644
--- a/Documentation/filesystems/f2fs.rst
+++ b/Documentation/filesystems/f2fs.rst
@@ -351,6 +351,12 @@ age_extent_cache Enable an age extent cache based on rb-tree. It records
data block update frequency of the extent per inode, in
order to provide better temperature hints for data block
allocation.
+hot_data_age_threshold=%u When age_extent_cache is on, it controls the age
+ threshold to indicate the data blocks as hot. By default it was
+ initialized as 262144 blocks(equals to 1GB).
+warm_data_age_threshold=%u When age_extent_cache is on, it controls the age
+ threshold to indicate the data blocks as warm. By default it was
+ initialized as 2621440 blocks(equals to 10GB).
======================== ============================================================

Debugfs Entries
diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c
index 8f1ef742551f..5bf9c1ed7a2f 100644
--- a/fs/f2fs/debug.c
+++ b/fs/f2fs/debug.c
@@ -62,6 +62,7 @@ void f2fs_update_sit_info(struct f2fs_sb_info *sbi)
#ifdef CONFIG_DEBUG_FS
static void update_general_status(struct f2fs_sb_info *sbi)
{
+ struct f2fs_age_extent_info *fai = &F2FS_OPTION(sbi).age_info;
struct f2fs_stat_info *si = F2FS_STAT(sbi);
struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi);
int i;
@@ -89,7 +90,7 @@ static void update_general_status(struct f2fs_sb_info *sbi)
si->hit_total[EX_READ] += si->hit_largest;

/* block age extent_cache only */
- si->allocated_data_blocks = atomic64_read(&sbi->allocated_data_blocks);
+ si->allocated_data_blocks = atomic64_read(&fai->allocated_data_blocks);

/* validation check of the segment numbers */
si->ndirty_node = get_pages(sbi, F2FS_DIRTY_NODES);
diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c
index 2fc675c45606..601659714aa9 100644
--- a/fs/f2fs/extent_cache.c
+++ b/fs/f2fs/extent_cache.c
@@ -883,9 +883,10 @@ static unsigned long long __calculate_block_age(unsigned long long new,
static int __get_new_block_age(struct inode *inode, struct extent_info *ei)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
+ struct f2fs_age_extent_info *fai = &F2FS_OPTION(sbi).age_info;
loff_t f_size = i_size_read(inode);
unsigned long long cur_blocks =
- atomic64_read(&sbi->allocated_data_blocks);
+ atomic64_read(&fai->allocated_data_blocks);

/*
* When I/O is not aligned to a PAGE_SIZE, update will happen to the last
@@ -1216,13 +1217,18 @@ static void __init_extent_tree_info(struct extent_tree_info *eti)

void f2fs_init_extent_cache_info(struct f2fs_sb_info *sbi)
{
+ struct f2fs_age_extent_info *fai = &F2FS_OPTION(sbi).age_info;
+
__init_extent_tree_info(&sbi->extent_tree[EX_READ]);
__init_extent_tree_info(&sbi->extent_tree[EX_BLOCK_AGE]);

/* initialize for block age extents */
- atomic64_set(&sbi->allocated_data_blocks, 0);
- sbi->hot_data_age_threshold = DEF_HOT_DATA_AGE_THRESHOLD;
- sbi->warm_data_age_threshold = DEF_WARM_DATA_AGE_THRESHOLD;
+ atomic64_set(&fai->allocated_data_blocks, 0);
+
+ if (!fai->hot_data_age_threshold)
+ fai->hot_data_age_threshold = DEF_HOT_DATA_AGE_THRESHOLD;
+ if (!fai->warm_data_age_threshold)
+ fai->warm_data_age_threshold = DEF_WARM_DATA_AGE_THRESHOLD;
}

int __init f2fs_create_extent_cache(void)
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index eb71edcf70de..32a0bf2977bc 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -64,6 +64,12 @@ enum {
FAULT_MAX,
};

+struct f2fs_age_extent_info {
+ atomic64_t allocated_data_blocks; /* for block age extent_cache */
+ unsigned int hot_data_age_threshold; /* The threshold used for hot data seperation*/
+ unsigned int warm_data_age_threshold; /* The threshold used for warm data seperation*/
+};
+
#ifdef CONFIG_F2FS_FAULT_INJECTION
#define F2FS_ALL_FAULT_TYPE ((1 << FAULT_MAX) - 1)

@@ -148,6 +154,7 @@ struct f2fs_mount_info {
kgid_t s_resgid; /* reserved blocks for gid */
int active_logs; /* # of active logs */
int inline_xattr_size; /* inline xattr size */
+ struct f2fs_age_extent_info age_info; /* For block age extent */
#ifdef CONFIG_F2FS_FAULT_INJECTION
struct f2fs_fault_info fault_info; /* For fault injection */
#endif
@@ -173,6 +180,8 @@ struct f2fs_mount_info {
* unusable when disabling checkpoint
*/

+ /* For block age extent_cache */
+
/* For compression */
unsigned char compress_algorithm; /* algorithm type */
unsigned char compress_log_size; /* cluster log size */
@@ -1674,11 +1683,6 @@ struct f2fs_sb_info {

/* for extent tree cache */
struct extent_tree_info extent_tree[NR_EXTENT_CACHES];
- atomic64_t allocated_data_blocks; /* for block age extent_cache */
-
- /* The threshold used for hot and warm data seperation*/
- unsigned int hot_data_age_threshold;
- unsigned int warm_data_age_threshold;

/* basic filesystem units */
unsigned int log_sectors_per_block; /* log2 sectors per block */
diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c
index dee712f7225f..c9b779fd7041 100644
--- a/fs/f2fs/segment.c
+++ b/fs/f2fs/segment.c
@@ -3159,14 +3159,15 @@ static int __get_segment_type_4(struct f2fs_io_info *fio)
static int __get_age_segment_type(struct inode *inode, pgoff_t pgofs)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
+ struct f2fs_age_extent_info *fai = &F2FS_OPTION(sbi).age_info;
struct extent_info ei;

if (f2fs_lookup_age_extent_cache(inode, pgofs, &ei)) {
if (!ei.age)
return NO_CHECK_TYPE;
- if (ei.age <= sbi->hot_data_age_threshold)
+ if (ei.age <= fai->hot_data_age_threshold)
return CURSEG_HOT_DATA;
- if (ei.age <= sbi->warm_data_age_threshold)
+ if (ei.age <= fai->warm_data_age_threshold)
return CURSEG_WARM_DATA;
return CURSEG_COLD_DATA;
}
@@ -3242,6 +3243,7 @@ void f2fs_allocate_data_block(struct f2fs_sb_info *sbi, struct page *page,
struct f2fs_summary *sum, int type,
struct f2fs_io_info *fio)
{
+ struct f2fs_age_extent_info *fai = &F2FS_OPTION(sbi).age_info;
struct sit_info *sit_i = SIT_I(sbi);
struct curseg_info *curseg = CURSEG_I(sbi, type);
unsigned long long old_mtime;
@@ -3316,7 +3318,7 @@ void f2fs_allocate_data_block(struct f2fs_sb_info *sbi, struct page *page,
locate_dirty_segment(sbi, GET_SEGNO(sbi, *new_blkaddr));

if (IS_DATASEG(type))
- atomic64_inc(&sbi->allocated_data_blocks);
+ atomic64_inc(&fai->allocated_data_blocks);

up_write(&sit_i->sentry_lock);

diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index 5bdab376b852..feea2006b070 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -164,6 +164,8 @@ enum {
Opt_discard_unit,
Opt_memory_mode,
Opt_age_extent_cache,
+ Opt_hot_data_age_threshold,
+ Opt_warm_data_age_threshold,
Opt_err,
};

@@ -243,6 +245,8 @@ static match_table_t f2fs_tokens = {
{Opt_discard_unit, "discard_unit=%s"},
{Opt_memory_mode, "memory=%s"},
{Opt_age_extent_cache, "age_extent_cache"},
+ {Opt_hot_data_age_threshold, "hot_data_age_threshold=%u"},
+ {Opt_warm_data_age_threshold, "warm_data_age_threshold=%u"},
{Opt_err, NULL},
};

@@ -658,6 +662,7 @@ static int f2fs_set_zstd_level(struct f2fs_sb_info *sbi, const char *str)
static int parse_options(struct super_block *sb, char *options, bool is_remount)
{
struct f2fs_sb_info *sbi = F2FS_SB(sb);
+ struct f2fs_age_extent_info *fai = &F2FS_OPTION(sbi).age_info;
substring_t args[MAX_OPT_ARGS];
#ifdef CONFIG_F2FS_FS_COMPRESSION
unsigned char (*ext)[F2FS_EXTENSION_LEN];
@@ -1262,6 +1267,32 @@ static int parse_options(struct super_block *sb, char *options, bool is_remount)
case Opt_age_extent_cache:
set_opt(sbi, AGE_EXTENT_CACHE);
break;
+ case Opt_hot_data_age_threshold:
+ if (!test_opt(sbi, AGE_EXTENT_CACHE)) {
+ f2fs_info(sbi, "age extent options not enabled");
+ break;
+ }
+ if (args->from && match_int(args, &arg))
+ return -EINVAL;
+ if (arg == 0 || arg > DEF_HOT_DATA_AGE_THRESHOLD) {
+ f2fs_err(sbi, "hot data age threshold is out of range");
+ return -EINVAL;
+ }
+ fai->hot_data_age_threshold = arg;
+ break;
+ case Opt_warm_data_age_threshold:
+ if (!test_opt(sbi, AGE_EXTENT_CACHE)) {
+ f2fs_info(sbi, "age extent options not enabled");
+ break;
+ }
+ if (args->from && match_int(args, &arg))
+ return -EINVAL;
+ if (arg == 0 || arg > DEF_WARM_DATA_AGE_THRESHOLD) {
+ f2fs_err(sbi, "warm data age threshold is out of range");
+ return -EINVAL;
+ }
+ fai->warm_data_age_threshold = arg;
+ break;
default:
f2fs_err(sbi, "Unrecognized mount option \"%s\" or missing value",
p);
@@ -1963,8 +1994,13 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
seq_puts(seq, ",read_extent_cache");
else
seq_puts(seq, ",no_read_extent_cache");
- if (test_opt(sbi, AGE_EXTENT_CACHE))
+ if (test_opt(sbi, AGE_EXTENT_CACHE)) {
+ struct f2fs_age_extent_info *fai = &F2FS_OPTION(sbi).age_info;
+
seq_puts(seq, ",age_extent_cache");
+ seq_printf(seq, ",hot_data_age_threshold=%u", fai->hot_data_age_threshold);
+ seq_printf(seq, ",warm_data_age_threshold=%u", fai->warm_data_age_threshold);
+ }
if (test_opt(sbi, DATA_FLUSH))
seq_puts(seq, ",data_flush");

diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c
index 2ab215110596..5b8e08aff0a6 100644
--- a/fs/f2fs/sysfs.c
+++ b/fs/f2fs/sysfs.c
@@ -36,6 +36,7 @@ enum {
FAULT_INFO_RATE, /* struct f2fs_fault_info */
FAULT_INFO_TYPE, /* struct f2fs_fault_info */
#endif
+ AGE_EXTENT_INFO, /* struct f2fs_age_extent_info */
RESERVED_BLOCKS, /* struct f2fs_sb_info */
CPRC_INFO, /* struct ckpt_req_control */
ATGC_INFO, /* struct atgc_management */
@@ -81,6 +82,8 @@ static unsigned char *__struct_ptr(struct f2fs_sb_info *sbi, int struct_type)
struct_type == FAULT_INFO_TYPE)
return (unsigned char *)&F2FS_OPTION(sbi).fault_info;
#endif
+ else if (struct_type == AGE_EXTENT_INFO)
+ return (unsigned char *)&F2FS_OPTION(sbi).age_info;
#ifdef CONFIG_F2FS_STAT_FS
else if (struct_type == STAT_INFO)
return (unsigned char *)F2FS_STAT(sbi);
@@ -669,7 +672,9 @@ static ssize_t __sbi_store(struct f2fs_attr *a,
}

if (!strcmp(a->attr.name, "hot_data_age_threshold")) {
- if (t == 0 || t >= sbi->warm_data_age_threshold)
+ struct f2fs_age_extent_info *fai = &F2FS_OPTION(sbi).age_info;
+
+ if (t == 0 || t >= fai->warm_data_age_threshold)
return -EINVAL;
if (t == *ui)
return count;
@@ -678,7 +683,9 @@ static ssize_t __sbi_store(struct f2fs_attr *a,
}

if (!strcmp(a->attr.name, "warm_data_age_threshold")) {
- if (t == 0 || t <= sbi->hot_data_age_threshold)
+ struct f2fs_age_extent_info *fai = &F2FS_OPTION(sbi).age_info;
+
+ if (t == 0 || t <= fai->hot_data_age_threshold)
return -EINVAL;
if (t == *ui)
return count;
@@ -942,8 +949,9 @@ F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, committed_atomic_block, committed_atomic_bl
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, revoked_atomic_block, revoked_atomic_block);

/* For block age extent cache */
-F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, hot_data_age_threshold, hot_data_age_threshold);
-F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, warm_data_age_threshold, warm_data_age_threshold);
+F2FS_RW_ATTR(AGE_EXTENT_INFO, f2fs_age_extent_info, hot_data_age_threshold, hot_data_age_threshold);
+F2FS_RW_ATTR(AGE_EXTENT_INFO, f2fs_age_extent_info, warm_data_age_threshold,
+ warm_data_age_threshold);

#define ATTR_LIST(name) (&f2fs_attr_##name.attr)
static struct attribute *f2fs_attrs[] = {
--
2.25.1