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 } 309