| // 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; |
| |
| /* |
| * 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->output_divisor; |
| bpf->rodata->entries = params->entries; |
| bpf->rodata->irq_threshold = params->stop_us; |
| bpf->rodata->thread_threshold = params->stop_total_us; |
| bpf->rodata->aa_only = params->aa_only; |
| |
| if (params->entries != 0) { |
| /* Pass histogram options */ |
| bpf->rodata->bucket_size = params->bucket_size; |
| |
| /* Set histogram array sizes */ |
| bpf_map__set_max_entries(bpf->maps.hist_irq, params->entries); |
| bpf_map__set_max_entries(bpf->maps.hist_thread, params->entries); |
| bpf_map__set_max_entries(bpf->maps.hist_user, params->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->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_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); |
| } |
| |
| 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; |
| } |
| |
| 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); |
| } |
| #endif /* HAVE_BPF_SKEL */ |