xref: /linux/fs/bcachefs/time_stats.h (revision 4b4f0876ab74167cc402ccd5ce9154e7dc666829)
1 /* SPDX-License-Identifier: GPL-2.0 */
2 /*
3  * bch2_time_stats - collect statistics on events that have a duration, with nicely
4  * formatted textual output on demand
5  *
6  * - percpu buffering of event collection: cheap enough to shotgun
7  *   everywhere without worrying about overhead
8  *
9  * tracks:
10  *  - number of events
11  *  - maximum event duration ever seen
12  *  - sum of all event durations
13  *  - average event duration, standard and weighted
14  *  - standard deviation of event durations, standard and weighted
15  * and analagous statistics for the frequency of events
16  *
17  * We provide both mean and weighted mean (exponentially weighted), and standard
18  * deviation and weighted standard deviation, to give an efficient-to-compute
19  * view of current behaviour versus. average behaviour - "did this event source
20  * just become wonky, or is this typical?".
21  *
22  * Particularly useful for tracking down latency issues.
23  */
24 #ifndef _BCACHEFS_TIME_STATS_H
25 #define _BCACHEFS_TIME_STATS_H
26 
27 #include <linux/sched/clock.h>
28 #include <linux/spinlock_types.h>
29 
30 #include "mean_and_variance.h"
31 
32 struct time_unit {
33 	const char	*name;
34 	u64		nsecs;
35 };
36 
37 /*
38  * given a nanosecond value, pick the preferred time units for printing:
39  */
40 const struct time_unit *bch2_pick_time_units(u64 ns);
41 
42 /*
43  * quantiles - do not use:
44  *
45  * Only enabled if bch2_time_stats->quantiles_enabled has been manually set - don't
46  * use in new code.
47  */
48 
49 #define NR_QUANTILES	15
50 #define QUANTILE_IDX(i)	inorder_to_eytzinger0(i, NR_QUANTILES)
51 #define QUANTILE_FIRST	eytzinger0_first(NR_QUANTILES)
52 #define QUANTILE_LAST	eytzinger0_last(NR_QUANTILES)
53 
54 struct quantiles {
55 	struct quantile_entry {
56 		u64	m;
57 		u64	step;
58 	}		entries[NR_QUANTILES];
59 };
60 
61 struct time_stat_buffer {
62 	unsigned	nr;
63 	struct time_stat_buffer_entry {
64 		u64	start;
65 		u64	end;
66 	}		entries[32];
67 };
68 
69 struct bch2_time_stats {
70 	spinlock_t	lock;
71 	bool		quantiles_enabled;
72 	/* all fields are in nanoseconds */
73 	u64             min_duration;
74 	u64		max_duration;
75 	u64		total_duration;
76 	u64             max_freq;
77 	u64             min_freq;
78 	u64		last_event;
79 	u64		last_event_start;
80 	struct quantiles quantiles;
81 
82 	struct mean_and_variance	  duration_stats;
83 	struct mean_and_variance	  freq_stats;
84 
85 /* default weight for weighted mean and variance calculations */
86 #define TIME_STATS_MV_WEIGHT	8
87 
88 	struct mean_and_variance_weighted duration_stats_weighted;
89 	struct mean_and_variance_weighted freq_stats_weighted;
90 	struct time_stat_buffer __percpu *buffer;
91 };
92 
93 void __bch2_time_stats_clear_buffer(struct bch2_time_stats *, struct time_stat_buffer *);
94 void __bch2_time_stats_update(struct bch2_time_stats *stats, u64, u64);
95 
96 /**
97  * time_stats_update - collect a new event being tracked
98  *
99  * @stats	- bch2_time_stats to update
100  * @start	- start time of event, recorded with local_clock()
101  *
102  * The end duration of the event will be the current time
103  */
104 static inline void bch2_time_stats_update(struct bch2_time_stats *stats, u64 start)
105 {
106 	__bch2_time_stats_update(stats, start, local_clock());
107 }
108 
109 /**
110  * track_event_change - track state change events
111  *
112  * @stats	- bch2_time_stats to update
113  * @v		- new state, true or false
114  *
115  * Use this when tracking time stats for state changes, i.e. resource X becoming
116  * blocked/unblocked.
117  */
118 static inline bool track_event_change(struct bch2_time_stats *stats, bool v)
119 {
120 	if (v != !!stats->last_event_start) {
121 		if (!v) {
122 			bch2_time_stats_update(stats, stats->last_event_start);
123 			stats->last_event_start = 0;
124 		} else {
125 			stats->last_event_start = local_clock() ?: 1;
126 			return true;
127 		}
128 	}
129 
130 	return false;
131 }
132 
133 void bch2_time_stats_exit(struct bch2_time_stats *);
134 void bch2_time_stats_init(struct bch2_time_stats *);
135 
136 #endif /* _BCACHEFS_TIME_STATS_H */
137