Skip to content

Commit e34293d

Browse files
lenticularis39rostedt
authored andcommitted
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 <[email protected]> Cc: Luis Goncalves <[email protected]> Cc: Gabriele Monaco <[email protected]> Cc: Clark Williams <[email protected]> Link: https://lore.kernel.org/[email protected] Signed-off-by: Tomas Glozar <[email protected]> Signed-off-by: Steven Rostedt (Google) <[email protected]>
1 parent 9dc3766 commit e34293d

File tree

6 files changed

+389
-1
lines changed

6 files changed

+389
-1
lines changed

tools/tracing/rtla/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ rtla-static
44
fixdep
55
feature
66
FEATURE-DUMP
7+
*.skel.h

tools/tracing/rtla/Makefile

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,17 @@ CFLAGS += $(INCLUDES) $(LIB_INCLUDES)
7373

7474
export CFLAGS OUTPUT srctree
7575

76+
ifeq ($(BUILD_BPF_SKEL),1)
77+
src/timerlat.bpf.o: src/timerlat.bpf.c
78+
$(QUIET_CLANG)$(CLANG) -g -O2 -target bpf -c $(filter %.c,$^) -o $@
79+
80+
src/timerlat.skel.h: src/timerlat.bpf.o
81+
$(QUIET_GENSKEL)$(BPFTOOL) gen skeleton $< > $@
82+
else
83+
src/timerlat.skel.h:
84+
$(Q)echo '/* BPF skeleton is disabled */' > src/timerlat.skel.h
85+
endif
86+
7687
$(RTLA): $(RTLA_IN)
7788
$(QUIET_LINK)$(CC) $(LDFLAGS) -o $(RTLA) $(RTLA_IN) $(EXTLIBS)
7889

@@ -83,14 +94,15 @@ static: $(RTLA_IN)
8394
rtla.%: fixdep FORCE
8495
make -f $(srctree)/tools/build/Makefile.build dir=. $@
8596

86-
$(RTLA_IN): fixdep FORCE
97+
$(RTLA_IN): fixdep FORCE src/timerlat.skel.h
8798
make $(build)=rtla
8899

89100
clean: doc_clean fixdep-clean
90101
$(call QUIET_CLEAN, rtla)
91102
$(Q)find . -name '*.o' -delete -o -name '\.*.cmd' -delete -o -name '\.*.d' -delete
92103
$(Q)rm -f rtla rtla-static fixdep FEATURE-DUMP rtla-*
93104
$(Q)rm -rf feature
105+
$(Q)rm -f src/timerlat.bpf.o src/timerlat.skel.h
94106
check: $(RTLA)
95107
RTLA=$(RTLA) prove -o -f tests/
96108
.PHONY: FORCE clean check

tools/tracing/rtla/src/Build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@ rtla-y += timerlat_top.o
88
rtla-y += timerlat_hist.o
99
rtla-y += timerlat_u.o
1010
rtla-y += timerlat_aa.o
11+
rtla-y += timerlat_bpf.o
1112
rtla-y += rtla.o

