summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKent Overstreet <kent.overstreet@linux.dev>2024-01-26 11:58:44 -0500
committerKent Overstreet <kent.overstreet@linux.dev>2025-02-07 18:16:25 -0500
commitf2a1b863f8cc89934c117468afd60bcfd5ffea5f (patch)
treef0a7ed87120b85f10f443286ceb27ce3b4af7b51
parenta8bbc86b836e80e9646bfa7ad16376fb3dd637e5 (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--MAINTAINERS7
-rw-r--r--fs/bcachefs/Kconfig1
-rw-r--r--fs/bcachefs/Makefile1
-rw-r--r--fs/bcachefs/bcachefs.h10
-rw-r--r--fs/bcachefs/btree_cache.c2
-rw-r--r--fs/bcachefs/btree_gc.c2
-rw-r--r--fs/bcachefs/btree_io.c8
-rw-r--r--fs/bcachefs/btree_iter.c8
-rw-r--r--fs/bcachefs/btree_locking.h2
-rw-r--r--fs/bcachefs/btree_update_interior.c8
-rw-r--r--fs/bcachefs/io_read.c4
-rw-r--r--fs/bcachefs/io_write.c4
-rw-r--r--fs/bcachefs/journal.c2
-rw-r--r--fs/bcachefs/journal_io.c6
-rw-r--r--fs/bcachefs/journal_types.h6
-rw-r--r--fs/bcachefs/nocow_locking.c2
-rw-r--r--fs/bcachefs/super.c12
-rw-r--r--fs/bcachefs/sysfs.c2
-rw-r--r--fs/bcachefs/util.c10
-rw-r--r--fs/bcachefs/util.h4
-rw-r--r--include/linux/time_stats.h (renamed from fs/bcachefs/time_stats.h)56
-rw-r--r--lib/Kconfig4
-rw-r--r--lib/Makefile2
-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");