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