@cryptotaxi247 / netdata / commits / b018c0a13

Address eBPF and Cgroup exit and messages (#22574)

thiagoftsm committed May 28, 2026 at 11:28 UTC b018c0a13ee7adef95b3685835214c73cf95f963
20 files changed +149 -18
src/collectors/cgroups.plugin/cgroup-discovery.c
+63 -4
@@ -24,6 +24,10 @@ struct cgroup *discovered_cgroup_root = NULL;
24 char cgroup_chart_id_prefix[] = "cgroup_";
25 char services_chart_id_prefix[] = "systemd_";
26 const char *cgroups_rename_script = NULL;
27 +static DICTIONARY *discovery_walkdir_open_errors = NULL;
28 +static size_t discovery_walkdir_open_errors_count = 0;
29 +
30 +#define DISCOVERY_WALKDIR_OPEN_ERRORS_MAX 256
31
32 // (legacy SHM globals removed — replaced by netipc in cgroup-netipc.c)
33
@@ -349,6 +353,54 @@ static inline struct cgroup *discovery_cgroup_find(const char *id) {
353 return cg;
354 }
355
356 +struct discovery_walkdir_open_error_prune {
357 + size_t remaining;
358 +};
359 +
360 +static int discovery_walkdir_open_error_prune_cb(const DICTIONARY_ITEM *item, void *value __maybe_unused, void *data) {
361 + struct discovery_walkdir_open_error_prune *prune = data;
362 +
363 + if (!prune->remaining)
364 + return -1;
365 +
366 + dictionary_del(discovery_walkdir_open_errors, dictionary_acquired_item_name(item));
367 + discovery_walkdir_open_errors_count--;
368 + prune->remaining--;
369 +
370 + return prune->remaining ? 0 : -1;
371 +}
372 +
373 +static inline bool discovery_walkdir_open_error_first(const char *dirpath) {
374 + if (unlikely(!discovery_walkdir_open_errors))
375 + discovery_walkdir_open_errors = dictionary_create(DICT_OPTION_SINGLE_THREADED);
376 +
377 + if (dictionary_get(discovery_walkdir_open_errors, dirpath))
378 + return false;
379 +
380 + if (discovery_walkdir_open_errors_count >= DISCOVERY_WALKDIR_OPEN_ERRORS_MAX) {
381 + struct discovery_walkdir_open_error_prune prune = {
382 + .remaining = discovery_walkdir_open_errors_count - DISCOVERY_WALKDIR_OPEN_ERRORS_MAX + 1,
383 + };
384 +
385 + dictionary_walkthrough_write(discovery_walkdir_open_errors, discovery_walkdir_open_error_prune_cb, &prune);
386 + }
387 +
388 + uint8_t logged = 1;
389 + dictionary_set(discovery_walkdir_open_errors, dirpath, &logged, sizeof(logged));
390 + discovery_walkdir_open_errors_count++;
391 +
392 + return true;
393 +}
394 +
395 +static inline void discovery_walkdir_open_errors_cleanup(void) {
396 + if (!discovery_walkdir_open_errors)
397 + return;
398 +
399 + dictionary_destroy(discovery_walkdir_open_errors);
400 + discovery_walkdir_open_errors = NULL;
401 + discovery_walkdir_open_errors_count = 0;
402 +}
403 +
404 static int calc_cgroup_depth(const char *id) {
405 int depth = 0;
406 const char *s;
@@ -407,10 +459,16 @@ static inline int discovery_find_walkdir(const char *base, const char *dirpath)
459
460 DIR *dir = opendir(dirpath);
461 if(!dir) {
410 - if(errno == EACCES && strcmp(dirpath, base) != 0)
411 - nd_log_collector(NDLP_DEBUG, "CGROUP: cannot open directory '%s': %s", dirpath, strerror(errno));
412 - else
413 - collector_error("CGROUP: cannot open directory '%s': %s", dirpath, strerror(errno));
462 + int err = errno;
463 + bool first_log_for_path = discovery_walkdir_open_error_first(dirpath);
464 +
465 + if(err == EACCES && strcmp(dirpath, base) != 0) {
466 + if (first_log_for_path)
467 + nd_log_collector(NDLP_DEBUG, "CGROUP: cannot open directory '%s': %s", dirpath, strerror(err));
468 + }
469 + else if (first_log_for_path) {
470 + collector_error("CGROUP: cannot open directory '%s': %s", dirpath, strerror(err));
471 + }
472 return ret;
473 }
474 ret = 1;
@@ -1187,6 +1245,7 @@ void cgroup_discovery_worker(void *ptr)
1245
1246 // Stop the netipc server first so its worker threads cannot iterate cgroup_root while we free it.
1247 cgroup_netipc_cleanup();
1248 + discovery_walkdir_open_errors_cleanup();
1249
1250 // free all cgroups
1251 netdata_mutex_lock(&cgroup_root_mutex);
src/collectors/ebpf.plugin/ebpf.c
+39 -7
@@ -34,6 +34,7 @@ uint32_t integration_with_collectors = NETDATA_EBPF_INTEGRATION_DISABLED;
34 ND_THREAD *socket_ipc = NULL;
35 static size_t global_iterations_counter = 1;
36 bool publish_internal_metrics = true;
37 +bool ebpf_program_loaded_any = false;
38
39 netdata_mutex_t lock;
40 netdata_mutex_t ebpf_exit_cleanup;
@@ -1014,7 +1015,7 @@ static inline void ebpf_check_before2go()
1015 /**
1016 * Close the collector gracefully
1017 */
1017 -static void ebpf_exit()
1018 +static void ebpf_cleanup(void)
1019 {
1020 #ifdef LIBBPF_MAJOR_VERSION
1021 netdata_mutex_lock(&ebpf_exit_cleanup);
@@ -1042,8 +1043,12 @@ static void ebpf_exit()
1043 }
1044 ebpf_cgroup_cache_cleanup();
1045 netdata_integration_cleanup_shm();
1046 +}
1047
1046 - exit(0);
1048 +static void ebpf_exit(int exit_code)
1049 +{
1050 + ebpf_cleanup();
1051 + exit(exit_code);
1052 }
1053
1054 /**
@@ -1161,7 +1166,7 @@ void ebpf_stop_threads(int sig)
1166 #endif
1167 netdata_mutex_unlock(&mutex_cgroup_shm);
1168
1164 - // Join the cgroup integration thread before ebpf_exit() tears down the netipc cache it reads.
1169 + // Join the cgroup integration thread before cleanup tears down the netipc cache it reads.
1170 if (cgroup_integration_thread.thread) {
1171 nd_thread_join(cgroup_integration_thread.thread);
1172 cgroup_integration_thread.thread = NULL;
@@ -1185,7 +1190,7 @@ void ebpf_stop_threads(int sig)
1190 netdata_log_info(
1191 "EBPF SHUTDOWN: total stop duration %llums.", (unsigned long long)(total_duration_ut / USEC_PER_MS));
1192
1188 - ebpf_exit();
1193 + ebpf_cleanup();
1194 }
1195
1196 /**
@@ -1545,7 +1550,7 @@ static void ebpf_parse_args(int argc, char **argv)
1550 netdata_log_error(
1551 "Cannot read process groups '%s/apps_groups.conf'. There are no internal defaults. Failing.",
1552 ebpf_stock_config_dir);
1548 - ebpf_exit();
1553 + ebpf_exit(1);
1554 }
1555 } else
1556 netdata_log_info("Loaded config file '%s/apps_groups.conf'", ebpf_user_config_dir);
@@ -2295,6 +2300,26 @@ static void ebpf_signal_stop_handler(int sig)
2300 ebpf_stop_signal = (sig > 0) ? sig : 1;
2301 }
2302
2303 +static bool ebpf_all_enabled_threads_stopped(void)
2304 +{
2305 + bool any_enabled = false;
2306 +
2307 + for (size_t i = 0; ebpf_threads[i].name != NULL; i++) {
2308 + if (!ebpf_threads[i].enabled)
2309 + continue;
2310 +
2311 + any_enabled = true;
2312 +
2313 + if (!ebpf_threads[i].thread)
2314 + continue;
2315 +
2316 + if (ebpf_module_enabled_get(&ebpf_modules[i]) < NETDATA_THREAD_EBPF_STOPPING)
2317 + return false;
2318 + }
2319 +
2320 + return any_enabled;
2321 +}
2322 +
2323 /**
2324 * Entry point
2325 *
@@ -2344,7 +2369,7 @@ int main(int argc, char **argv)
2369
2370 netdata_configured_host_prefix = getenv("NETDATA_HOST_PREFIX");
2371 if (verify_netdata_host_prefix(true) == -1)
2347 - ebpf_exit();
2372 + ebpf_exit(1);
2373
2374 ebpf_allocate_common_vectors();
2375
@@ -2402,6 +2427,7 @@ int main(int argc, char **argv)
2427 heartbeat_init(&hb, USEC_PER_SEC);
2428 int update_apps_every = (int)EBPF_CFG_UPDATE_APPS_EVERY_DEFAULT;
2429 int update_apps_list = update_apps_every - 1;
2430 + int exit_code = 0;
2431 //Plugin will be killed when it receives a signal
2432 for (; !ebpf_plugin_stop(); global_iterations_counter++) {
2433 (void)heartbeat_next(&hb);
@@ -2414,6 +2440,12 @@ int main(int argc, char **argv)
2440 if (ebpf_plugin_stop())
2441 break;
2442
2443 + if (!ebpf_program_loaded() && ebpf_all_enabled_threads_stopped()) {
2444 + netdata_log_error("EBPF: failed to load any eBPF program, shutting down.");
2445 + exit_code = 1;
2446 + break;
2447 + }
2448 +
2449 if (global_iterations_counter % EBPF_DEFAULT_UPDATE_EVERY == 0) {
2450 netdata_mutex_lock(&lock);
2451 ebpf_create_statistic_charts(EBPF_DEFAULT_UPDATE_EVERY);
@@ -2438,5 +2470,5 @@ int main(int argc, char **argv)
2470
2471 ebpf_stop_threads((int)ebpf_stop_signal);
2472
2441 - return 0;
2473 + return exit_code;
2474 }
src/collectors/ebpf.plugin/ebpf.h
+11
@@ -121,6 +121,7 @@ typedef struct netdata_ebpf_judy_pid_stats {
121 } netdata_ebpf_judy_pid_stats_t;
122
123 extern ebpf_module_t ebpf_modules[];
124 +extern bool ebpf_program_loaded_any;
125
126 typedef struct ebpf_tracepoint {
127 bool enabled;
@@ -367,6 +368,16 @@ static inline bool ebpf_plugin_stop(void)
368 nd_thread_signaled_to_cancel();
369 }
370
371 +static inline void ebpf_mark_program_loaded(void)
372 +{
373 + __atomic_store_n(&ebpf_program_loaded_any, true, __ATOMIC_RELEASE);
374 +}
375 +
376 +static inline bool ebpf_program_loaded(void)
377 +{
378 + return __atomic_load_n(&ebpf_program_loaded_any, __ATOMIC_ACQUIRE);
379 +}
380 +
381 // `enabled` is sampled from stats/shutdown paths without a single shared mutex.
382 // Keep those state transitions defined without changing the plugin's lock layout.
383 static inline enum ebpf_threads_status ebpf_module_enabled_get(ebpf_module_t *em)
src/collectors/ebpf.plugin/ebpf_cachestat.c
+1
@@ -1850,6 +1850,7 @@ void ebpf_cachestat_thread(void *ptr)
1850 if (ebpf_cachestat_load_bpf(em)) {
1851 goto endcachestat;
1852 }
1853 + ebpf_mark_program_loaded();
1854
1855 ebpf_cachestat_allocate_global_vectors();
1856
src/collectors/ebpf.plugin/ebpf_dcstat.c
+1
@@ -1590,6 +1590,7 @@ void ebpf_dcstat_thread(void *ptr)
1590 if (ebpf_dcstat_load_bpf(em)) {
1591 goto enddcstat;
1592 }
1593 + ebpf_mark_program_loaded();
1594
1595 ebpf_dcstat_allocate_global_vectors();
1596
src/collectors/ebpf.plugin/ebpf_disk.c
+1
@@ -925,6 +925,7 @@ void ebpf_disk_thread(void *ptr)
925 if (ebpf_disk_load_bpf(em)) {
926 goto enddisk;
927 }
928 + ebpf_mark_program_loaded();
929
930 int algorithms[NETDATA_EBPF_HIST_MAX_BINS];
931 ebpf_fill_algorithms(algorithms, NETDATA_EBPF_HIST_MAX_BINS, NETDATA_EBPF_INCREMENTAL_IDX);
src/collectors/ebpf.plugin/ebpf_fd.c
+1
@@ -1689,6 +1689,7 @@ void ebpf_fd_thread(void *ptr)
1689 if (ebpf_fd_load_bpf(em)) {
1690 goto endfd;
1691 }
1692 + ebpf_mark_program_loaded();
1693
1694 ebpf_fd_allocate_global_vectors();
1695
src/collectors/ebpf.plugin/ebpf_filesystem.c
+16 -7
@@ -618,8 +618,15 @@ int ebpf_filesystem_initialize_ebpf_data(ebpf_module_t *em)
618 int i;
619 const char *saved_name = em->info.thread_name;
620 uint64_t kernels = em->kernels;
621 + bool loaded_any = false;
622 for (i = 0; localfs[i].filesystem; i++) {
623 ebpf_filesystem_partitions_t *efp = &localfs[i];
624 + if (efp->flags & NETDATA_FILESYSTEM_FLAG_HAS_PARTITION)
625 + loaded_any = true;
626 +
627 + if (efp->load_failed)
628 + continue;
629 +
630 if (!efp->probe_links && efp->flags & NETDATA_FILESYSTEM_LOAD_EBPF_PROGRAM) {
631 em->info.thread_name = efp->filesystem;
632 em->kernels = efp->kernels;
@@ -630,34 +637,36 @@ int ebpf_filesystem_initialize_ebpf_data(ebpf_module_t *em)
637 if (em->load & EBPF_LOAD_LEGACY) {
638 efp->probe_links = ebpf_load_program(ebpf_plugin_dir, em, running_on_kernel, isrh, &efp->objects);
639 if (!efp->probe_links) {
640 + efp->load_failed = true;
641 em->info.thread_name = saved_name;
642 em->kernels = kernels;
643 em->maps = NULL;
636 - netdata_mutex_unlock(&lock);
637 - return -1;
644 + continue;
645 }
646 }
647 #ifdef LIBBPF_MAJOR_VERSION
648 else {
649 efp->fs_obj = filesystem_bpf__open();
650 if (!efp->fs_obj) {
651 + efp->load_failed = true;
652 em->info.thread_name = saved_name;
653 em->kernels = kernels;
654 em->maps = NULL;
647 - netdata_mutex_unlock(&lock);
648 - return -1;
655 + continue;
656 } else if (ebpf_fs_load_and_attach(em->maps, efp->fs_obj, efp->functions, NULL)) {
657 + efp->load_failed = true;
658 filesystem_bpf__destroy(efp->fs_obj);
659 efp->fs_obj = NULL;
660 em->info.thread_name = saved_name;
661 em->kernels = kernels;
662 em->maps = NULL;
655 - netdata_mutex_unlock(&lock);
656 - return -1;
663 + continue;
664 }
665 }
666 #endif
667 efp->flags |= NETDATA_FILESYSTEM_FLAG_HAS_PARTITION;
668 + loaded_any = true;
669 + ebpf_mark_program_loaded();
670 ebpf_update_kernel_memory(&plugin_statistics, efp->fs_maps, EBPF_ACTION_STAT_ADD);
671
672 // Needed for filesystems like btrfs
@@ -682,7 +691,7 @@ int ebpf_filesystem_initialize_ebpf_data(ebpf_module_t *em)
691 filesystem_hash_values = callocz(ebpf_nprocs, sizeof(netdata_idx_t));
692 }
693
685 - return 0;
694 + return loaded_any ? 0 : -1;
695 }
696
697 /**
src/collectors/ebpf.plugin/ebpf_hardirq.c
+1
@@ -696,6 +696,7 @@ void ebpf_hardirq_thread(void *ptr)
696 if (ebpf_hardirq_load_bpf(em)) {
697 goto endhardirq;
698 }
699 + ebpf_mark_program_loaded();
700
701 ebpf_hardirq_allocate_global_vectors();
702
src/collectors/ebpf.plugin/ebpf_mdflush.c
+1
@@ -418,6 +418,7 @@ void ebpf_mdflush_thread(void *ptr)
418 netdata_log_error("Cannot load eBPF software.");
419 goto endmdflush;
420 }
421 + ebpf_mark_program_loaded();
422
423 mdflush_safe_clean = true;
424 mdflush_collector(em);
src/collectors/ebpf.plugin/ebpf_mount.c
+1
@@ -527,6 +527,7 @@ void ebpf_mount_thread(void *ptr)
527 if (ebpf_mount_load_bpf(em)) {
528 goto endmount;
529 }
530 + ebpf_mark_program_loaded();
531
532 int algorithms[NETDATA_EBPF_MOUNT_SYSCALL] = {NETDATA_EBPF_INCREMENTAL_IDX, NETDATA_EBPF_INCREMENTAL_IDX};
533
src/collectors/ebpf.plugin/ebpf_oomkill.c
+1
@@ -590,6 +590,7 @@ void ebpf_oomkill_thread(void *ptr)
590 if (!em->probe_links) {
591 goto endoomkill;
592 }
593 + ebpf_mark_program_loaded();
594
595 netdata_mutex_lock(&lock);
596 ebpf_update_stats(&plugin_statistics, em);
src/collectors/ebpf.plugin/ebpf_process.c
+2
@@ -1825,6 +1825,8 @@ void ebpf_process_thread(void *ptr)
1825 if (ebpf_process_load_bpf(em)) {
1826 em->global_charts = em->apps_charts = em->cgroup_charts = NETDATA_THREAD_EBPF_STOPPING;
1827 ebpf_module_enabled_set(em, NETDATA_THREAD_EBPF_STOPPING);
1828 + } else {
1829 + ebpf_mark_program_loaded();
1830 }
1831
1832 int algorithms[NETDATA_KEY_PUBLISH_PROCESS_END] = {
src/collectors/ebpf.plugin/ebpf_shm.c
+1
@@ -1447,6 +1447,7 @@ void ebpf_shm_thread(void *ptr)
1447 if (ebpf_shm_load_bpf(em)) {
1448 goto endshm;
1449 }
1450 + ebpf_mark_program_loaded();
1451
1452 ebpf_shm_allocate_global_vectors();
1453
src/collectors/ebpf.plugin/ebpf_socket.c
+1
@@ -3126,6 +3126,7 @@ void ebpf_socket_thread(void *ptr)
3126 if (ebpf_socket_load_bpf(em)) {
3127 goto endsocket;
3128 }
3129 + ebpf_mark_program_loaded();
3130
3131 int algorithms[NETDATA_MAX_SOCKET_VECTOR] = {
3132 NETDATA_EBPF_ABSOLUTE_IDX,
src/collectors/ebpf.plugin/ebpf_softirq.c
+1
@@ -263,6 +263,7 @@ void ebpf_softirq_thread(void *ptr)
263 if (!em->probe_links) {
264 goto endsoftirq;
265 }
266 + ebpf_mark_program_loaded();
267
268 softirq_safe_clean = true;
269 softirq_collector(em);
src/collectors/ebpf.plugin/ebpf_swap.c
+1
@@ -1332,6 +1332,7 @@ void ebpf_swap_thread(void *ptr)
1332 if (ebpf_swap_load_bpf(em)) {
1333 goto endswap;
1334 }
1335 + ebpf_mark_program_loaded();
1336
1337 ebpf_swap_allocate_global_vectors();
1338
src/collectors/ebpf.plugin/ebpf_sync.c
+4
@@ -360,6 +360,8 @@ static int ebpf_sync_initialize_syscall(ebpf_module_t *em)
360 if (em->load & EBPF_LOAD_LEGACY) {
361 if (ebpf_sync_load_legacy(w, em))
362 errors++;
363 + else
364 + ebpf_mark_program_loaded();
365
366 em->info.thread_name = saved_name;
367 }
@@ -378,6 +380,8 @@ static int ebpf_sync_initialize_syscall(ebpf_module_t *em)
380 w->sync_obj = NULL;
381 w->enabled = false;
382 errors++;
383 + } else {
384 + ebpf_mark_program_loaded();
385 }
386 }
387 } else {
src/collectors/ebpf.plugin/ebpf_vfs.c
+1
@@ -2985,6 +2985,7 @@ void ebpf_vfs_thread(void *ptr)
2985 if (ebpf_vfs_load_bpf(em)) {
2986 goto endvfs;
2987 }
2988 + ebpf_mark_program_loaded();
2989
2990 int algorithms[NETDATA_KEY_PUBLISH_VFS_END] = {
2991 NETDATA_EBPF_INCREMENTAL_IDX,
src/collectors/ebpf.plugin/libbpf_api/ebpf.h
+1
@@ -418,6 +418,7 @@ typedef struct ebpf_filesystem_partitions {
418 char *family_name;
419 struct bpf_object *objects;
420 struct bpf_link **probe_links;
421 + bool load_failed;
422
423 netdata_ebpf_histogram_t hread;
424 netdata_ebpf_histogram_t hwrite;