diff options
| author | Tomas Glozar <tglozar@redhat.com> | 2025-02-18 15:58:55 +0100 |
|---|---|---|
| committer | Steven Rostedt (Google) <rostedt@goodmis.org> | 2025-03-04 12:35:18 -0500 |
| commit | e34293ddcebd6bd39ff04666f010fce8d1f5e15a (patch) | |
| tree | 4762608c0b46b1b6aa0879fefb360002aac09da5 /tools/tracing/rtla/src/timerlat.bpf.c | |
| parent | 9dc3766ed07c95c9a77fa98dcbc83dcb7f49df3d (diff) | |
rtla/timerlat: Add BPF skeleton to collect samples
Add BPF program that attaches to the osnoise:timerlat_sample tracepoint
and collects both the summary and the histogram (if requested) into BPF
maps (one map of each kind per context).
The program is designed to be used for both timerlat-top and
timerlat-hist. If using with timerlat-top, the "entries" parameter is
set to zero, which prevents the BPF program from recording histogram
entries. In that case, the maps for histograms do not have to be
created, as the BPF verifier will identify the code using them as
unreachable.
An IRQ or thread latency threshold might be supplied to stop recording
if hit, similar to the timerlat tracer threshold, which stops ftrace
tracing if hit. A BPF ringbuffer is used to signal threshold overflow to
userspace. In aa-only mode, this is the only function of the BPF
program.
Cc: John Kacur <jkacur@redhat.com>
Cc: Luis Goncalves <lgoncalv@redhat.com>
Cc: Gabriele Monaco <gmonaco@redhat.com>
Cc: Clark Williams <williams@redhat.com>
Link: https://lore.kernel.org/20250218145859.27762-5-tglozar@redhat.com
Signed-off-by: Tomas Glozar <tglozar@redhat.com>
Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
Diffstat (limited to 'tools/tracing/rtla/src/timerlat.bpf.c')
| -rw-r--r-- | tools/tracing/rtla/src/timerlat.bpf.c | 149 |
1 files changed, 149 insertions, 0 deletions
diff --git a/tools/tracing/rtla/src/timerlat.bpf.c b/tools/tracing/rtla/src/timerlat.bpf.c new file mode 100644 index 000000000000..96196d46e170 --- /dev/null +++ b/tools/tracing/rtla/src/timerlat.bpf.c @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <linux/bpf.h> +#include <bpf/bpf_tracing.h> +#include <stdbool.h> +#include "timerlat_bpf.h" + +#define nosubprog __always_inline +#define MAX_ENTRIES_DEFAULT 4096 + +char LICENSE[] SEC("license") = "GPL"; + +struct trace_event_raw_timerlat_sample { + unsigned long long timer_latency; + int context; +} __attribute__((preserve_access_index)); + +struct { + __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); + __uint(max_entries, MAX_ENTRIES_DEFAULT); + __type(key, unsigned int); + __type(value, unsigned long long); +} hist_irq SEC(".maps"), hist_thread SEC(".maps"), hist_user SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); + __uint(max_entries, SUMMARY_FIELD_N); + __type(key, unsigned int); + __type(value, unsigned long long); +} summary_irq SEC(".maps"), summary_thread SEC(".maps"), summary_user SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 1); +} signal_stop_tracing SEC(".maps"); + +/* Params to be set by rtla */ +const volatile int bucket_size = 1; +const volatile int output_divisor = 1000; +const volatile int entries = 256; +const volatile int irq_threshold; +const volatile int thread_threshold; +const volatile bool aa_only; + +int stop_tracing; + +nosubprog unsigned long long map_get(void *map, + unsigned int key) +{ + unsigned long long *value_ptr; + + value_ptr = bpf_map_lookup_elem(map, &key); + + return !value_ptr ? 0 : *value_ptr; +} + +nosubprog void map_set(void *map, + unsigned int key, + unsigned long long value) +{ + bpf_map_update_elem(map, &key, &value, BPF_ANY); +} + +nosubprog void map_increment(void *map, + unsigned int key) +{ + map_set(map, key, map_get(map, key) + 1); +} + +nosubprog void update_main_hist(void *map, + int bucket) +{ + if (entries == 0) + /* No histogram */ + return; + + if (bucket >= entries) + /* Overflow */ + return; + + map_increment(map, bucket); +} + +nosubprog void update_summary(void *map, + unsigned long long latency, + int bucket) +{ + if (aa_only) + /* Auto-analysis only, nothing to be done here */ + return; + + map_set(map, SUMMARY_CURRENT, latency); + + if (bucket >= entries) + /* Overflow */ + map_increment(map, SUMMARY_OVERFLOW); + + if (latency > map_get(map, SUMMARY_MAX)) + map_set(map, SUMMARY_MAX, latency); + + if (latency < map_get(map, SUMMARY_MIN) || map_get(map, SUMMARY_COUNT) == 0) + map_set(map, SUMMARY_MIN, latency); + + map_increment(map, SUMMARY_COUNT); + map_set(map, SUMMARY_SUM, map_get(map, SUMMARY_SUM) + latency); +} + +nosubprog void set_stop_tracing(void) +{ + int value = 0; + + /* Suppress further sample processing */ + stop_tracing = 1; + + /* Signal to userspace */ + bpf_ringbuf_output(&signal_stop_tracing, &value, sizeof(value), 0); +} + +SEC("tp/osnoise/timerlat_sample") +int handle_timerlat_sample(struct trace_event_raw_timerlat_sample *tp_args) +{ + unsigned long long latency, latency_us; + int bucket; + + if (stop_tracing) + return 0; + + latency = tp_args->timer_latency / output_divisor; + latency_us = tp_args->timer_latency / 1000; + bucket = latency / bucket_size; + + if (tp_args->context == 0) { + update_main_hist(&hist_irq, bucket); + update_summary(&summary_irq, latency, bucket); + + if (irq_threshold != 0 && latency_us >= irq_threshold) + set_stop_tracing(); + } else if (tp_args->context == 1) { + update_main_hist(&hist_thread, bucket); + update_summary(&summary_thread, latency, bucket); + + if (thread_threshold != 0 && latency_us >= thread_threshold) + set_stop_tracing(); + } else { + update_main_hist(&hist_user, bucket); + update_summary(&summary_user, latency, bucket); + } + + return 0; +} |