tools/tracing/rtla/src/timerlat.bpf.c

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
#include <linux/bpf.h>
3+
#include <bpf/bpf_tracing.h>
4+
#include <stdbool.h>
5+
#include "timerlat_bpf.h"
6+
7+
#define nosubprog __always_inline
8+
#define MAX_ENTRIES_DEFAULT 4096
9+
10+
char LICENSE[] SEC("license") = "GPL";
11+
12+
struct trace_event_raw_timerlat_sample {
13+
unsigned long long timer_latency;
14+
int context;
15+
} __attribute__((preserve_access_index));
16+
17+
struct {
18+
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
19+
__uint(max_entries, MAX_ENTRIES_DEFAULT);
20+
__type(key, unsigned int);
21+
__type(value, unsigned long long);
22+
} hist_irq SEC(".maps"), hist_thread SEC(".maps"), hist_user SEC(".maps");
23+
24+
struct {
25+
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
26+
__uint(max_entries, SUMMARY_FIELD_N);
27+
__type(key, unsigned int);
28+
__type(value, unsigned long long);
29+
} summary_irq SEC(".maps"), summary_thread SEC(".maps"), summary_user SEC(".maps");
30+
31+
struct {
32+
__uint(type, BPF_MAP_TYPE_RINGBUF);
33+
__uint(max_entries, 1);
34+
} signal_stop_tracing SEC(".maps");
35+
36+
/* Params to be set by rtla */
37+
const volatile int bucket_size = 1;
38+
const volatile int output_divisor = 1000;
39+
const volatile int entries = 256;
40+
const volatile int irq_threshold;
41+
const volatile int thread_threshold;
42+
const volatile bool aa_only;
43+
44+
int stop_tracing;
45+
46+
nosubprog unsigned long long map_get(void *map,
47+
unsigned int key)
48+
{
49+
unsigned long long *value_ptr;
50+
51+
value_ptr = bpf_map_lookup_elem(map, &key);
52+
53+
return !value_ptr ? 0 : *value_ptr;
54+
}
55+
56+
nosubprog void map_set(void *map,
57+
unsigned int key,
58+
unsigned long long value)
59+
{
60+
bpf_map_update_elem(map, &key, &value, BPF_ANY);
61+
}
62+
63+
nosubprog void map_increment(void *map,
64+
unsigned int key)
65+
{
66+
map_set(map, key, map_get(map, key) + 1);
67+
}
68+
69+
nosubprog void update_main_hist(void *map,
70+
int bucket)
71+
{
72+
if (entries == 0)
73+
/* No histogram */
74+
return;
75+
76+
if (bucket >= entries)
77+
/* Overflow */
78+
return;
79+
80+
map_increment(map, bucket);
81+
}
82+
83+
nosubprog void update_summary(void *map,
84+
unsigned long long latency,
85+
int bucket)
86+
{
87+
if (aa_only)
88+
/* Auto-analysis only, nothing to be done here */
89+
return;
90+
91+
map_set(map, SUMMARY_CURRENT, latency);
92+
93+
if (bucket >= entries)
94+
/* Overflow */
95+
map_increment(map, SUMMARY_OVERFLOW);
96+
97+
if (latency > map_get(map, SUMMARY_MAX))
98+
map_set(map, SUMMARY_MAX, latency);
99+
100+
if (latency < map_get(map, SUMMARY_MIN) || map_get(map, SUMMARY_COUNT) == 0)
101+
map_set(map, SUMMARY_MIN, latency);
102+
103+
map_increment(map, SUMMARY_COUNT);
104+
map_set(map, SUMMARY_SUM, map_get(map, SUMMARY_SUM) + latency);
105+
}
106+
107+
nosubprog void set_stop_tracing(void)
108+
{
109+
int value = 0;
110+
111+
/* Suppress further sample processing */
112+
stop_tracing = 1;
113+
114+
/* Signal to userspace */
115+
bpf_ringbuf_output(&signal_stop_tracing, &value, sizeof(value), 0);
116+
}
117+
118+
SEC("tp/osnoise/timerlat_sample")
119+
int handle_timerlat_sample(struct trace_event_raw_timerlat_sample *tp_args)
120+
{
121+
unsigned long long latency, latency_us;
122+
int bucket;
123+
124+
if (stop_tracing)
125+
return 0;
126+
127+
latency = tp_args->timer_latency / output_divisor;
128+
latency_us = tp_args->timer_latency / 1000;
129+
bucket = latency / bucket_size;
130+
131+
if (tp_args->context == 0) {
132+
update_main_hist(&hist_irq, bucket);
133+
update_summary(&summary_irq, latency, bucket);
134+
135+
if (irq_threshold != 0 && latency_us >= irq_threshold)
136+
set_stop_tracing();
137+
} else if (tp_args->context == 1) {
138+
update_main_hist(&hist_thread, bucket);
139+
update_summary(&summary_thread, latency, bucket);
140+
141+
if (thread_threshold != 0 && latency_us >= thread_threshold)
142+
set_stop_tracing();
143+
} else {
144+
update_main_hist(&hist_user, bucket);
145+
update_summary(&summary_user, latency, bucket);
146+
}
147+
148+
return 0;
149+
}

