@@ -634,3 +634,17 @@ Date: July 2022
Contact: "Daeho Jeong" <daehojeong@google.com>
Description: Show the accumulated total revoked atomic write block count after boot.
If you write "0" here, you can initialize to "0".
+
+What: /sys/fs/f2fs/<disk>/hot_data_age_threshold
+Date: November 2022
+Contact: "Ping Xiong" <xiongping1@xiaomi.com>
+Description: When DATA SEPARATION 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).
+
+What: /sys/fs/f2fs/<disk>/warm_data_age_threshold
+Date: November 2022
+Contact: "Ping Xiong" <xiongping1@xiaomi.com>
+Description: When DATA SEPARATION 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).
@@ -17,6 +17,13 @@
#define LAST_AGE_WEIGHT 30
#define SAME_AGE_REGION 1024
+/*
+ * Define data block with age less than 1GB as hot data
+ * define data block with age less than 10GB but more than 1GB as warm data
+ */
+#define DEF_HOT_DATA_AGE_THRESHOLD 262144
+#define DEF_WARM_DATA_AGE_THRESHOLD 2621440
+
static struct kmem_cache *age_extent_tree_slab;
static struct kmem_cache *age_extent_node_slab;
@@ -29,6 +36,9 @@ static inline void f2fs_inc_data_block_alloc(struct f2fs_sb_info *sbi)
static void f2fs_init_block_age_info(struct f2fs_sb_info *sbi)
{
atomic64_set(&sbi->total_data_alloc, 0);
+
+ sbi->hot_data_age_threshold = DEF_HOT_DATA_AGE_THRESHOLD;
+ sbi->warm_data_age_threshold = DEF_WARM_DATA_AGE_THRESHOLD;
}
static inline bool f2fs_may_age_extent_tree(struct inode *inode)
@@ -697,6 +707,25 @@ unsigned long f2fs_count_age_extent_cache(struct f2fs_sb_info *sbi)
atomic_read(&sbi->total_age_ext_node);
}
+int f2fs_get_data_segment_type(struct inode *inode, pgoff_t pgofs)
+{
+ struct age_extent_info ei;
+ struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
+
+ if (f2fs_lookup_age_extent_cache(inode, pgofs, &ei)) {
+ if (ei.age != 0) {
+ if (ei.age <= sbi->hot_data_age_threshold)
+ return CURSEG_HOT_DATA;
+ else if (ei.age <= sbi->warm_data_age_threshold)
+ return CURSEG_WARM_DATA;
+ else
+ return CURSEG_COLD_DATA;
+ }
+ }
+
+ return NO_CHECK_TYPE;
+}
+
void f2fs_destroy_age_extent_cache(void)
{
kmem_cache_destroy(age_extent_node_slab);
@@ -1854,6 +1854,9 @@ struct f2fs_sb_info {
#ifdef CONFIG_F2FS_FS_DATA_SEPARATION
atomic64_t total_data_alloc;
+ /* The threshold used for hot and warm data seperation*/
+ unsigned int hot_data_age_threshold;
+ unsigned int warm_data_age_threshold;
#endif
/* Reference to checksum algorithm driver via cryptoapi */
@@ -4241,6 +4244,7 @@ void f2fs_update_age_extent_cache(struct inode *inode, pgoff_t fofs,
void f2fs_update_data_block_age(struct dnode_of_data *dn);
void f2fs_truncate_age_extent_cache(struct inode *inode, pgoff_t fofs,
unsigned int len);
+int f2fs_get_data_segment_type(struct inode *inode, pgoff_t pgofs);
int __init f2fs_create_age_extent_cache(void);
void f2fs_destroy_age_extent_cache(void);
#endif
@@ -3163,6 +3163,9 @@ static int __get_segment_type_6(struct f2fs_io_info *fio)
if (fio->type == DATA) {
struct inode *inode = fio->page->mapping->host;
+#ifdef CONFIG_F2FS_FS_DATA_SEPARATION
+ int type;
+#endif
if (is_inode_flag_set(inode, FI_ALIGNED_WRITE))
return CURSEG_COLD_DATA_PINNED;
@@ -3176,6 +3179,12 @@ static int __get_segment_type_6(struct f2fs_io_info *fio)
}
if (file_is_cold(inode) || f2fs_need_compress_data(inode))
return CURSEG_COLD_DATA;
+
+#ifdef CONFIG_F2FS_FS_DATA_SEPARATION
+ type = f2fs_get_data_segment_type(inode, fio->page->index);
+ if (type != NO_CHECK_TYPE)
+ return type;
+#endif
if (file_is_hot(inode) ||
is_inode_flag_set(inode, FI_HOT_DATA) ||
f2fs_is_cow_file(inode))
@@ -648,6 +648,25 @@ static ssize_t __sbi_store(struct f2fs_attr *a,
sbi->revoked_atomic_block = 0;
return count;
}
+#ifdef CONFIG_F2FS_FS_DATA_SEPARATION
+ if (!strcmp(a->attr.name, "hot_data_age_threshold")) {
+ if (t == 0 || t >= sbi->warm_data_age_threshold)
+ return -EINVAL;
+ if (t == *ui)
+ return count;
+ *ui = (unsigned int)t;
+ return count;
+ }
+
+ if (!strcmp(a->attr.name, "warm_data_age_threshold")) {
+ if (t == 0 || t <= sbi->hot_data_age_threshold)
+ return -EINVAL;
+ if (t == *ui)
+ return count;
+ *ui = (unsigned int)t;
+ return count;
+ }
+#endif
*ui = (unsigned int)t;
@@ -902,6 +921,11 @@ F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, peak_atomic_write, peak_atomic_write);
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, committed_atomic_block, committed_atomic_block);
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, revoked_atomic_block, revoked_atomic_block);
+#ifdef CONFIG_F2FS_FS_DATA_SEPARATION
+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);
+#endif
+
#define ATTR_LIST(name) (&f2fs_attr_##name.attr)
static struct attribute *f2fs_attrs[] = {
ATTR_LIST(gc_urgent_sleep_time),
@@ -995,6 +1019,10 @@ static struct attribute *f2fs_attrs[] = {
ATTR_LIST(peak_atomic_write),
ATTR_LIST(committed_atomic_block),
ATTR_LIST(revoked_atomic_block),
+#ifdef CONFIG_F2FS_FS_DATA_SEPARATION
+ ATTR_LIST(hot_data_age_threshold),
+ ATTR_LIST(warm_data_age_threshold),
+#endif
NULL,
};
ATTRIBUTE_GROUPS(f2fs);
@@ -1692,7 +1692,7 @@ DECLARE_EVENT_CLASS(f2fs_add_age_extent_node,
__entry->blocks = ei->last_blocks;
),
- TP_printk("dev = (%d,%d), ino = %lu, node_cnt = %lu, "
+ TP_printk("dev = (%d,%d), ino = %lu, node_cnt = %u, "
"age_ext_info(fofs: %u, len: %u, age: %llu, blocks: %llu)",
show_dev_ino(__entry),
__entry->cnt,