master
c 718 lines 20.5 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "ebpf.h"
4 #include "ebpf_hardirq.h"
5 #include "libbpf_api/ebpf_library.h"
6
7 struct config hardirq_config = APPCONFIG_INITIALIZER;
8
9 static char *hardirq_counter_dimension_name[NETDATA_HARDIRQ_DIMENSION] = {"latency"};
10
11 static netdata_syscall_stat_t hardirq_counter_aggregated_data[NETDATA_HARDIRQ_DIMENSION];
12 static netdata_publish_syscall_t hardirq_counter_publish_aggregated[NETDATA_HARDIRQ_DIMENSION];
13
14 static ebpf_local_maps_t hardirq_maps[] = {
15 {.name = "tbl_hardirq",
16 .internal_input = NETDATA_HARDIRQ_MAX_IRQS,
17 .user_input = 0,
18 .type = NETDATA_EBPF_MAP_STATIC,
19 .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
20 #ifdef LIBBPF_MAJOR_VERSION
21 .map_type = BPF_MAP_TYPE_PERCPU_HASH
22 #endif
23 },
24 {.name = "tbl_hardirq_static",
25 .internal_input = HARDIRQ_EBPF_STATIC_END,
26 .user_input = 0,
27 .type = NETDATA_EBPF_MAP_STATIC,
28 .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
29 #ifdef LIBBPF_MAJOR_VERSION
30 .map_type = BPF_MAP_TYPE_PERCPU_ARRAY
31 #endif
32 },
33 {.name = NULL,
34 .internal_input = 0,
35 .user_input = 0,
36 .type = NETDATA_EBPF_MAP_CONTROLLER,
37 .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
38 #ifdef LIBBPF_MAJOR_VERSION
39 .map_type = BPF_MAP_TYPE_PERCPU_ARRAY
40 #endif
41 }};
42
43 #define HARDIRQ_TP_CLASS_IRQ "irq"
44 #define HARDIRQ_TP_CLASS_IRQ_VECTORS "irq_vectors"
45 static ebpf_tracepoint_t hardirq_tracepoints[] = {
46 {.enabled = false, .class = HARDIRQ_TP_CLASS_IRQ, .event = "irq_handler_entry"},
47 {.enabled = false, .class = HARDIRQ_TP_CLASS_IRQ, .event = "irq_handler_exit"},
48 {.enabled = false, .class = HARDIRQ_TP_CLASS_IRQ_VECTORS, .event = "thermal_apic_entry"},
49 {.enabled = false, .class = HARDIRQ_TP_CLASS_IRQ_VECTORS, .event = "thermal_apic_exit"},
50 {.enabled = false, .class = HARDIRQ_TP_CLASS_IRQ_VECTORS, .event = "threshold_apic_entry"},
51 {.enabled = false, .class = HARDIRQ_TP_CLASS_IRQ_VECTORS, .event = "threshold_apic_exit"},
52 {.enabled = false, .class = HARDIRQ_TP_CLASS_IRQ_VECTORS, .event = "error_apic_entry"},
53 {.enabled = false, .class = HARDIRQ_TP_CLASS_IRQ_VECTORS, .event = "error_apic_exit"},
54 {.enabled = false, .class = HARDIRQ_TP_CLASS_IRQ_VECTORS, .event = "deferred_error_apic_entry"},
55 {.enabled = false, .class = HARDIRQ_TP_CLASS_IRQ_VECTORS, .event = "deferred_error_apic_exit"},
56 {.enabled = false, .class = HARDIRQ_TP_CLASS_IRQ_VECTORS, .event = "spurious_apic_entry"},
57 {.enabled = false, .class = HARDIRQ_TP_CLASS_IRQ_VECTORS, .event = "spurious_apic_exit"},
58 {.enabled = false, .class = HARDIRQ_TP_CLASS_IRQ_VECTORS, .event = "call_function_entry"},
59 {.enabled = false, .class = HARDIRQ_TP_CLASS_IRQ_VECTORS, .event = "call_function_exit"},
60 {.enabled = false, .class = HARDIRQ_TP_CLASS_IRQ_VECTORS, .event = "call_function_single_entry"},
61 {.enabled = false, .class = HARDIRQ_TP_CLASS_IRQ_VECTORS, .event = "call_function_single_exit"},
62 {.enabled = false, .class = HARDIRQ_TP_CLASS_IRQ_VECTORS, .event = "reschedule_entry"},
63 {.enabled = false, .class = HARDIRQ_TP_CLASS_IRQ_VECTORS, .event = "reschedule_exit"},
64 {.enabled = false, .class = HARDIRQ_TP_CLASS_IRQ_VECTORS, .event = "local_timer_entry"},
65 {.enabled = false, .class = HARDIRQ_TP_CLASS_IRQ_VECTORS, .event = "local_timer_exit"},
66 {.enabled = false, .class = HARDIRQ_TP_CLASS_IRQ_VECTORS, .event = "irq_work_entry"},
67 {.enabled = false, .class = HARDIRQ_TP_CLASS_IRQ_VECTORS, .event = "irq_work_exit"},
68 {.enabled = false, .class = HARDIRQ_TP_CLASS_IRQ_VECTORS, .event = "x86_platform_ipi_entry"},
69 {.enabled = false, .class = HARDIRQ_TP_CLASS_IRQ_VECTORS, .event = "x86_platform_ipi_exit"},
70 {.enabled = false, .class = NULL, .event = NULL}};
71
72 static hardirq_static_val_t hardirq_static_vals[] = {
73 {.idx = HARDIRQ_EBPF_STATIC_APIC_THERMAL, .name = "apic_thermal", .latency = 0},
74 {.idx = HARDIRQ_EBPF_STATIC_APIC_THRESHOLD, .name = "apic_threshold", .latency = 0},
75 {.idx = HARDIRQ_EBPF_STATIC_APIC_ERROR, .name = "apic_error", .latency = 0},
76 {.idx = HARDIRQ_EBPF_STATIC_APIC_DEFERRED_ERROR, .name = "apic_deferred_error", .latency = 0},
77 {.idx = HARDIRQ_EBPF_STATIC_APIC_SPURIOUS, .name = "apic_spurious", .latency = 0},
78 {.idx = HARDIRQ_EBPF_STATIC_FUNC_CALL, .name = "func_call", .latency = 0},
79 {.idx = HARDIRQ_EBPF_STATIC_FUNC_CALL_SINGLE, .name = "func_call_single", .latency = 0},
80 {.idx = HARDIRQ_EBPF_STATIC_RESCHEDULE, .name = "reschedule", .latency = 0},
81 {.idx = HARDIRQ_EBPF_STATIC_LOCAL_TIMER, .name = "local_timer", .latency = 0},
82 {.idx = HARDIRQ_EBPF_STATIC_IRQ_WORK, .name = "irq_work", .latency = 0},
83 {.idx = HARDIRQ_EBPF_STATIC_X86_PLATFORM_IPI, .name = "x86_platform_ipi", .latency = 0},
84 };
85
86 static bool hardirq_safe_clean = false;
87
88 #ifdef LIBBPF_MAJOR_VERSION
89 /**
90 * Set hash table
91 *
92 * Set the values for maps according the value given by kernel.
93 *
94 * @param obj is the main structure for bpf objects.
95 */
96 static inline void ebpf_hardirq_set_hash_table(struct hardirq_bpf *obj)
97 {
98 hardirq_maps[HARDIRQ_MAP_LATENCY].map_fd = bpf_map__fd(obj->maps.tbl_hardirq);
99 hardirq_maps[HARDIRQ_MAP_LATENCY_STATIC].map_fd = bpf_map__fd(obj->maps.tbl_hardirq_static);
100 }
101
102 /**
103 * Load and Attach
104 *
105 * Load and attach bpf software.
106 *
107 * @param obj is the main structure for bpf objects.
108 * @param em structure with configuration
109 *
110 * @return It returns 0 on success and -1 otherwise.
111 */
112 static inline int ebpf_hardirq_load_and_attach(struct hardirq_bpf *obj)
113 {
114 int ret = hardirq_bpf__load(obj);
115 if (ret) {
116 return -1;
117 }
118
119 ret = hardirq_bpf__attach(obj);
120 if (ret) {
121 return -1;
122 }
123
124 ebpf_hardirq_set_hash_table(obj);
125
126 return 0;
127 }
128 #endif
129
130 /*****************************************************************
131 *
132 * JudyL SECTION
133 *
134 *****************************************************************/
135
136 static Pvoid_t ebpf_hardirq_JudyL = NULL;
137
138 /**
139 * eBPF hardirq get
140 *
141 * Get a hardirq_val_t entry to be used with a specific IRQ.
142 *
143 * @return it returns the address on success.
144 */
145 hardirq_val_t *ebpf_hardirq_get(int irq)
146 {
147 Pvoid_t *PValue = JudyLGet(ebpf_hardirq_JudyL, (Word_t)irq, PJE0);
148 if (PValue && *PValue)
149 return *PValue;
150
151 JError_t J_Error;
152 PValue = JudyLIns(&ebpf_hardirq_JudyL, (Word_t)irq, &J_Error);
153 if (unlikely(PValue == PJERR)) {
154 netdata_log_error(
155 "Cannot insert IRQ %d to JudyL, JU_ERRNO_* == %u, ID == %d", irq, JU_ERRNO(&J_Error), JU_ERRID(&J_Error));
156 return NULL;
157 }
158
159 if (unlikely(!PValue)) {
160 netdata_log_error("JudyLIns returned NULL for IRQ %d", irq);
161 return NULL;
162 }
163
164 hardirq_val_t *target = callocz(1, sizeof(hardirq_val_t));
165 target->irq = irq;
166 *PValue = target;
167
168 return target;
169 }
170
171 /**
172 * eBPF hardirq release
173 *
174 * @param irq IRQ number to release.
175 */
176 void ebpf_hardirq_release(int irq)
177 {
178 Pvoid_t *PValue = JudyLGet(ebpf_hardirq_JudyL, (Word_t)irq, PJE0);
179 if (PValue && *PValue) {
180 freez(*PValue);
181 JudyLDel(&ebpf_hardirq_JudyL, (Word_t)irq, PJE0);
182 }
183 }
184
185 /*****************************************************************
186 *
187 * EXIT FUNCTIONS
188 *
189 *****************************************************************/
190
191 /**
192 * Obsolete global
193 *
194 * Obsolete global charts created by thread.
195 *
196 * @param em a pointer to `struct ebpf_module`
197 */
198 static void ebpf_obsolete_hardirq_global(ebpf_module_t *em)
199 {
200 ebpf_write_chart_obsolete(
201 NETDATA_EBPF_SYSTEM_GROUP,
202 "hardirq_latency",
203 "",
204 "Hardware IRQ latency",
205 EBPF_COMMON_UNITS_MILLISECONDS,
206 "interrupts",
207 NETDATA_EBPF_CHART_TYPE_STACKED,
208 NETDATA_EBPF_SYSTEM_HARDIRQ_LATENCY_CTX,
209 NETDATA_CHART_PRIO_HARDIRQ_LATENCY,
210 em->update_every);
211 }
212
213 /**
214 * Hardirq Exit
215 *
216 * Cancel child and exit.
217 *
218 * @param ptr thread data.
219 */
220 static void hardirq_cleanup(void *pptr)
221 {
222 return;
223 ebpf_module_t *em = CLEANUP_FUNCTION_GET_PTR(pptr);
224 if (!em)
225 return;
226
227 if (ebpf_module_enabled_get(em) == NETDATA_THREAD_EBPF_FUNCTION_RUNNING && !ebpf_plugin_stop()) {
228 netdata_mutex_lock(&lock);
229
230 if (hardirq_safe_clean)
231 ebpf_obsolete_hardirq_global(em);
232
233 netdata_mutex_unlock(&lock);
234 fflush(stdout);
235 }
236
237 if (!hardirq_safe_clean) {
238 netdata_mutex_lock(&ebpf_exit_cleanup);
239 ebpf_module_enabled_set(em, NETDATA_THREAD_EBPF_STOPPED);
240 netdata_mutex_unlock(&ebpf_exit_cleanup);
241 return;
242 }
243
244 if (!ebpf_plugin_stop()) {
245 if ((em->load & EBPF_LOAD_LEGACY) && em->probe_links) {
246 ebpf_unload_legacy_code(em->objects, em->probe_links);
247 em->objects = NULL;
248 em->probe_links = NULL;
249 }
250 #ifdef LIBBPF_MAJOR_VERSION
251 else if (hardirq_bpf_obj) {
252 //hardirq_bpf__destroy(hardirq_bpf_obj);
253 hardirq_bpf_obj = NULL;
254 }
255 #endif
256 }
257
258 /*
259 if (unlikely(ebpf_hardirq_JudyL)) {
260 Word_t index = 0;
261 Pvoid_t *PValue;
262 for (PValue = JudyLFirst(ebpf_hardirq_JudyL, &index, PJE0); PValue != NULL && PValue != PJERR;
263 PValue = JudyLNext(ebpf_hardirq_JudyL, &index, PJE0)) {
264 hardirq_val_t *v = *PValue;
265 if (v)
266 freez(v);
267 }
268 JudyLFreeArray(&ebpf_hardirq_JudyL, PJE0);
269 ebpf_hardirq_JudyL = NULL;
270 }
271 */
272
273 for (int i = 0; hardirq_tracepoints[i].class != NULL; i++) {
274 ebpf_disable_tracepoint(&hardirq_tracepoints[i]);
275 }
276
277 netdata_mutex_lock(&ebpf_exit_cleanup);
278 ebpf_module_enabled_set(em, NETDATA_THREAD_EBPF_STOPPED);
279 netdata_mutex_unlock(&ebpf_exit_cleanup);
280 }
281
282 /*****************************************************************
283 * MAIN LOOP
284 *****************************************************************/
285
286 /**
287 * Parse interrupts
288 *
289 * Parse /proc/interrupts to get names used in metrics
290 *
291 * @param irq_name vector to store data.
292 * @param irq irq value
293 *
294 * @return It returns 0 on success and -1 otherwise
295 */
296 static int hardirq_parse_interrupts(char *irq_name, int irq)
297 {
298 static procfile *ff = NULL;
299 static int cpus = -1;
300 if (unlikely(!ff)) {
301 char filename[FILENAME_MAX + 1];
302 snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/proc/interrupts");
303 ff = procfile_open(filename, " \t:", PROCFILE_FLAG_DEFAULT);
304 }
305 if (unlikely(!ff))
306 return -1;
307
308 ff = procfile_readall(ff);
309 if (unlikely(!ff))
310 return -1;
311
312 size_t words = procfile_linewords(ff, 0);
313 if (unlikely(cpus == -1)) {
314 uint32_t w;
315 cpus = 0;
316 for (w = 0; w < words; w++) {
317 if (likely(strncmp(procfile_lineword(ff, 0, w), "CPU", 3) == 0))
318 cpus++;
319 }
320 }
321
322 size_t lines = procfile_lines(ff), l;
323 if (unlikely(!lines)) {
324 collector_error("Cannot read /proc/interrupts, zero lines reported.");
325 return -1;
326 }
327
328 for (l = 1; l < lines; l++) {
329 words = procfile_linewords(ff, l);
330 if (unlikely(!words))
331 continue;
332 const char *id = procfile_lineword(ff, l, 0);
333 if (!isdigit(id[0]))
334 continue;
335
336 int cmp = str2i(id);
337 if (cmp != irq)
338 continue;
339
340 if (unlikely((uint32_t)(cpus + 2) < words)) {
341 const char *name = procfile_lineword(ff, l, words - 1);
342 snprintfz(irq_name, NETDATA_HARDIRQ_NAME_LEN - 1, "%d_%s", irq, name);
343 }
344 }
345
346 return 0;
347 }
348
349 /**
350 * Read Latency Map
351 *
352 * Read data from kernel ring to user ring.
353 *
354 * @param mapfd hash map id.
355 *
356 * @return it returns 0 on success and -1 otherwise
357 static int hardirq_read_latency_map(int mapfd)
358 {
359 static hardirq_ebpf_static_val_t *hardirq_ebpf_dynamic_vals = NULL;
360 if (!hardirq_ebpf_dynamic_vals)
361 hardirq_ebpf_dynamic_vals = callocz(ebpf_nprocs, sizeof(hardirq_ebpf_static_val_t));
362
363 hardirq_ebpf_key_t key = {};
364 hardirq_ebpf_key_t next_key = {};
365
366 while (bpf_map_get_next_key(mapfd, &key, &next_key) == 0) {
367 if (ebpf_plugin_stop())
368 break;
369
370 int test = bpf_map_lookup_elem(mapfd, &key, hardirq_ebpf_dynamic_vals);
371 if (unlikely(test < 0)) {
372 key = next_key;
373 continue;
374 }
375
376 if (unlikely(key.irq < 0 || key.irq >= NETDATA_HARDIRQ_MAX_IRQS)) {
377 key = next_key;
378 continue;
379 }
380
381 hardirq_val_t *v = ebpf_hardirq_get(key.irq);
382 if (unlikely(!v)) {
383 key = next_key;
384 continue;
385 }
386
387 if (!v->dim_exists) {
388 if (hardirq_parse_interrupts(v->name, v->irq)) {
389 ebpf_hardirq_release(v->irq);
390 key = next_key;
391 continue;
392 }
393 v->dim_exists = true;
394 }
395
396 uint64_t latency = 0;
397 int i;
398 for (i = 0; i < ebpf_nprocs; i++) {
399 latency += hardirq_ebpf_dynamic_vals[i].latency / 1000;
400 }
401 v->latency = latency;
402
403 key = next_key;
404 }
405
406 return 0;
407 }
408 */
409
410 /**
411 * Read Latency Static Map
412 *
413 * Read data from kernel ring to user ring.
414 *
415 * @param mapfd array map id.
416 */
417 static void hardirq_read_latency_static_map(int mapfd)
418 {
419 static hardirq_ebpf_static_val_t *hardirq_per_cpu_vals = NULL;
420 if (!hardirq_per_cpu_vals)
421 hardirq_per_cpu_vals = callocz(ebpf_nprocs, sizeof(hardirq_ebpf_static_val_t));
422
423 int end = (running_on_kernel < NETDATA_KERNEL_V4_15) ? 1 : ebpf_nprocs;
424 uint32_t i;
425 for (i = 0; i < HARDIRQ_EBPF_STATIC_END; i++) {
426 int test = bpf_map_lookup_elem(mapfd, &i, hardirq_per_cpu_vals);
427 if (unlikely(test < 0)) {
428 continue;
429 }
430
431 uint64_t latency = 0;
432 int cpu_i;
433 for (cpu_i = 0; cpu_i < end; cpu_i++) {
434 latency += hardirq_per_cpu_vals[cpu_i].latency / 1000;
435 }
436
437 hardirq_static_vals[i].latency = latency;
438 }
439 }
440
441 /**
442 * Read eBPF maps for hard IRQ.
443 *
444 * @return When it is not possible to parse /proc, it returns -1, on success it returns 0.
445 */
446 static int hardirq_reader(void)
447 {
448 /*
449 if (hardirq_read_latency_map(hardirq_maps[HARDIRQ_MAP_LATENCY].map_fd))
450 return -1;
451 */
452
453 hardirq_read_latency_static_map(hardirq_maps[HARDIRQ_MAP_LATENCY_STATIC].map_fd);
454
455 return 0;
456 }
457
458 /**
459 * Create charts
460 *
461 * Call ebpf_create_chart to create the charts for the collector.
462 *
463 * @param update_every value to overwrite the update frequency set by the server.
464 */
465 static void ebpf_create_hardirq_charts(int update_every)
466 {
467 ebpf_create_chart(
468 NETDATA_EBPF_SYSTEM_GROUP,
469 "hardirq_latency",
470 "Hardware IRQ latency",
471 EBPF_COMMON_UNITS_MILLISECONDS,
472 "interrupts",
473 NETDATA_EBPF_SYSTEM_HARDIRQ_LATENCY_CTX,
474 NETDATA_EBPF_CHART_TYPE_STACKED,
475 NETDATA_CHART_PRIO_HARDIRQ_LATENCY,
476 ebpf_create_global_dimension,
477 hardirq_counter_publish_aggregated,
478 1,
479 update_every,
480 NETDATA_EBPF_MODULE_NAME_HARDIRQ);
481
482 fflush(stdout);
483 }
484
485 /**
486 * Create static dimensions
487 *
488 * Create dimensions for static IRQs.
489 */
490 static void hardirq_create_static_dims(void)
491 {
492 uint32_t i;
493 for (i = 0; i < HARDIRQ_EBPF_STATIC_END; i++) {
494 ebpf_write_global_dimension(
495 hardirq_static_vals[i].name, hardirq_static_vals[i].name, ebpf_algorithms[NETDATA_EBPF_INCREMENTAL_IDX]);
496 }
497 }
498
499 /**
500 * Write dimensions
501 *
502 * Traverse JudyL array to write dimensions.
503 *
504 * @return It returns 1 to continue the iteration.
505 */
506 static int hardirq_write_dims(Word_t index, hardirq_val_t *v)
507 {
508 (void)index;
509
510 if (!v->dim_exists) {
511 ebpf_write_global_dimension(v->name, v->name, ebpf_algorithms[NETDATA_EBPF_INCREMENTAL_IDX]);
512 v->dim_exists = true;
513 }
514
515 write_chart_dimension(v->name, v->latency);
516
517 return 1;
518 }
519
520 /**
521 * Write all dimensions
522 *
523 * Traverse JudyL array and call hardirq_write_dims for each entry.
524 */
525 static inline void hardirq_write_all_dims(void)
526 {
527 Word_t index = 0;
528 Pvoid_t *PValue;
529 for (PValue = JudyLFirst(ebpf_hardirq_JudyL, &index, PJE0); PValue != NULL && PValue != PJERR;
530 PValue = JudyLNext(ebpf_hardirq_JudyL, &index, PJE0)) {
531 hardirq_val_t *v = *PValue;
532 if (v)
533 hardirq_write_dims(index, v);
534 }
535 }
536
537 /**
538 * Write static dimensions
539 *
540 * Write dimensions for static IRQs.
541 */
542 static inline void hardirq_write_static_dims(void)
543 {
544 uint32_t i;
545 for (i = 0; i < HARDIRQ_EBPF_STATIC_END; i++) {
546 write_chart_dimension(hardirq_static_vals[i].name, hardirq_static_vals[i].latency);
547 }
548 }
549
550 /**
551 * Main loop for this collector.
552 *
553 * @param em the main thread structure.
554 */
555 static void hardirq_collector(ebpf_module_t *em)
556 {
557 netdata_mutex_lock(&lock);
558 ebpf_create_hardirq_charts(em->update_every);
559 hardirq_create_static_dims();
560 ebpf_update_stats(&plugin_statistics, em);
561 ebpf_update_kernel_memory_with_vector(&plugin_statistics, em->maps, EBPF_ACTION_STAT_ADD);
562 netdata_mutex_unlock(&lock);
563
564 heartbeat_t hb;
565 heartbeat_init(&hb, USEC_PER_SEC);
566 int update_every = em->update_every;
567 int counter = update_every - 1;
568 uint32_t running_time = 0;
569 uint32_t lifetime = em->lifetime;
570 while (!ebpf_plugin_stop() && running_time < lifetime) {
571 if (ebpf_plugin_stop())
572 break;
573
574 heartbeat_next(&hb);
575
576 if (ebpf_plugin_stop())
577 break;
578
579 if (++counter != update_every)
580 continue;
581
582 counter = 0;
583 if (hardirq_reader()) {
584 hardirq_safe_clean = false;
585 break;
586 }
587
588 netdata_mutex_lock(&lock);
589
590 ebpf_write_begin_chart(NETDATA_EBPF_SYSTEM_GROUP, "hardirq_latency", "");
591 //hardirq_write_all_dims();
592 hardirq_write_static_dims();
593 ebpf_write_end_chart();
594
595 netdata_mutex_unlock(&lock);
596
597 if (ebpf_plugin_stop())
598 break;
599
600 netdata_mutex_lock(&ebpf_exit_cleanup);
601 if (running_time)
602 running_time += update_every;
603 else
604 running_time = update_every;
605
606 em->running_time = running_time;
607 netdata_mutex_unlock(&ebpf_exit_cleanup);
608 }
609 }
610
611 /*****************************************************************
612 * EBPF HARDIRQ THREAD
613 *****************************************************************/
614
615 /**
616 * Load BPF
617 *
618 * Load BPF files.
619 *
620 * @param em the structure with configuration
621 *
622 * @return It returns 0 on success and -1 otherwise.
623 */
624 static int ebpf_hardirq_load_bpf(ebpf_module_t *em)
625 {
626 int ret = 0;
627 #ifdef LIBBPF_MAJOR_VERSION
628 ebpf_define_map_type(em->maps, em->maps_per_core, running_on_kernel);
629 #endif
630
631 if (em->load & EBPF_LOAD_LEGACY) {
632 em->probe_links = ebpf_load_program(ebpf_plugin_dir, em, running_on_kernel, isrh, &em->objects);
633 if (!em->probe_links) {
634 ret = -1;
635 }
636 }
637 #ifdef LIBBPF_MAJOR_VERSION
638 else {
639 hardirq_bpf_obj = hardirq_bpf__open();
640 if (!hardirq_bpf_obj)
641 ret = -1;
642 else {
643 ret = ebpf_hardirq_load_and_attach(hardirq_bpf_obj);
644 if (ret) {
645 hardirq_bpf__destroy(hardirq_bpf_obj);
646 hardirq_bpf_obj = NULL;
647 }
648 }
649 }
650 #endif
651
652 if (ret)
653 netdata_log_error("%s %s", EBPF_DEFAULT_ERROR_MSG, em->info.thread_name);
654
655 return ret;
656 }
657
658 /**
659 * Allocate vectors used with this thread.
660 *
661 * We are not testing the return, because callocz does this and shutdown the software
662 * case it was not possible to allocate.
663 */
664 static void ebpf_hardirq_allocate_global_vectors()
665 {
666 memset(hardirq_counter_aggregated_data, 0, NETDATA_HARDIRQ_DIMENSION * sizeof(netdata_syscall_stat_t));
667 memset(hardirq_counter_publish_aggregated, 0, NETDATA_HARDIRQ_DIMENSION * sizeof(netdata_publish_syscall_t));
668 }
669
670 /**
671 * Hard IRQ latency thread.
672 *
673 * @param ptr a `ebpf_module_t *`.
674 * @return always NULL.
675 */
676 void ebpf_hardirq_thread(void *ptr)
677 {
678 return;
679 ebpf_module_t *em = (ebpf_module_t *)ptr;
680
681 CLEANUP_FUNCTION_REGISTER(hardirq_cleanup) cleanup_ptr = em;
682
683 if (!ebpf_module_thread_has_valid_state(em)) {
684 goto endhardirq;
685 }
686
687 em->maps = hardirq_maps;
688
689 if (ebpf_enable_tracepoints(hardirq_tracepoints) == 0) {
690 goto endhardirq;
691 }
692
693 #ifdef LIBBPF_MAJOR_VERSION
694 ebpf_adjust_thread_load(em, default_btf);
695 #endif
696 if (ebpf_hardirq_load_bpf(em)) {
697 goto endhardirq;
698 }
699 ebpf_mark_program_loaded();
700
701 ebpf_hardirq_allocate_global_vectors();
702
703 int algorithms[NETDATA_HARDIRQ_DIMENSION] = {NETDATA_EBPF_INCREMENTAL_IDX};
704
705 ebpf_global_labels(
706 hardirq_counter_aggregated_data,
707 hardirq_counter_publish_aggregated,
708 hardirq_counter_dimension_name,
709 hardirq_counter_dimension_name,
710 algorithms,
711 NETDATA_HARDIRQ_DIMENSION);
712
713 hardirq_safe_clean = true;
714 hardirq_collector(em);
715
716 endhardirq:
717 ebpf_update_disabled_plugin_stats(em);
718 }