diff options
author | Kent Overstreet <kent.overstreet@linux.dev> | 2024-01-26 11:58:44 -0500 |
---|---|---|
committer | Kent Overstreet <kent.overstreet@linux.dev> | 2025-02-07 18:16:25 -0500 |
commit | f2a1b863f8cc89934c117468afd60bcfd5ffea5f (patch) | |
tree | f0a7ed87120b85f10f443286ceb27ce3b4af7b51 | |
parent | a8bbc86b836e80e9646bfa7ad16376fb3dd637e5 (diff) |
time_stats: Promote to lib/
Library code from bcachefs for tracking latency measurements.
The main interface is
time_stats_update(stats, start_time);
which collects a new event with an end time of the current time.
It features percpu buffering of input values, making it very low
overhead, and nicely formatted output to printbufs or seq_buf.
Sample output, from the bcache conversion:
root@moria-kvm:/sys/fs/bcache/bdaedb8c-4554-4dd2-87e4-276e51eb47cc# cat internal/btree_sort_times
count: 6414
since mount recent
duration of events
min: 440 ns
max: 1102 us
total: 674 ms
mean: 105 us 102 us
stddev: 101 us 88 us
time between events
min: 881 ns
max: 3 s
mean: 7 ms 6 ms
stddev: 52 ms 6 ms
Cc: Darrick J. Wong <djwong@kernel.org>
Cc: Dave Chinner <david@fromorbit.com>
Cc: Theodore Ts'o <tytso@mit.edu>
Cc: Coly Li <colyli@suse.de>
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
-rw-r--r-- | MAINTAINERS | 7 | ||||
-rw-r--r-- | fs/bcachefs/Kconfig | 1 | ||||
-rw-r--r-- | fs/bcachefs/Makefile | 1 | ||||
-rw-r--r-- | fs/bcachefs/bcachefs.h | 10 | ||||
-rw-r--r-- | fs/bcachefs/btree_cache.c | 2 | ||||
-rw-r--r-- | fs/bcachefs/btree_gc.c | 2 | ||||
-rw-r--r-- | fs/bcachefs/btree_io.c | 8 | ||||
-rw-r--r-- | fs/bcachefs/btree_iter.c | 8 | ||||
-rw-r--r-- | fs/bcachefs/btree_locking.h | 2 | ||||
-rw-r--r-- | fs/bcachefs/btree_update_interior.c | 8 | ||||
-rw-r--r-- | fs/bcachefs/io_read.c | 4 | ||||
-rw-r--r-- | fs/bcachefs/io_write.c | 4 | ||||
-rw-r--r-- | fs/bcachefs/journal.c | 2 | ||||
-rw-r--r-- | fs/bcachefs/journal_io.c | 6 | ||||
-rw-r--r-- | fs/bcachefs/journal_types.h | 6 | ||||
-rw-r--r-- | fs/bcachefs/nocow_locking.c | 2 | ||||
-rw-r--r-- | fs/bcachefs/super.c | 12 | ||||
-rw-r--r-- | fs/bcachefs/sysfs.c | 2 | ||||
-rw-r--r-- | fs/bcachefs/util.c | 10 | ||||
-rw-r--r-- | fs/bcachefs/util.h | 4 | ||||
-rw-r--r-- | include/linux/time_stats.h (renamed from fs/bcachefs/time_stats.h) | 56 | ||||
-rw-r--r-- | lib/Kconfig | 4 | ||||
-rw-r--r-- | lib/Makefile | 2 | ||||
-rw-r--r-- | lib/time_stats.c (renamed from fs/bcachefs/time_stats.c) | 44 |
24 files changed, 114 insertions, 93 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 377f26ee9b1b..bcf10b4e83e3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -23869,6 +23869,13 @@ F: kernel/time/timekeeping* F: kernel/time/time_test.c F: tools/testing/selftests/timers/ +TIME STATS: +M: Kent Overstreet <kent.overstreet@linux.dev> +M: Darrick J. Wong <djwong@kernel.org> +S: Maintained +F: include/linux/time_stats.h +F: lib/time_stats.c + TIPC NETWORK LAYER M: Jon Maloy <jmaloy@redhat.com> L: netdev@vger.kernel.org (core kernel code) diff --git a/fs/bcachefs/Kconfig b/fs/bcachefs/Kconfig index 943b68197c46..cb31795ef0ca 100644 --- a/fs/bcachefs/Kconfig +++ b/fs/bcachefs/Kconfig @@ -27,6 +27,7 @@ config BCACHEFS_FS select SYMBOLIC_ERRNAME select MIN_HEAP select MEAN_AND_VARIANCE + select TIME_STATS help The bcachefs filesystem - a modern, copy on write filesystem, with support for multiple devices, compression, checksumming, etc. diff --git a/fs/bcachefs/Makefile b/fs/bcachefs/Makefile index a144943115b8..b34da2a35792 100644 --- a/fs/bcachefs/Makefile +++ b/fs/bcachefs/Makefile @@ -87,7 +87,6 @@ bcachefs-y := \ super-io.o \ sysfs.o \ tests.o \ - time_stats.o \ thread_with_file.o \ trace.o \ two_state_shared_lock.o \ diff --git a/fs/bcachefs/bcachefs.h b/fs/bcachefs/bcachefs.h index 7c787d5c3de1..aea9e582d761 100644 --- a/fs/bcachefs/bcachefs.h +++ b/fs/bcachefs/bcachefs.h @@ -200,6 +200,7 @@ #include <linux/seqlock.h> #include <linux/shrinker.h> #include <linux/srcu.h> +#include <linux/time_stats.h> #include <linux/types.h> #include <linux/workqueue.h> #include <linux/zstd.h> @@ -214,7 +215,6 @@ #include "recovery_passes_types.h" #include "sb-errors_types.h" #include "seqmutex.h" -#include "time_stats.h" #include "util.h" #ifdef CONFIG_BCACHEFS_DEBUG @@ -587,7 +587,7 @@ struct bch_dev { /* The rest of this all shows up in sysfs */ atomic64_t cur_latency[2]; - struct bch2_time_stats_quantiles io_latency[2]; + struct time_stats_quantiles io_latency[2]; #define CONGESTED_MAX 1024 atomic_t congested; @@ -639,8 +639,8 @@ struct btree_debug { #define BCH_TRANSACTIONS_NR 128 struct btree_transaction_stats { - struct bch2_time_stats duration; - struct bch2_time_stats lock_hold_times; + struct time_stats duration; + struct time_stats lock_hold_times; struct mutex lock; unsigned nr_max_paths; unsigned journal_entries_size; @@ -1086,7 +1086,7 @@ struct bch_fs { u64 counters_on_mount[BCH_COUNTER_NR]; u64 __percpu *counters; - struct bch2_time_stats times[BCH_TIME_STAT_NR]; + struct time_stats times[BCH_TIME_STAT_NR]; struct btree_transaction_stats btree_transaction_stats[BCH_TRANSACTIONS_NR]; diff --git a/fs/bcachefs/btree_cache.c b/fs/bcachefs/btree_cache.c index ca755e8d1a37..b2c4b4ee2ca0 100644 --- a/fs/bcachefs/btree_cache.c +++ b/fs/bcachefs/btree_cache.c @@ -852,7 +852,7 @@ out: bch2_btree_keys_init(b); set_btree_node_accessed(b); - bch2_time_stats_update(&c->times[BCH_TIME_btree_node_mem_alloc], + time_stats_update(&c->times[BCH_TIME_btree_node_mem_alloc], start_time); int ret = bch2_trans_relock(trans); diff --git a/fs/bcachefs/btree_gc.c b/fs/bcachefs/btree_gc.c index ff681e733598..11b1c08174eb 100644 --- a/fs/bcachefs/btree_gc.c +++ b/fs/bcachefs/btree_gc.c @@ -1215,7 +1215,7 @@ int bch2_gc_gens(struct bch_fs *c) c->gc_count++; - bch2_time_stats_update(&c->times[BCH_TIME_btree_gc], start_time); + time_stats_update(&c->times[BCH_TIME_btree_gc], start_time); trace_and_count(c, gc_gens_end, c); err: for_each_member_device(c, ca) { diff --git a/fs/bcachefs/btree_io.c b/fs/bcachefs/btree_io.c index 4aa97b59d92e..44410abf0e8d 100644 --- a/fs/bcachefs/btree_io.c +++ b/fs/bcachefs/btree_io.c @@ -331,7 +331,7 @@ static void btree_node_sort(struct bch_fs *c, struct btree *b, BUG_ON(vstruct_end(&out->keys) > (void *) out + bytes); if (sorting_entire_node) - bch2_time_stats_update(&c->times[BCH_TIME_btree_node_sort], + time_stats_update(&c->times[BCH_TIME_btree_node_sort], start_time); /* Make sure we preserve bset journal_seq: */ @@ -401,7 +401,7 @@ void bch2_btree_sort_into(struct bch_fs *c, &dst->format, true); - bch2_time_stats_update(&c->times[BCH_TIME_btree_node_sort], + time_stats_update(&c->times[BCH_TIME_btree_node_sort], start_time); set_btree_bset_end(dst, dst->set); @@ -1296,7 +1296,7 @@ int bch2_btree_node_read_done(struct bch_fs *c, struct bch_dev *ca, out: mempool_free(iter, &c->fill_iter); printbuf_exit(&buf); - bch2_time_stats_update(&c->times[BCH_TIME_btree_node_read_done], start_time); + time_stats_update(&c->times[BCH_TIME_btree_node_read_done], start_time); return retry_read; fsck_err: if (ret == -BCH_ERR_btree_node_read_err_want_retry || @@ -1371,7 +1371,7 @@ start: } } - bch2_time_stats_update(&c->times[BCH_TIME_btree_node_read], + time_stats_update(&c->times[BCH_TIME_btree_node_read], rb->start_time); bio_put(&rb->bio); diff --git a/fs/bcachefs/btree_iter.c b/fs/bcachefs/btree_iter.c index 5988219c6908..6ad3aa12cbc4 100644 --- a/fs/bcachefs/btree_iter.c +++ b/fs/bcachefs/btree_iter.c @@ -3253,7 +3253,7 @@ u32 bch2_trans_begin(struct btree_trans *trans) if (!IS_ENABLED(CONFIG_BCACHEFS_NO_LATENCY_ACCT) && time_after64(now, trans->last_begin_time + 10)) - __bch2_time_stats_update(&btree_trans_stats(trans)->duration, + __time_stats_update(&btree_trans_stats(trans)->duration, trans->last_begin_time, now); if (!trans->restarted && @@ -3595,7 +3595,7 @@ void bch2_fs_btree_iter_exit(struct bch_fs *c) s < c->btree_transaction_stats + ARRAY_SIZE(c->btree_transaction_stats); s++) { kfree(s->max_paths_text); - bch2_time_stats_exit(&s->lock_hold_times); + time_stats_exit(&s->lock_hold_times); } if (c->btree_trans_barrier_initialized) { @@ -3613,8 +3613,8 @@ void bch2_fs_btree_iter_init_early(struct bch_fs *c) for (s = c->btree_transaction_stats; s < c->btree_transaction_stats + ARRAY_SIZE(c->btree_transaction_stats); s++) { - bch2_time_stats_init(&s->duration); - bch2_time_stats_init(&s->lock_hold_times); + time_stats_init(&s->duration); + time_stats_init(&s->lock_hold_times); mutex_init(&s->lock); } diff --git a/fs/bcachefs/btree_locking.h b/fs/bcachefs/btree_locking.h index b54ef48eb8cc..d7cd88fe5098 100644 --- a/fs/bcachefs/btree_locking.h +++ b/fs/bcachefs/btree_locking.h @@ -110,7 +110,7 @@ static void btree_trans_lock_hold_time_update(struct btree_trans *trans, struct btree_path *path, unsigned level) { #ifdef CONFIG_BCACHEFS_LOCK_TIME_STATS - __bch2_time_stats_update(&btree_trans_stats(trans)->lock_hold_times, + __time_stats_update(&btree_trans_stats(trans)->lock_hold_times, path->l[level].lock_taken_time, local_clock()); #endif diff --git a/fs/bcachefs/btree_update_interior.c b/fs/bcachefs/btree_update_interior.c index ab111fec1701..e1ef3e4b3436 100644 --- a/fs/bcachefs/btree_update_interior.c +++ b/fs/bcachefs/btree_update_interior.c @@ -559,7 +559,7 @@ static void bch2_btree_update_free(struct btree_update *as, struct btree_trans * bch2_disk_reservation_put(c, &as->disk_res); bch2_btree_reserve_put(as, trans); - bch2_time_stats_update(&c->times[BCH_TIME_btree_interior_update_total], + time_stats_update(&c->times[BCH_TIME_btree_interior_update_total], as->start_time); mutex_lock(&c->btree_interior_update_lock); @@ -1111,7 +1111,7 @@ static void bch2_btree_update_done(struct btree_update *as, struct btree_trans * continue_at(&as->cl, btree_update_set_nodes_written, as->c->btree_interior_update_worker); - bch2_time_stats_update(&c->times[BCH_TIME_btree_interior_update_foreground], + time_stats_update(&c->times[BCH_TIME_btree_interior_update_foreground], start_time); } @@ -1738,7 +1738,7 @@ out: bch2_trans_verify_locks(trans); - bch2_time_stats_update(&c->times[n2 + time_stats_update(&c->times[n2 ? BCH_TIME_btree_node_split : BCH_TIME_btree_node_compact], start_time); @@ -2106,7 +2106,7 @@ int __bch2_foreground_maybe_merge(struct btree_trans *trans, bch2_btree_update_done(as, trans); - bch2_time_stats_update(&c->times[BCH_TIME_btree_node_merge], start_time); + time_stats_update(&c->times[BCH_TIME_btree_node_merge], start_time); out: err: if (new_path) diff --git a/fs/bcachefs/io_read.c b/fs/bcachefs/io_read.c index 821ff222b361..7f656b1af503 100644 --- a/fs/bcachefs/io_read.c +++ b/fs/bcachefs/io_read.c @@ -166,7 +166,7 @@ static void promote_done(struct bch_write_op *wop) struct promote_op *op = container_of(wop, struct promote_op, write.op); struct bch_fs *c = op->write.rbio.c; - bch2_time_stats_update(&c->times[BCH_TIME_data_promote], op->start_time); + time_stats_update(&c->times[BCH_TIME_data_promote], op->start_time); promote_free(&op->write.rbio); } @@ -403,7 +403,7 @@ static inline struct bch_read_bio *bch2_rbio_free(struct bch_read_bio *rbio) static void bch2_rbio_done(struct bch_read_bio *rbio) { if (rbio->start_time) - bch2_time_stats_update(&rbio->c->times[BCH_TIME_data_read], + time_stats_update(&rbio->c->times[BCH_TIME_data_read], rbio->start_time); bio_endio(&rbio->bio); } diff --git a/fs/bcachefs/io_write.c b/fs/bcachefs/io_write.c index 0177198e90eb..88b7ed4a5bea 100644 --- a/fs/bcachefs/io_write.c +++ b/fs/bcachefs/io_write.c @@ -87,7 +87,7 @@ void bch2_latency_acct(struct bch_dev *ca, u64 submit_time, int rw) bch2_congested_acct(ca, io_latency, now, rw); - __bch2_time_stats_update(&ca->io_latency[rw].stats, submit_time, now); + __time_stats_update(&ca->io_latency[rw].stats, submit_time, now); } #endif @@ -480,7 +480,7 @@ static void bch2_write_done(struct closure *cl) EBUG_ON(op->open_buckets.nr); - bch2_time_stats_update(&c->times[BCH_TIME_data_write], op->start_time); + time_stats_update(&c->times[BCH_TIME_data_write], op->start_time); bch2_disk_reservation_put(c, &op->res); if (!(op->flags & BCH_WRITE_move)) diff --git a/fs/bcachefs/journal.c b/fs/bcachefs/journal.c index 20b748f61b21..252348e72194 100644 --- a/fs/bcachefs/journal.c +++ b/fs/bcachefs/journal.c @@ -824,7 +824,7 @@ int bch2_journal_flush_seq(struct journal *j, u64 seq, unsigned task_state) task_state); if (!ret) - bch2_time_stats_update(j->flush_seq_time, start_time); + time_stats_update(j->flush_seq_time, start_time); return ret ?: ret2 < 0 ? ret2 : 0; } diff --git a/fs/bcachefs/journal_io.c b/fs/bcachefs/journal_io.c index f2ff28e6697c..28f69f870574 100644 --- a/fs/bcachefs/journal_io.c +++ b/fs/bcachefs/journal_io.c @@ -1614,9 +1614,9 @@ static CLOSURE_CALLBACK(journal_write_done) u64 seq = le64_to_cpu(w->data->seq); int err = 0; - bch2_time_stats_update(!JSET_NO_FLUSH(w->data) - ? j->flush_write_time - : j->noflush_write_time, j->write_start_time); + time_stats_update(!JSET_NO_FLUSH(w->data) + ? j->flush_write_time + : j->noflush_write_time, j->write_start_time); if (!w->devs_written.nr) { bch_err(c, "unable to write journal to sufficient devices"); diff --git a/fs/bcachefs/journal_types.h b/fs/bcachefs/journal_types.h index 6a098c7e3f2e..c956a2deb3f9 100644 --- a/fs/bcachefs/journal_types.h +++ b/fs/bcachefs/journal_types.h @@ -299,9 +299,9 @@ struct journal { u64 nr_noflush_writes; u64 entry_bytes_written; - struct bch2_time_stats *flush_write_time; - struct bch2_time_stats *noflush_write_time; - struct bch2_time_stats *flush_seq_time; + struct time_stats *flush_write_time; + struct time_stats *noflush_write_time; + struct time_stats *flush_seq_time; #ifdef CONFIG_DEBUG_LOCK_ALLOC struct lockdep_map res_map; diff --git a/fs/bcachefs/nocow_locking.c b/fs/bcachefs/nocow_locking.c index 3c21981a4a1c..181efa4a83fa 100644 --- a/fs/bcachefs/nocow_locking.c +++ b/fs/bcachefs/nocow_locking.c @@ -85,7 +85,7 @@ void __bch2_bucket_nocow_lock(struct bucket_nocow_lock_table *t, u64 start_time = local_clock(); __closure_wait_event(&l->wait, __bch2_bucket_nocow_trylock(l, dev_bucket, flags)); - bch2_time_stats_update(&c->times[BCH_TIME_nocow_lock_contended], start_time); + time_stats_update(&c->times[BCH_TIME_nocow_lock_contended], start_time); } } diff --git a/fs/bcachefs/super.c b/fs/bcachefs/super.c index 6d97d412fed9..2a07c6ebbd8f 100644 --- a/fs/bcachefs/super.c +++ b/fs/bcachefs/super.c @@ -546,7 +546,7 @@ int bch2_fs_read_write_early(struct bch_fs *c) static void __bch2_fs_free(struct bch_fs *c) { for (unsigned i = 0; i < BCH_TIME_STAT_NR; i++) - bch2_time_stats_exit(&c->times[i]); + time_stats_exit(&c->times[i]); bch2_find_btree_nodes_exit(&c->found_btree_nodes); bch2_free_pending_node_rewrites(c); @@ -783,7 +783,7 @@ static struct bch_fs *bch2_fs_alloc(struct bch_sb *sb, struct bch_opts opts) sema_init(&c->online_fsck_mutex, 1); for (i = 0; i < BCH_TIME_STAT_NR; i++) - bch2_time_stats_init(&c->times[i]); + time_stats_init(&c->times[i]); bch2_fs_copygc_init(c); bch2_fs_btree_key_cache_init_early(&c->btree_key_cache); @@ -1206,8 +1206,8 @@ static void bch2_dev_free(struct bch_dev *ca) bch2_dev_buckets_free(ca); kfree(ca->sb_read_scratch); - bch2_time_stats_quantiles_exit(&ca->io_latency[WRITE]); - bch2_time_stats_quantiles_exit(&ca->io_latency[READ]); + time_stats_quantiles_exit(&ca->io_latency[WRITE]); + time_stats_quantiles_exit(&ca->io_latency[READ]); percpu_ref_exit(&ca->io_ref); #ifndef CONFIG_BCACHEFS_DEBUG @@ -1317,8 +1317,8 @@ static struct bch_dev *__bch2_dev_alloc(struct bch_fs *c, INIT_WORK(&ca->io_error_work, bch2_io_error_work); - bch2_time_stats_quantiles_init(&ca->io_latency[READ]); - bch2_time_stats_quantiles_init(&ca->io_latency[WRITE]); + time_stats_quantiles_init(&ca->io_latency[READ]); + time_stats_quantiles_init(&ca->io_latency[WRITE]); ca->mi = bch2_mi_to_cpu(member); diff --git a/fs/bcachefs/sysfs.c b/fs/bcachefs/sysfs.c index b3f2c651c1f8..bbde8f61d7ae 100644 --- a/fs/bcachefs/sysfs.c +++ b/fs/bcachefs/sysfs.c @@ -708,7 +708,7 @@ STORE(bch2_fs_time_stats) #define x(name) \ if (attr == &sysfs_time_stat_##name) \ - bch2_time_stats_reset(&c->times[BCH_TIME_##name]); + time_stats_reset(&c->times[BCH_TIME_##name]); BCH_TIME_STATS() #undef x return size; diff --git a/fs/bcachefs/util.c b/fs/bcachefs/util.c index ef678d935afb..4ba2208ca0f4 100644 --- a/fs/bcachefs/util.c +++ b/fs/bcachefs/util.c @@ -358,14 +358,14 @@ void bch2_prt_datetime(struct printbuf *out, time64_t sec) void bch2_pr_time_units(struct printbuf *out, u64 ns) { - const struct time_unit *u = bch2_pick_time_units(ns); + const struct time_unit *u = pick_time_units(ns); prt_printf(out, "%llu %s", div64_u64(ns, u->nsecs), u->name); } static void bch2_pr_time_units_aligned(struct printbuf *out, u64 ns) { - const struct time_unit *u = bch2_pick_time_units(ns); + const struct time_unit *u = pick_time_units(ns); prt_printf(out, "%llu \r%s", div64_u64(ns, u->nsecs), u->name); } @@ -379,7 +379,7 @@ static inline void pr_name_and_units(struct printbuf *out, const char *name, u64 #define TABSTOP_SIZE 12 -void bch2_time_stats_to_text(struct printbuf *out, struct bch2_time_stats *stats) +void bch2_time_stats_to_text(struct printbuf *out, struct time_stats *stats) { struct quantiles *quantiles = time_stats_to_quantiles(stats); s64 f_mean = 0, d_mean = 0; @@ -390,7 +390,7 @@ void bch2_time_stats_to_text(struct printbuf *out, struct bch2_time_stats *stats spin_lock_irq(&stats->lock); for_each_possible_cpu(cpu) - __bch2_time_stats_clear_buffer(stats, per_cpu_ptr(stats->buffer, cpu)); + __time_stats_clear_buffer(stats, per_cpu_ptr(stats->buffer, cpu)); spin_unlock_irq(&stats->lock); } @@ -469,7 +469,7 @@ void bch2_time_stats_to_text(struct printbuf *out, struct bch2_time_stats *stats if (quantiles) { int i = eytzinger0_first(NR_QUANTILES); const struct time_unit *u = - bch2_pick_time_units(quantiles->entries[i].m); + pick_time_units(quantiles->entries[i].m); u64 last_q = 0; prt_printf(out, "quantiles (%s):\t", u->name); diff --git a/fs/bcachefs/util.h b/fs/bcachefs/util.h index 9be49422d6ea..2d1759fcb210 100644 --- a/fs/bcachefs/util.h +++ b/fs/bcachefs/util.h @@ -16,12 +16,12 @@ #include <linux/preempt.h> #include <linux/ratelimit.h> #include <linux/slab.h> +#include <linux/time_stats.h> #include <linux/vmalloc.h> #include <linux/workqueue.h> #include <linux/mean_and_variance.h> #include "darray.h" -#include "time_stats.h" struct closure; @@ -226,7 +226,7 @@ static inline void prt_bdevname(struct printbuf *out, struct block_device *bdev) #endif } -void bch2_time_stats_to_text(struct printbuf *, struct bch2_time_stats *); +void bch2_time_stats_to_text(struct printbuf *, struct time_stats *); #define ewma_add(ewma, val, weight) \ ({ \ diff --git a/fs/bcachefs/time_stats.h b/include/linux/time_stats.h index 57438f6d8013..047c4d99fb56 100644 --- a/fs/bcachefs/time_stats.h +++ b/include/linux/time_stats.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * bch2_time_stats - collect statistics on events that have a duration, with nicely + * time_stats - collect statistics on events that have a duration, with nicely * formatted textual output on demand * * - percpu buffering of event collection: cheap enough to shotgun @@ -21,8 +21,8 @@ * * Particularly useful for tracking down latency issues. */ -#ifndef _BCACHEFS_TIME_STATS_H -#define _BCACHEFS_TIME_STATS_H +#ifndef _LINUX_TIME_STATS_H +#define _LINUX_TIME_STATS_H #include <linux/mean_and_variance.h> #include <linux/sched/clock.h> @@ -37,12 +37,12 @@ struct time_unit { /* * given a nanosecond value, pick the preferred time units for printing: */ -const struct time_unit *bch2_pick_time_units(u64 ns); +const struct time_unit *pick_time_units(u64 ns); /* * quantiles - do not use: * - * Only enabled if bch2_time_stats->quantiles_enabled has been manually set - don't + * Only enabled if time_stats->quantiles_enabled has been manually set - don't * use in new code. */ @@ -66,7 +66,7 @@ struct time_stat_buffer { } entries[31]; }; -struct bch2_time_stats { +struct time_stats { spinlock_t lock; bool have_quantiles; struct time_stat_buffer __percpu *buffer; @@ -90,48 +90,48 @@ struct bch2_time_stats { struct mean_and_variance_weighted freq_stats_weighted; }; -struct bch2_time_stats_quantiles { - struct bch2_time_stats stats; +struct time_stats_quantiles { + struct time_stats stats; struct quantiles quantiles; }; -static inline struct quantiles *time_stats_to_quantiles(struct bch2_time_stats *stats) +static inline struct quantiles *time_stats_to_quantiles(struct time_stats *stats) { return stats->have_quantiles - ? &container_of(stats, struct bch2_time_stats_quantiles, stats)->quantiles + ? &container_of(stats, struct time_stats_quantiles, stats)->quantiles : NULL; } -void __bch2_time_stats_clear_buffer(struct bch2_time_stats *, struct time_stat_buffer *); -void __bch2_time_stats_update(struct bch2_time_stats *stats, u64, u64); +void __time_stats_clear_buffer(struct time_stats *, struct time_stat_buffer *); +void __time_stats_update(struct time_stats *stats, u64, u64); /** * time_stats_update - collect a new event being tracked * - * @stats - bch2_time_stats to update + * @stats - time_stats to update * @start - start time of event, recorded with local_clock() * * The end duration of the event will be the current time */ -static inline void bch2_time_stats_update(struct bch2_time_stats *stats, u64 start) +static inline void time_stats_update(struct time_stats *stats, u64 start) { - __bch2_time_stats_update(stats, start, local_clock()); + __time_stats_update(stats, start, local_clock()); } /** * track_event_change - track state change events * - * @stats - bch2_time_stats to update + * @stats - time_stats to update * @v - new state, true or false * * Use this when tracking time stats for state changes, i.e. resource X becoming * blocked/unblocked. */ -static inline bool track_event_change(struct bch2_time_stats *stats, bool v) +static inline bool track_event_change(struct time_stats *stats, bool v) { if (v != !!stats->last_event_start) { if (!v) { - bch2_time_stats_update(stats, stats->last_event_start); + time_stats_update(stats, stats->last_event_start); stats->last_event_start = 0; } else { stats->last_event_start = local_clock() ?: 1; @@ -142,25 +142,25 @@ static inline bool track_event_change(struct bch2_time_stats *stats, bool v) return false; } -void bch2_time_stats_reset(struct bch2_time_stats *); +void time_stats_reset(struct time_stats *); #define TIME_STATS_PRINT_NO_ZEROES (1U << 0) /* print nothing if zero count */ struct seq_buf; -void bch2_time_stats_to_seq_buf(struct seq_buf *, struct bch2_time_stats *, - const char *epoch_name, unsigned int flags); +void time_stats_to_seq_buf(struct seq_buf *, struct time_stats *, + const char *epoch_name, unsigned int flags); -void bch2_time_stats_exit(struct bch2_time_stats *); -void bch2_time_stats_init(struct bch2_time_stats *); +void time_stats_exit(struct time_stats *); +void time_stats_init(struct time_stats *); -static inline void bch2_time_stats_quantiles_exit(struct bch2_time_stats_quantiles *statq) +static inline void time_stats_quantiles_exit(struct time_stats_quantiles *statq) { - bch2_time_stats_exit(&statq->stats); + time_stats_exit(&statq->stats); } -static inline void bch2_time_stats_quantiles_init(struct bch2_time_stats_quantiles *statq) +static inline void time_stats_quantiles_init(struct time_stats_quantiles *statq) { - bch2_time_stats_init(&statq->stats); + time_stats_init(&statq->stats); statq->stats.have_quantiles = true; memset(&statq->quantiles, 0, sizeof(statq->quantiles)); } -#endif /* _BCACHEFS_TIME_STATS_H */ +#endif /* _LINUX_TIME_STATS_H */ diff --git a/lib/Kconfig b/lib/Kconfig index dccb61b7d698..4cfcd56110c0 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -758,3 +758,7 @@ config UNION_FIND config MIN_HEAP bool + +config TIME_STATS + tristate + select MEAN_AND_VARIANCE diff --git a/lib/Makefile b/lib/Makefile index d5cfc7afbbb8..9027645c88f6 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -354,6 +354,8 @@ obj-$(CONFIG_SBITMAP) += sbitmap.o obj-$(CONFIG_PARMAN) += parman.o +obj-$(CONFIG_TIME_STATS) += time_stats.o + obj-y += group_cpus.o # GCC library routines diff --git a/fs/bcachefs/time_stats.c b/lib/time_stats.c index 0004980af226..8235a3797538 100644 --- a/fs/bcachefs/time_stats.c +++ b/lib/time_stats.c @@ -6,10 +6,9 @@ #include <linux/percpu.h> #include <linux/preempt.h> #include <linux/time.h> +#include <linux/time_stats.h> #include <linux/spinlock.h> -#include "time_stats.h" - static const struct time_unit time_units[] = { { "ns", 1 }, { "us", NSEC_PER_USEC }, @@ -23,7 +22,7 @@ static const struct time_unit time_units[] = { { "eon", U64_MAX }, }; -const struct time_unit *bch2_pick_time_units(u64 ns) +const struct time_unit *pick_time_units(u64 ns) { const struct time_unit *u; @@ -35,6 +34,7 @@ const struct time_unit *bch2_pick_time_units(u64 ns) return u; } +EXPORT_SYMBOL_GPL(pick_time_units); static void quantiles_update(struct quantiles *q, u64 v) { @@ -66,7 +66,7 @@ static void quantiles_update(struct quantiles *q, u64 v) } } -static inline void time_stats_update_one(struct bch2_time_stats *stats, +static inline void time_stats_update_one(struct time_stats *stats, u64 start, u64 end) { u64 duration, freq; @@ -99,8 +99,8 @@ static inline void time_stats_update_one(struct bch2_time_stats *stats, stats->last_event = end; } -void __bch2_time_stats_clear_buffer(struct bch2_time_stats *stats, - struct time_stat_buffer *b) +void __time_stats_clear_buffer(struct time_stats *stats, + struct time_stat_buffer *b) { for (struct time_stat_buffer_entry *i = b->entries; i < b->entries + ARRAY_SIZE(b->entries); @@ -108,18 +108,19 @@ void __bch2_time_stats_clear_buffer(struct bch2_time_stats *stats, time_stats_update_one(stats, i->start, i->end); b->nr = 0; } +EXPORT_SYMBOL_GPL(__time_stats_clear_buffer); -static noinline void time_stats_clear_buffer(struct bch2_time_stats *stats, +static noinline void time_stats_clear_buffer(struct time_stats *stats, struct time_stat_buffer *b) { unsigned long flags; spin_lock_irqsave(&stats->lock, flags); - __bch2_time_stats_clear_buffer(stats, b); + __time_stats_clear_buffer(stats, b); spin_unlock_irqrestore(&stats->lock, flags); } -void __bch2_time_stats_update(struct bch2_time_stats *stats, u64 start, u64 end) +void __time_stats_update(struct time_stats *stats, u64 start, u64 end) { unsigned long flags; @@ -150,11 +151,12 @@ void __bch2_time_stats_update(struct bch2_time_stats *stats, u64 start, u64 end) preempt_enable(); } } +EXPORT_SYMBOL_GPL(__time_stats_update); -void bch2_time_stats_reset(struct bch2_time_stats *stats) +void time_stats_reset(struct time_stats *stats) { spin_lock_irq(&stats->lock); - unsigned offset = offsetof(struct bch2_time_stats, min_duration); + unsigned offset = offsetof(struct time_stats, min_duration); memset((void *) stats + offset, 0, sizeof(*stats) - offset); if (stats->buffer) { @@ -169,17 +171,17 @@ void bch2_time_stats_reset(struct bch2_time_stats *stats) static void seq_buf_time_units_aligned(struct seq_buf *out, u64 ns) { - const struct time_unit *u = bch2_pick_time_units(ns); + const struct time_unit *u = pick_time_units(ns); seq_buf_printf(out, "%8llu %s", div64_u64(ns, u->nsecs), u->name); } -static inline u64 time_stats_lifetime(const struct bch2_time_stats *stats) +static inline u64 time_stats_lifetime(const struct time_stats *stats) { return local_clock() - stats->start_time; } -void bch2_time_stats_to_seq_buf(struct seq_buf *out, struct bch2_time_stats *stats, +void time_stats_to_seq_buf(struct seq_buf *out, struct time_stats *stats, const char *epoch_name, unsigned int flags) { struct quantiles *quantiles = time_stats_to_quantiles(stats); @@ -192,7 +194,7 @@ void bch2_time_stats_to_seq_buf(struct seq_buf *out, struct bch2_time_stats *sta spin_lock_irq(&stats->lock); for_each_possible_cpu(cpu) - __bch2_time_stats_clear_buffer(stats, per_cpu_ptr(stats->buffer, cpu)); + __time_stats_clear_buffer(stats, per_cpu_ptr(stats->buffer, cpu)); spin_unlock_irq(&stats->lock); } @@ -261,7 +263,7 @@ void bch2_time_stats_to_seq_buf(struct seq_buf *out, struct bch2_time_stats *sta if (quantiles) { int i = eytzinger0_first(NR_QUANTILES); const struct time_unit *u = - bch2_pick_time_units(quantiles->entries[i].m); + pick_time_units(quantiles->entries[i].m); u64 last_q = 0; seq_buf_printf(out, "quantiles (%s):\t", u->name); @@ -276,13 +278,15 @@ void bch2_time_stats_to_seq_buf(struct seq_buf *out, struct bch2_time_stats *sta } } } +EXPORT_SYMBOL_GPL(time_stats_to_seq_buf); -void bch2_time_stats_exit(struct bch2_time_stats *stats) +void time_stats_exit(struct time_stats *stats) { free_percpu(stats->buffer); } +EXPORT_SYMBOL_GPL(time_stats_exit); -void bch2_time_stats_init(struct bch2_time_stats *stats) +void time_stats_init(struct time_stats *stats) { memset(stats, 0, sizeof(*stats)); stats->min_duration = U64_MAX; @@ -290,3 +294,7 @@ void bch2_time_stats_init(struct bch2_time_stats *stats) stats->start_time = local_clock(); spin_lock_init(&stats->lock); } +EXPORT_SYMBOL_GPL(time_stats_init); + +MODULE_AUTHOR("Kent Overstreet"); +MODULE_LICENSE("GPL"); |