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 if (kwork->cpu_list) { 126 unsigned int idx; 127 int nr_cpus, fd; 128 struct perf_cpu_map *map; 129 struct perf_cpu cpu; 130 131 fd = bpf_map__fd(skel->maps.kwork_top_cpu_filter); 132 if (fd < 0) { 133 pr_debug("Invalid cpu filter fd\n"); 134 return -1; 135 } 136 137 map = perf_cpu_map__new(kwork->cpu_list); 138 if (!map) { 139 pr_debug("Invalid cpu_list\n"); 140 return -1; 141 } 142 143 nr_cpus = libbpf_num_possible_cpus(); 144 perf_cpu_map__for_each_cpu(cpu, idx, map) { 145 u8 val = 1; 146 147 if (cpu.cpu >= nr_cpus) { 148 perf_cpu_map__put(map); 149 pr_err("Requested cpu %d too large\n", cpu.cpu); 150 return -1; 151 } 152 bpf_map_update_elem(fd, &cpu.cpu, &val, BPF_ANY); 153 } 154 perf_cpu_map__put(map); 155 } 156 157 return 0; 158 } 159 160 int perf_kwork__top_prepare_bpf(struct perf_kwork *kwork) 161 { 162 struct bpf_program *prog; 163 struct kwork_class *class; 164 struct kwork_class_bpf *class_bpf; 165 enum kwork_class_type type; 166 167 skel = kwork_top_bpf__open(); 168 if (!skel) { 169 pr_debug("Failed to open kwork top skeleton\n"); 170 return -1; 171 } 172 173 /* 174 * set all progs to non-autoload, 175 * then set corresponding progs according to config 176 */ 177 bpf_object__for_each_program(prog, skel->obj) 178 bpf_program__set_autoload(prog, false); 179 180 list_for_each_entry(class, &kwork->class_list, list) { 181 type = class->type; 182 if (!valid_kwork_class_type(type) || 183 !kwork_class_bpf_supported_list[type]) { 184 pr_err("Unsupported bpf trace class %s\n", class->name); 185 goto out; 186 } 187 188 class_bpf = kwork_class_bpf_supported_list[type]; 189 class_bpf->class = class; 190 191 if (class_bpf->load_prepare) 192 class_bpf->load_prepare(); 193 } 194 195 if (kwork->cpu_list) 196 skel->rodata->has_cpu_filter = 1; 197 198 if (kwork_top_bpf__load(skel)) { 199 pr_debug("Failed to load kwork top skeleton\n"); 200 goto out; 201 } 202 203 if (setup_filters(kwork)) 204 goto out; 205 206 if (kwork_top_bpf__attach(skel)) { 207 pr_debug("Failed to attach kwork top skeleton\n"); 208 goto out; 209 } 210 211 return 0; 212 213 out: 214 kwork_top_bpf__destroy(skel); 215 return -1; 216 } 217 218 static void read_task_info(struct kwork_work *work) 219 { 220 int fd; 221 struct task_data data; 222 struct task_key key = { 223 .pid = work->id, 224 .cpu = work->cpu, 225 }; 226 227 fd = bpf_map__fd(skel->maps.kwork_top_tasks); 228 if (fd < 0) { 229 pr_debug("Invalid top tasks map fd\n"); 230 return; 231 } 232 233 if (!bpf_map_lookup_elem(fd, &key, &data)) { 234 work->tgid = data.tgid; 235 work->is_kthread = data.is_kthread; 236 work->name = strdup(data.comm); 237 } 238 } 239 static int add_work(struct perf_kwork *kwork, struct work_key *key, 240 struct work_data *data, int cpu) 241 { 242 struct kwork_class_bpf *bpf_trace; 243 struct kwork_work *work; 244 struct kwork_work tmp = { 245 .id = key->pid, 246 .cpu = cpu, 247 .name = NULL, 248 }; 249 enum kwork_class_type type = key->type; 250 251 if (!valid_kwork_class_type(type)) { 252 pr_debug("Invalid class type %d to add work\n", type); 253 return -1; 254 } 255 256 bpf_trace = kwork_class_bpf_supported_list[type]; 257 tmp.class = bpf_trace->class; 258 259 work = kwork->add_work(kwork, tmp.class, &tmp); 260 if (!work) 261 return -1; 262 263 work->total_runtime = data->runtime; 264 read_task_info(work); 265 266 return 0; 267 } 268 269 int perf_kwork__top_read_bpf(struct perf_kwork *kwork) 270 { 271 int i, fd, nr_cpus; 272 struct work_data *data; 273 struct work_key key, prev; 274 275 fd = bpf_map__fd(skel->maps.kwork_top_works); 276 if (fd < 0) { 277 pr_debug("Invalid top runtime fd\n"); 278 return -1; 279 } 280 281 nr_cpus = libbpf_num_possible_cpus(); 282 data = calloc(nr_cpus, sizeof(struct work_data)); 283 if (!data) 284 return -1; 285 286 memset(&prev, 0, sizeof(prev)); 287 while (!bpf_map_get_next_key(fd, &prev, &key)) { 288 if ((bpf_map_lookup_elem(fd, &key, data)) != 0) { 289 pr_debug("Failed to lookup top elem\n"); 290 return -1; 291 } 292 293 for (i = 0; i < nr_cpus; i++) { 294 if (data[i].runtime == 0) 295 continue; 296 297 if (add_work(kwork, &key, &data[i], i)) 298 return -1; 299 } 300 prev = key; 301 } 302 free(data); 303 304 return 0; 305 } 306 307 void perf_kwork__top_cleanup_bpf(void) 308 { 309 kwork_top_bpf__destroy(skel); 310 } 311