| 
									
										
										
										
											2013-03-23 16:11:31 -07:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * bcache stats code | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright 2012 Google, Inc. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "bcache.h"
 | 
					
						
							|  |  |  | #include "stats.h"
 | 
					
						
							|  |  |  | #include "btree.h"
 | 
					
						
							|  |  |  | #include "sysfs.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * We keep absolute totals of various statistics, and addionally a set of three | 
					
						
							|  |  |  |  * rolling averages. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Every so often, a timer goes off and rescales the rolling averages. | 
					
						
							|  |  |  |  * accounting_rescale[] is how many times the timer has to go off before we | 
					
						
							|  |  |  |  * rescale each set of numbers; that gets us half lives of 5 minutes, one hour, | 
					
						
							|  |  |  |  * and one day. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * accounting_delay is how often the timer goes off - 22 times in 5 minutes, | 
					
						
							|  |  |  |  * and accounting_weight is what we use to rescale: | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * pow(31 / 32, 22) ~= 1/2 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * So that we don't have to increment each set of numbers every time we (say) | 
					
						
							|  |  |  |  * get a cache hit, we increment a single atomic_t in acc->collector, and when | 
					
						
							|  |  |  |  * the rescale function runs it resets the atomic counter to 0 and adds its | 
					
						
							|  |  |  |  * old value to each of the exported numbers. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * To reduce rounding error, the numbers in struct cache_stats are all | 
					
						
							|  |  |  |  * stored left shifted by 16, and scaled back in the sysfs show() function. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const unsigned DAY_RESCALE		= 288; | 
					
						
							|  |  |  | static const unsigned HOUR_RESCALE		= 12; | 
					
						
							|  |  |  | static const unsigned FIVE_MINUTE_RESCALE	= 1; | 
					
						
							|  |  |  | static const unsigned accounting_delay		= (HZ * 300) / 22; | 
					
						
							|  |  |  | static const unsigned accounting_weight		= 32; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* sysfs reading/writing */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | read_attribute(cache_hits); | 
					
						
							|  |  |  | read_attribute(cache_misses); | 
					
						
							|  |  |  | read_attribute(cache_bypass_hits); | 
					
						
							|  |  |  | read_attribute(cache_bypass_misses); | 
					
						
							|  |  |  | read_attribute(cache_hit_ratio); | 
					
						
							|  |  |  | read_attribute(cache_readaheads); | 
					
						
							|  |  |  | read_attribute(cache_miss_collisions); | 
					
						
							|  |  |  | read_attribute(bypassed); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | SHOW(bch_stats) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct cache_stats *s = | 
					
						
							|  |  |  | 		container_of(kobj, struct cache_stats, kobj); | 
					
						
							|  |  |  | #define var(stat)		(s->stat >> 16)
 | 
					
						
							|  |  |  | 	var_print(cache_hits); | 
					
						
							|  |  |  | 	var_print(cache_misses); | 
					
						
							|  |  |  | 	var_print(cache_bypass_hits); | 
					
						
							|  |  |  | 	var_print(cache_bypass_misses); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	sysfs_print(cache_hit_ratio, | 
					
						
							|  |  |  | 		    DIV_SAFE(var(cache_hits) * 100, | 
					
						
							|  |  |  | 			     var(cache_hits) + var(cache_misses))); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var_print(cache_readaheads); | 
					
						
							|  |  |  | 	var_print(cache_miss_collisions); | 
					
						
							|  |  |  | 	sysfs_hprint(bypassed,	var(sectors_bypassed) << 9); | 
					
						
							|  |  |  | #undef var
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | STORE(bch_stats) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return size; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void bch_stats_release(struct kobject *k) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct attribute *bch_stats_files[] = { | 
					
						
							|  |  |  | 	&sysfs_cache_hits, | 
					
						
							|  |  |  | 	&sysfs_cache_misses, | 
					
						
							|  |  |  | 	&sysfs_cache_bypass_hits, | 
					
						
							|  |  |  | 	&sysfs_cache_bypass_misses, | 
					
						
							|  |  |  | 	&sysfs_cache_hit_ratio, | 
					
						
							|  |  |  | 	&sysfs_cache_readaheads, | 
					
						
							|  |  |  | 	&sysfs_cache_miss_collisions, | 
					
						
							|  |  |  | 	&sysfs_bypassed, | 
					
						
							|  |  |  | 	NULL | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | static KTYPE(bch_stats); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int bch_cache_accounting_add_kobjs(struct cache_accounting *acc, | 
					
						
							|  |  |  | 				   struct kobject *parent) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int ret = kobject_add(&acc->total.kobj, parent, | 
					
						
							|  |  |  | 			      "stats_total"); | 
					
						
							|  |  |  | 	ret = ret ?: kobject_add(&acc->five_minute.kobj, parent, | 
					
						
							|  |  |  | 				 "stats_five_minute"); | 
					
						
							|  |  |  | 	ret = ret ?: kobject_add(&acc->hour.kobj, parent, | 
					
						
							|  |  |  | 				 "stats_hour"); | 
					
						
							|  |  |  | 	ret = ret ?: kobject_add(&acc->day.kobj, parent, | 
					
						
							|  |  |  | 				 "stats_day"); | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void bch_cache_accounting_clear(struct cache_accounting *acc) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	memset(&acc->total.cache_hits, | 
					
						
							|  |  |  | 	       0, | 
					
						
							|  |  |  | 	       sizeof(unsigned long) * 7); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void bch_cache_accounting_destroy(struct cache_accounting *acc) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	kobject_put(&acc->total.kobj); | 
					
						
							|  |  |  | 	kobject_put(&acc->five_minute.kobj); | 
					
						
							|  |  |  | 	kobject_put(&acc->hour.kobj); | 
					
						
							|  |  |  | 	kobject_put(&acc->day.kobj); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	atomic_set(&acc->closing, 1); | 
					
						
							|  |  |  | 	if (del_timer_sync(&acc->timer)) | 
					
						
							|  |  |  | 		closure_return(&acc->cl); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* EWMA scaling */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void scale_stat(unsigned long *stat) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	*stat =  ewma_add(*stat, 0, accounting_weight, 0); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void scale_stats(struct cache_stats *stats, unsigned long rescale_at) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (++stats->rescale == rescale_at) { | 
					
						
							|  |  |  | 		stats->rescale = 0; | 
					
						
							|  |  |  | 		scale_stat(&stats->cache_hits); | 
					
						
							|  |  |  | 		scale_stat(&stats->cache_misses); | 
					
						
							|  |  |  | 		scale_stat(&stats->cache_bypass_hits); | 
					
						
							|  |  |  | 		scale_stat(&stats->cache_bypass_misses); | 
					
						
							|  |  |  | 		scale_stat(&stats->cache_readaheads); | 
					
						
							|  |  |  | 		scale_stat(&stats->cache_miss_collisions); | 
					
						
							|  |  |  | 		scale_stat(&stats->sectors_bypassed); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void scale_accounting(unsigned long data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct cache_accounting *acc = (struct cache_accounting *) data; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define move_stat(name) do {						\
 | 
					
						
							|  |  |  | 	unsigned t = atomic_xchg(&acc->collector.name, 0);		\ | 
					
						
							|  |  |  | 	t <<= 16;							\ | 
					
						
							|  |  |  | 	acc->five_minute.name += t;					\ | 
					
						
							|  |  |  | 	acc->hour.name += t;						\ | 
					
						
							|  |  |  | 	acc->day.name += t;						\ | 
					
						
							|  |  |  | 	acc->total.name += t;						\ | 
					
						
							|  |  |  | } while (0) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	move_stat(cache_hits); | 
					
						
							|  |  |  | 	move_stat(cache_misses); | 
					
						
							|  |  |  | 	move_stat(cache_bypass_hits); | 
					
						
							|  |  |  | 	move_stat(cache_bypass_misses); | 
					
						
							|  |  |  | 	move_stat(cache_readaheads); | 
					
						
							|  |  |  | 	move_stat(cache_miss_collisions); | 
					
						
							|  |  |  | 	move_stat(sectors_bypassed); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	scale_stats(&acc->total, 0); | 
					
						
							|  |  |  | 	scale_stats(&acc->day, DAY_RESCALE); | 
					
						
							|  |  |  | 	scale_stats(&acc->hour, HOUR_RESCALE); | 
					
						
							|  |  |  | 	scale_stats(&acc->five_minute, FIVE_MINUTE_RESCALE); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	acc->timer.expires += accounting_delay; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!atomic_read(&acc->closing)) | 
					
						
							|  |  |  | 		add_timer(&acc->timer); | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		closure_return(&acc->cl); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void mark_cache_stats(struct cache_stat_collector *stats, | 
					
						
							|  |  |  | 			     bool hit, bool bypass) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (!bypass) | 
					
						
							|  |  |  | 		if (hit) | 
					
						
							|  |  |  | 			atomic_inc(&stats->cache_hits); | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			atomic_inc(&stats->cache_misses); | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		if (hit) | 
					
						
							|  |  |  | 			atomic_inc(&stats->cache_bypass_hits); | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			atomic_inc(&stats->cache_bypass_misses); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-09-10 19:02:45 -07:00
										 |  |  | void bch_mark_cache_accounting(struct cache_set *c, struct bcache_device *d, | 
					
						
							|  |  |  | 			       bool hit, bool bypass) | 
					
						
							| 
									
										
										
										
											2013-03-23 16:11:31 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-09-10 19:02:45 -07:00
										 |  |  | 	struct cached_dev *dc = container_of(d, struct cached_dev, disk); | 
					
						
							| 
									
										
										
										
											2013-03-23 16:11:31 -07:00
										 |  |  | 	mark_cache_stats(&dc->accounting.collector, hit, bypass); | 
					
						
							| 
									
										
										
										
											2013-09-10 19:02:45 -07:00
										 |  |  | 	mark_cache_stats(&c->accounting.collector, hit, bypass); | 
					
						
							| 
									
										
										
										
											2013-03-23 16:11:31 -07:00
										 |  |  | #ifdef CONFIG_CGROUP_BCACHE
 | 
					
						
							|  |  |  | 	mark_cache_stats(&(bch_bio_to_cgroup(s->orig_bio)->stats), hit, bypass); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-09-10 19:02:45 -07:00
										 |  |  | void bch_mark_cache_readahead(struct cache_set *c, struct bcache_device *d) | 
					
						
							| 
									
										
										
										
											2013-03-23 16:11:31 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-09-10 19:02:45 -07:00
										 |  |  | 	struct cached_dev *dc = container_of(d, struct cached_dev, disk); | 
					
						
							| 
									
										
										
										
											2013-03-23 16:11:31 -07:00
										 |  |  | 	atomic_inc(&dc->accounting.collector.cache_readaheads); | 
					
						
							| 
									
										
										
										
											2013-09-10 19:02:45 -07:00
										 |  |  | 	atomic_inc(&c->accounting.collector.cache_readaheads); | 
					
						
							| 
									
										
										
										
											2013-03-23 16:11:31 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-09-10 19:02:45 -07:00
										 |  |  | void bch_mark_cache_miss_collision(struct cache_set *c, struct bcache_device *d) | 
					
						
							| 
									
										
										
										
											2013-03-23 16:11:31 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-09-10 19:02:45 -07:00
										 |  |  | 	struct cached_dev *dc = container_of(d, struct cached_dev, disk); | 
					
						
							| 
									
										
										
										
											2013-03-23 16:11:31 -07:00
										 |  |  | 	atomic_inc(&dc->accounting.collector.cache_miss_collisions); | 
					
						
							| 
									
										
										
										
											2013-09-10 19:02:45 -07:00
										 |  |  | 	atomic_inc(&c->accounting.collector.cache_miss_collisions); | 
					
						
							| 
									
										
										
										
											2013-03-23 16:11:31 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-09-10 19:02:45 -07:00
										 |  |  | void bch_mark_sectors_bypassed(struct cache_set *c, struct cached_dev *dc, | 
					
						
							|  |  |  | 			       int sectors) | 
					
						
							| 
									
										
										
										
											2013-03-23 16:11:31 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	atomic_add(sectors, &dc->accounting.collector.sectors_bypassed); | 
					
						
							| 
									
										
										
										
											2013-09-10 19:02:45 -07:00
										 |  |  | 	atomic_add(sectors, &c->accounting.collector.sectors_bypassed); | 
					
						
							| 
									
										
										
										
											2013-03-23 16:11:31 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2013-05-15 00:11:26 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | void bch_cache_accounting_init(struct cache_accounting *acc, | 
					
						
							|  |  |  | 			       struct closure *parent) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	kobject_init(&acc->total.kobj,		&bch_stats_ktype); | 
					
						
							|  |  |  | 	kobject_init(&acc->five_minute.kobj,	&bch_stats_ktype); | 
					
						
							|  |  |  | 	kobject_init(&acc->hour.kobj,		&bch_stats_ktype); | 
					
						
							|  |  |  | 	kobject_init(&acc->day.kobj,		&bch_stats_ktype); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	closure_init(&acc->cl, parent); | 
					
						
							|  |  |  | 	init_timer(&acc->timer); | 
					
						
							|  |  |  | 	acc->timer.expires	= jiffies + accounting_delay; | 
					
						
							|  |  |  | 	acc->timer.data		= (unsigned long) acc; | 
					
						
							|  |  |  | 	acc->timer.function	= scale_accounting; | 
					
						
							|  |  |  | 	add_timer(&acc->timer); | 
					
						
							|  |  |  | } |