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;