tools/tracing/rtla/src/timerlat_bpf.c

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
#ifdef HAVE_BPF_SKEL
3+
#include "timerlat.h"
4+
#include "timerlat_bpf.h"
5+
#include "timerlat.skel.h"
6+
7+
static struct timerlat_bpf *bpf;
8+
9+
/*
10+
* timerlat_bpf_init - load and initialize BPF program to collect timerlat data
11+
*/
12+
int timerlat_bpf_init(struct timerlat_params *params)
13+
{
14+
int err;
15+
16+
debug_msg("Loading BPF program\n");
17+
18+
bpf = timerlat_bpf__open();
19+
if (!bpf)
20+
return 1;
21+
22+
/* Pass common options */
23+
bpf->rodata->output_divisor = params->output_divisor;
24+
bpf->rodata->entries = params->entries;
25+
bpf->rodata->irq_threshold = params->stop_us;
26+
bpf->rodata->thread_threshold = params->stop_total_us;
27+
bpf->rodata->aa_only = params->aa_only;
28+
29+
if (params->entries != 0) {
30+
/* Pass histogram options */
31+
bpf->rodata->bucket_size = params->bucket_size;
32+
33+
/* Set histogram array sizes */
34+
bpf_map__set_max_entries(bpf->maps.hist_irq, params->entries);
35+
bpf_map__set_max_entries(bpf->maps.hist_thread, params->entries);
36+
bpf_map__set_max_entries(bpf->maps.hist_user, params->entries);
37+
} else {
38+
/* No entries, disable histogram */
39+
bpf_map__set_autocreate(bpf->maps.hist_irq, false);
40+
bpf_map__set_autocreate(bpf->maps.hist_thread, false);
41+
bpf_map__set_autocreate(bpf->maps.hist_user, false);
42+
}
43+
44+
if (params->aa_only) {
45+
/* Auto-analysis only, disable summary */
46+
bpf_map__set_autocreate(bpf->maps.summary_irq, false);
47+
bpf_map__set_autocreate(bpf->maps.summary_thread, false);
48+
bpf_map__set_autocreate(bpf->maps.summary_user, false);
49+
}
50+
51+
/* Load and verify BPF program */
52+
err = timerlat_bpf__load(bpf);
53+
if (err) {
54+
timerlat_bpf__destroy(bpf);
55+
return err;
56+
}
57+
58+
return 0;
59+
}
60+
61+
/*
62+
* timerlat_bpf_attach - attach BPF program to collect timerlat data
63+
*/
64+
int timerlat_bpf_attach(void)
65+
{
66+
debug_msg("Attaching BPF program\n");
67+
68+
return timerlat_bpf__attach(bpf);
69+
}
70+
71+
/*
72+
* timerlat_bpf_detach - detach BPF program to collect timerlat data
73+
*/
74+
void timerlat_bpf_detach(void)
75+
{
76+
timerlat_bpf__detach(bpf);
77+
}
78+
79+
/*
80+
* timerlat_bpf_detach - destroy BPF program to collect timerlat data
81+
*/
82+
void timerlat_bpf_destroy(void)
83+
{
84+
timerlat_bpf__destroy(bpf);
85+
}
86+
87+
static int handle_rb_event(void *ctx, void *data, size_t data_sz)
88+
{
89+
return 0;
90+
}
91+
92+
/*
93+
* timerlat_bpf_wait - wait until tracing is stopped or signal
94+
*/
95+
int timerlat_bpf_wait(int timeout)
96+
{
97+
struct ring_buffer *rb;
98+
int retval;
99+
100+
rb = ring_buffer__new(bpf_map__fd(bpf->maps.signal_stop_tracing),
101+
handle_rb_event, NULL, NULL);
102+
retval = ring_buffer__poll(rb, timeout * 1000);
103+
ring_buffer__free(rb);
104+
105+
return retval;
106+
}
107+
108+
static int get_value(struct bpf_map *map_irq,
109+
struct bpf_map *map_thread,
110+
struct bpf_map *map_user,
111+
int key,
112+
long long *value_irq,
113+
long long *value_thread,
114+
long long *value_user,
115+
int cpus)
116+
{
117+
int err;
118+
119+
err = bpf_map__lookup_elem(map_irq, &key,
120+
sizeof(unsigned int), value_irq,
121+
sizeof(long long) * cpus, 0);
122+
if (err)
123+
return err;
124+
err = bpf_map__lookup_elem(map_thread, &key,
125+
sizeof(unsigned int), value_thread,
126+
sizeof(long long) * cpus, 0);
127+
if (err)
128+
return err;
129+
err = bpf_map__lookup_elem(map_user, &key,
130+
sizeof(unsigned int), value_user,
131+
sizeof(long long) * cpus, 0);
132+
if (err)
133+
return err;
134+
return 0;
135+
}
136+
137+
/*
138+
* timerlat_bpf_get_hist_value - get value from BPF hist map
139+
*/
140+
int timerlat_bpf_get_hist_value(int key,
141+
long long *value_irq,
142+
long long *value_thread,
143+
long long *value_user,
144+
int cpus)
145+
{
146+
return get_value(bpf->maps.hist_irq,
147+
bpf->maps.hist_thread,
148+
bpf->maps.hist_user,
149+
key, value_irq, value_thread, value_user, cpus);
150+
}
151+
152+
/*
153+
* timerlat_bpf_get_summary_value - get value from BPF summary map
154+
*/
155+
int timerlat_bpf_get_summary_value(enum summary_field key,
156+
long long *value_irq,
157+
long long *value_thread,
158+
long long *value_user,
159+
int cpus)
160+
{
161+
return get_value(bpf->maps.summary_irq,
162+
bpf->maps.summary_thread,
163+
bpf->maps.summary_user,
164+
key, value_irq, value_thread, value_user, cpus);
165+
}
166+
#endif /* HAVE_BPF_SKEL */

0 commit comments

Comments
 (0)