18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * bcache stats code
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright 2012 Google, Inc.
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include "bcache.h"
98c2ecf20Sopenharmony_ci#include "stats.h"
108c2ecf20Sopenharmony_ci#include "btree.h"
118c2ecf20Sopenharmony_ci#include "sysfs.h"
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci/*
148c2ecf20Sopenharmony_ci * We keep absolute totals of various statistics, and addionally a set of three
158c2ecf20Sopenharmony_ci * rolling averages.
168c2ecf20Sopenharmony_ci *
178c2ecf20Sopenharmony_ci * Every so often, a timer goes off and rescales the rolling averages.
188c2ecf20Sopenharmony_ci * accounting_rescale[] is how many times the timer has to go off before we
198c2ecf20Sopenharmony_ci * rescale each set of numbers; that gets us half lives of 5 minutes, one hour,
208c2ecf20Sopenharmony_ci * and one day.
218c2ecf20Sopenharmony_ci *
228c2ecf20Sopenharmony_ci * accounting_delay is how often the timer goes off - 22 times in 5 minutes,
238c2ecf20Sopenharmony_ci * and accounting_weight is what we use to rescale:
248c2ecf20Sopenharmony_ci *
258c2ecf20Sopenharmony_ci * pow(31 / 32, 22) ~= 1/2
268c2ecf20Sopenharmony_ci *
278c2ecf20Sopenharmony_ci * So that we don't have to increment each set of numbers every time we (say)
288c2ecf20Sopenharmony_ci * get a cache hit, we increment a single atomic_t in acc->collector, and when
298c2ecf20Sopenharmony_ci * the rescale function runs it resets the atomic counter to 0 and adds its
308c2ecf20Sopenharmony_ci * old value to each of the exported numbers.
318c2ecf20Sopenharmony_ci *
328c2ecf20Sopenharmony_ci * To reduce rounding error, the numbers in struct cache_stats are all
338c2ecf20Sopenharmony_ci * stored left shifted by 16, and scaled back in the sysfs show() function.
348c2ecf20Sopenharmony_ci */
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic const unsigned int DAY_RESCALE		= 288;
378c2ecf20Sopenharmony_cistatic const unsigned int HOUR_RESCALE		= 12;
388c2ecf20Sopenharmony_cistatic const unsigned int FIVE_MINUTE_RESCALE	= 1;
398c2ecf20Sopenharmony_cistatic const unsigned int accounting_delay	= (HZ * 300) / 22;
408c2ecf20Sopenharmony_cistatic const unsigned int accounting_weight	= 32;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci/* sysfs reading/writing */
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ciread_attribute(cache_hits);
458c2ecf20Sopenharmony_ciread_attribute(cache_misses);
468c2ecf20Sopenharmony_ciread_attribute(cache_bypass_hits);
478c2ecf20Sopenharmony_ciread_attribute(cache_bypass_misses);
488c2ecf20Sopenharmony_ciread_attribute(cache_hit_ratio);
498c2ecf20Sopenharmony_ciread_attribute(cache_readaheads);
508c2ecf20Sopenharmony_ciread_attribute(cache_miss_collisions);
518c2ecf20Sopenharmony_ciread_attribute(bypassed);
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ciSHOW(bch_stats)
548c2ecf20Sopenharmony_ci{
558c2ecf20Sopenharmony_ci	struct cache_stats *s =
568c2ecf20Sopenharmony_ci		container_of(kobj, struct cache_stats, kobj);
578c2ecf20Sopenharmony_ci#define var(stat)		(s->stat >> 16)
588c2ecf20Sopenharmony_ci	var_print(cache_hits);
598c2ecf20Sopenharmony_ci	var_print(cache_misses);
608c2ecf20Sopenharmony_ci	var_print(cache_bypass_hits);
618c2ecf20Sopenharmony_ci	var_print(cache_bypass_misses);
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	sysfs_print(cache_hit_ratio,
648c2ecf20Sopenharmony_ci		    DIV_SAFE(var(cache_hits) * 100,
658c2ecf20Sopenharmony_ci			     var(cache_hits) + var(cache_misses)));
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	var_print(cache_readaheads);
688c2ecf20Sopenharmony_ci	var_print(cache_miss_collisions);
698c2ecf20Sopenharmony_ci	sysfs_hprint(bypassed,	var(sectors_bypassed) << 9);
708c2ecf20Sopenharmony_ci#undef var
718c2ecf20Sopenharmony_ci	return 0;
728c2ecf20Sopenharmony_ci}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ciSTORE(bch_stats)
758c2ecf20Sopenharmony_ci{
768c2ecf20Sopenharmony_ci	return size;
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistatic void bch_stats_release(struct kobject *k)
808c2ecf20Sopenharmony_ci{
818c2ecf20Sopenharmony_ci}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistatic struct attribute *bch_stats_files[] = {
848c2ecf20Sopenharmony_ci	&sysfs_cache_hits,
858c2ecf20Sopenharmony_ci	&sysfs_cache_misses,
868c2ecf20Sopenharmony_ci	&sysfs_cache_bypass_hits,
878c2ecf20Sopenharmony_ci	&sysfs_cache_bypass_misses,
888c2ecf20Sopenharmony_ci	&sysfs_cache_hit_ratio,
898c2ecf20Sopenharmony_ci	&sysfs_cache_readaheads,
908c2ecf20Sopenharmony_ci	&sysfs_cache_miss_collisions,
918c2ecf20Sopenharmony_ci	&sysfs_bypassed,
928c2ecf20Sopenharmony_ci	NULL
938c2ecf20Sopenharmony_ci};
948c2ecf20Sopenharmony_cistatic KTYPE(bch_stats);
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ciint bch_cache_accounting_add_kobjs(struct cache_accounting *acc,
978c2ecf20Sopenharmony_ci				   struct kobject *parent)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	int ret = kobject_add(&acc->total.kobj, parent,
1008c2ecf20Sopenharmony_ci			      "stats_total");
1018c2ecf20Sopenharmony_ci	ret = ret ?: kobject_add(&acc->five_minute.kobj, parent,
1028c2ecf20Sopenharmony_ci				 "stats_five_minute");
1038c2ecf20Sopenharmony_ci	ret = ret ?: kobject_add(&acc->hour.kobj, parent,
1048c2ecf20Sopenharmony_ci				 "stats_hour");
1058c2ecf20Sopenharmony_ci	ret = ret ?: kobject_add(&acc->day.kobj, parent,
1068c2ecf20Sopenharmony_ci				 "stats_day");
1078c2ecf20Sopenharmony_ci	return ret;
1088c2ecf20Sopenharmony_ci}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_civoid bch_cache_accounting_clear(struct cache_accounting *acc)
1118c2ecf20Sopenharmony_ci{
1128c2ecf20Sopenharmony_ci	acc->total.cache_hits = 0;
1138c2ecf20Sopenharmony_ci	acc->total.cache_misses = 0;
1148c2ecf20Sopenharmony_ci	acc->total.cache_bypass_hits = 0;
1158c2ecf20Sopenharmony_ci	acc->total.cache_bypass_misses = 0;
1168c2ecf20Sopenharmony_ci	acc->total.cache_readaheads = 0;
1178c2ecf20Sopenharmony_ci	acc->total.cache_miss_collisions = 0;
1188c2ecf20Sopenharmony_ci	acc->total.sectors_bypassed = 0;
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_civoid bch_cache_accounting_destroy(struct cache_accounting *acc)
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci	kobject_put(&acc->total.kobj);
1248c2ecf20Sopenharmony_ci	kobject_put(&acc->five_minute.kobj);
1258c2ecf20Sopenharmony_ci	kobject_put(&acc->hour.kobj);
1268c2ecf20Sopenharmony_ci	kobject_put(&acc->day.kobj);
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	atomic_set(&acc->closing, 1);
1298c2ecf20Sopenharmony_ci	if (del_timer_sync(&acc->timer))
1308c2ecf20Sopenharmony_ci		closure_return(&acc->cl);
1318c2ecf20Sopenharmony_ci}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci/* EWMA scaling */
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_cistatic void scale_stat(unsigned long *stat)
1368c2ecf20Sopenharmony_ci{
1378c2ecf20Sopenharmony_ci	*stat =  ewma_add(*stat, 0, accounting_weight, 0);
1388c2ecf20Sopenharmony_ci}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_cistatic void scale_stats(struct cache_stats *stats, unsigned long rescale_at)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	if (++stats->rescale == rescale_at) {
1438c2ecf20Sopenharmony_ci		stats->rescale = 0;
1448c2ecf20Sopenharmony_ci		scale_stat(&stats->cache_hits);
1458c2ecf20Sopenharmony_ci		scale_stat(&stats->cache_misses);
1468c2ecf20Sopenharmony_ci		scale_stat(&stats->cache_bypass_hits);
1478c2ecf20Sopenharmony_ci		scale_stat(&stats->cache_bypass_misses);
1488c2ecf20Sopenharmony_ci		scale_stat(&stats->cache_readaheads);
1498c2ecf20Sopenharmony_ci		scale_stat(&stats->cache_miss_collisions);
1508c2ecf20Sopenharmony_ci		scale_stat(&stats->sectors_bypassed);
1518c2ecf20Sopenharmony_ci	}
1528c2ecf20Sopenharmony_ci}
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_cistatic void scale_accounting(struct timer_list *t)
1558c2ecf20Sopenharmony_ci{
1568c2ecf20Sopenharmony_ci	struct cache_accounting *acc = from_timer(acc, t, timer);
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci#define move_stat(name) do {						\
1598c2ecf20Sopenharmony_ci	unsigned int t = atomic_xchg(&acc->collector.name, 0);		\
1608c2ecf20Sopenharmony_ci	t <<= 16;							\
1618c2ecf20Sopenharmony_ci	acc->five_minute.name += t;					\
1628c2ecf20Sopenharmony_ci	acc->hour.name += t;						\
1638c2ecf20Sopenharmony_ci	acc->day.name += t;						\
1648c2ecf20Sopenharmony_ci	acc->total.name += t;						\
1658c2ecf20Sopenharmony_ci} while (0)
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	move_stat(cache_hits);
1688c2ecf20Sopenharmony_ci	move_stat(cache_misses);
1698c2ecf20Sopenharmony_ci	move_stat(cache_bypass_hits);
1708c2ecf20Sopenharmony_ci	move_stat(cache_bypass_misses);
1718c2ecf20Sopenharmony_ci	move_stat(cache_readaheads);
1728c2ecf20Sopenharmony_ci	move_stat(cache_miss_collisions);
1738c2ecf20Sopenharmony_ci	move_stat(sectors_bypassed);
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	scale_stats(&acc->total, 0);
1768c2ecf20Sopenharmony_ci	scale_stats(&acc->day, DAY_RESCALE);
1778c2ecf20Sopenharmony_ci	scale_stats(&acc->hour, HOUR_RESCALE);
1788c2ecf20Sopenharmony_ci	scale_stats(&acc->five_minute, FIVE_MINUTE_RESCALE);
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	acc->timer.expires += accounting_delay;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	if (!atomic_read(&acc->closing))
1838c2ecf20Sopenharmony_ci		add_timer(&acc->timer);
1848c2ecf20Sopenharmony_ci	else
1858c2ecf20Sopenharmony_ci		closure_return(&acc->cl);
1868c2ecf20Sopenharmony_ci}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_cistatic void mark_cache_stats(struct cache_stat_collector *stats,
1898c2ecf20Sopenharmony_ci			     bool hit, bool bypass)
1908c2ecf20Sopenharmony_ci{
1918c2ecf20Sopenharmony_ci	if (!bypass)
1928c2ecf20Sopenharmony_ci		if (hit)
1938c2ecf20Sopenharmony_ci			atomic_inc(&stats->cache_hits);
1948c2ecf20Sopenharmony_ci		else
1958c2ecf20Sopenharmony_ci			atomic_inc(&stats->cache_misses);
1968c2ecf20Sopenharmony_ci	else
1978c2ecf20Sopenharmony_ci		if (hit)
1988c2ecf20Sopenharmony_ci			atomic_inc(&stats->cache_bypass_hits);
1998c2ecf20Sopenharmony_ci		else
2008c2ecf20Sopenharmony_ci			atomic_inc(&stats->cache_bypass_misses);
2018c2ecf20Sopenharmony_ci}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_civoid bch_mark_cache_accounting(struct cache_set *c, struct bcache_device *d,
2048c2ecf20Sopenharmony_ci			       bool hit, bool bypass)
2058c2ecf20Sopenharmony_ci{
2068c2ecf20Sopenharmony_ci	struct cached_dev *dc = container_of(d, struct cached_dev, disk);
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	mark_cache_stats(&dc->accounting.collector, hit, bypass);
2098c2ecf20Sopenharmony_ci	mark_cache_stats(&c->accounting.collector, hit, bypass);
2108c2ecf20Sopenharmony_ci}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_civoid bch_mark_cache_readahead(struct cache_set *c, struct bcache_device *d)
2138c2ecf20Sopenharmony_ci{
2148c2ecf20Sopenharmony_ci	struct cached_dev *dc = container_of(d, struct cached_dev, disk);
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	atomic_inc(&dc->accounting.collector.cache_readaheads);
2178c2ecf20Sopenharmony_ci	atomic_inc(&c->accounting.collector.cache_readaheads);
2188c2ecf20Sopenharmony_ci}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_civoid bch_mark_cache_miss_collision(struct cache_set *c, struct bcache_device *d)
2218c2ecf20Sopenharmony_ci{
2228c2ecf20Sopenharmony_ci	struct cached_dev *dc = container_of(d, struct cached_dev, disk);
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	atomic_inc(&dc->accounting.collector.cache_miss_collisions);
2258c2ecf20Sopenharmony_ci	atomic_inc(&c->accounting.collector.cache_miss_collisions);
2268c2ecf20Sopenharmony_ci}
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_civoid bch_mark_sectors_bypassed(struct cache_set *c, struct cached_dev *dc,
2298c2ecf20Sopenharmony_ci			       int sectors)
2308c2ecf20Sopenharmony_ci{
2318c2ecf20Sopenharmony_ci	atomic_add(sectors, &dc->accounting.collector.sectors_bypassed);
2328c2ecf20Sopenharmony_ci	atomic_add(sectors, &c->accounting.collector.sectors_bypassed);
2338c2ecf20Sopenharmony_ci}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_civoid bch_cache_accounting_init(struct cache_accounting *acc,
2368c2ecf20Sopenharmony_ci			       struct closure *parent)
2378c2ecf20Sopenharmony_ci{
2388c2ecf20Sopenharmony_ci	kobject_init(&acc->total.kobj,		&bch_stats_ktype);
2398c2ecf20Sopenharmony_ci	kobject_init(&acc->five_minute.kobj,	&bch_stats_ktype);
2408c2ecf20Sopenharmony_ci	kobject_init(&acc->hour.kobj,		&bch_stats_ktype);
2418c2ecf20Sopenharmony_ci	kobject_init(&acc->day.kobj,		&bch_stats_ktype);
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	closure_init(&acc->cl, parent);
2448c2ecf20Sopenharmony_ci	timer_setup(&acc->timer, scale_accounting, 0);
2458c2ecf20Sopenharmony_ci	acc->timer.expires	= jiffies + accounting_delay;
2468c2ecf20Sopenharmony_ci	add_timer(&acc->timer);
2478c2ecf20Sopenharmony_ci}
248