master
c 1,715 lines 51.9 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "ebpf.h"
4 #include "ebpf_fd.h"
5 #include "libbpf_api/ebpf_library.h"
6
7 static char *fd_dimension_names[NETDATA_FD_SYSCALL_END] = {"open", "close"};
8 static char *fd_id_names[NETDATA_FD_SYSCALL_END] = {"do_sys_open", "__close_fd"};
9
10 #ifdef LIBBPF_MAJOR_VERSION
11 static char *close_targets[NETDATA_EBPF_MAX_FD_TARGETS] = {"close_fd", "__close_fd"};
12 static char *open_targets[NETDATA_EBPF_MAX_FD_TARGETS] = {"do_sys_openat2", "do_sys_open"};
13 #endif
14
15 static netdata_syscall_stat_t fd_aggregated_data[NETDATA_FD_SYSCALL_END];
16 static netdata_publish_syscall_t fd_publish_aggregated[NETDATA_FD_SYSCALL_END];
17
18 static ebpf_local_maps_t fd_maps[] = {
19 {.name = "tbl_fd_pid",
20 .internal_input = ND_EBPF_DEFAULT_PID_SIZE,
21 .user_input = 0,
22 .type = NETDATA_EBPF_MAP_RESIZABLE | NETDATA_EBPF_MAP_PID,
23 .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
24 #ifdef LIBBPF_MAJOR_VERSION
25 .map_type = BPF_MAP_TYPE_PERCPU_HASH
26 #endif
27 },
28 {.name = "tbl_fd_global",
29 .internal_input = NETDATA_KEY_END_VECTOR,
30 .user_input = 0,
31 .type = NETDATA_EBPF_MAP_STATIC,
32 .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
33 #ifdef LIBBPF_MAJOR_VERSION
34 .map_type = BPF_MAP_TYPE_PERCPU_ARRAY
35 #endif
36 },
37 {.name = "fd_ctrl",
38 .internal_input = NETDATA_CONTROLLER_END,
39 .user_input = 0,
40 .type = NETDATA_EBPF_MAP_CONTROLLER,
41 .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
42 #ifdef LIBBPF_MAJOR_VERSION
43 .map_type = BPF_MAP_TYPE_PERCPU_ARRAY
44 #endif
45 },
46 {.name = NULL,
47 .internal_input = 0,
48 .user_input = 0,
49 .type = NETDATA_EBPF_MAP_CONTROLLER,
50 .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
51 #ifdef LIBBPF_MAJOR_VERSION
52 .map_type = BPF_MAP_TYPE_PERCPU_ARRAY
53 #endif
54 }};
55
56 struct config fd_config = APPCONFIG_INITIALIZER;
57
58 static netdata_idx_t fd_hash_values[NETDATA_FD_COUNTER];
59 static netdata_idx_t *fd_values = NULL;
60
61 netdata_fd_stat_t *fd_vector = NULL;
62 static bool fd_safe_clean = false;
63
64 static int fd_use_close_fd = -1;
65
66 netdata_ebpf_targets_t fd_targets[] = {
67 {.name = "open", .mode = EBPF_LOAD_TRAMPOLINE},
68 {.name = "close", .mode = EBPF_LOAD_TRAMPOLINE},
69 {.name = NULL, .mode = EBPF_LOAD_TRAMPOLINE}};
70
71 struct netdata_static_thread ebpf_read_fd = {
72 .name = "EBPF_READ_FD",
73 .config_section = NULL,
74 .config_name = NULL,
75 .env_name = NULL,
76 .enabled = 1,
77 .thread = NULL,
78 .init_routine = NULL,
79 .start_routine = NULL};
80
81 #ifdef LIBBPF_MAJOR_VERSION
82 /**
83 * Check if using close_fd
84 *
85 * @return true if using close_fd, false if using __close_fd
86 */
87 static inline int ebpf_fd_using_close_fd(void)
88 {
89 if (fd_use_close_fd == -1)
90 fd_use_close_fd = !strcmp(fd_targets[NETDATA_FD_SYSCALL_CLOSE].name, close_targets[NETDATA_FD_CLOSE_FD]);
91 return fd_use_close_fd;
92 }
93
94 /**
95 * Disable probe
96 *
97 * Disable all probes to use exclusively another method.
98 *
99 * @param obj is the main structure for bpf objects
100 */
101 static inline void ebpf_fd_disable_probes(struct fd_bpf *obj)
102 {
103 bpf_program__set_autoload(obj->progs.netdata_sys_open_kprobe, false);
104 bpf_program__set_autoload(obj->progs.netdata_sys_open_kretprobe, false);
105 if (ebpf_fd_using_close_fd()) {
106 bpf_program__set_autoload(obj->progs.netdata___close_fd_kretprobe, false);
107 bpf_program__set_autoload(obj->progs.netdata___close_fd_kprobe, false);
108 bpf_program__set_autoload(obj->progs.netdata_close_fd_kprobe, false);
109 } else {
110 bpf_program__set_autoload(obj->progs.netdata___close_fd_kprobe, false);
111 bpf_program__set_autoload(obj->progs.netdata_close_fd_kretprobe, false);
112 bpf_program__set_autoload(obj->progs.netdata_close_fd_kprobe, false);
113 }
114 }
115
116 /*
117 * Disable specific probe
118 *
119 * Disable probes according the kernel version
120 *
121 * @param obj is the main structure for bpf objects
122 */
123 static inline void ebpf_disable_specific_probes(struct fd_bpf *obj)
124 {
125 if (ebpf_fd_using_close_fd()) {
126 bpf_program__set_autoload(obj->progs.netdata___close_fd_kretprobe, false);
127 bpf_program__set_autoload(obj->progs.netdata___close_fd_kprobe, false);
128 } else {
129 bpf_program__set_autoload(obj->progs.netdata_close_fd_kretprobe, false);
130 bpf_program__set_autoload(obj->progs.netdata_close_fd_kprobe, false);
131 }
132 }
133
134 /*
135 * Disable trampoline
136 *
137 * Disable all trampoline to use exclusively another method.
138 *
139 * @param obj is the main structure for bpf objects.
140 */
141 static inline void ebpf_disable_trampoline(struct fd_bpf *obj)
142 {
143 bpf_program__set_autoload(obj->progs.netdata_sys_open_fentry, false);
144 bpf_program__set_autoload(obj->progs.netdata_sys_open_fexit, false);
145 bpf_program__set_autoload(obj->progs.netdata_close_fd_fentry, false);
146 bpf_program__set_autoload(obj->progs.netdata_close_fd_fexit, false);
147 bpf_program__set_autoload(obj->progs.netdata___close_fd_fentry, false);
148 bpf_program__set_autoload(obj->progs.netdata___close_fd_fexit, false);
149 }
150
151 /*
152 * Disable specific trampoline
153 *
154 * Disable trampoline according to kernel version.
155 *
156 * @param obj is the main structure for bpf objects.
157 */
158 static inline void ebpf_disable_specific_trampoline(struct fd_bpf *obj)
159 {
160 if (ebpf_fd_using_close_fd()) {
161 bpf_program__set_autoload(obj->progs.netdata___close_fd_fentry, false);
162 bpf_program__set_autoload(obj->progs.netdata___close_fd_fexit, false);
163 } else {
164 bpf_program__set_autoload(obj->progs.netdata_close_fd_fentry, false);
165 bpf_program__set_autoload(obj->progs.netdata_close_fd_fexit, false);
166 }
167 }
168
169 /**
170 * Set trampoline target
171 *
172 * Set the targets we will monitor.
173 *
174 * @param obj is the main structure for bpf objects.
175 */
176 static void ebpf_set_trampoline_target(struct fd_bpf *obj)
177 {
178 bpf_program__set_attach_target(obj->progs.netdata_sys_open_fentry, 0, fd_targets[NETDATA_FD_SYSCALL_OPEN].name);
179 bpf_program__set_attach_target(obj->progs.netdata_sys_open_fexit, 0, fd_targets[NETDATA_FD_SYSCALL_OPEN].name);
180
181 if (ebpf_fd_using_close_fd()) {
182 bpf_program__set_attach_target(
183 obj->progs.netdata_close_fd_fentry, 0, fd_targets[NETDATA_FD_SYSCALL_CLOSE].name);
184 bpf_program__set_attach_target(obj->progs.netdata_close_fd_fexit, 0, fd_targets[NETDATA_FD_SYSCALL_CLOSE].name);
185 } else {
186 bpf_program__set_attach_target(
187 obj->progs.netdata___close_fd_fentry, 0, fd_targets[NETDATA_FD_SYSCALL_CLOSE].name);
188 bpf_program__set_attach_target(
189 obj->progs.netdata___close_fd_fexit, 0, fd_targets[NETDATA_FD_SYSCALL_CLOSE].name);
190 }
191 }
192
193 /**
194 * Mount Attach Probe
195 *
196 * Attach probes to target
197 *
198 * @param obj is the main structure for bpf objects.
199 *
200 * @return It returns 0 on success and -1 otherwise.
201 */
202 static int ebpf_fd_attach_probe(struct fd_bpf *obj)
203 {
204 obj->links.netdata_sys_open_kprobe =
205 bpf_program__attach_kprobe(obj->progs.netdata_sys_open_kprobe, false, fd_targets[NETDATA_FD_SYSCALL_OPEN].name);
206 long ret = libbpf_get_error(obj->links.netdata_sys_open_kprobe);
207 if (ret)
208 return -1;
209
210 obj->links.netdata_sys_open_kretprobe = bpf_program__attach_kprobe(
211 obj->progs.netdata_sys_open_kretprobe, true, fd_targets[NETDATA_FD_SYSCALL_OPEN].name);
212 ret = libbpf_get_error(obj->links.netdata_sys_open_kretprobe);
213 if (ret)
214 return -1;
215
216 if (ebpf_fd_using_close_fd()) {
217 obj->links.netdata_close_fd_kretprobe = bpf_program__attach_kprobe(
218 obj->progs.netdata_close_fd_kretprobe, true, fd_targets[NETDATA_FD_SYSCALL_CLOSE].name);
219 ret = libbpf_get_error(obj->links.netdata_close_fd_kretprobe);
220 if (ret)
221 return -1;
222
223 obj->links.netdata_close_fd_kprobe = bpf_program__attach_kprobe(
224 obj->progs.netdata_close_fd_kprobe, false, fd_targets[NETDATA_FD_SYSCALL_CLOSE].name);
225 ret = libbpf_get_error(obj->links.netdata_close_fd_kprobe);
226 if (ret)
227 return -1;
228 } else {
229 obj->links.netdata___close_fd_kretprobe = bpf_program__attach_kprobe(
230 obj->progs.netdata___close_fd_kretprobe, true, fd_targets[NETDATA_FD_SYSCALL_CLOSE].name);
231 ret = libbpf_get_error(obj->links.netdata___close_fd_kretprobe);
232 if (ret)
233 return -1;
234
235 obj->links.netdata___close_fd_kprobe = bpf_program__attach_kprobe(
236 obj->progs.netdata___close_fd_kprobe, false, fd_targets[NETDATA_FD_SYSCALL_CLOSE].name);
237 ret = libbpf_get_error(obj->links.netdata___close_fd_kprobe);
238 if (ret)
239 return -1;
240 }
241
242 return 0;
243 }
244
245 /**
246 * FD Fill Address
247 *
248 * Fill address value used to load probes/trampoline.
249 */
250 static inline void ebpf_fd_fill_address(ebpf_addresses_t *address, char **targets)
251 {
252 int i;
253 for (i = 0; i < NETDATA_EBPF_MAX_FD_TARGETS; i++) {
254 address->function = targets[i];
255 ebpf_load_addresses(address, -1);
256 if (address->addr)
257 break;
258 }
259 }
260
261 /**
262 * Set target values
263 *
264 * Set pointers used to load data.
265 *
266 * @return It returns 0 on success and -1 otherwise.
267 */
268 static int ebpf_fd_set_target_values(void)
269 {
270 ebpf_addresses_t address = {.function = NULL, .hash = 0, .addr = 0};
271 ebpf_fd_fill_address(&address, close_targets);
272
273 if (!address.addr)
274 return -1;
275
276 fd_targets[NETDATA_FD_SYSCALL_CLOSE].name = address.function;
277
278 address.addr = 0;
279 ebpf_fd_fill_address(&address, open_targets);
280
281 if (!address.addr)
282 return -1;
283
284 fd_targets[NETDATA_FD_SYSCALL_OPEN].name = address.function;
285
286 return 0;
287 }
288
289 /**
290 * Set hash tables
291 *
292 * Set the values for maps according the value given by kernel.
293 *
294 * @param obj is the main structure for bpf objects.
295 */
296 static int ebpf_fd_set_hash_tables(struct fd_bpf *obj)
297 {
298 int map_fd;
299
300 map_fd = bpf_map__fd(obj->maps.tbl_fd_global);
301 if (map_fd < 0) {
302 netdata_log_error("Failed to get fd for tbl_fd_global map");
303 return -1;
304 }
305 fd_maps[NETDATA_FD_GLOBAL_STATS].map_fd = map_fd;
306
307 map_fd = bpf_map__fd(obj->maps.tbl_fd_pid);
308 if (map_fd < 0) {
309 netdata_log_error("Failed to get fd for tbl_fd_pid map");
310 return -1;
311 }
312 fd_maps[NETDATA_FD_PID_STATS].map_fd = map_fd;
313
314 map_fd = bpf_map__fd(obj->maps.fd_ctrl);
315 if (map_fd < 0) {
316 netdata_log_error("Failed to get fd for fd_ctrl map");
317 return -1;
318 }
319 fd_maps[NETDATA_FD_CONTROLLER].map_fd = map_fd;
320
321 return 0;
322 }
323
324 /**
325 * Adjust Map Size
326 *
327 * Resize maps according input from users.
328 *
329 * @param obj is the main structure for bpf objects.
330 * @param em structure with configuration
331 */
332 static void ebpf_fd_adjust_map(struct fd_bpf *obj, ebpf_module_t *em)
333 {
334 ebpf_update_map_size(obj->maps.tbl_fd_pid, &fd_maps[NETDATA_FD_PID_STATS], em, bpf_map__name(obj->maps.tbl_fd_pid));
335
336 ebpf_update_map_type(obj->maps.tbl_fd_global, &fd_maps[NETDATA_FD_GLOBAL_STATS]);
337 ebpf_update_map_type(obj->maps.tbl_fd_pid, &fd_maps[NETDATA_FD_PID_STATS]);
338 ebpf_update_map_type(obj->maps.fd_ctrl, &fd_maps[NETDATA_FD_CONTROLLER]);
339 }
340
341 /**
342 * Load and attach
343 *
344 * Load and attach the eBPF code in kernel.
345 *
346 * @param obj is the main structure for bpf objects.
347 * @param em structure with configuration
348 *
349 * @return it returns 0 on success and -1 otherwise
350 */
351 static inline int ebpf_fd_load_and_attach(struct fd_bpf *obj, ebpf_module_t *em)
352 {
353 netdata_ebpf_targets_t *mt = em->targets;
354 netdata_ebpf_program_loaded_t test = mt[NETDATA_FD_SYSCALL_OPEN].mode;
355
356 if (ebpf_fd_set_target_values()) {
357 netdata_log_error("%s file descriptor.", NETDATA_EBPF_DEFAULT_FNT_NOT_FOUND);
358 return -1;
359 }
360
361 if (test == EBPF_LOAD_TRAMPOLINE) {
362 ebpf_fd_disable_probes(obj);
363 ebpf_disable_specific_trampoline(obj);
364
365 ebpf_set_trampoline_target(obj);
366 } else {
367 ebpf_disable_trampoline(obj);
368 ebpf_disable_specific_probes(obj);
369 }
370
371 ebpf_fd_adjust_map(obj, em);
372
373 int ret = fd_bpf__load(obj);
374 if (ret) {
375 return ret;
376 }
377
378 ret = (test == EBPF_LOAD_TRAMPOLINE) ? fd_bpf__attach(obj) : ebpf_fd_attach_probe(obj);
379 if (!ret) {
380 ret = ebpf_fd_set_hash_tables(obj);
381
382 if (!ret) {
383 ebpf_update_controller(fd_maps[NETDATA_FD_CONTROLLER].map_fd, em);
384 }
385 }
386
387 return ret;
388 }
389 #endif
390
391 /*****************************************************************
392 *
393 * FUNCTIONS TO CLOSE THE THREAD
394 *
395 *****************************************************************/
396
397 static void ebpf_obsolete_specific_fd_charts(char *type, ebpf_module_t *em);
398
399 /**
400 * Obsolete services
401 *
402 * Obsolete all service charts created
403 *
404 * @param em a pointer to `struct ebpf_module`
405 */
406 static void ebpf_obsolete_fd_services(ebpf_module_t *em, char *id)
407 {
408 ebpf_write_chart_obsolete(
409 id,
410 NETDATA_SYSCALL_APPS_FILE_OPEN,
411 "",
412 "Number of open files",
413 EBPF_COMMON_UNITS_CALLS_PER_SEC,
414 NETDATA_APPS_FILE_GROUP,
415 NETDATA_EBPF_CHART_TYPE_STACKED,
416 NETDATA_CGROUP_FD_OPEN_CONTEXT,
417 NETDATA_EBPF_FD_CHART_PRIORITY_OPEN,
418 em->update_every);
419
420 if (em->mode < MODE_ENTRY) {
421 ebpf_write_chart_obsolete(
422 id,
423 NETDATA_SYSCALL_APPS_FILE_OPEN_ERROR,
424 "",
425 "Fails to open files",
426 EBPF_COMMON_UNITS_CALLS_PER_SEC,
427 NETDATA_APPS_FILE_GROUP,
428 NETDATA_EBPF_CHART_TYPE_STACKED,
429 NETDATA_CGROUP_FD_OPEN_ERR_CONTEXT,
430 NETDATA_EBPF_FD_CHART_PRIORITY_OPEN_ERR,
431 em->update_every);
432 }
433
434 ebpf_write_chart_obsolete(
435 id,
436 NETDATA_SYSCALL_APPS_FILE_CLOSED,
437 "",
438 "Files closed",
439 EBPF_COMMON_UNITS_CALLS_PER_SEC,
440 NETDATA_APPS_FILE_GROUP,
441 NETDATA_EBPF_CHART_TYPE_STACKED,
442 NETDATA_CGROUP_FD_CLOSE_CONTEXT,
443 NETDATA_EBPF_FD_CHART_PRIORITY_CLOSE,
444 em->update_every);
445
446 if (em->mode < MODE_ENTRY) {
447 ebpf_write_chart_obsolete(
448 id,
449 NETDATA_SYSCALL_APPS_FILE_CLOSE_ERROR,
450 "",
451 "Fails to close files",
452 EBPF_COMMON_UNITS_CALLS_PER_SEC,
453 NETDATA_APPS_FILE_GROUP,
454 NETDATA_EBPF_CHART_TYPE_STACKED,
455 NETDATA_CGROUP_FD_CLOSE_ERR_CONTEXT,
456 NETDATA_EBPF_FD_CHART_PRIORITY_CLOSE_ERR,
457 em->update_every);
458 }
459 }
460
461 /**
462 * Obsolete cgroup chart
463 *
464 * Send obsolete for all charts created before to close.
465 *
466 * @param em a pointer to `struct ebpf_module`
467 */
468 static inline void ebpf_obsolete_fd_cgroup_charts(ebpf_module_t *em)
469 {
470 netdata_mutex_lock(&mutex_cgroup_shm);
471
472 ebpf_cgroup_target_t *ect;
473 for (ect = ebpf_cgroup_pids; ect; ect = ect->next) {
474 if (ect->systemd) {
475 ebpf_obsolete_fd_services(em, ect->name);
476
477 continue;
478 }
479
480 ebpf_obsolete_specific_fd_charts(ect->name, em);
481 }
482 netdata_mutex_unlock(&mutex_cgroup_shm);
483 }
484
485 /**
486 * Obsolette apps charts
487 *
488 * Obsolete apps charts.
489 *
490 * @param em a pointer to the structure with the default values.
491 */
492 void ebpf_obsolete_fd_apps_charts(struct ebpf_module *em)
493 {
494 struct ebpf_target *w;
495 int update_every = em->update_every;
496 netdata_mutex_lock(&collect_data_mutex);
497 for (w = apps_groups_root_target; w; w = w->next) {
498 if (unlikely(!(w->charts_created & (1 << EBPF_MODULE_FD_IDX))))
499 continue;
500
501 ebpf_write_chart_obsolete(
502 NETDATA_APP_FAMILY,
503 w->clean_name,
504 "_ebpf_file_open",
505 "Number of open files",
506 EBPF_COMMON_UNITS_CALLS_PER_SEC,
507 NETDATA_APPS_FILE_FDS,
508 NETDATA_EBPF_CHART_TYPE_STACKED,
509 "app.ebpf_file_open",
510 NETDATA_EBPF_FD_CHART_PRIORITY_APPS_OPEN,
511 update_every);
512
513 if (em->mode < MODE_ENTRY) {
514 ebpf_write_chart_obsolete(
515 NETDATA_APP_FAMILY,
516 w->clean_name,
517 "_ebpf_file_open_error",
518 "Fails to open files.",
519 EBPF_COMMON_UNITS_CALLS_PER_SEC,
520 NETDATA_APPS_FILE_FDS,
521 NETDATA_EBPF_CHART_TYPE_STACKED,
522 "app.ebpf_file_open_error",
523 NETDATA_EBPF_FD_CHART_PRIORITY_APPS_OPEN_ERR,
524 update_every);
525 }
526
527 ebpf_write_chart_obsolete(
528 NETDATA_APPS_FAMILY,
529 w->clean_name,
530 "_ebpf_file_closed",
531 "Files closed.",
532 EBPF_COMMON_UNITS_CALLS_PER_SEC,
533 NETDATA_APPS_FILE_FDS,
534 NETDATA_EBPF_CHART_TYPE_STACKED,
535 "app.ebpf_file_closed",
536 NETDATA_EBPF_FD_CHART_PRIORITY_APPS_CLOSE,
537 update_every);
538
539 if (em->mode < MODE_ENTRY) {
540 ebpf_write_chart_obsolete(
541 NETDATA_APPS_FAMILY,
542 w->clean_name,
543 "_ebpf_file_close_error",
544 "Fails to close files.",
545 EBPF_COMMON_UNITS_CALLS_PER_SEC,
546 NETDATA_APPS_FILE_FDS,
547 NETDATA_EBPF_CHART_TYPE_STACKED,
548 "app.ebpf_fd_close_error",
549 NETDATA_EBPF_FD_CHART_PRIORITY_APPS_CLOSE_ERR,
550 update_every);
551 }
552 w->charts_created &= ~(1 << EBPF_MODULE_FD_IDX);
553 }
554 netdata_mutex_unlock(&collect_data_mutex);
555 }
556
557 /**
558 * Obsolete global
559 *
560 * Obsolete global charts created by thread.
561 *
562 * @param em a pointer to `struct ebpf_module`
563 */
564 static void ebpf_obsolete_fd_global(ebpf_module_t *em)
565 {
566 ebpf_write_chart_obsolete(
567 NETDATA_FILESYSTEM_FAMILY,
568 NETDATA_FILE_OPEN_CLOSE_COUNT,
569 "",
570 "Open and close calls",
571 EBPF_COMMON_UNITS_CALLS_PER_SEC,
572 NETDATA_FILE_GROUP,
573 NETDATA_EBPF_CHART_TYPE_LINE,
574 NETDATA_FS_FILEDESCRIPTOR_CONTEXT,
575 NETDATA_CHART_PRIO_EBPF_FD_CHARTS,
576 em->update_every);
577
578 if (em->mode < MODE_ENTRY) {
579 ebpf_write_chart_obsolete(
580 NETDATA_FILESYSTEM_FAMILY,
581 NETDATA_FILE_OPEN_ERR_COUNT,
582 "",
583 "Open fails",
584 EBPF_COMMON_UNITS_CALLS_PER_SEC,
585 NETDATA_FILE_GROUP,
586 NETDATA_EBPF_CHART_TYPE_LINE,
587 NETDATA_FS_FILEDESCRIPTOR_ERROR_CONTEXT,
588 NETDATA_CHART_PRIO_EBPF_FD_CHARTS + 1,
589 em->update_every);
590 }
591 }
592
593 /**
594 * FD Exit
595 *
596 * Cancel child thread and exit.
597 *
598 * @param ptr thread data.
599 */
600 void ebpf_fd_unload_bpf(ebpf_module_t *em)
601 {
602 #ifdef LIBBPF_MAJOR_VERSION
603 if (fd_bpf_obj) {
604 fd_bpf__destroy(fd_bpf_obj);
605 fd_bpf_obj = NULL;
606 }
607 #endif
608 if ((em->load & EBPF_LOAD_LEGACY) && em->probe_links) {
609 ebpf_unload_legacy_code(em->objects, em->probe_links);
610 em->objects = NULL;
611 em->probe_links = NULL;
612 }
613 }
614
615 static void ebpf_fd_exit(void *pptr)
616 {
617 ebpf_set_pid_map_fd(NETDATA_EBPF_PIDS_FD_IDX, -1);
618 ebpf_module_t *em = CLEANUP_FUNCTION_GET_PTR(pptr);
619 if (!em)
620 return;
621
622 if (!fd_safe_clean) {
623 netdata_mutex_lock(&ebpf_exit_cleanup);
624 ebpf_module_enabled_set(em, NETDATA_THREAD_EBPF_STOPPED);
625 netdata_mutex_unlock(&ebpf_exit_cleanup);
626 return;
627 }
628
629 netdata_mutex_lock(&lock);
630 collect_pids &= ~(1 << EBPF_MODULE_FD_IDX);
631 netdata_mutex_unlock(&lock);
632
633 if (ebpf_read_fd.thread) {
634 nd_thread_signal_cancel(ebpf_read_fd.thread);
635 nd_thread_join(ebpf_read_fd.thread);
636 }
637
638 // Drop this module's bits from the shared PID pool so its slots don't
639 // stay pinned if the plugin keeps running after the module stops.
640 if (integration_shm && ebpf_shm_sem_wait_or_stop(shm_mutex_ebpf_integration)) {
641 netdata_ebpf_sweep_shm_for_module_unsafe(NETDATA_EBPF_PIDS_FD_IDX);
642 sem_post(shm_mutex_ebpf_integration);
643 }
644
645 if (ebpf_module_enabled_get(em) == NETDATA_THREAD_EBPF_FUNCTION_RUNNING && !ebpf_plugin_stop()) {
646 netdata_mutex_lock(&lock);
647 if (em->cgroup_charts) {
648 ebpf_obsolete_fd_cgroup_charts(em);
649 fflush(stdout);
650 }
651
652 if (em->apps_charts & NETDATA_EBPF_APPS_FLAG_CHART_CREATED) {
653 ebpf_obsolete_fd_apps_charts(em);
654 }
655
656 ebpf_obsolete_fd_global(em);
657
658 fflush(stdout);
659 netdata_mutex_unlock(&lock);
660 }
661
662 freez(fd_vector);
663 fd_vector = NULL;
664 freez(fd_values);
665 fd_values = NULL;
666
667 if (!ebpf_plugin_stop() && em->functions.bpf_unload)
668 em->functions.bpf_unload(em);
669
670 netdata_mutex_lock(&ebpf_exit_cleanup);
671 ebpf_module_enabled_set(em, NETDATA_THREAD_EBPF_STOPPED);
672 netdata_mutex_unlock(&ebpf_exit_cleanup);
673 }
674
675 /*****************************************************************
676 *
677 * MAIN LOOP
678 *
679 *****************************************************************/
680
681 /**
682 * Send data to Netdata calling auxiliary functions.
683 *
684 * @param em the structure with thread information
685 */
686 static void ebpf_fd_send_data(ebpf_module_t *em)
687 {
688 fd_publish_aggregated[NETDATA_FD_SYSCALL_OPEN].ncall = fd_hash_values[NETDATA_KEY_CALLS_DO_SYS_OPEN];
689 fd_publish_aggregated[NETDATA_FD_SYSCALL_OPEN].nerr = fd_hash_values[NETDATA_KEY_ERROR_DO_SYS_OPEN];
690
691 fd_publish_aggregated[NETDATA_FD_SYSCALL_CLOSE].ncall = fd_hash_values[NETDATA_KEY_CALLS_CLOSE_FD];
692 fd_publish_aggregated[NETDATA_FD_SYSCALL_CLOSE].nerr = fd_hash_values[NETDATA_KEY_ERROR_CLOSE_FD];
693
694 write_count_chart(
695 NETDATA_FILE_OPEN_CLOSE_COUNT, NETDATA_FILESYSTEM_FAMILY, fd_publish_aggregated, NETDATA_FD_SYSCALL_END);
696
697 if (em->mode < MODE_ENTRY) {
698 write_err_chart(
699 NETDATA_FILE_OPEN_ERR_COUNT, NETDATA_FILESYSTEM_FAMILY, fd_publish_aggregated, NETDATA_FD_SYSCALL_END);
700 }
701 }
702
703 /**
704 * Read global counter
705 *
706 * Read the table with number of calls for all functions
707 *
708 * @param stats vector used to read data from control table.
709 * @param maps_per_core do I need to read all cores?
710 */
711 static void ebpf_fd_read_global_tables(netdata_idx_t *stats, int maps_per_core)
712 {
713 ebpf_read_global_table_stats(
714 fd_hash_values,
715 fd_values,
716 fd_maps[NETDATA_FD_GLOBAL_STATS].map_fd,
717 maps_per_core,
718 NETDATA_KEY_CALLS_DO_SYS_OPEN,
719 NETDATA_FD_COUNTER);
720
721 ebpf_read_global_table_stats(
722 stats,
723 fd_values,
724 fd_maps[NETDATA_FD_CONTROLLER].map_fd,
725 maps_per_core,
726 NETDATA_CONTROLLER_PID_TABLE_ADD,
727 NETDATA_CONTROLLER_END);
728 }
729
730 /**
731 * Apps Accumulator
732 *
733 * Sum all values read from kernel and store in the first address.
734 *
735 * @param out the vector with read values.
736 * @param maps_per_core do I need to read all cores?
737 */
738 static void fd_apps_accumulator(netdata_fd_stat_t *out, int maps_per_core)
739 {
740 int i, end = (maps_per_core) ? ebpf_nprocs : 1;
741 netdata_fd_stat_t *total = &out[0];
742 uint64_t ct = total->ct;
743 for (i = 1; i < end; i++) {
744 if (ebpf_plugin_stop())
745 break;
746
747 netdata_fd_stat_t *w = &out[i];
748 total->open_call += w->open_call;
749 total->close_call += w->close_call;
750 total->open_err += w->open_err;
751 total->close_err += w->close_err;
752
753 if (w->ct > ct)
754 ct = w->ct;
755
756 if (!total->name[0] && w->name[0])
757 strncpyz(total->name, w->name, sizeof(total->name) - 1);
758 }
759 }
760
761 /**
762 * Read APPS table
763 *
764 * Read the apps table and store data inside the structure.
765 *
766 * @param maps_per_core do I need to read all cores?
767 */
768 static void ebpf_read_fd_apps_table(int maps_per_core)
769 {
770 netdata_fd_stat_t *fv = fd_vector;
771 int fd = fd_maps[NETDATA_FD_PID_STATS].map_fd;
772 size_t length = sizeof(netdata_fd_stat_t);
773 if (maps_per_core)
774 length *= ebpf_nprocs;
775
776 uint32_t key = 0, next_key = 0;
777 while (bpf_map_get_next_key(fd, &key, &next_key) == 0) {
778 if (ebpf_plugin_stop())
779 break;
780
781 key = next_key;
782
783 if (bpf_map_lookup_elem(fd, &key, fv)) {
784 netdata_log_error("Failed to lookup PID %u in FD map", key);
785 goto end_fd_loop;
786 }
787
788 fd_apps_accumulator(fv, maps_per_core);
789
790 netdata_ebpf_pid_stats_t *local_pid = netdata_ebpf_get_shm_pointer_unsafe(key, NETDATA_EBPF_PIDS_FD_IDX);
791 if (!local_pid)
792 goto end_fd_loop;
793 netdata_publish_fd_stat_t *publish_fd = &local_pid->fd;
794
795 if (kill((pid_t)key, 0) == -1 && errno == ESRCH) {
796 if (netdata_ebpf_reset_shm_pointer_unsafe(fd, key, NETDATA_EBPF_PIDS_FD_IDX))
797 memset(publish_fd, 0, sizeof(*publish_fd));
798 } else if (!publish_fd->ct || publish_fd->ct != fv->ct) {
799 publish_fd->ct = fv->ct;
800 publish_fd->open_call = fv->open_call;
801 publish_fd->close_call = fv->close_call;
802 publish_fd->open_err = fv->open_err;
803 publish_fd->close_err = fv->close_err;
804 }
805
806 end_fd_loop:
807 // We are cleaning to avoid passing data read from one process to other.
808 memset(fv, 0, length);
809 }
810 }
811
812 /**
813 * Sum PIDs
814 *
815 * Sum values for all targets.
816 *
817 * @param fd the output
818 * @param root list of pids
819 */
820 static void ebpf_fd_sum_pids(netdata_fd_stat_t *fd, struct ebpf_pid_on_target *root)
821 {
822 memset(fd, 0, sizeof(netdata_fd_stat_t));
823
824 for (; root; root = root->next) {
825 uint32_t pid = root->pid;
826 netdata_ebpf_pid_stats_t *pid_stat = netdata_ebpf_lookup_shm_pointer_unsafe(pid);
827 if (!pid_stat || !(pid_stat->threads & (1U << (NETDATA_EBPF_PIDS_FD_IDX << 1))))
828 continue;
829 netdata_publish_fd_stat_t *w = &pid_stat->fd;
830
831 fd->open_call += w->open_call;
832 fd->close_call += w->close_call;
833 fd->open_err += w->open_err;
834 fd->close_err += w->close_err;
835 }
836 }
837
838 /**
839 * Resume apps data
840 */
841 void ebpf_fd_resume_apps_data(void)
842 {
843 struct ebpf_target *w;
844
845 netdata_mutex_lock(&collect_data_mutex);
846 for (w = apps_groups_root_target; w; w = w->next) {
847 if (ebpf_plugin_stop())
848 break;
849
850 if (unlikely(!(w->charts_created & (1 << EBPF_MODULE_FD_IDX))))
851 continue;
852
853 ebpf_fd_sum_pids(&w->fd, w->root_pid);
854 }
855 netdata_mutex_unlock(&collect_data_mutex);
856 }
857
858 /**
859 * Update cgroup
860 *
861 * Update cgroup data collected per PID.
862 *
863 * @param maps_per_core do I need to read all cores?
864 */
865 static void ebpf_update_fd_cgroup(void)
866 {
867 ebpf_cgroup_target_t *ect;
868
869 netdata_mutex_lock(&mutex_cgroup_shm);
870 for (ect = ebpf_cgroup_pids; ect; ect = ect->next) {
871 if (ebpf_plugin_stop())
872 break;
873
874 struct pid_on_target2 *pids;
875 for (pids = ect->pids; pids; pids = pids->next) {
876 uint32_t pid = pids->pid;
877 netdata_publish_fd_stat_t *out = &pids->fd;
878
879 netdata_ebpf_pid_stats_t *pid_stat = netdata_ebpf_lookup_shm_pointer_unsafe(pid);
880 if (!pid_stat || !(pid_stat->threads & (1U << (NETDATA_EBPF_PIDS_FD_IDX << 1))))
881 continue;
882
883 netdata_publish_fd_stat_t *in = &pid_stat->fd;
884
885 memcpy(out, in, sizeof(netdata_publish_fd_stat_t));
886 }
887 }
888 netdata_mutex_unlock(&mutex_cgroup_shm);
889 }
890
891 /**
892 * FD thread
893 *
894 * Thread used to generate fd charts.
895 *
896 * @param ptr a pointer to `struct ebpf_module`
897 *
898 * @return It always return NULL
899 */
900 void ebpf_read_fd_thread(void *ptr)
901 {
902 ebpf_module_t *em = (ebpf_module_t *)ptr;
903
904 int maps_per_core = em->maps_per_core;
905 int update_every = em->update_every;
906 int collect_pid = (em->apps_charts || em->cgroup_charts);
907 if (!collect_pid)
908 return;
909
910 int counter = update_every - 1;
911
912 uint32_t lifetime = em->lifetime;
913 int cgroups = em->cgroup_charts;
914 uint32_t running_time = 0;
915 ebpf_set_pid_map_fd(NETDATA_EBPF_PIDS_FD_IDX, fd_maps[NETDATA_FD_PID_STATS].map_fd);
916
917 heartbeat_t hb;
918 heartbeat_init(&hb, USEC_PER_SEC);
919 while (!ebpf_plugin_stop() && running_time < lifetime) {
920 if (ebpf_plugin_stop())
921 break;
922
923 heartbeat_next(&hb);
924 if (ebpf_plugin_stop())
925 break;
926
927 if (++counter != update_every)
928 continue;
929
930 if (!ebpf_shm_sem_wait_or_stop(shm_mutex_ebpf_integration)) {
931 if (errno != ECANCELED)
932 netdata_log_error("FD: Failed to wait on semaphore.");
933 break;
934 }
935 ebpf_read_fd_apps_table(maps_per_core);
936 ebpf_fd_resume_apps_data();
937 if (ebpf_plugin_stop()) {
938 if (sem_post(shm_mutex_ebpf_integration))
939 netdata_log_error("FD: Failed to post semaphore.");
940 break;
941 }
942
943 if (cgroups && ebpf_cgroup_integration_active_get())
944 ebpf_update_fd_cgroup();
945
946 if (sem_post(shm_mutex_ebpf_integration)) {
947 netdata_log_error("FD: Failed to post semaphore.");
948 break;
949 }
950
951 counter = 0;
952
953 if (ebpf_plugin_stop())
954 break;
955
956 netdata_mutex_lock(&ebpf_exit_cleanup);
957 if (running_time && !em->running_time)
958 running_time = update_every;
959 else
960 running_time += update_every;
961
962 em->running_time = running_time;
963 netdata_mutex_unlock(&ebpf_exit_cleanup);
964 }
965 }
966
967 /**
968 * Send data to Netdata calling auxiliary functions.
969 *
970 * @param em the structure with thread information
971 * @param root the target list.
972 */
973 void ebpf_fd_send_apps_data(ebpf_module_t *em, struct ebpf_target *root)
974 {
975 struct ebpf_target *w;
976 netdata_mutex_lock(&collect_data_mutex);
977 for (w = root; w; w = w->next) {
978 if (ebpf_plugin_stop())
979 break;
980
981 if (unlikely(!(w->charts_created & (1 << EBPF_MODULE_FD_IDX))))
982 continue;
983
984 ebpf_write_begin_chart(NETDATA_APP_FAMILY, w->clean_name, "_ebpf_file_open");
985 write_chart_dimension("calls", w->fd.open_call);
986 ebpf_write_end_chart();
987
988 if (em->mode < MODE_ENTRY) {
989 ebpf_write_begin_chart(NETDATA_APP_FAMILY, w->clean_name, "_ebpf_file_open_error");
990 write_chart_dimension("calls", w->fd.open_err);
991 ebpf_write_end_chart();
992 }
993
994 ebpf_write_begin_chart(NETDATA_APP_FAMILY, w->clean_name, "_ebpf_file_closed");
995 write_chart_dimension("calls", w->fd.close_call);
996 ebpf_write_end_chart();
997
998 if (em->mode < MODE_ENTRY) {
999 ebpf_write_begin_chart(NETDATA_APP_FAMILY, w->clean_name, "_ebpf_file_close_error");
1000 write_chart_dimension("calls", w->fd.close_err);
1001 ebpf_write_end_chart();
1002 }
1003 }
1004 netdata_mutex_unlock(&collect_data_mutex);
1005 }
1006
1007 /**
1008 * Sum PIDs
1009 *
1010 * Sum values for all targets.
1011 *
1012 * @param fd structure used to store data
1013 * @param pids input data
1014 */
1015 static void ebpf_fd_sum_cgroup_pids(netdata_publish_fd_stat_t *fd, struct pid_on_target2 *pids)
1016 {
1017 netdata_fd_stat_t accumulator;
1018 memset(&accumulator, 0, sizeof(accumulator));
1019
1020 while (pids) {
1021 netdata_publish_fd_stat_t *w = &pids->fd;
1022
1023 accumulator.open_err += w->open_err;
1024 accumulator.open_call += w->open_call;
1025 accumulator.close_call += w->close_call;
1026 accumulator.close_err += w->close_err;
1027
1028 pids = pids->next;
1029 }
1030
1031 fd->open_call = accumulator.open_call;
1032 fd->open_err = accumulator.open_err;
1033 fd->close_call = accumulator.close_call;
1034 fd->close_err = accumulator.close_err;
1035 }
1036
1037 /**
1038 * Create specific file descriptor charts
1039 *
1040 * Create charts for cgroup/application.
1041 *
1042 * @param type the chart type.
1043 * @param em the main thread structure.
1044 */
1045 static void ebpf_create_specific_fd_charts(char *type, ebpf_module_t *em)
1046 {
1047 char *label = (!strncmp(type, "cgroup_", 7)) ? &type[7] : type;
1048 ebpf_create_chart(
1049 type,
1050 NETDATA_SYSCALL_APPS_FILE_OPEN,
1051 "Number of open files",
1052 EBPF_COMMON_UNITS_CALLS_PER_SEC,
1053 NETDATA_APPS_FILE_GROUP,
1054 NETDATA_CGROUP_FD_OPEN_CONTEXT,
1055 NETDATA_EBPF_CHART_TYPE_LINE,
1056 NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + NETDATA_EBPF_FD_CHART_PRIORITY_CGROUP_OPEN,
1057 ebpf_create_global_dimension,
1058 &fd_publish_aggregated[NETDATA_FD_SYSCALL_OPEN],
1059 1,
1060 em->update_every,
1061 NETDATA_EBPF_MODULE_NAME_FD);
1062 ebpf_create_chart_labels("cgroup_name", label, RRDLABEL_SRC_AUTO);
1063 ebpf_commit_label();
1064
1065 if (em->mode < MODE_ENTRY) {
1066 ebpf_create_chart(
1067 type,
1068 NETDATA_SYSCALL_APPS_FILE_OPEN_ERROR,
1069 "Fails to open files",
1070 EBPF_COMMON_UNITS_CALLS_PER_SEC,
1071 NETDATA_APPS_FILE_GROUP,
1072 NETDATA_CGROUP_FD_OPEN_ERR_CONTEXT,
1073 NETDATA_EBPF_CHART_TYPE_LINE,
1074 NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + NETDATA_EBPF_FD_CHART_PRIORITY_CGROUP_OPEN_ERR,
1075 ebpf_create_global_dimension,
1076 &fd_publish_aggregated[NETDATA_FD_SYSCALL_OPEN],
1077 1,
1078 em->update_every,
1079 NETDATA_EBPF_MODULE_NAME_FD);
1080 ebpf_create_chart_labels("cgroup_name", label, RRDLABEL_SRC_AUTO);
1081 ebpf_commit_label();
1082 }
1083
1084 ebpf_create_chart(
1085 type,
1086 NETDATA_SYSCALL_APPS_FILE_CLOSED,
1087 "Files closed",
1088 EBPF_COMMON_UNITS_CALLS_PER_SEC,
1089 NETDATA_APPS_FILE_GROUP,
1090 NETDATA_CGROUP_FD_CLOSE_CONTEXT,
1091 NETDATA_EBPF_CHART_TYPE_LINE,
1092 NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + NETDATA_EBPF_FD_CHART_PRIORITY_CGROUP_CLOSE,
1093 ebpf_create_global_dimension,
1094 &fd_publish_aggregated[NETDATA_FD_SYSCALL_CLOSE],
1095 1,
1096 em->update_every,
1097 NETDATA_EBPF_MODULE_NAME_FD);
1098 ebpf_create_chart_labels("cgroup_name", label, RRDLABEL_SRC_AUTO);
1099 ebpf_commit_label();
1100
1101 if (em->mode < MODE_ENTRY) {
1102 ebpf_create_chart(
1103 type,
1104 NETDATA_SYSCALL_APPS_FILE_CLOSE_ERROR,
1105 "Fails to close files",
1106 EBPF_COMMON_UNITS_CALLS_PER_SEC,
1107 NETDATA_APPS_FILE_GROUP,
1108 NETDATA_CGROUP_FD_CLOSE_ERR_CONTEXT,
1109 NETDATA_EBPF_CHART_TYPE_LINE,
1110 NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + NETDATA_EBPF_FD_CHART_PRIORITY_CGROUP_CLOSE_ERR,
1111 ebpf_create_global_dimension,
1112 &fd_publish_aggregated[NETDATA_FD_SYSCALL_CLOSE],
1113 1,
1114 em->update_every,
1115 NETDATA_EBPF_MODULE_NAME_FD);
1116 ebpf_create_chart_labels("cgroup_name", label, RRDLABEL_SRC_AUTO);
1117 ebpf_commit_label();
1118 }
1119 }
1120
1121 /**
1122 * Obsolete specific file descriptor charts
1123 *
1124 * Obsolete charts for cgroup/application.
1125 *
1126 * @param type the chart type.
1127 * @param em the main thread structure.
1128 */
1129 static void ebpf_obsolete_specific_fd_charts(char *type, ebpf_module_t *em)
1130 {
1131 ebpf_write_chart_obsolete(
1132 type,
1133 NETDATA_SYSCALL_APPS_FILE_OPEN,
1134 "",
1135 "Number of open files",
1136 EBPF_COMMON_UNITS_CALLS_PER_SEC,
1137 NETDATA_APPS_FILE_GROUP,
1138 NETDATA_EBPF_CHART_TYPE_LINE,
1139 NETDATA_CGROUP_FD_OPEN_CONTEXT,
1140 NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + NETDATA_EBPF_FD_CHART_PRIORITY_CGROUP_OPEN,
1141 em->update_every);
1142
1143 if (em->mode < MODE_ENTRY) {
1144 ebpf_write_chart_obsolete(
1145 type,
1146 NETDATA_SYSCALL_APPS_FILE_OPEN_ERROR,
1147 "",
1148 "Fails to open files",
1149 EBPF_COMMON_UNITS_CALLS_PER_SEC,
1150 NETDATA_APPS_FILE_GROUP,
1151 NETDATA_EBPF_CHART_TYPE_LINE,
1152 NETDATA_CGROUP_FD_OPEN_ERR_CONTEXT,
1153 NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + NETDATA_EBPF_FD_CHART_PRIORITY_CGROUP_OPEN_ERR,
1154 em->update_every);
1155 }
1156
1157 ebpf_write_chart_obsolete(
1158 type,
1159 NETDATA_SYSCALL_APPS_FILE_CLOSED,
1160 "",
1161 "Files closed",
1162 EBPF_COMMON_UNITS_CALLS_PER_SEC,
1163 NETDATA_APPS_FILE_GROUP,
1164 NETDATA_EBPF_CHART_TYPE_LINE,
1165 NETDATA_CGROUP_FD_CLOSE_CONTEXT,
1166 NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + NETDATA_EBPF_FD_CHART_PRIORITY_CGROUP_CLOSE,
1167 em->update_every);
1168
1169 if (em->mode < MODE_ENTRY) {
1170 ebpf_write_chart_obsolete(
1171 type,
1172 NETDATA_SYSCALL_APPS_FILE_CLOSE_ERROR,
1173 "",
1174 "Fails to close files",
1175 EBPF_COMMON_UNITS_CALLS_PER_SEC,
1176 NETDATA_APPS_FILE_GROUP,
1177 NETDATA_EBPF_CHART_TYPE_LINE,
1178 NETDATA_CGROUP_FD_CLOSE_ERR_CONTEXT,
1179 NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + NETDATA_EBPF_FD_CHART_PRIORITY_CGROUP_CLOSE_ERR,
1180 em->update_every);
1181 }
1182 }
1183
1184 /*
1185 * Send specific file descriptor data
1186 *
1187 * Send data for specific cgroup/apps.
1188 *
1189 * @param type chart type
1190 * @param values structure with values that will be sent to netdata
1191 */
1192 static void ebpf_send_specific_fd_data(char *type, netdata_publish_fd_stat_t *values, ebpf_module_t *em)
1193 {
1194 ebpf_write_begin_chart(type, NETDATA_SYSCALL_APPS_FILE_OPEN, "");
1195 write_chart_dimension(fd_publish_aggregated[NETDATA_FD_SYSCALL_OPEN].name, (long long)values->open_call);
1196 ebpf_write_end_chart();
1197
1198 if (em->mode < MODE_ENTRY) {
1199 ebpf_write_begin_chart(type, NETDATA_SYSCALL_APPS_FILE_OPEN_ERROR, "");
1200 write_chart_dimension(fd_publish_aggregated[NETDATA_FD_SYSCALL_OPEN].name, (long long)values->open_err);
1201 ebpf_write_end_chart();
1202 }
1203
1204 ebpf_write_begin_chart(type, NETDATA_SYSCALL_APPS_FILE_CLOSED, "");
1205 write_chart_dimension(fd_publish_aggregated[NETDATA_FD_SYSCALL_CLOSE].name, (long long)values->close_call);
1206 ebpf_write_end_chart();
1207
1208 if (em->mode < MODE_ENTRY) {
1209 ebpf_write_begin_chart(type, NETDATA_SYSCALL_APPS_FILE_CLOSE_ERROR, "");
1210 write_chart_dimension(fd_publish_aggregated[NETDATA_FD_SYSCALL_CLOSE].name, (long long)values->close_err);
1211 ebpf_write_end_chart();
1212 }
1213 }
1214
1215 /**
1216 * Create systemd file descriptor charts
1217 *
1218 * Create charts when systemd is enabled
1219 *
1220 * @param em the main collector structure
1221 **/
1222 static void ebpf_create_systemd_fd_charts(ebpf_module_t *em)
1223 {
1224 static ebpf_systemd_args_t data_open = {
1225 .title = "Number of open files",
1226 .units = EBPF_COMMON_UNITS_CALLS_PER_SEC,
1227 .family = NETDATA_APPS_FILE_GROUP,
1228 .charttype = NETDATA_EBPF_CHART_TYPE_STACKED,
1229 .order = NETDATA_EBPF_FD_CHART_PRIORITY_OPEN,
1230 .algorithm = EBPF_CHART_ALGORITHM_INCREMENTAL,
1231 .context = NETDATA_SYSTEMD_FD_OPEN_CONTEXT,
1232 .module = NETDATA_EBPF_MODULE_NAME_FD,
1233 .update_every = 0,
1234 .suffix = NETDATA_SYSCALL_APPS_FILE_OPEN,
1235 .dimension = "calls"};
1236
1237 static ebpf_systemd_args_t data_open_error = {
1238 .title = "Fails to open files",
1239 .units = EBPF_COMMON_UNITS_CALLS_PER_SEC,
1240 .family = NETDATA_APPS_FILE_GROUP,
1241 .charttype = NETDATA_EBPF_CHART_TYPE_STACKED,
1242 .order = NETDATA_EBPF_FD_CHART_PRIORITY_OPEN_ERR,
1243 .algorithm = EBPF_CHART_ALGORITHM_INCREMENTAL,
1244 .context = NETDATA_SYSTEMD_FD_OPEN_ERR_CONTEXT,
1245 .module = NETDATA_EBPF_MODULE_NAME_FD,
1246 .update_every = 0,
1247 .suffix = NETDATA_SYSCALL_APPS_FILE_OPEN_ERROR,
1248 .dimension = "calls"};
1249
1250 static ebpf_systemd_args_t data_close = {
1251 .title = "Files closed",
1252 .units = EBPF_COMMON_UNITS_CALLS_PER_SEC,
1253 .family = NETDATA_APPS_FILE_GROUP,
1254 .charttype = NETDATA_EBPF_CHART_TYPE_STACKED,
1255 .order = NETDATA_EBPF_FD_CHART_PRIORITY_CLOSE,
1256 .algorithm = EBPF_CHART_ALGORITHM_INCREMENTAL,
1257 .context = NETDATA_SYSTEMD_FD_CLOSE_CONTEXT,
1258 .module = NETDATA_EBPF_MODULE_NAME_FD,
1259 .update_every = 0,
1260 .suffix = NETDATA_SYSCALL_APPS_FILE_CLOSED,
1261 .dimension = "calls"};
1262
1263 static ebpf_systemd_args_t data_close_error = {
1264 .title = "Fails to close files",
1265 .units = EBPF_COMMON_UNITS_CALLS_PER_SEC,
1266 .family = NETDATA_APPS_FILE_GROUP,
1267 .charttype = NETDATA_EBPF_CHART_TYPE_STACKED,
1268 .order = NETDATA_EBPF_FD_CHART_PRIORITY_CLOSE_ERR,
1269 .algorithm = EBPF_CHART_ALGORITHM_INCREMENTAL,
1270 .context = NETDATA_SYSTEMD_FD_CLOSE_ERR_CONTEXT,
1271 .module = NETDATA_EBPF_MODULE_NAME_FD,
1272 .update_every = 0,
1273 .suffix = NETDATA_SYSCALL_APPS_FILE_CLOSE_ERROR,
1274 .dimension = "calls"};
1275
1276 if (!data_open.update_every) {
1277 data_open.update_every = em->update_every;
1278 data_open_error.update_every = em->update_every;
1279 data_close.update_every = em->update_every;
1280 data_close_error.update_every = em->update_every;
1281 }
1282
1283 ebpf_cgroup_target_t *w;
1284 netdata_run_mode_t mode = em->mode;
1285 for (w = ebpf_cgroup_pids; w; w = w->next) {
1286 if (ebpf_plugin_stop())
1287 break;
1288
1289 if (unlikely(!w->systemd || w->flags & NETDATA_EBPF_SERVICES_HAS_FD_CHART))
1290 continue;
1291
1292 data_open.id = data_open_error.id = data_close.id = data_close_error.id = w->name;
1293 ebpf_create_charts_on_systemd(&data_open);
1294 ebpf_create_charts_on_systemd(&data_close);
1295 if (mode < MODE_ENTRY) {
1296 ebpf_create_charts_on_systemd(&data_open_error);
1297 ebpf_create_charts_on_systemd(&data_close_error);
1298 }
1299
1300 w->flags |= NETDATA_EBPF_SERVICES_HAS_FD_CHART;
1301 }
1302 }
1303
1304 /**
1305 * Send Systemd charts
1306 *
1307 * Send collected data to Netdata.
1308 *
1309 * @param em the main collector structure
1310 */
1311 static void ebpf_send_systemd_fd_charts(ebpf_module_t *em)
1312 {
1313 ebpf_cgroup_target_t *ect;
1314 for (ect = ebpf_cgroup_pids; ect; ect = ect->next) {
1315 if (ebpf_plugin_stop())
1316 break;
1317
1318 if (unlikely(!(ect->flags & NETDATA_EBPF_SERVICES_HAS_FD_CHART))) {
1319 continue;
1320 }
1321
1322 ebpf_write_begin_chart(ect->name, NETDATA_SYSCALL_APPS_FILE_OPEN, "");
1323 write_chart_dimension("calls", ect->publish_systemd_fd.open_call);
1324 ebpf_write_end_chart();
1325
1326 if (em->mode < MODE_ENTRY) {
1327 ebpf_write_begin_chart(ect->name, NETDATA_SYSCALL_APPS_FILE_OPEN_ERROR, "");
1328 write_chart_dimension("calls", ect->publish_systemd_fd.open_err);
1329 ebpf_write_end_chart();
1330 }
1331
1332 ebpf_write_begin_chart(ect->name, NETDATA_SYSCALL_APPS_FILE_CLOSED, "");
1333 write_chart_dimension("calls", ect->publish_systemd_fd.close_call);
1334 ebpf_write_end_chart();
1335
1336 if (em->mode < MODE_ENTRY) {
1337 ebpf_write_begin_chart(ect->name, NETDATA_SYSCALL_APPS_FILE_CLOSE_ERROR, "");
1338 write_chart_dimension("calls", ect->publish_systemd_fd.close_err);
1339 ebpf_write_end_chart();
1340 }
1341 }
1342 }
1343
1344 /**
1345 * Send data to Netdata calling auxiliary functions.
1346 *
1347 * @param em the main collector structure
1348 */
1349 static void ebpf_fd_send_cgroup_data(ebpf_module_t *em)
1350 {
1351 netdata_mutex_lock(&mutex_cgroup_shm);
1352 ebpf_cgroup_target_t *ect;
1353 for (ect = ebpf_cgroup_pids; ect; ect = ect->next) {
1354 ebpf_fd_sum_cgroup_pids(&ect->publish_systemd_fd, ect->pids);
1355 }
1356
1357 if (ebpf_plugin_stop()) {
1358 netdata_mutex_unlock(&mutex_cgroup_shm);
1359 return;
1360 }
1361
1362 if (ebpf_cgroup_systemd_enabled_get()) {
1363 if (ebpf_send_cgroup_chart_get()) {
1364 ebpf_create_systemd_fd_charts(em);
1365 }
1366
1367 ebpf_send_systemd_fd_charts(em);
1368 }
1369
1370 for (ect = ebpf_cgroup_pids; ect; ect = ect->next) {
1371 if (ebpf_plugin_stop())
1372 break;
1373
1374 if (ect->systemd)
1375 continue;
1376
1377 if (!(ect->flags & NETDATA_EBPF_CGROUP_HAS_FD_CHART) && ect->updated) {
1378 ebpf_create_specific_fd_charts(ect->name, em);
1379 ect->flags |= NETDATA_EBPF_CGROUP_HAS_FD_CHART;
1380 }
1381
1382 if (ect->flags & NETDATA_EBPF_CGROUP_HAS_FD_CHART) {
1383 if (ect->updated) {
1384 ebpf_send_specific_fd_data(ect->name, &ect->publish_systemd_fd, em);
1385 } else {
1386 ebpf_obsolete_specific_fd_charts(ect->name, em);
1387 ect->flags &= ~NETDATA_EBPF_CGROUP_HAS_FD_CHART;
1388 }
1389 }
1390 }
1391
1392 netdata_mutex_unlock(&mutex_cgroup_shm);
1393 }
1394
1395 /**
1396 * Main loop for this collector.
1397 */
1398 static void fd_collector(ebpf_module_t *em)
1399 {
1400 int cgroups = em->cgroup_charts;
1401 int update_every = em->update_every;
1402 int counter = update_every - 1;
1403 int maps_per_core = em->maps_per_core;
1404 uint32_t running_time = 0;
1405 uint32_t lifetime = em->lifetime;
1406 netdata_idx_t *stats = em->hash_table_stats;
1407 memset(stats, 0, sizeof(em->hash_table_stats));
1408 heartbeat_t hb;
1409 heartbeat_init(&hb, USEC_PER_SEC);
1410 while (!ebpf_plugin_stop() && running_time < lifetime) {
1411 if (ebpf_plugin_stop())
1412 break;
1413
1414 heartbeat_next(&hb);
1415
1416 if (ebpf_plugin_stop())
1417 break;
1418
1419 if (++counter != update_every)
1420 continue;
1421
1422 counter = 0;
1423 netdata_apps_integration_flags_t apps = em->apps_charts;
1424 ebpf_fd_read_global_tables(stats, maps_per_core);
1425
1426 if (ebpf_plugin_stop())
1427 break;
1428
1429 netdata_mutex_lock(&lock);
1430
1431 ebpf_fd_send_data(em);
1432
1433 if (apps & NETDATA_EBPF_APPS_FLAG_CHART_CREATED)
1434 ebpf_fd_send_apps_data(em, apps_groups_root_target);
1435
1436 if (ebpf_plugin_stop()) {
1437 netdata_mutex_unlock(&lock);
1438 break;
1439 }
1440
1441 if (cgroups && ebpf_cgroup_integration_active_get())
1442 ebpf_fd_send_cgroup_data(em);
1443
1444 netdata_mutex_unlock(&lock);
1445
1446 if (ebpf_plugin_stop())
1447 break;
1448
1449 netdata_mutex_lock(&ebpf_exit_cleanup);
1450 if (running_time && !em->running_time)
1451 running_time = update_every;
1452 else
1453 running_time += update_every;
1454
1455 em->running_time = running_time;
1456 netdata_mutex_unlock(&ebpf_exit_cleanup);
1457 }
1458 }
1459
1460 /*****************************************************************
1461 *
1462 * CREATE CHARTS
1463 *
1464 *****************************************************************/
1465
1466 /**
1467 * Create apps charts
1468 *
1469 * Call ebpf_create_chart to create the charts on apps submenu.
1470 *
1471 * @param em a pointer to the structure with the default values.
1472 */
1473 void ebpf_fd_create_apps_charts(struct ebpf_module *em, void *ptr)
1474 {
1475 struct ebpf_target *root = ptr;
1476 struct ebpf_target *w;
1477 int update_every = em->update_every;
1478 for (w = root; w; w = w->next) {
1479 if (ebpf_plugin_stop())
1480 break;
1481
1482 if (unlikely(!w->exposed))
1483 continue;
1484
1485 ebpf_write_chart_cmd(
1486 NETDATA_APP_FAMILY,
1487 w->clean_name,
1488 "_ebpf_file_open",
1489 "Number of open files",
1490 EBPF_COMMON_UNITS_CALLS_PER_SEC,
1491 NETDATA_APPS_FILE_FDS,
1492 NETDATA_EBPF_CHART_TYPE_STACKED,
1493 "app.ebpf_file_open",
1494 20220,
1495 update_every,
1496 NETDATA_EBPF_MODULE_NAME_FD);
1497 ebpf_create_chart_labels("app_group", w->name, RRDLABEL_SRC_AUTO);
1498 ebpf_commit_label();
1499 fprintf(stdout, "DIMENSION calls '' %s 1 1\n", ebpf_algorithms[NETDATA_EBPF_INCREMENTAL_IDX]);
1500
1501 if (em->mode < MODE_ENTRY) {
1502 ebpf_write_chart_cmd(
1503 NETDATA_APP_FAMILY,
1504 w->clean_name,
1505 "_ebpf_file_open_error",
1506 "Fails to open files.",
1507 EBPF_COMMON_UNITS_CALLS_PER_SEC,
1508 NETDATA_APPS_FILE_FDS,
1509 NETDATA_EBPF_CHART_TYPE_STACKED,
1510 "app.ebpf_file_open_error",
1511 20221,
1512 update_every,
1513 NETDATA_EBPF_MODULE_NAME_FD);
1514 ebpf_create_chart_labels("app_group", w->name, RRDLABEL_SRC_AUTO);
1515 ebpf_commit_label();
1516 fprintf(stdout, "DIMENSION calls '' %s 1 1\n", ebpf_algorithms[NETDATA_EBPF_INCREMENTAL_IDX]);
1517 }
1518
1519 ebpf_write_chart_cmd(
1520 NETDATA_APP_FAMILY,
1521 w->clean_name,
1522 "_ebpf_file_closed",
1523 "Files closed.",
1524 EBPF_COMMON_UNITS_CALLS_PER_SEC,
1525 NETDATA_APPS_FILE_FDS,
1526 NETDATA_EBPF_CHART_TYPE_STACKED,
1527 "app.ebpf_file_closed",
1528 20222,
1529 update_every,
1530 NETDATA_EBPF_MODULE_NAME_FD);
1531 ebpf_create_chart_labels("app_group", w->name, RRDLABEL_SRC_AUTO);
1532 ebpf_commit_label();
1533 fprintf(stdout, "DIMENSION calls '' %s 1 1\n", ebpf_algorithms[NETDATA_EBPF_INCREMENTAL_IDX]);
1534
1535 if (em->mode < MODE_ENTRY) {
1536 ebpf_write_chart_cmd(
1537 NETDATA_APP_FAMILY,
1538 w->clean_name,
1539 "_ebpf_file_close_error",
1540 "Fails to close files.",
1541 EBPF_COMMON_UNITS_CALLS_PER_SEC,
1542 NETDATA_APPS_FILE_FDS,
1543 NETDATA_EBPF_CHART_TYPE_STACKED,
1544 "app.ebpf_file_close_error",
1545 NETDATA_EBPF_FD_CHART_PRIORITY_APPS_CLOSE_ERR,
1546 update_every,
1547 NETDATA_EBPF_MODULE_NAME_FD);
1548 ebpf_create_chart_labels("app_group", w->name, RRDLABEL_SRC_AUTO);
1549 ebpf_commit_label();
1550 fprintf(stdout, "DIMENSION calls '' %s 1 1\n", ebpf_algorithms[NETDATA_EBPF_INCREMENTAL_IDX]);
1551 }
1552
1553 w->charts_created |= 1 << EBPF_MODULE_FD_IDX;
1554 }
1555
1556 em->apps_charts |= NETDATA_EBPF_APPS_FLAG_CHART_CREATED;
1557 }
1558
1559 /**
1560 * Create global charts
1561 *
1562 * Call ebpf_create_chart to create the charts for the collector.
1563 *
1564 * @param em a pointer to the structure with the default values.
1565 */
1566 static void ebpf_create_fd_global_charts(ebpf_module_t *em)
1567 {
1568 ebpf_create_chart(
1569 NETDATA_FILESYSTEM_FAMILY,
1570 NETDATA_FILE_OPEN_CLOSE_COUNT,
1571 "Open and close calls",
1572 EBPF_COMMON_UNITS_CALLS_PER_SEC,
1573 NETDATA_FILE_GROUP,
1574 NETDATA_FS_FILEDESCRIPTOR_CONTEXT,
1575 NETDATA_EBPF_CHART_TYPE_LINE,
1576 NETDATA_CHART_PRIO_EBPF_FD_CHARTS,
1577 ebpf_create_global_dimension,
1578 fd_publish_aggregated,
1579 NETDATA_FD_SYSCALL_END,
1580 em->update_every,
1581 NETDATA_EBPF_MODULE_NAME_FD);
1582
1583 if (em->mode < MODE_ENTRY) {
1584 ebpf_create_chart(
1585 NETDATA_FILESYSTEM_FAMILY,
1586 NETDATA_FILE_OPEN_ERR_COUNT,
1587 "Open fails",
1588 EBPF_COMMON_UNITS_CALLS_PER_SEC,
1589 NETDATA_FILE_GROUP,
1590 NETDATA_FS_FILEDESCRIPTOR_ERROR_CONTEXT,
1591 NETDATA_EBPF_CHART_TYPE_LINE,
1592 NETDATA_CHART_PRIO_EBPF_FD_CHARTS + 1,
1593 ebpf_create_global_dimension,
1594 fd_publish_aggregated,
1595 NETDATA_FD_SYSCALL_END,
1596 em->update_every,
1597 NETDATA_EBPF_MODULE_NAME_FD);
1598 }
1599
1600 fflush(stdout);
1601 }
1602
1603 /*****************************************************************
1604 *
1605 * MAIN THREAD
1606 *
1607 *****************************************************************/
1608
1609 /**
1610 * Allocate vectors used with this thread.
1611 *
1612 * We are not testing the return, because callocz does this and shutdown the software
1613 * case it was not possible to allocate.
1614 */
1615 static inline void ebpf_fd_allocate_global_vectors(void)
1616 {
1617 fd_vector = callocz((size_t)ebpf_nprocs, sizeof(netdata_fd_stat_t));
1618 fd_values = callocz((size_t)ebpf_nprocs, sizeof(netdata_idx_t));
1619 }
1620
1621 /*
1622 * Load BPF
1623 *
1624 * Load BPF files.
1625 *
1626 * @param em the structure with configuration
1627 */
1628 static int ebpf_fd_load_bpf(ebpf_module_t *em)
1629 {
1630 #ifdef LIBBPF_MAJOR_VERSION
1631 ebpf_define_map_type(fd_maps, em->maps_per_core, running_on_kernel);
1632 #endif
1633
1634 int ret = 0;
1635 ebpf_adjust_apps_cgroup(em, em->targets[NETDATA_FD_SYSCALL_OPEN].mode);
1636 if (em->load & EBPF_LOAD_LEGACY) {
1637 em->probe_links = ebpf_load_program(ebpf_plugin_dir, em, running_on_kernel, isrh, &em->objects);
1638 if (!em->probe_links) {
1639 ret = -1;
1640 }
1641 }
1642 #ifdef LIBBPF_MAJOR_VERSION
1643 else {
1644 fd_bpf_obj = fd_bpf__open();
1645 if (!fd_bpf_obj)
1646 ret = -1;
1647 else {
1648 ret = ebpf_fd_load_and_attach(fd_bpf_obj, em);
1649 if (ret) {
1650 fd_bpf__destroy(fd_bpf_obj);
1651 fd_bpf_obj = NULL;
1652 }
1653 }
1654 }
1655 #endif
1656
1657 if (ret)
1658 netdata_log_error("%s %s", EBPF_DEFAULT_ERROR_MSG, em->info.thread_name);
1659
1660 return ret;
1661 }
1662
1663 /**
1664 * Directory Cache thread
1665 *
1666 * Thread used to make dcstat thread
1667 *
1668 * @param ptr a pointer to `struct ebpf_module`
1669 *
1670 * @return It always returns NULL
1671 */
1672 void ebpf_fd_thread(void *ptr)
1673 {
1674 ebpf_module_t *em = (ebpf_module_t *)ptr;
1675
1676 fd_safe_clean = false;
1677
1678 CLEANUP_FUNCTION_REGISTER(ebpf_fd_exit) cleanup_ptr = em;
1679
1680 if (!ebpf_module_thread_has_valid_state(em)) {
1681 goto endfd;
1682 }
1683
1684 em->maps = fd_maps;
1685
1686 #ifdef LIBBPF_MAJOR_VERSION
1687 ebpf_adjust_thread_load(em, default_btf);
1688 #endif
1689 if (ebpf_fd_load_bpf(em)) {
1690 goto endfd;
1691 }
1692 ebpf_mark_program_loaded();
1693
1694 ebpf_fd_allocate_global_vectors();
1695
1696 int algorithms[NETDATA_FD_SYSCALL_END] = {NETDATA_EBPF_INCREMENTAL_IDX, NETDATA_EBPF_INCREMENTAL_IDX};
1697
1698 ebpf_global_labels(
1699 fd_aggregated_data, fd_publish_aggregated, fd_dimension_names, fd_id_names, algorithms, NETDATA_FD_SYSCALL_END);
1700
1701 netdata_mutex_lock(&lock);
1702 ebpf_create_fd_global_charts(em);
1703 ebpf_update_stats(&plugin_statistics, em);
1704 ebpf_update_kernel_memory_with_vector(&plugin_statistics, em->maps, EBPF_ACTION_STAT_ADD);
1705
1706 netdata_mutex_unlock(&lock);
1707
1708 ebpf_read_fd.thread = nd_thread_create(ebpf_read_fd.name, NETDATA_THREAD_OPTION_DEFAULT, ebpf_read_fd_thread, em);
1709
1710 fd_safe_clean = true;
1711 fd_collector(em);
1712
1713 endfd:
1714 ebpf_update_disabled_plugin_stats(em);
1715 }