// 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 cpus) { int err; err = bpf_map__lookup_elem(map_irq, &key, sizeof(unsigned int), value_irq, sizeof(long long) * cpus, 0); if (err) return err; err = bpf_map__lookup_elem(map_thread, &key, sizeof(unsigned int), value_thread, sizeof(long long) * cpus, 0); if (err) return err; err = bpf_map__lookup_elem(map_user, &key, sizeof(unsigned int), value_user, sizeof(long long) * 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, int cpus) { return get_value(bpf->maps.hist_irq, bpf->maps.hist_thread, bpf->maps.hist_user, key, value_irq, value_thread, value_user, cpus); } /* * 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, int cpus) { return get_value(bpf->maps.summary_irq, bpf->maps.summary_thread, bpf->maps.summary_user, key, value_irq, value_thread, value_user, cpus); } /* * 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 */