Merge tag 'dlm-6.9' of git://git.kernel.org/pub/scm/linux/kernel/git/teigland/linux-dlm
[linux-2.6-block.git] / tools / perf / util / bpf_kwork_top.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * bpf_kwork_top.c
4  *
5  * Copyright (c) 2022  Huawei Inc,  Yang Jihong <yangjihong1@huawei.com>
6  */
7
8 #include <time.h>
9 #include <fcntl.h>
10 #include <signal.h>
11 #include <stdio.h>
12 #include <unistd.h>
13
14 #include <linux/time64.h>
15
16 #include "util/debug.h"
17 #include "util/evsel.h"
18 #include "util/kwork.h"
19
20 #include <bpf/bpf.h>
21 #include <perf/cpumap.h>
22
23 #include "util/bpf_skel/kwork_top.skel.h"
24
25 /*
26  * This should be in sync with "util/kwork_top.bpf.c"
27  */
28 #define MAX_COMMAND_LEN 16
29
30 struct time_data {
31         __u64 timestamp;
32 };
33
34 struct work_data {
35         __u64 runtime;
36 };
37
38 struct task_data {
39         __u32 tgid;
40         __u32 is_kthread;
41         char comm[MAX_COMMAND_LEN];
42 };
43
44 struct work_key {
45         __u32 type;
46         __u32 pid;
47         __u64 task_p;
48 };
49
50 struct task_key {
51         __u32 pid;
52         __u32 cpu;
53 };
54
55 struct kwork_class_bpf {
56         struct kwork_class *class;
57         void (*load_prepare)(void);
58 };
59
60 static struct kwork_top_bpf *skel;
61
62 void perf_kwork__top_start(void)
63 {
64         struct timespec ts;
65
66         clock_gettime(CLOCK_MONOTONIC, &ts);
67         skel->bss->from_timestamp = (u64)ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec;
68         skel->bss->enabled = 1;
69         pr_debug("perf kwork top start at: %lld\n", skel->bss->from_timestamp);
70 }
71
72 void perf_kwork__top_finish(void)
73 {
74         struct timespec ts;
75
76         skel->bss->enabled = 0;
77         clock_gettime(CLOCK_MONOTONIC, &ts);
78         skel->bss->to_timestamp = (u64)ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec;
79         pr_debug("perf kwork top finish at: %lld\n", skel->bss->to_timestamp);
80 }
81
82 static void irq_load_prepare(void)
83 {
84         bpf_program__set_autoload(skel->progs.on_irq_handler_entry, true);
85         bpf_program__set_autoload(skel->progs.on_irq_handler_exit, true);
86 }
87
88 static struct kwork_class_bpf kwork_irq_bpf = {
89         .load_prepare = irq_load_prepare,
90 };
91
92 static void softirq_load_prepare(void)
93 {
94         bpf_program__set_autoload(skel->progs.on_softirq_entry, true);
95         bpf_program__set_autoload(skel->progs.on_softirq_exit, true);
96 }
97
98 static struct kwork_class_bpf kwork_softirq_bpf = {
99         .load_prepare = softirq_load_prepare,
100 };
101
102 static void sched_load_prepare(void)
103 {
104         bpf_program__set_autoload(skel->progs.on_switch, true);
105 }
106
107 static struct kwork_class_bpf kwork_sched_bpf = {
108         .load_prepare = sched_load_prepare,
109 };
110
111 static struct kwork_class_bpf *
112 kwork_class_bpf_supported_list[KWORK_CLASS_MAX] = {
113         [KWORK_CLASS_IRQ]       = &kwork_irq_bpf,
114         [KWORK_CLASS_SOFTIRQ]   = &kwork_softirq_bpf,
115         [KWORK_CLASS_SCHED]     = &kwork_sched_bpf,
116 };
117
118 static bool valid_kwork_class_type(enum kwork_class_type type)
119 {
120         return type >= 0 && type < KWORK_CLASS_MAX;
121 }
122
123 static int setup_filters(struct perf_kwork *kwork)
124 {
125         u8 val = 1;
126         int i, nr_cpus, fd;
127         struct perf_cpu_map *map;
128
129         if (kwork->cpu_list) {
130                 fd = bpf_map__fd(skel->maps.kwork_top_cpu_filter);
131                 if (fd < 0) {
132                         pr_debug("Invalid cpu filter fd\n");
133                         return -1;
134                 }
135
136                 map = perf_cpu_map__new(kwork->cpu_list);
137                 if (!map) {
138                         pr_debug("Invalid cpu_list\n");
139                         return -1;
140                 }
141
142                 nr_cpus = libbpf_num_possible_cpus();
143                 for (i = 0; i < perf_cpu_map__nr(map); i++) {
144                         struct perf_cpu cpu = perf_cpu_map__cpu(map, i);
145
146                         if (cpu.cpu >= nr_cpus) {
147                                 perf_cpu_map__put(map);
148                                 pr_err("Requested cpu %d too large\n", cpu.cpu);
149                                 return -1;
150                         }
151                         bpf_map_update_elem(fd, &cpu.cpu, &val, BPF_ANY);
152                 }
153                 perf_cpu_map__put(map);
154
155                 skel->bss->has_cpu_filter = 1;
156         }
157
158         return 0;
159 }
160
161 int perf_kwork__top_prepare_bpf(struct perf_kwork *kwork __maybe_unused)
162 {
163         struct bpf_program *prog;
164         struct kwork_class *class;
165         struct kwork_class_bpf *class_bpf;
166         enum kwork_class_type type;
167
168         skel = kwork_top_bpf__open();
169         if (!skel) {
170                 pr_debug("Failed to open kwork top skeleton\n");
171                 return -1;
172         }
173
174         /*
175          * set all progs to non-autoload,
176          * then set corresponding progs according to config
177          */
178         bpf_object__for_each_program(prog, skel->obj)
179                 bpf_program__set_autoload(prog, false);
180
181         list_for_each_entry(class, &kwork->class_list, list) {
182                 type = class->type;
183                 if (!valid_kwork_class_type(type) ||
184                     !kwork_class_bpf_supported_list[type]) {
185                         pr_err("Unsupported bpf trace class %s\n", class->name);
186                         goto out;
187                 }
188
189                 class_bpf = kwork_class_bpf_supported_list[type];
190                 class_bpf->class = class;
191
192                 if (class_bpf->load_prepare)
193                         class_bpf->load_prepare();
194         }
195
196         if (kwork_top_bpf__load(skel)) {
197                 pr_debug("Failed to load kwork top skeleton\n");
198                 goto out;
199         }
200
201         if (setup_filters(kwork))
202                 goto out;
203
204         if (kwork_top_bpf__attach(skel)) {
205                 pr_debug("Failed to attach kwork top skeleton\n");
206                 goto out;
207         }
208
209         return 0;
210
211 out:
212         kwork_top_bpf__destroy(skel);
213         return -1;
214 }
215
216 static void read_task_info(struct kwork_work *work)
217 {
218         int fd;
219         struct task_data data;
220         struct task_key key = {
221                 .pid = work->id,
222                 .cpu = work->cpu,
223         };
224
225         fd = bpf_map__fd(skel->maps.kwork_top_tasks);
226         if (fd < 0) {
227                 pr_debug("Invalid top tasks map fd\n");
228                 return;
229         }
230
231         if (!bpf_map_lookup_elem(fd, &key, &data)) {
232                 work->tgid = data.tgid;
233                 work->is_kthread = data.is_kthread;
234                 work->name = strdup(data.comm);
235         }
236 }
237 static int add_work(struct perf_kwork *kwork, struct work_key *key,
238                     struct work_data *data, int cpu)
239 {
240         struct kwork_class_bpf *bpf_trace;
241         struct kwork_work *work;
242         struct kwork_work tmp = {
243                 .id = key->pid,
244                 .cpu = cpu,
245                 .name = NULL,
246         };
247         enum kwork_class_type type = key->type;
248
249         if (!valid_kwork_class_type(type)) {
250                 pr_debug("Invalid class type %d to add work\n", type);
251                 return -1;
252         }
253
254         bpf_trace = kwork_class_bpf_supported_list[type];
255         tmp.class = bpf_trace->class;
256
257         work = perf_kwork_add_work(kwork, tmp.class, &tmp);
258         if (!work)
259                 return -1;
260
261         work->total_runtime = data->runtime;
262         read_task_info(work);
263
264         return 0;
265 }
266
267 int perf_kwork__top_read_bpf(struct perf_kwork *kwork)
268 {
269         int i, fd, nr_cpus;
270         struct work_data *data;
271         struct work_key key, prev;
272
273         fd = bpf_map__fd(skel->maps.kwork_top_works);
274         if (fd < 0) {
275                 pr_debug("Invalid top runtime fd\n");
276                 return -1;
277         }
278
279         nr_cpus = libbpf_num_possible_cpus();
280         data = calloc(nr_cpus, sizeof(struct work_data));
281         if (!data)
282                 return -1;
283
284         memset(&prev, 0, sizeof(prev));
285         while (!bpf_map_get_next_key(fd, &prev, &key)) {
286                 if ((bpf_map_lookup_elem(fd, &key, data)) != 0) {
287                         pr_debug("Failed to lookup top elem\n");
288                         return -1;
289                 }
290
291                 for (i = 0; i < nr_cpus; i++) {
292                         if (data[i].runtime == 0)
293                                 continue;
294
295                         if (add_work(kwork, &key, &data[i], i))
296                                 return -1;
297                 }
298                 prev = key;
299         }
300         free(data);
301
302         return 0;
303 }
304
305 void perf_kwork__top_cleanup_bpf(void)
306 {
307         kwork_top_bpf__destroy(skel);
308 }