at for-next 4.4 kB view raw
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#include <linux/string.h> 30 31#include "mean_and_variance.h" 32 33struct time_unit { 34 const char *name; 35 u64 nsecs; 36}; 37 38/* 39 * given a nanosecond value, pick the preferred time units for printing: 40 */ 41const struct time_unit *bch2_pick_time_units(u64 ns); 42 43/* 44 * quantiles - do not use: 45 * 46 * Only enabled if bch2_time_stats->quantiles_enabled has been manually set - don't 47 * use in new code. 48 */ 49 50#define NR_QUANTILES 15 51#define QUANTILE_IDX(i) inorder_to_eytzinger0(i, NR_QUANTILES) 52#define QUANTILE_FIRST eytzinger0_first(NR_QUANTILES) 53#define QUANTILE_LAST eytzinger0_last(NR_QUANTILES) 54 55struct quantiles { 56 struct quantile_entry { 57 u64 m; 58 u64 step; 59 } entries[NR_QUANTILES]; 60}; 61 62struct time_stat_buffer { 63 unsigned nr; 64 struct time_stat_buffer_entry { 65 u64 start; 66 u64 end; 67 } entries[31]; 68}; 69 70struct bch2_time_stats { 71 spinlock_t lock; 72 bool have_quantiles; 73 struct time_stat_buffer __percpu *buffer; 74 /* all fields are in nanoseconds */ 75 u64 min_duration; 76 u64 max_duration; 77 u64 total_duration; 78 u64 max_freq; 79 u64 min_freq; 80 u64 last_event; 81 u64 last_event_start; 82 83 struct mean_and_variance duration_stats; 84 struct mean_and_variance freq_stats; 85 86/* default weight for weighted mean and variance calculations */ 87#define TIME_STATS_MV_WEIGHT 8 88 89 struct mean_and_variance_weighted duration_stats_weighted; 90 struct mean_and_variance_weighted freq_stats_weighted; 91}; 92 93struct bch2_time_stats_quantiles { 94 struct bch2_time_stats stats; 95 struct quantiles quantiles; 96}; 97 98static inline struct quantiles *time_stats_to_quantiles(struct bch2_time_stats *stats) 99{ 100 return stats->have_quantiles 101 ? &container_of(stats, struct bch2_time_stats_quantiles, stats)->quantiles 102 : NULL; 103} 104 105void __bch2_time_stats_clear_buffer(struct bch2_time_stats *, struct time_stat_buffer *); 106void __bch2_time_stats_update(struct bch2_time_stats *stats, u64, u64); 107 108/** 109 * time_stats_update - collect a new event being tracked 110 * 111 * @stats - bch2_time_stats to update 112 * @start - start time of event, recorded with local_clock() 113 * 114 * The end duration of the event will be the current time 115 */ 116static inline void bch2_time_stats_update(struct bch2_time_stats *stats, u64 start) 117{ 118 __bch2_time_stats_update(stats, start, local_clock()); 119} 120 121/** 122 * track_event_change - track state change events 123 * 124 * @stats - bch2_time_stats to update 125 * @v - new state, true or false 126 * 127 * Use this when tracking time stats for state changes, i.e. resource X becoming 128 * blocked/unblocked. 129 */ 130static inline bool track_event_change(struct bch2_time_stats *stats, bool v) 131{ 132 if (v != !!stats->last_event_start) { 133 if (!v) { 134 bch2_time_stats_update(stats, stats->last_event_start); 135 stats->last_event_start = 0; 136 } else { 137 stats->last_event_start = local_clock() ?: 1; 138 return true; 139 } 140 } 141 142 return false; 143} 144 145void bch2_time_stats_reset(struct bch2_time_stats *); 146void bch2_time_stats_exit(struct bch2_time_stats *); 147void bch2_time_stats_init(struct bch2_time_stats *); 148 149static inline void bch2_time_stats_quantiles_exit(struct bch2_time_stats_quantiles *statq) 150{ 151 bch2_time_stats_exit(&statq->stats); 152} 153static inline void bch2_time_stats_quantiles_init(struct bch2_time_stats_quantiles *statq) 154{ 155 bch2_time_stats_init(&statq->stats); 156 statq->stats.have_quantiles = true; 157 memset(&statq->quantiles, 0, sizeof(statq->quantiles)); 158} 159 160#endif /* _BCACHEFS_TIME_STATS_H */