Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
cafe5635 KO |
2 | /* |
3 | * bcache stats code | |
4 | * | |
5 | * Copyright 2012 Google, Inc. | |
6 | */ | |
7 | ||
8 | #include "bcache.h" | |
9 | #include "stats.h" | |
10 | #include "btree.h" | |
cafe5635 KO |
11 | #include "sysfs.h" |
12 | ||
13 | /* | |
14 | * We keep absolute totals of various statistics, and addionally a set of three | |
15 | * rolling averages. | |
16 | * | |
17 | * Every so often, a timer goes off and rescales the rolling averages. | |
18 | * accounting_rescale[] is how many times the timer has to go off before we | |
19 | * rescale each set of numbers; that gets us half lives of 5 minutes, one hour, | |
20 | * and one day. | |
21 | * | |
22 | * accounting_delay is how often the timer goes off - 22 times in 5 minutes, | |
23 | * and accounting_weight is what we use to rescale: | |
24 | * | |
25 | * pow(31 / 32, 22) ~= 1/2 | |
26 | * | |
27 | * So that we don't have to increment each set of numbers every time we (say) | |
28 | * get a cache hit, we increment a single atomic_t in acc->collector, and when | |
29 | * the rescale function runs it resets the atomic counter to 0 and adds its | |
30 | * old value to each of the exported numbers. | |
31 | * | |
32 | * To reduce rounding error, the numbers in struct cache_stats are all | |
33 | * stored left shifted by 16, and scaled back in the sysfs show() function. | |
34 | */ | |
35 | ||
36 | static const unsigned DAY_RESCALE = 288; | |
37 | static const unsigned HOUR_RESCALE = 12; | |
38 | static const unsigned FIVE_MINUTE_RESCALE = 1; | |
39 | static const unsigned accounting_delay = (HZ * 300) / 22; | |
40 | static const unsigned accounting_weight = 32; | |
41 | ||
42 | /* sysfs reading/writing */ | |
43 | ||
44 | read_attribute(cache_hits); | |
45 | read_attribute(cache_misses); | |
46 | read_attribute(cache_bypass_hits); | |
47 | read_attribute(cache_bypass_misses); | |
48 | read_attribute(cache_hit_ratio); | |
49 | read_attribute(cache_readaheads); | |
50 | read_attribute(cache_miss_collisions); | |
51 | read_attribute(bypassed); | |
52 | ||
53 | SHOW(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 | ||
74 | STORE(bch_stats) | |
75 | { | |
76 | return size; | |
77 | } | |
78 | ||
79 | static void bch_stats_release(struct kobject *k) | |
80 | { | |
81 | } | |
82 | ||
83 | static 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 | }; | |
94 | static KTYPE(bch_stats); | |
95 | ||
cafe5635 KO |
96 | int 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 | ||
110 | void bch_cache_accounting_clear(struct cache_accounting *acc) | |
111 | { | |
112 | memset(&acc->total.cache_hits, | |
113 | 0, | |
114 | sizeof(unsigned long) * 7); | |
115 | } | |
116 | ||
117 | void 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 | /* EWMA scaling */ | |
130 | ||
131 | static void scale_stat(unsigned long *stat) | |
132 | { | |
133 | *stat = ewma_add(*stat, 0, accounting_weight, 0); | |
134 | } | |
135 | ||
136 | static 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_readaheads); | |
145 | scale_stat(&stats->cache_miss_collisions); | |
146 | scale_stat(&stats->sectors_bypassed); | |
147 | } | |
148 | } | |
149 | ||
150 | static void scale_accounting(unsigned long data) | |
151 | { | |
152 | struct cache_accounting *acc = (struct cache_accounting *) data; | |
153 | ||
154 | #define move_stat(name) do { \ | |
155 | unsigned t = atomic_xchg(&acc->collector.name, 0); \ | |
156 | t <<= 16; \ | |
157 | acc->five_minute.name += t; \ | |
158 | acc->hour.name += t; \ | |
159 | acc->day.name += t; \ | |
160 | acc->total.name += t; \ | |
161 | } while (0) | |
162 | ||
163 | move_stat(cache_hits); | |
164 | move_stat(cache_misses); | |
165 | move_stat(cache_bypass_hits); | |
166 | move_stat(cache_bypass_misses); | |
167 | move_stat(cache_readaheads); | |
168 | move_stat(cache_miss_collisions); | |
169 | move_stat(sectors_bypassed); | |
170 | ||
171 | scale_stats(&acc->total, 0); | |
172 | scale_stats(&acc->day, DAY_RESCALE); | |
173 | scale_stats(&acc->hour, HOUR_RESCALE); | |
174 | scale_stats(&acc->five_minute, FIVE_MINUTE_RESCALE); | |
175 | ||
176 | acc->timer.expires += accounting_delay; | |
177 | ||
178 | if (!atomic_read(&acc->closing)) | |
179 | add_timer(&acc->timer); | |
180 | else | |
181 | closure_return(&acc->cl); | |
182 | } | |
183 | ||
184 | static void mark_cache_stats(struct cache_stat_collector *stats, | |
185 | bool hit, bool bypass) | |
186 | { | |
187 | if (!bypass) | |
188 | if (hit) | |
189 | atomic_inc(&stats->cache_hits); | |
190 | else | |
191 | atomic_inc(&stats->cache_misses); | |
192 | else | |
193 | if (hit) | |
194 | atomic_inc(&stats->cache_bypass_hits); | |
195 | else | |
196 | atomic_inc(&stats->cache_bypass_misses); | |
197 | } | |
198 | ||
220bb38c KO |
199 | void bch_mark_cache_accounting(struct cache_set *c, struct bcache_device *d, |
200 | bool hit, bool bypass) | |
cafe5635 | 201 | { |
220bb38c | 202 | struct cached_dev *dc = container_of(d, struct cached_dev, disk); |
cafe5635 | 203 | mark_cache_stats(&dc->accounting.collector, hit, bypass); |
220bb38c | 204 | mark_cache_stats(&c->accounting.collector, hit, bypass); |
cafe5635 KO |
205 | } |
206 | ||
220bb38c | 207 | void bch_mark_cache_readahead(struct cache_set *c, struct bcache_device *d) |
cafe5635 | 208 | { |
220bb38c | 209 | struct cached_dev *dc = container_of(d, struct cached_dev, disk); |
cafe5635 | 210 | atomic_inc(&dc->accounting.collector.cache_readaheads); |
220bb38c | 211 | atomic_inc(&c->accounting.collector.cache_readaheads); |
cafe5635 KO |
212 | } |
213 | ||
220bb38c | 214 | void bch_mark_cache_miss_collision(struct cache_set *c, struct bcache_device *d) |
cafe5635 | 215 | { |
220bb38c | 216 | struct cached_dev *dc = container_of(d, struct cached_dev, disk); |
cafe5635 | 217 | atomic_inc(&dc->accounting.collector.cache_miss_collisions); |
220bb38c | 218 | atomic_inc(&c->accounting.collector.cache_miss_collisions); |
cafe5635 KO |
219 | } |
220 | ||
220bb38c KO |
221 | void bch_mark_sectors_bypassed(struct cache_set *c, struct cached_dev *dc, |
222 | int sectors) | |
cafe5635 | 223 | { |
cafe5635 | 224 | atomic_add(sectors, &dc->accounting.collector.sectors_bypassed); |
220bb38c | 225 | atomic_add(sectors, &c->accounting.collector.sectors_bypassed); |
cafe5635 | 226 | } |
f59fce84 KO |
227 | |
228 | void bch_cache_accounting_init(struct cache_accounting *acc, | |
229 | struct closure *parent) | |
230 | { | |
231 | kobject_init(&acc->total.kobj, &bch_stats_ktype); | |
232 | kobject_init(&acc->five_minute.kobj, &bch_stats_ktype); | |
233 | kobject_init(&acc->hour.kobj, &bch_stats_ktype); | |
234 | kobject_init(&acc->day.kobj, &bch_stats_ktype); | |
235 | ||
236 | closure_init(&acc->cl, parent); | |
237 | init_timer(&acc->timer); | |
238 | acc->timer.expires = jiffies + accounting_delay; | |
239 | acc->timer.data = (unsigned long) acc; | |
240 | acc->timer.function = scale_accounting; | |
241 | add_timer(&acc->timer); | |
242 | } |