| 1 | |
| 2 | #ifdef HAVE_BPF_SKEL
|
| 3 | #define _GNU_SOURCE
|
| 4 | #include "timerlat.h"
|
| 5 | #include "timerlat_bpf.h"
|
| 6 | #include "timerlat.skel.h"
|
| 7 |
|
| 8 | static struct timerlat_bpf *bpf;
|
| 9 |
|
| 10 |
|
| 11 | static struct bpf_object *obj;
|
| 12 | static struct bpf_program *prog;
|
| 13 |
|
| 14 | |
| 15 | * timerlat_bpf_init - load and initialize BPF program to collect timerlat data
|
| 16 |
|
| 17 | int timerlat_bpf_init(struct timerlat_params *params)
|
| 18 | {
|
| 19 | int err;
|
| 20 |
|
| 21 | debug_msg("Loading BPF program\n");
|
| 22 |
|
| 23 | bpf = timerlat_bpf__open();
|
| 24 | if (!bpf)
|
| 25 | return 1;
|
| 26 |
|
| 27 |
|
| 28 | bpf->rodata->output_divisor = params->common.output_divisor;
|
| 29 | bpf->rodata->entries = params->common.hist.entries;
|
| 30 | bpf->rodata->irq_threshold = params->common.stop_us;
|
| 31 | bpf->rodata->thread_threshold = params->common.stop_total_us;
|
| 32 | bpf->rodata->aa_only = params->common.aa_only;
|
| 33 |
|
| 34 | if (params->common.hist.entries != 0) {
|
| 35 |
|
| 36 | bpf->rodata->bucket_size = params->common.hist.bucket_size;
|
| 37 |
|
| 38 |
|
| 39 | bpf_map__set_max_entries(bpf->maps.hist_irq, params->common.hist.entries);
|
| 40 | bpf_map__set_max_entries(bpf->maps.hist_thread, params->common.hist.entries);
|
| 41 | bpf_map__set_max_entries(bpf->maps.hist_user, params->common.hist.entries);
|
| 42 | } else {
|
| 43 |
|
| 44 | bpf_map__set_autocreate(bpf->maps.hist_irq, false);
|
| 45 | bpf_map__set_autocreate(bpf->maps.hist_thread, false);
|
| 46 | bpf_map__set_autocreate(bpf->maps.hist_user, false);
|
| 47 | }
|
| 48 |
|
| 49 | if (params->common.aa_only) {
|
| 50 |
|
| 51 | bpf_map__set_autocreate(bpf->maps.summary_irq, false);
|
| 52 | bpf_map__set_autocreate(bpf->maps.summary_thread, false);
|
| 53 | bpf_map__set_autocreate(bpf->maps.summary_user, false);
|
| 54 | }
|
| 55 |
|
| 56 |
|
| 57 | err = timerlat_bpf__load(bpf);
|
| 58 | if (err) {
|
| 59 | timerlat_bpf__destroy(bpf);
|
| 60 | return err;
|
| 61 | }
|
| 62 |
|
| 63 | return 0;
|
| 64 | }
|
| 65 |
|
| 66 | |
| 67 | * timerlat_bpf_set_action - set action on threshold executed on BPF side
|
| 68 |
|
| 69 | static int timerlat_bpf_set_action(struct bpf_program *prog)
|
| 70 | {
|
| 71 | unsigned int key = 0, value = bpf_program__fd(prog);
|
| 72 |
|
| 73 | return bpf_map__update_elem(bpf->maps.bpf_action,
|
| 74 | &key, sizeof(key),
|
| 75 | &value, sizeof(value),
|
| 76 | BPF_ANY);
|
| 77 | }
|
| 78 |
|
| 79 | |
| 80 | * timerlat_bpf_attach - attach BPF program to collect timerlat data
|
| 81 |
|
| 82 | int timerlat_bpf_attach(void)
|
| 83 | {
|
| 84 | debug_msg("Attaching BPF program\n");
|
| 85 |
|
| 86 | return timerlat_bpf__attach(bpf);
|
| 87 | }
|
| 88 |
|
| 89 | |
| 90 | * timerlat_bpf_detach - detach BPF program to collect timerlat data
|
| 91 |
|
| 92 | void timerlat_bpf_detach(void)
|
| 93 | {
|
| 94 | timerlat_bpf__detach(bpf);
|
| 95 | }
|
| 96 |
|
| 97 | |
| 98 | * timerlat_bpf_detach - destroy BPF program to collect timerlat data
|
| 99 |
|
| 100 | void timerlat_bpf_destroy(void)
|
| 101 | {
|
| 102 | timerlat_bpf__destroy(bpf);
|
| 103 | bpf = NULL;
|
| 104 | if (obj)
|
| 105 | bpf_object__close(obj);
|
| 106 | obj = NULL;
|
| 107 | prog = NULL;
|
| 108 | }
|
| 109 |
|
| 110 | static int handle_rb_event(void *ctx, void *data, size_t data_sz)
|
| 111 | {
|
| 112 | return 0;
|
| 113 | }
|
| 114 |
|
| 115 | |
| 116 | * timerlat_bpf_wait - wait until tracing is stopped or signal
|
| 117 |
|
| 118 | int timerlat_bpf_wait(int timeout)
|
| 119 | {
|
| 120 | struct ring_buffer *rb;
|
| 121 | int retval;
|
| 122 |
|
| 123 | rb = ring_buffer__new(bpf_map__fd(bpf->maps.signal_stop_tracing),
|
| 124 | handle_rb_event, NULL, NULL);
|
| 125 | retval = ring_buffer__poll(rb, timeout * 1000);
|
| 126 | ring_buffer__free(rb);
|
| 127 |
|
| 128 | return retval;
|
| 129 | }
|
| 130 |
|
| 131 | |
| 132 | * timerlat_bpf_restart_tracing - restart stopped tracing
|
| 133 |
|
| 134 | int timerlat_bpf_restart_tracing(void)
|
| 135 | {
|
| 136 | unsigned int key = 0;
|
| 137 | unsigned long long value = 0;
|
| 138 |
|
| 139 | return bpf_map__update_elem(bpf->maps.stop_tracing,
|
| 140 | &key, sizeof(key),
|
| 141 | &value, sizeof(value), BPF_ANY);
|
| 142 | }
|
| 143 |
|
| 144 | static int get_value(struct bpf_map *map_irq,
|
| 145 | struct bpf_map *map_thread,
|
| 146 | struct bpf_map *map_user,
|
| 147 | int key,
|
| 148 | long long *value_irq,
|
| 149 | long long *value_thread,
|
| 150 | long long *value_user)
|
| 151 | {
|
| 152 | int err;
|
| 153 |
|
| 154 | err = bpf_map__lookup_elem(map_irq, &key,
|
| 155 | sizeof(unsigned int), value_irq,
|
| 156 | sizeof(long long) * nr_cpus, 0);
|
| 157 | if (err)
|
| 158 | return err;
|
| 159 | err = bpf_map__lookup_elem(map_thread, &key,
|
| 160 | sizeof(unsigned int), value_thread,
|
| 161 | sizeof(long long) * nr_cpus, 0);
|
| 162 | if (err)
|
| 163 | return err;
|
| 164 | err = bpf_map__lookup_elem(map_user, &key,
|
| 165 | sizeof(unsigned int), value_user,
|
| 166 | sizeof(long long) * nr_cpus, 0);
|
| 167 | if (err)
|
| 168 | return err;
|
| 169 | return 0;
|
| 170 | }
|
| 171 |
|
| 172 | |
| 173 | * timerlat_bpf_get_hist_value - get value from BPF hist map
|
| 174 |
|
| 175 | int timerlat_bpf_get_hist_value(int key,
|
| 176 | long long *value_irq,
|
| 177 | long long *value_thread,
|
| 178 | long long *value_user)
|
| 179 | {
|
| 180 | return get_value(bpf->maps.hist_irq,
|
| 181 | bpf->maps.hist_thread,
|
| 182 | bpf->maps.hist_user,
|
| 183 | key, value_irq, value_thread, value_user);
|
| 184 | }
|
| 185 |
|
| 186 | |
| 187 | * timerlat_bpf_get_summary_value - get value from BPF summary map
|
| 188 |
|
| 189 | int timerlat_bpf_get_summary_value(enum summary_field key,
|
| 190 | long long *value_irq,
|
| 191 | long long *value_thread,
|
| 192 | long long *value_user)
|
| 193 | {
|
| 194 | return get_value(bpf->maps.summary_irq,
|
| 195 | bpf->maps.summary_thread,
|
| 196 | bpf->maps.summary_user,
|
| 197 | key, value_irq, value_thread, value_user);
|
| 198 | }
|
| 199 |
|
| 200 | |
| 201 | * timerlat_load_bpf_action_program - load and register a BPF action program
|
| 202 |
|
| 203 | int timerlat_load_bpf_action_program(const char *program_path)
|
| 204 | {
|
| 205 | int err;
|
| 206 |
|
| 207 | obj = bpf_object__open_file(program_path, NULL);
|
| 208 | if (!obj) {
|
| 209 | err_msg("Failed to open BPF action program: %s\n", program_path);
|
| 210 | goto out_err;
|
| 211 | }
|
| 212 |
|
| 213 | err = bpf_object__load(obj);
|
| 214 | if (err) {
|
| 215 | err_msg("Failed to load BPF action program: %s\n", program_path);
|
| 216 | goto out_obj_err;
|
| 217 | }
|
| 218 |
|
| 219 | prog = bpf_object__find_program_by_name(obj, "action_handler");
|
| 220 | if (!prog) {
|
| 221 | err_msg("BPF action program must have 'action_handler' function: %s\n",
|
| 222 | program_path);
|
| 223 | goto out_obj_err;
|
| 224 | }
|
| 225 |
|
| 226 | err = timerlat_bpf_set_action(prog);
|
| 227 | if (err) {
|
| 228 | err_msg("Failed to register BPF action program: %s\n", program_path);
|
| 229 | goto out_prog_err;
|
| 230 | }
|
| 231 |
|
| 232 | return 0;
|
| 233 |
|
| 234 | out_prog_err:
|
| 235 | prog = NULL;
|
| 236 | out_obj_err:
|
| 237 | bpf_object__close(obj);
|
| 238 | obj = NULL;
|
| 239 | out_err:
|
| 240 | return 1;
|
| 241 | }
|
| 242 |
|
| 243 | #endif
|