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