1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * bcache stats code
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright 2012 Google, Inc.
6*4882a593Smuzhiyun */
7*4882a593Smuzhiyun
8*4882a593Smuzhiyun #include "bcache.h"
9*4882a593Smuzhiyun #include "stats.h"
10*4882a593Smuzhiyun #include "btree.h"
11*4882a593Smuzhiyun #include "sysfs.h"
12*4882a593Smuzhiyun
13*4882a593Smuzhiyun /*
14*4882a593Smuzhiyun * We keep absolute totals of various statistics, and addionally a set of three
15*4882a593Smuzhiyun * rolling averages.
16*4882a593Smuzhiyun *
17*4882a593Smuzhiyun * Every so often, a timer goes off and rescales the rolling averages.
18*4882a593Smuzhiyun * accounting_rescale[] is how many times the timer has to go off before we
19*4882a593Smuzhiyun * rescale each set of numbers; that gets us half lives of 5 minutes, one hour,
20*4882a593Smuzhiyun * and one day.
21*4882a593Smuzhiyun *
22*4882a593Smuzhiyun * accounting_delay is how often the timer goes off - 22 times in 5 minutes,
23*4882a593Smuzhiyun * and accounting_weight is what we use to rescale:
24*4882a593Smuzhiyun *
25*4882a593Smuzhiyun * pow(31 / 32, 22) ~= 1/2
26*4882a593Smuzhiyun *
27*4882a593Smuzhiyun * So that we don't have to increment each set of numbers every time we (say)
28*4882a593Smuzhiyun * get a cache hit, we increment a single atomic_t in acc->collector, and when
29*4882a593Smuzhiyun * the rescale function runs it resets the atomic counter to 0 and adds its
30*4882a593Smuzhiyun * old value to each of the exported numbers.
31*4882a593Smuzhiyun *
32*4882a593Smuzhiyun * To reduce rounding error, the numbers in struct cache_stats are all
33*4882a593Smuzhiyun * stored left shifted by 16, and scaled back in the sysfs show() function.
34*4882a593Smuzhiyun */
35*4882a593Smuzhiyun
36*4882a593Smuzhiyun static const unsigned int DAY_RESCALE = 288;
37*4882a593Smuzhiyun static const unsigned int HOUR_RESCALE = 12;
38*4882a593Smuzhiyun static const unsigned int FIVE_MINUTE_RESCALE = 1;
39*4882a593Smuzhiyun static const unsigned int accounting_delay = (HZ * 300) / 22;
40*4882a593Smuzhiyun static const unsigned int accounting_weight = 32;
41*4882a593Smuzhiyun
42*4882a593Smuzhiyun /* sysfs reading/writing */
43*4882a593Smuzhiyun
44*4882a593Smuzhiyun read_attribute(cache_hits);
45*4882a593Smuzhiyun read_attribute(cache_misses);
46*4882a593Smuzhiyun read_attribute(cache_bypass_hits);
47*4882a593Smuzhiyun read_attribute(cache_bypass_misses);
48*4882a593Smuzhiyun read_attribute(cache_hit_ratio);
49*4882a593Smuzhiyun read_attribute(cache_readaheads);
50*4882a593Smuzhiyun read_attribute(cache_miss_collisions);
51*4882a593Smuzhiyun read_attribute(bypassed);
52*4882a593Smuzhiyun
SHOW(bch_stats)53*4882a593Smuzhiyun SHOW(bch_stats)
54*4882a593Smuzhiyun {
55*4882a593Smuzhiyun struct cache_stats *s =
56*4882a593Smuzhiyun container_of(kobj, struct cache_stats, kobj);
57*4882a593Smuzhiyun #define var(stat) (s->stat >> 16)
58*4882a593Smuzhiyun var_print(cache_hits);
59*4882a593Smuzhiyun var_print(cache_misses);
60*4882a593Smuzhiyun var_print(cache_bypass_hits);
61*4882a593Smuzhiyun var_print(cache_bypass_misses);
62*4882a593Smuzhiyun
63*4882a593Smuzhiyun sysfs_print(cache_hit_ratio,
64*4882a593Smuzhiyun DIV_SAFE(var(cache_hits) * 100,
65*4882a593Smuzhiyun var(cache_hits) + var(cache_misses)));
66*4882a593Smuzhiyun
67*4882a593Smuzhiyun var_print(cache_readaheads);
68*4882a593Smuzhiyun var_print(cache_miss_collisions);
69*4882a593Smuzhiyun sysfs_hprint(bypassed, var(sectors_bypassed) << 9);
70*4882a593Smuzhiyun #undef var
71*4882a593Smuzhiyun return 0;
72*4882a593Smuzhiyun }
73*4882a593Smuzhiyun
STORE(bch_stats)74*4882a593Smuzhiyun STORE(bch_stats)
75*4882a593Smuzhiyun {
76*4882a593Smuzhiyun return size;
77*4882a593Smuzhiyun }
78*4882a593Smuzhiyun
bch_stats_release(struct kobject * k)79*4882a593Smuzhiyun static void bch_stats_release(struct kobject *k)
80*4882a593Smuzhiyun {
81*4882a593Smuzhiyun }
82*4882a593Smuzhiyun
83*4882a593Smuzhiyun static struct attribute *bch_stats_files[] = {
84*4882a593Smuzhiyun &sysfs_cache_hits,
85*4882a593Smuzhiyun &sysfs_cache_misses,
86*4882a593Smuzhiyun &sysfs_cache_bypass_hits,
87*4882a593Smuzhiyun &sysfs_cache_bypass_misses,
88*4882a593Smuzhiyun &sysfs_cache_hit_ratio,
89*4882a593Smuzhiyun &sysfs_cache_readaheads,
90*4882a593Smuzhiyun &sysfs_cache_miss_collisions,
91*4882a593Smuzhiyun &sysfs_bypassed,
92*4882a593Smuzhiyun NULL
93*4882a593Smuzhiyun };
94*4882a593Smuzhiyun static KTYPE(bch_stats);
95*4882a593Smuzhiyun
bch_cache_accounting_add_kobjs(struct cache_accounting * acc,struct kobject * parent)96*4882a593Smuzhiyun int bch_cache_accounting_add_kobjs(struct cache_accounting *acc,
97*4882a593Smuzhiyun struct kobject *parent)
98*4882a593Smuzhiyun {
99*4882a593Smuzhiyun int ret = kobject_add(&acc->total.kobj, parent,
100*4882a593Smuzhiyun "stats_total");
101*4882a593Smuzhiyun ret = ret ?: kobject_add(&acc->five_minute.kobj, parent,
102*4882a593Smuzhiyun "stats_five_minute");
103*4882a593Smuzhiyun ret = ret ?: kobject_add(&acc->hour.kobj, parent,
104*4882a593Smuzhiyun "stats_hour");
105*4882a593Smuzhiyun ret = ret ?: kobject_add(&acc->day.kobj, parent,
106*4882a593Smuzhiyun "stats_day");
107*4882a593Smuzhiyun return ret;
108*4882a593Smuzhiyun }
109*4882a593Smuzhiyun
bch_cache_accounting_clear(struct cache_accounting * acc)110*4882a593Smuzhiyun void bch_cache_accounting_clear(struct cache_accounting *acc)
111*4882a593Smuzhiyun {
112*4882a593Smuzhiyun acc->total.cache_hits = 0;
113*4882a593Smuzhiyun acc->total.cache_misses = 0;
114*4882a593Smuzhiyun acc->total.cache_bypass_hits = 0;
115*4882a593Smuzhiyun acc->total.cache_bypass_misses = 0;
116*4882a593Smuzhiyun acc->total.cache_readaheads = 0;
117*4882a593Smuzhiyun acc->total.cache_miss_collisions = 0;
118*4882a593Smuzhiyun acc->total.sectors_bypassed = 0;
119*4882a593Smuzhiyun }
120*4882a593Smuzhiyun
bch_cache_accounting_destroy(struct cache_accounting * acc)121*4882a593Smuzhiyun void bch_cache_accounting_destroy(struct cache_accounting *acc)
122*4882a593Smuzhiyun {
123*4882a593Smuzhiyun kobject_put(&acc->total.kobj);
124*4882a593Smuzhiyun kobject_put(&acc->five_minute.kobj);
125*4882a593Smuzhiyun kobject_put(&acc->hour.kobj);
126*4882a593Smuzhiyun kobject_put(&acc->day.kobj);
127*4882a593Smuzhiyun
128*4882a593Smuzhiyun atomic_set(&acc->closing, 1);
129*4882a593Smuzhiyun if (del_timer_sync(&acc->timer))
130*4882a593Smuzhiyun closure_return(&acc->cl);
131*4882a593Smuzhiyun }
132*4882a593Smuzhiyun
133*4882a593Smuzhiyun /* EWMA scaling */
134*4882a593Smuzhiyun
scale_stat(unsigned long * stat)135*4882a593Smuzhiyun static void scale_stat(unsigned long *stat)
136*4882a593Smuzhiyun {
137*4882a593Smuzhiyun *stat = ewma_add(*stat, 0, accounting_weight, 0);
138*4882a593Smuzhiyun }
139*4882a593Smuzhiyun
scale_stats(struct cache_stats * stats,unsigned long rescale_at)140*4882a593Smuzhiyun static void scale_stats(struct cache_stats *stats, unsigned long rescale_at)
141*4882a593Smuzhiyun {
142*4882a593Smuzhiyun if (++stats->rescale == rescale_at) {
143*4882a593Smuzhiyun stats->rescale = 0;
144*4882a593Smuzhiyun scale_stat(&stats->cache_hits);
145*4882a593Smuzhiyun scale_stat(&stats->cache_misses);
146*4882a593Smuzhiyun scale_stat(&stats->cache_bypass_hits);
147*4882a593Smuzhiyun scale_stat(&stats->cache_bypass_misses);
148*4882a593Smuzhiyun scale_stat(&stats->cache_readaheads);
149*4882a593Smuzhiyun scale_stat(&stats->cache_miss_collisions);
150*4882a593Smuzhiyun scale_stat(&stats->sectors_bypassed);
151*4882a593Smuzhiyun }
152*4882a593Smuzhiyun }
153*4882a593Smuzhiyun
scale_accounting(struct timer_list * t)154*4882a593Smuzhiyun static void scale_accounting(struct timer_list *t)
155*4882a593Smuzhiyun {
156*4882a593Smuzhiyun struct cache_accounting *acc = from_timer(acc, t, timer);
157*4882a593Smuzhiyun
158*4882a593Smuzhiyun #define move_stat(name) do { \
159*4882a593Smuzhiyun unsigned int t = atomic_xchg(&acc->collector.name, 0); \
160*4882a593Smuzhiyun t <<= 16; \
161*4882a593Smuzhiyun acc->five_minute.name += t; \
162*4882a593Smuzhiyun acc->hour.name += t; \
163*4882a593Smuzhiyun acc->day.name += t; \
164*4882a593Smuzhiyun acc->total.name += t; \
165*4882a593Smuzhiyun } while (0)
166*4882a593Smuzhiyun
167*4882a593Smuzhiyun move_stat(cache_hits);
168*4882a593Smuzhiyun move_stat(cache_misses);
169*4882a593Smuzhiyun move_stat(cache_bypass_hits);
170*4882a593Smuzhiyun move_stat(cache_bypass_misses);
171*4882a593Smuzhiyun move_stat(cache_readaheads);
172*4882a593Smuzhiyun move_stat(cache_miss_collisions);
173*4882a593Smuzhiyun move_stat(sectors_bypassed);
174*4882a593Smuzhiyun
175*4882a593Smuzhiyun scale_stats(&acc->total, 0);
176*4882a593Smuzhiyun scale_stats(&acc->day, DAY_RESCALE);
177*4882a593Smuzhiyun scale_stats(&acc->hour, HOUR_RESCALE);
178*4882a593Smuzhiyun scale_stats(&acc->five_minute, FIVE_MINUTE_RESCALE);
179*4882a593Smuzhiyun
180*4882a593Smuzhiyun acc->timer.expires += accounting_delay;
181*4882a593Smuzhiyun
182*4882a593Smuzhiyun if (!atomic_read(&acc->closing))
183*4882a593Smuzhiyun add_timer(&acc->timer);
184*4882a593Smuzhiyun else
185*4882a593Smuzhiyun closure_return(&acc->cl);
186*4882a593Smuzhiyun }
187*4882a593Smuzhiyun
mark_cache_stats(struct cache_stat_collector * stats,bool hit,bool bypass)188*4882a593Smuzhiyun static void mark_cache_stats(struct cache_stat_collector *stats,
189*4882a593Smuzhiyun bool hit, bool bypass)
190*4882a593Smuzhiyun {
191*4882a593Smuzhiyun if (!bypass)
192*4882a593Smuzhiyun if (hit)
193*4882a593Smuzhiyun atomic_inc(&stats->cache_hits);
194*4882a593Smuzhiyun else
195*4882a593Smuzhiyun atomic_inc(&stats->cache_misses);
196*4882a593Smuzhiyun else
197*4882a593Smuzhiyun if (hit)
198*4882a593Smuzhiyun atomic_inc(&stats->cache_bypass_hits);
199*4882a593Smuzhiyun else
200*4882a593Smuzhiyun atomic_inc(&stats->cache_bypass_misses);
201*4882a593Smuzhiyun }
202*4882a593Smuzhiyun
bch_mark_cache_accounting(struct cache_set * c,struct bcache_device * d,bool hit,bool bypass)203*4882a593Smuzhiyun void bch_mark_cache_accounting(struct cache_set *c, struct bcache_device *d,
204*4882a593Smuzhiyun bool hit, bool bypass)
205*4882a593Smuzhiyun {
206*4882a593Smuzhiyun struct cached_dev *dc = container_of(d, struct cached_dev, disk);
207*4882a593Smuzhiyun
208*4882a593Smuzhiyun mark_cache_stats(&dc->accounting.collector, hit, bypass);
209*4882a593Smuzhiyun mark_cache_stats(&c->accounting.collector, hit, bypass);
210*4882a593Smuzhiyun }
211*4882a593Smuzhiyun
bch_mark_cache_readahead(struct cache_set * c,struct bcache_device * d)212*4882a593Smuzhiyun void bch_mark_cache_readahead(struct cache_set *c, struct bcache_device *d)
213*4882a593Smuzhiyun {
214*4882a593Smuzhiyun struct cached_dev *dc = container_of(d, struct cached_dev, disk);
215*4882a593Smuzhiyun
216*4882a593Smuzhiyun atomic_inc(&dc->accounting.collector.cache_readaheads);
217*4882a593Smuzhiyun atomic_inc(&c->accounting.collector.cache_readaheads);
218*4882a593Smuzhiyun }
219*4882a593Smuzhiyun
bch_mark_cache_miss_collision(struct cache_set * c,struct bcache_device * d)220*4882a593Smuzhiyun void bch_mark_cache_miss_collision(struct cache_set *c, struct bcache_device *d)
221*4882a593Smuzhiyun {
222*4882a593Smuzhiyun struct cached_dev *dc = container_of(d, struct cached_dev, disk);
223*4882a593Smuzhiyun
224*4882a593Smuzhiyun atomic_inc(&dc->accounting.collector.cache_miss_collisions);
225*4882a593Smuzhiyun atomic_inc(&c->accounting.collector.cache_miss_collisions);
226*4882a593Smuzhiyun }
227*4882a593Smuzhiyun
bch_mark_sectors_bypassed(struct cache_set * c,struct cached_dev * dc,int sectors)228*4882a593Smuzhiyun void bch_mark_sectors_bypassed(struct cache_set *c, struct cached_dev *dc,
229*4882a593Smuzhiyun int sectors)
230*4882a593Smuzhiyun {
231*4882a593Smuzhiyun atomic_add(sectors, &dc->accounting.collector.sectors_bypassed);
232*4882a593Smuzhiyun atomic_add(sectors, &c->accounting.collector.sectors_bypassed);
233*4882a593Smuzhiyun }
234*4882a593Smuzhiyun
bch_cache_accounting_init(struct cache_accounting * acc,struct closure * parent)235*4882a593Smuzhiyun void bch_cache_accounting_init(struct cache_accounting *acc,
236*4882a593Smuzhiyun struct closure *parent)
237*4882a593Smuzhiyun {
238*4882a593Smuzhiyun kobject_init(&acc->total.kobj, &bch_stats_ktype);
239*4882a593Smuzhiyun kobject_init(&acc->five_minute.kobj, &bch_stats_ktype);
240*4882a593Smuzhiyun kobject_init(&acc->hour.kobj, &bch_stats_ktype);
241*4882a593Smuzhiyun kobject_init(&acc->day.kobj, &bch_stats_ktype);
242*4882a593Smuzhiyun
243*4882a593Smuzhiyun closure_init(&acc->cl, parent);
244*4882a593Smuzhiyun timer_setup(&acc->timer, scale_accounting, 0);
245*4882a593Smuzhiyun acc->timer.expires = jiffies + accounting_delay;
246*4882a593Smuzhiyun add_timer(&acc->timer);
247*4882a593Smuzhiyun }
248