310 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * bpf_kwork_top.c4 *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 1629 30struct time_data {31 __u64 timestamp;32};33 34struct work_data {35 __u64 runtime;36};37 38struct task_data {39 __u32 tgid;40 __u32 is_kthread;41 char comm[MAX_COMMAND_LEN];42};43 44struct work_key {45 __u32 type;46 __u32 pid;47 __u64 task_p;48};49 50struct task_key {51 __u32 pid;52 __u32 cpu;53};54 55struct kwork_class_bpf {56 struct kwork_class *class;57 void (*load_prepare)(void);58};59 60static struct kwork_top_bpf *skel;61 62void 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 72void 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 82static 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 88static struct kwork_class_bpf kwork_irq_bpf = {89 .load_prepare = irq_load_prepare,90};91 92static 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 98static struct kwork_class_bpf kwork_softirq_bpf = {99 .load_prepare = softirq_load_prepare,100};101 102static void sched_load_prepare(void)103{104 bpf_program__set_autoload(skel->progs.on_switch, true);105}106 107static struct kwork_class_bpf kwork_sched_bpf = {108 .load_prepare = sched_load_prepare,109};110 111static struct kwork_class_bpf *112kwork_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 118static bool valid_kwork_class_type(enum kwork_class_type type)119{120 return type >= 0 && type < KWORK_CLASS_MAX;121}122 123static int setup_filters(struct perf_kwork *kwork)124{125 if (kwork->cpu_list) {126 int idx, nr_cpus, fd;127 struct perf_cpu_map *map;128 struct perf_cpu cpu;129 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 perf_cpu_map__for_each_cpu(cpu, idx, map) {144 u8 val = 1;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 156 return 0;157}158 159int perf_kwork__top_prepare_bpf(struct perf_kwork *kwork)160{161 struct bpf_program *prog;162 struct kwork_class *class;163 struct kwork_class_bpf *class_bpf;164 enum kwork_class_type type;165 166 skel = kwork_top_bpf__open();167 if (!skel) {168 pr_debug("Failed to open kwork top skeleton\n");169 return -1;170 }171 172 /*173 * set all progs to non-autoload,174 * then set corresponding progs according to config175 */176 bpf_object__for_each_program(prog, skel->obj)177 bpf_program__set_autoload(prog, false);178 179 list_for_each_entry(class, &kwork->class_list, list) {180 type = class->type;181 if (!valid_kwork_class_type(type) ||182 !kwork_class_bpf_supported_list[type]) {183 pr_err("Unsupported bpf trace class %s\n", class->name);184 goto out;185 }186 187 class_bpf = kwork_class_bpf_supported_list[type];188 class_bpf->class = class;189 190 if (class_bpf->load_prepare)191 class_bpf->load_prepare();192 }193 194 if (kwork->cpu_list)195 skel->rodata->has_cpu_filter = 1;196 197 if (kwork_top_bpf__load(skel)) {198 pr_debug("Failed to load kwork top skeleton\n");199 goto out;200 }201 202 if (setup_filters(kwork))203 goto out;204 205 if (kwork_top_bpf__attach(skel)) {206 pr_debug("Failed to attach kwork top skeleton\n");207 goto out;208 }209 210 return 0;211 212out:213 kwork_top_bpf__destroy(skel);214 return -1;215}216 217static void read_task_info(struct kwork_work *work)218{219 int fd;220 struct task_data data;221 struct task_key key = {222 .pid = work->id,223 .cpu = work->cpu,224 };225 226 fd = bpf_map__fd(skel->maps.kwork_top_tasks);227 if (fd < 0) {228 pr_debug("Invalid top tasks map fd\n");229 return;230 }231 232 if (!bpf_map_lookup_elem(fd, &key, &data)) {233 work->tgid = data.tgid;234 work->is_kthread = data.is_kthread;235 work->name = strdup(data.comm);236 }237}238static int add_work(struct perf_kwork *kwork, struct work_key *key,239 struct work_data *data, int cpu)240{241 struct kwork_class_bpf *bpf_trace;242 struct kwork_work *work;243 struct kwork_work tmp = {244 .id = key->pid,245 .cpu = cpu,246 .name = NULL,247 };248 enum kwork_class_type type = key->type;249 250 if (!valid_kwork_class_type(type)) {251 pr_debug("Invalid class type %d to add work\n", type);252 return -1;253 }254 255 bpf_trace = kwork_class_bpf_supported_list[type];256 tmp.class = bpf_trace->class;257 258 work = perf_kwork_add_work(kwork, tmp.class, &tmp);259 if (!work)260 return -1;261 262 work->total_runtime = data->runtime;263 read_task_info(work);264 265 return 0;266}267 268int perf_kwork__top_read_bpf(struct perf_kwork *kwork)269{270 int i, fd, nr_cpus;271 struct work_data *data;272 struct work_key key, prev;273 274 fd = bpf_map__fd(skel->maps.kwork_top_works);275 if (fd < 0) {276 pr_debug("Invalid top runtime fd\n");277 return -1;278 }279 280 nr_cpus = libbpf_num_possible_cpus();281 data = calloc(nr_cpus, sizeof(struct work_data));282 if (!data)283 return -1;284 285 memset(&prev, 0, sizeof(prev));286 while (!bpf_map_get_next_key(fd, &prev, &key)) {287 if ((bpf_map_lookup_elem(fd, &key, data)) != 0) {288 pr_debug("Failed to lookup top elem\n");289 return -1;290 }291 292 for (i = 0; i < nr_cpus; i++) {293 if (data[i].runtime == 0)294 continue;295 296 if (add_work(kwork, &key, &data[i], i))297 return -1;298 }299 prev = key;300 }301 free(data);302 303 return 0;304}305 306void perf_kwork__top_cleanup_bpf(void)307{308 kwork_top_bpf__destroy(skel);309}310