f2fs: introduce lifetime write IO statistics
This patch introduces lifetime IO write statistics exposed to the sysfs interface. The write IO amount is obtained from block layer, accumulated in the file system and stored in the hot node summary of checkpoint. Signed-off-by: Shuoran Liu <liushuoran@huawei.com> Signed-off-by: Pengyang Hou <houpengyang@huawei.com> [Jaegeuk Kim: add sysfs documentation] Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
This commit is contained in:
parent
6fe2bc9561
commit
8f1dbbbbdf
|
@ -106,3 +106,9 @@ Description:
|
|||
Controls dirty nat entries ratio threshold, if current
|
||||
ratio exceeds configured threshold, checkpoint will
|
||||
be triggered for flushing dirty nat entries.
|
||||
|
||||
What: /sys/fs/f2fs/<disk>/lifetime_write_kbytes
|
||||
Date: January 2016
|
||||
Contact: "Shuoran Liu" <liushuoran@huawei.com>
|
||||
Description:
|
||||
Shows total written kbytes issued to disk.
|
||||
|
|
|
@ -921,6 +921,9 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
|
|||
int cp_payload_blks = __cp_payload(sbi);
|
||||
block_t discard_blk = NEXT_FREE_BLKADDR(sbi, curseg);
|
||||
bool invalidate = false;
|
||||
struct super_block *sb = sbi->sb;
|
||||
struct curseg_info *seg_i = CURSEG_I(sbi, CURSEG_HOT_NODE);
|
||||
u64 kbytes_written;
|
||||
|
||||
/*
|
||||
* This avoids to conduct wrong roll-forward operations and uses
|
||||
|
@ -1034,6 +1037,14 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
|
|||
|
||||
write_data_summaries(sbi, start_blk);
|
||||
start_blk += data_sum_blocks;
|
||||
|
||||
/* Record write statistics in the hot node summary */
|
||||
kbytes_written = sbi->kbytes_written;
|
||||
if (sb->s_bdev->bd_part)
|
||||
kbytes_written += BD_PART_WRITTEN(sbi);
|
||||
|
||||
seg_i->sum_blk->info.kbytes_written = cpu_to_le64(kbytes_written);
|
||||
|
||||
if (__remain_node_summaries(cpc->reason)) {
|
||||
write_node_summaries(sbi, start_blk);
|
||||
start_blk += NR_CURSEG_NODE_TYPE;
|
||||
|
|
|
@ -846,8 +846,19 @@ struct f2fs_sb_info {
|
|||
struct list_head s_list;
|
||||
struct mutex umount_mutex;
|
||||
unsigned int shrinker_run_no;
|
||||
|
||||
/* For write statistics */
|
||||
u64 sectors_written_start;
|
||||
u64 kbytes_written;
|
||||
};
|
||||
|
||||
/* For write statistics. Suppose sector size is 512 bytes,
|
||||
* and the return value is in kbytes. s is of struct f2fs_sb_info.
|
||||
*/
|
||||
#define BD_PART_WRITTEN(s) \
|
||||
(((u64)part_stat_read(s->sb->s_bdev->bd_part, sectors[1]) - \
|
||||
s->sectors_written_start) >> 1)
|
||||
|
||||
static inline void f2fs_update_time(struct f2fs_sb_info *sbi, int type)
|
||||
{
|
||||
sbi->last_time[type] = jiffies;
|
||||
|
|
|
@ -126,6 +126,19 @@ static unsigned char *__struct_ptr(struct f2fs_sb_info *sbi, int struct_type)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static ssize_t lifetime_write_kbytes_show(struct f2fs_attr *a,
|
||||
struct f2fs_sb_info *sbi, char *buf)
|
||||
{
|
||||
struct super_block *sb = sbi->sb;
|
||||
|
||||
if (!sb->s_bdev->bd_part)
|
||||
return snprintf(buf, PAGE_SIZE, "0\n");
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%llu\n",
|
||||
(unsigned long long)(sbi->kbytes_written +
|
||||
BD_PART_WRITTEN(sbi)));
|
||||
}
|
||||
|
||||
static ssize_t f2fs_sbi_show(struct f2fs_attr *a,
|
||||
struct f2fs_sb_info *sbi, char *buf)
|
||||
{
|
||||
|
@ -204,6 +217,9 @@ static struct f2fs_attr f2fs_attr_##_name = { \
|
|||
f2fs_sbi_show, f2fs_sbi_store, \
|
||||
offsetof(struct struct_name, elname))
|
||||
|
||||
#define F2FS_GENERAL_RO_ATTR(name) \
|
||||
static struct f2fs_attr f2fs_attr_##name = __ATTR(name, 0444, name##_show, NULL)
|
||||
|
||||
F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_min_sleep_time, min_sleep_time);
|
||||
F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_max_sleep_time, max_sleep_time);
|
||||
F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_no_gc_sleep_time, no_gc_sleep_time);
|
||||
|
@ -221,6 +237,7 @@ F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, max_victim_search, max_victim_search);
|
|||
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, dir_level, dir_level);
|
||||
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, cp_interval, interval_time[CP_TIME]);
|
||||
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, idle_interval, interval_time[REQ_TIME]);
|
||||
F2FS_GENERAL_RO_ATTR(lifetime_write_kbytes);
|
||||
|
||||
#define ATTR_LIST(name) (&f2fs_attr_##name.attr)
|
||||
static struct attribute *f2fs_attrs[] = {
|
||||
|
@ -241,6 +258,7 @@ static struct attribute *f2fs_attrs[] = {
|
|||
ATTR_LIST(dirty_nats_ratio),
|
||||
ATTR_LIST(cp_interval),
|
||||
ATTR_LIST(idle_interval),
|
||||
ATTR_LIST(lifetime_write_kbytes),
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
@ -768,8 +786,6 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
|
|||
bool need_stop_gc = false;
|
||||
bool no_extent_cache = !test_opt(sbi, EXTENT_CACHE);
|
||||
|
||||
sync_filesystem(sb);
|
||||
|
||||
/*
|
||||
* Save the old mount options in case we
|
||||
* need to restore them.
|
||||
|
@ -777,6 +793,13 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
|
|||
org_mount_opt = sbi->mount_opt;
|
||||
active_logs = sbi->active_logs;
|
||||
|
||||
if (*flags & MS_RDONLY) {
|
||||
set_opt(sbi, FASTBOOT);
|
||||
set_sbi_flag(sbi, SBI_IS_DIRTY);
|
||||
}
|
||||
|
||||
sync_filesystem(sb);
|
||||
|
||||
sbi->mount_opt.opt = 0;
|
||||
default_options(sbi);
|
||||
|
||||
|
@ -1244,6 +1267,7 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
|
|||
bool retry = true, need_fsck = false;
|
||||
char *options = NULL;
|
||||
int recovery, i, valid_super_block;
|
||||
struct curseg_info *seg_i;
|
||||
|
||||
try_onemore:
|
||||
err = -EINVAL;
|
||||
|
@ -1374,6 +1398,17 @@ try_onemore:
|
|||
goto free_nm;
|
||||
}
|
||||
|
||||
/* For write statistics */
|
||||
if (sb->s_bdev->bd_part)
|
||||
sbi->sectors_written_start =
|
||||
(u64)part_stat_read(sb->s_bdev->bd_part, sectors[1]);
|
||||
|
||||
/* Read accumulated write IO statistics if exists */
|
||||
seg_i = CURSEG_I(sbi, CURSEG_HOT_NODE);
|
||||
if (__exist_node_summaries(sbi))
|
||||
sbi->kbytes_written =
|
||||
le64_to_cpu(seg_i->sum_blk->info.kbytes_written);
|
||||
|
||||
build_gc_manager(sbi);
|
||||
|
||||
/* get an inode for node space */
|
||||
|
|
|
@ -358,6 +358,12 @@ struct summary_footer {
|
|||
sizeof(struct sit_journal_entry))
|
||||
#define SIT_JOURNAL_RESERVED ((SUM_JOURNAL_SIZE - 2) %\
|
||||
sizeof(struct sit_journal_entry))
|
||||
|
||||
/* Reserved area should make size of f2fs_extra_info equals to
|
||||
* that of nat_journal and sit_journal.
|
||||
*/
|
||||
#define EXTRA_INFO_RESERVED (SUM_JOURNAL_SIZE - 2 - 8)
|
||||
|
||||
/*
|
||||
* frequently updated NAT/SIT entries can be stored in the spare area in
|
||||
* summary blocks
|
||||
|
@ -387,6 +393,11 @@ struct sit_journal {
|
|||
__u8 reserved[SIT_JOURNAL_RESERVED];
|
||||
} __packed;
|
||||
|
||||
struct f2fs_extra_info {
|
||||
__le64 kbytes_written;
|
||||
__u8 reserved[EXTRA_INFO_RESERVED];
|
||||
} __packed;
|
||||
|
||||
/* 4KB-sized summary block structure */
|
||||
struct f2fs_summary_block {
|
||||
struct f2fs_summary entries[ENTRIES_IN_SUM];
|
||||
|
@ -394,10 +405,11 @@ struct f2fs_summary_block {
|
|||
__le16 n_nats;
|
||||
__le16 n_sits;
|
||||
};
|
||||
/* spare area is used by NAT or SIT journals */
|
||||
/* spare area is used by NAT or SIT journals or extra info */
|
||||
union {
|
||||
struct nat_journal nat_j;
|
||||
struct sit_journal sit_j;
|
||||
struct f2fs_extra_info info;
|
||||
};
|
||||
struct summary_footer footer;
|
||||
} __packed;
|
||||
|
|
Loading…
Reference in New Issue