f2fs: multidevice: add stats in debugfs

This patch adds per-device stats in debugfs, the examples
are as below:

mkfs.f2fs -f -c /dev/vdc /dev/vdb
mount /dev/vdb /mnt/f2fs/
cat /sys/kernel/debug/f2fs/status

Multidevice stats:
  [seg:      inuse    dirty     full     free  prefree]
  #0             5        0        0     4007        0
  #1             1        0        0     8191        0

mkfs.f2fs -f -s 2 -c /dev/vdc /dev/vdb
mount /dev/vdb /mnt/f2fs
cat /sys/kernel/debug/f2fs/status

Multidevice stats:
  [seg:      inuse    dirty     full     free  prefree] [sec:      inuse    dirty     full     free  prefree]
  #0             5        0        0     4005        0                 5        0        0     2000        0
  #1             1        0        0     8191        0                 1        0        0     4095        0

Signed-off-by: Chao Yu <chao@kernel.org>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
This commit is contained in:
Chao Yu 2024-10-16 16:24:20 +08:00 committed by Jaegeuk Kim
parent 6babe00ccd
commit 2d56b4e391
2 changed files with 121 additions and 0 deletions

View File

@ -60,6 +60,70 @@ void f2fs_update_sit_info(struct f2fs_sb_info *sbi)
}
#ifdef CONFIG_DEBUG_FS
static void update_multidevice_stats(struct f2fs_sb_info *sbi)
{
struct f2fs_stat_info *si = F2FS_STAT(sbi);
struct f2fs_dev_stats *dev_stats = si->dev_stats;
int i, j;
if (!f2fs_is_multi_device(sbi))
return;
memset(dev_stats, 0, sizeof(struct f2fs_dev_stats) * sbi->s_ndevs);
for (i = 0; i < sbi->s_ndevs; i++) {
unsigned int start_segno, end_segno;
block_t start_blk, end_blk;
if (i == 0) {
start_blk = MAIN_BLKADDR(sbi);
end_blk = FDEV(i).end_blk + 1 - SEG0_BLKADDR(sbi);
} else {
start_blk = FDEV(i).start_blk;
end_blk = FDEV(i).end_blk + 1;
}
start_segno = GET_SEGNO(sbi, start_blk);
end_segno = GET_SEGNO(sbi, end_blk);
for (j = start_segno; j < end_segno; j++) {
unsigned int seg_blks, sec_blks;
seg_blks = get_seg_entry(sbi, j)->valid_blocks;
/* update segment stats */
if (IS_CURSEG(sbi, j))
dev_stats[i].devstats[0][DEVSTAT_INUSE]++;
else if (seg_blks == BLKS_PER_SEG(sbi))
dev_stats[i].devstats[0][DEVSTAT_FULL]++;
else if (seg_blks != 0)
dev_stats[i].devstats[0][DEVSTAT_DIRTY]++;
else if (!test_bit(j, FREE_I(sbi)->free_segmap))
dev_stats[i].devstats[0][DEVSTAT_FREE]++;
else
dev_stats[i].devstats[0][DEVSTAT_PREFREE]++;
if (!__is_large_section(sbi) ||
(j % SEGS_PER_SEC(sbi)) != 0)
continue;
sec_blks = get_sec_entry(sbi, j)->valid_blocks;
/* update section stats */
if (IS_CURSEC(sbi, GET_SEC_FROM_SEG(sbi, j)))
dev_stats[i].devstats[1][DEVSTAT_INUSE]++;
else if (sec_blks == BLKS_PER_SEC(sbi))
dev_stats[i].devstats[1][DEVSTAT_FULL]++;
else if (sec_blks != 0)
dev_stats[i].devstats[1][DEVSTAT_DIRTY]++;
else if (!test_bit(GET_SEC_FROM_SEG(sbi, j),
FREE_I(sbi)->free_secmap))
dev_stats[i].devstats[1][DEVSTAT_FREE]++;
else
dev_stats[i].devstats[1][DEVSTAT_PREFREE]++;
}
}
}
static void update_general_status(struct f2fs_sb_info *sbi)
{
struct f2fs_stat_info *si = F2FS_STAT(sbi);
@ -214,6 +278,8 @@ static void update_general_status(struct f2fs_sb_info *sbi)
si->valid_blks[type] += blks;
}
update_multidevice_stats(sbi);
for (i = 0; i < MAX_CALL_TYPE; i++)
si->cp_call_count[i] = atomic_read(&sbi->cp_call_count[i]);
@ -498,6 +564,36 @@ static int stat_show(struct seq_file *s, void *v)
si->dirty_count);
seq_printf(s, " - Prefree: %d\n - Free: %d (%d)\n\n",
si->prefree_count, si->free_segs, si->free_secs);
if (f2fs_is_multi_device(sbi)) {
seq_puts(s, "Multidevice stats:\n");
seq_printf(s, " [seg: %8s %8s %8s %8s %8s]",
"inuse", "dirty", "full", "free", "prefree");
if (__is_large_section(sbi))
seq_printf(s, " [sec: %8s %8s %8s %8s %8s]\n",
"inuse", "dirty", "full", "free", "prefree");
else
seq_puts(s, "\n");
for (i = 0; i < sbi->s_ndevs; i++) {
seq_printf(s, " #%-2d %8u %8u %8u %8u %8u", i,
si->dev_stats[i].devstats[0][DEVSTAT_INUSE],
si->dev_stats[i].devstats[0][DEVSTAT_DIRTY],
si->dev_stats[i].devstats[0][DEVSTAT_FULL],
si->dev_stats[i].devstats[0][DEVSTAT_FREE],
si->dev_stats[i].devstats[0][DEVSTAT_PREFREE]);
if (!__is_large_section(sbi)) {
seq_puts(s, "\n");
continue;
}
seq_printf(s, " %8u %8u %8u %8u %8u\n",
si->dev_stats[i].devstats[1][DEVSTAT_INUSE],
si->dev_stats[i].devstats[1][DEVSTAT_DIRTY],
si->dev_stats[i].devstats[1][DEVSTAT_FULL],
si->dev_stats[i].devstats[1][DEVSTAT_FREE],
si->dev_stats[i].devstats[1][DEVSTAT_PREFREE]);
}
seq_puts(s, "\n");
}
seq_printf(s, "CP calls: %d (BG: %d)\n",
si->cp_call_count[TOTAL_CALL],
si->cp_call_count[BACKGROUND]);
@ -665,6 +761,7 @@ int f2fs_build_stats(struct f2fs_sb_info *sbi)
{
struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi);
struct f2fs_stat_info *si;
struct f2fs_dev_stats *dev_stats;
unsigned long flags;
int i;
@ -672,6 +769,15 @@ int f2fs_build_stats(struct f2fs_sb_info *sbi)
if (!si)
return -ENOMEM;
dev_stats = f2fs_kzalloc(sbi, sizeof(struct f2fs_dev_stats) *
sbi->s_ndevs, GFP_KERNEL);
if (!dev_stats) {
kfree(si);
return -ENOMEM;
}
si->dev_stats = dev_stats;
si->all_area_segs = le32_to_cpu(raw_super->segment_count);
si->sit_area_segs = le32_to_cpu(raw_super->segment_count_sit);
si->nat_area_segs = le32_to_cpu(raw_super->segment_count_nat);
@ -724,6 +830,7 @@ void f2fs_destroy_stats(struct f2fs_sb_info *sbi)
list_del(&si->stat_list);
raw_spin_unlock_irqrestore(&f2fs_stat_lock, flags);
kfree(si->dev_stats);
kfree(si);
}

View File

@ -3937,6 +3937,19 @@ void f2fs_destroy_recovery_cache(void);
* debug.c
*/
#ifdef CONFIG_F2FS_STAT_FS
enum {
DEVSTAT_INUSE,
DEVSTAT_DIRTY,
DEVSTAT_FULL,
DEVSTAT_FREE,
DEVSTAT_PREFREE,
DEVSTAT_MAX,
};
struct f2fs_dev_stats {
unsigned int devstats[2][DEVSTAT_MAX]; /* 0: segs, 1: secs */
};
struct f2fs_stat_info {
struct list_head stat_list;
struct f2fs_sb_info *sbi;
@ -4000,6 +4013,7 @@ struct f2fs_stat_info {
unsigned int block_count[2];
unsigned int inplace_count;
unsigned long long base_mem, cache_mem, page_mem;
struct f2fs_dev_stats *dev_stats;
};
static inline struct f2fs_stat_info *F2FS_STAT(struct f2fs_sb_info *sbi)