1
2
3
4
5
6
7
8#include "bcache.h"
9#include "stats.h"
10#include "btree.h"
11#include "sysfs.h"
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36static const unsigned int DAY_RESCALE = 288;
37static const unsigned int HOUR_RESCALE = 12;
38static const unsigned int FIVE_MINUTE_RESCALE = 1;
39static const unsigned int accounting_delay = (HZ * 300) / 22;
40static const unsigned int accounting_weight = 32;
41
42
43
44read_attribute(cache_hits);
45read_attribute(cache_misses);
46read_attribute(cache_bypass_hits);
47read_attribute(cache_bypass_misses);
48read_attribute(cache_hit_ratio);
49read_attribute(cache_miss_collisions);
50read_attribute(bypassed);
51
52SHOW(bch_stats)
53{
54 struct cache_stats *s =
55 container_of(kobj, struct cache_stats, kobj);
56#define var(stat) (s->stat >> 16)
57 var_print(cache_hits);
58 var_print(cache_misses);
59 var_print(cache_bypass_hits);
60 var_print(cache_bypass_misses);
61
62 sysfs_print(cache_hit_ratio,
63 DIV_SAFE(var(cache_hits) * 100,
64 var(cache_hits) + var(cache_misses)));
65
66 var_print(cache_miss_collisions);
67 sysfs_hprint(bypassed, var(sectors_bypassed) << 9);
68#undef var
69 return 0;
70}
71
72STORE(bch_stats)
73{
74 return size;
75}
76
77static void bch_stats_release(struct kobject *k)
78{
79}
80
81static struct attribute *bch_stats_files[] = {
82 &sysfs_cache_hits,
83 &sysfs_cache_misses,
84 &sysfs_cache_bypass_hits,
85 &sysfs_cache_bypass_misses,
86 &sysfs_cache_hit_ratio,
87 &sysfs_cache_miss_collisions,
88 &sysfs_bypassed,
89 NULL
90};
91static KTYPE(bch_stats);
92
93int bch_cache_accounting_add_kobjs(struct cache_accounting *acc,
94 struct kobject *parent)
95{
96 int ret = kobject_add(&acc->total.kobj, parent,
97 "stats_total");
98 ret = ret ?: kobject_add(&acc->five_minute.kobj, parent,
99 "stats_five_minute");
100 ret = ret ?: kobject_add(&acc->hour.kobj, parent,
101 "stats_hour");
102 ret = ret ?: kobject_add(&acc->day.kobj, parent,
103 "stats_day");
104 return ret;
105}
106
107void bch_cache_accounting_clear(struct cache_accounting *acc)
108{
109 acc->total.cache_hits = 0;
110 acc->total.cache_misses = 0;
111 acc->total.cache_bypass_hits = 0;
112 acc->total.cache_bypass_misses = 0;
113 acc->total.cache_miss_collisions = 0;
114 acc->total.sectors_bypassed = 0;
115}
116
117void bch_cache_accounting_destroy(struct cache_accounting *acc)
118{
119 kobject_put(&acc->total.kobj);
120 kobject_put(&acc->five_minute.kobj);
121 kobject_put(&acc->hour.kobj);
122 kobject_put(&acc->day.kobj);
123
124 atomic_set(&acc->closing, 1);
125 if (del_timer_sync(&acc->timer))
126 closure_return(&acc->cl);
127}
128
129
130
131static void scale_stat(unsigned long *stat)
132{
133 *stat = ewma_add(*stat, 0, accounting_weight, 0);
134}
135
136static void scale_stats(struct cache_stats *stats, unsigned long rescale_at)
137{
138 if (++stats->rescale == rescale_at) {
139 stats->rescale = 0;
140 scale_stat(&stats->cache_hits);
141 scale_stat(&stats->cache_misses);
142 scale_stat(&stats->cache_bypass_hits);
143 scale_stat(&stats->cache_bypass_misses);
144 scale_stat(&stats->cache_miss_collisions);
145 scale_stat(&stats->sectors_bypassed);
146 }
147}
148
149static void scale_accounting(struct timer_list *t)
150{
151 struct cache_accounting *acc = from_timer(acc, t, timer);
152
153#define move_stat(name) do { \
154 unsigned int t = atomic_xchg(&acc->collector.name, 0); \
155 t <<= 16; \
156 acc->five_minute.name += t; \
157 acc->hour.name += t; \
158 acc->day.name += t; \
159 acc->total.name += t; \
160} while (0)
161
162 move_stat(cache_hits);
163 move_stat(cache_misses);
164 move_stat(cache_bypass_hits);
165 move_stat(cache_bypass_misses);
166 move_stat(cache_miss_collisions);
167 move_stat(sectors_bypassed);
168
169 scale_stats(&acc->total, 0);
170 scale_stats(&acc->day, DAY_RESCALE);
171 scale_stats(&acc->hour, HOUR_RESCALE);
172 scale_stats(&acc->five_minute, FIVE_MINUTE_RESCALE);
173
174 acc->timer.expires += accounting_delay;
175
176 if (!atomic_read(&acc->closing))
177 add_timer(&acc->timer);
178 else
179 closure_return(&acc->cl);
180}
181
182static void mark_cache_stats(struct cache_stat_collector *stats,
183 bool hit, bool bypass)
184{
185 if (!bypass)
186 if (hit)
187 atomic_inc(&stats->cache_hits);
188 else
189 atomic_inc(&stats->cache_misses);
190 else
191 if (hit)
192 atomic_inc(&stats->cache_bypass_hits);
193 else
194 atomic_inc(&stats->cache_bypass_misses);
195}
196
197void bch_mark_cache_accounting(struct cache_set *c, struct bcache_device *d,
198 bool hit, bool bypass)
199{
200 struct cached_dev *dc = container_of(d, struct cached_dev, disk);
201
202 mark_cache_stats(&dc->accounting.collector, hit, bypass);
203 mark_cache_stats(&c->accounting.collector, hit, bypass);
204}
205
206void bch_mark_cache_miss_collision(struct cache_set *c, struct bcache_device *d)
207{
208 struct cached_dev *dc = container_of(d, struct cached_dev, disk);
209
210 atomic_inc(&dc->accounting.collector.cache_miss_collisions);
211 atomic_inc(&c->accounting.collector.cache_miss_collisions);
212}
213
214void bch_mark_sectors_bypassed(struct cache_set *c, struct cached_dev *dc,
215 int sectors)
216{
217 atomic_add(sectors, &dc->accounting.collector.sectors_bypassed);
218 atomic_add(sectors, &c->accounting.collector.sectors_bypassed);
219}
220
221void bch_cache_accounting_init(struct cache_accounting *acc,
222 struct closure *parent)
223{
224 kobject_init(&acc->total.kobj, &bch_stats_ktype);
225 kobject_init(&acc->five_minute.kobj, &bch_stats_ktype);
226 kobject_init(&acc->hour.kobj, &bch_stats_ktype);
227 kobject_init(&acc->day.kobj, &bch_stats_ktype);
228
229 closure_init(&acc->cl, parent);
230 timer_setup(&acc->timer, scale_accounting, 0);
231 acc->timer.expires = jiffies + accounting_delay;
232 add_timer(&acc->timer);
233}
234