Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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
10char LICENSE[] SEC("license") = "GPL";
11
12struct trace_event_raw_timerlat_sample {
13 unsigned long long timer_latency;
14 int context;
15} __attribute__((preserve_access_index));
16
17struct {
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
24struct {
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
31struct {
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 */
37const volatile int bucket_size = 1;
38const volatile int output_divisor = 1000;
39const volatile int entries = 256;
40const volatile int irq_threshold;
41const volatile int thread_threshold;
42const volatile bool aa_only;
43
44int stop_tracing;
45
46nosubprog 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
56nosubprog 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
63nosubprog void map_increment(void *map,
64 unsigned int key)
65{
66 map_set(map, key, map_get(map, key) + 1);
67}
68
69nosubprog 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
83nosubprog 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
107nosubprog 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
118SEC("tp/osnoise/timerlat_sample")
119int 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}