// SPDX-License-Identifier: GPL-2.0
/*
* f2fs debugging statistics
*
* Copyright (c) 2012 Samsung Electronics Co., Ltd.
* http://www.samsung.com/
* Copyright (c) 2012 Linux Foundation
* Copyright (c) 2012 Greg Kroah-Hartman <gregkh@linuxfoundation.org>
*/
#include <linux/fs.h>
#include <linux/backing-dev.h>
#include <linux/f2fs_fs.h>
#include <linux/blkdev.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include "f2fs.h"
#include "node.h"
#include "segment.h"
#include "gc.h"
static LIST_HEAD(f2fs_stat_list);
static DEFINE_SPINLOCK(f2fs_stat_lock);
#ifdef CONFIG_DEBUG_FS
static struct dentry *f2fs_debugfs_root;
#endif
/*
* This function calculates BDF of every segments
*/
void f2fs_update_sit_info(struct f2fs_sb_info *sbi)
{
struct f2fs_stat_info *si = F2FS_STAT(sbi);
unsigned long long blks_per_sec, hblks_per_sec, total_vblocks;
unsigned long long bimodal, dist;
unsigned int segno, vblocks;
int ndirty = 0;
bimodal = 0;
total_vblocks = 0;
blks_per_sec = CAP_BLKS_PER_SEC(sbi);
hblks_per_sec = blks_per_sec / 2;
for (segno = 0; segno < MAIN_SEGS(sbi); segno += SEGS_PER_SEC(sbi)) {
vblocks = get_valid_blocks(sbi, segno, true);
dist = abs(vblocks - hblks_per_sec);
bimodal += dist * dist;
if (vblocks > 0 && vblocks < blks_per_sec) {
total_vblocks += vblocks;
ndirty++;
}
}
dist = div_u64(MAIN_SECS(sbi) * hblks_per_sec * hblks_per_sec, 100);
si->bimodal = div64_u64(bimodal, dist);
if (si->dirty_count)
si->avg_vblocks = div_u64(total_vblocks, ndirty);
else
si->avg_vblocks = 0;
}
#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);
struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi);
int i;
/* these will be changed if online resize is done */
si->main_area_segs = le32_to_cpu(raw_super->segment_count_main);
si->main_area_sections = le32_to_cpu(raw_super-><