| // SPDX-License-Identifier: GPL-2.0 | |
| #ifdef HAVE_BPF_SKEL | |
| #define _GNU_SOURCE | |
| #include "timerlat.h" | |
| #include "timerlat_bpf.h" | |
| #include "timerlat.skel.h" | |
| static struct timerlat_bpf *bpf; | |
| /* BPF object and program for action program */ | |
| static struct bpf_object *obj; | |
| static struct bpf_program *prog; | |
| /* | |
| * timerlat_bpf_init - load and initialize BPF program to collect timerlat data | |
| */ | |
| int timerlat_bpf_init(struct timerlat_params *params) | |
| { | |
| int err; | |
| debug_msg("Loading BPF program\n"); | |
| bpf = timerlat_bpf__open(); | |
| if (!bpf) | |
| return 1; | |
| /* Pass common options */ | |
| bpf->rodata->output_divisor = params->common.output_divisor; | |
| bpf->rodata->entries = params->common.hist.entries; | |
| bpf->rodata->irq_threshold = params->common.stop_us; | |
| bpf->rodata->thread_threshold = params->common.stop_total_us; | |
| bpf->rodata->aa_only = params->common.aa_only; | |
| if (params->common.hist.entries != 0) { | |
| /* Pass histogram options */ | |
| bpf->rodata->bucket_size = params->common.hist.bucket_size; | |
| /* Set histogram array sizes */ | |
| bpf_map__set_max_entries(bpf->maps.hist_irq, params->common.hist.entries); | |
| bpf_map__set_max_entries(bpf->maps.hist_thread, params->common.hist.entries); | |
| bpf_map__set_max_entries(bpf->maps.hist_user, params->common.hist.entries); | |
| } else { | |
| /* No entries, disable histogram */ | |
| bpf_map__set_autocreate(bpf->maps.hist_irq, false); | |
| bpf_map__set_autocreate(bpf->maps.hist_thread, false); | |
| bpf_map__set_autocreate(bpf->maps.hist_user, false); | |
| } | |
| if (params->common.aa_only) { | |
| /* Auto-analysis only, disable summary */ | |
| bpf_map__set_autocreate(bpf->maps.summary_irq, false); | |
| bpf_map__set_autocreate(bpf->maps.summary_thread, false); | |
| bpf_map__set_autocreate(bpf->maps.summary_user, false); | |
| } | |
| /* Load and verify BPF program */ | |
| err = timerlat_bpf__load(bpf); | |
| if (err) { | |
| timerlat_bpf__destroy(bpf); | |
| return err; | |
| } | |
| return 0; | |
| } | |
| /* | |
| * timerlat_bpf_set_action - set action on threshold executed on BPF side | |
| */ | |
| static int timerlat_bpf_set_action(struct bpf_program *prog) | |
| { | |
| unsigned int key = 0, value = bpf_program__fd(prog); | |
| return bpf_map__update_elem(bpf->maps.bpf_action, | |
| &key, sizeof(key), | |
| &value, sizeof(value), | |
| BPF_ANY); | |
| } | |
| /* | |
| * timerlat_bpf_attach - attach BPF program to collect timerlat data | |
| */ | |
| int timerlat_bpf_attach(void) | |
| { | |
| debug_msg("Attaching BPF program\n"); | |
| return timerlat_bpf__attach(bpf); | |
| } | |
| /* | |
| * timerlat_bpf_detach - detach BPF program to collect timerlat data | |
| */ | |
| void timerlat_bpf_detach(void) | |
| { | |
| timerlat_bpf__detach(bpf); | |
| } | |
| /* | |
| * timerlat_bpf_detach - destroy BPF program to collect timerlat data | |
| */ | |
| void timerlat_bpf_destroy(void) | |
| { | |
| timerlat_bpf__destroy(bpf); | |
| bpf = NULL; | |
| if (obj) | |
| bpf_object__close(obj); | |
| obj = NULL; | |
| prog = NULL; | |
| } | |
| static int handle_rb_event(void *ctx, void *data, size_t data_sz) | |
| { | |
| return 0; | |
| } | |
| /* | |
| * timerlat_bpf_wait - wait until tracing is stopped or signal | |
| */ | |
| int timerlat_bpf_wait(int timeout) | |
| { | |
| struct ring_buffer *rb; | |
| int retval; | |
| rb = ring_buffer__new(bpf_map__fd(bpf->maps.signal_stop_tracing), | |
| handle_rb_event, NULL, NULL); | |
| retval = ring_buffer__poll(rb, timeout * 1000); | |
| ring_buffer__free(rb); | |
| return retval; | |
| } | |
| /* | |
| * timerlat_bpf_restart_tracing - restart stopped tracing | |
| */ | |
| int timerlat_bpf_restart_tracing(void) | |
| { | |
| unsigned int key = 0; | |
| unsigned long long value = 0; | |
| return bpf_map__update_elem(bpf->maps.stop_tracing, | |
| &key, sizeof(key), | |
| &value, sizeof(value), BPF_ANY); | |
| } | |
| static int get_value(struct bpf_map *map_irq, | |
| struct bpf_map *map_thread, | |
| struct bpf_map *map_user, | |
| int key, | |
| long long *value_irq, | |
| long long *value_thread, | |
| long long *value_user) | |
| { | |
| int err; | |
| err = bpf_map__lookup_elem(map_irq, &key, | |
| sizeof(unsigned int), value_irq, | |
| sizeof(long long) * nr_cpus, 0); | |
| if (err) | |
| return err; | |
| err = bpf_map__lookup_elem(map_thread, &key, | |
| sizeof(unsigned int), value_thread, | |
| sizeof(long long) * nr_cpus, 0); | |
| if (err) | |
| return err; | |
| err = bpf_map__lookup_elem(map_user, &key, | |
| sizeof(unsigned int), value_user, | |
| sizeof(long long) * nr_cpus, 0); | |
| if (err) | |
| return err; | |
| return 0; | |
| } | |
| /* | |
| * timerlat_bpf_get_hist_value - get value from BPF hist map | |
| */ | |
| int timerlat_bpf_get_hist_value(int key, | |
| long long *value_irq, | |
| long long *value_thread, | |
| long long *value_user) | |
| { | |
| return get_value(bpf->maps.hist_irq, | |
| bpf->maps.hist_thread, | |
| bpf->maps.hist_user, | |
| key, value_irq, value_thread, value_user); | |
| } | |
| /* | |
| * timerlat_bpf_get_summary_value - get value from BPF summary map | |
| */ | |
| int timerlat_bpf_get_summary_value(enum summary_field key, | |
| long long *value_irq, | |
| long long *value_thread, | |
| long long *value_user) | |
| { | |
| return get_value(bpf->maps.summary_irq, | |
| bpf->maps.summary_thread, | |
| bpf->maps.summary_user, | |
| key, value_irq, value_thread, value_user); | |
| } | |
| /* | |
| * timerlat_load_bpf_action_program - load and register a BPF action program | |
| */ | |
| int timerlat_load_bpf_action_program(const char *program_path) | |
| { | |
| int err; | |
| obj = bpf_object__open_file(program_path, NULL); | |
| if (!obj) { | |
| err_msg("Failed to open BPF action program: %s\n", program_path); | |
| goto out_err; | |
| } | |
| err = bpf_object__load(obj); | |
| if (err) { | |
| err_msg("Failed to load BPF action program: %s\n", program_path); | |
| goto out_obj_err; | |
| } | |
| prog = bpf_object__find_program_by_name(obj, "action_handler"); | |
| if (!prog) { | |
| err_msg("BPF action program must have 'action_handler' function: %s\n", | |
| program_path); | |
| goto out_obj_err; | |
| } | |
| err = timerlat_bpf_set_action(prog); | |
| if (err) { | |
| err_msg("Failed to register BPF action program: %s\n", program_path); | |
| goto out_prog_err; | |
| } | |
| return 0; | |
| out_prog_err: | |
| prog = NULL; | |
| out_obj_err: | |
| bpf_object__close(obj); | |
| obj = NULL; | |
| out_err: | |
| return 1; | |
| } | |
| #endif /* HAVE_BPF_SKEL */ |