From 9d77b555836213097bbf766b9d090ff569c774da Mon Sep 17 00:00:00 2001 From: thiagoftsm Date: Mon, 25 May 2026 00:40:40 +0000 Subject: [PATCH 01/12] improve_algs: Fix algorithms after tests on netdata/netdata --- gotests/main.go | 22 ++++++++---- gotests/main_test.go | 84 ++++++++++++++++++++++++++++++++++++-------- tests/tester_user.c | 45 ++++++++++++++++-------- 3 files changed, 115 insertions(+), 36 deletions(-) diff --git a/gotests/main.go b/gotests/main.go index 3bd56737..d96b7091 100644 --- a/gotests/main.go +++ b/gotests/main.go @@ -724,6 +724,14 @@ func modeSuffix(bufferMode bool, arenaMode bool) string { return "" } +func effectiveModeFlags(moduleName string, kernelVersion int, bufferMode bool, arenaMode bool) (bool, bool) { + if moduleName == "cachestat" && !bufferMode && !arenaMode && kernelVersion >= netdataEBPFKernel510 { + return true, false + } + + return bufferMode, arenaMode +} + func candidateMatches(filename string, moduleName string, isReturn bool, version string, rhfVersion int, bufferMode bool, arenaMode bool) bool { prefix := fmt.Sprintf("%cnetdata_ebpf_%s%s.", map[bool]rune{true: 'r', false: 'p'}[isReturn], moduleName, modeSuffix(bufferMode, arenaMode)) if !strings.HasPrefix(filename, prefix) || !strings.HasSuffix(filename, ".o") { @@ -1052,27 +1060,29 @@ func runNetdataTests(w io.Writer, rhfVersion int, kernelVersion int, isReturn bo supportedMapTypes := detectSupportedMapTypes(rhfVersion, kernelVersion) for _, mod := range ebpfModules { + bufferMode, arenaMode := effectiveModeFlags(mod.name, kernelVersion, opts.bufferMode, opts.arenaMode) + if opts.flags&mod.flags == 0 { continue } - if opts.arenaMode && !moduleHasArena(mod.name) { + if arenaMode && !moduleHasArena(mod.name) { continue } - if opts.bufferMode && !moduleHasBuffer(mod.name) { + if bufferMode && !moduleHasBuffer(mod.name) { continue } kernels := mod.kernels - if opts.arenaMode && mod.arenaKernels != 0 { + if arenaMode && mod.arenaKernels != 0 { kernels = mod.arenaKernels - } else if opts.bufferMode && mod.bufferKernels != 0 { + } else if bufferMode && mod.bufferKernels != 0 { kernels = mod.bufferKernels } maxIndex := selectMaxIndex(rhfVersion, kernelVersion) idx := selectIndex(kernels, rhfVersion, kernelVersion) - candidates := discoverCandidates(mod.name, isReturn, rhfVersion, kernels, maxIndex, opts.netdataPath, opts.bufferMode, opts.arenaMode) + candidates := discoverCandidates(mod.name, isReturn, rhfVersion, kernels, maxIndex, opts.netdataPath, bufferMode, arenaMode) compatible, incompatible, unsupportedType := filterCompatibleCandidates(candidates, supportedMapTypes) if len(compatible) == 0 { @@ -1083,7 +1093,7 @@ func runNetdataTests(w io.Writer, rhfVersion int, kernelVersion int, isReturn bo continue } - compatible = []string{mountName(idx, mod.name, isReturn, rhfVersion, opts.netdataPath, opts.bufferMode, opts.arenaMode)} + compatible = []string{mountName(idx, mod.name, isReturn, rhfVersion, opts.netdataPath, bufferMode, arenaMode)} } for _, filename := range compatible { diff --git a/gotests/main_test.go b/gotests/main_test.go index 9256f1d0..daade5c8 100644 --- a/gotests/main_test.go +++ b/gotests/main_test.go @@ -135,7 +135,7 @@ VERSION_ID="12" want: -1, }, { - name: "empty content", + name: "empty content", content: ``, want: -1, }, @@ -763,6 +763,60 @@ func TestModeSuffix(t *testing.T) { } } +func TestEffectiveModeFlags(t *testing.T) { + tests := map[string]struct { + module string + kernelVersion int + bufferMode bool + arenaMode bool + wantBuffer bool + wantArena bool + }{ + "cachestat-defaults-to-buffer-on-supported-kernel": { + module: "cachestat", + kernelVersion: netdataEBPFKernel510, + wantBuffer: true, + wantArena: false, + }, + "cachestat-keeps-tracing-before-buffer-support": { + module: "cachestat", + kernelVersion: netdataEBPFKernel414, + wantBuffer: false, + wantArena: false, + }, + "explicit-buffer-stays-buffer": { + module: "cachestat", + kernelVersion: netdataEBPFKernel612, + bufferMode: true, + wantBuffer: true, + wantArena: false, + }, + "explicit-arena-wins": { + module: "cachestat", + kernelVersion: netdataEBPFKernel612, + bufferMode: true, + arenaMode: true, + wantBuffer: true, + wantArena: true, + }, + "other-modules-unaffected": { + module: "swap", + kernelVersion: netdataEBPFKernel612, + wantBuffer: false, + wantArena: false, + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + gotBuffer, gotArena := effectiveModeFlags(tc.module, tc.kernelVersion, tc.bufferMode, tc.arenaMode) + if gotBuffer != tc.wantBuffer || gotArena != tc.wantArena { + t.Fatalf("effectiveModeFlags() = (%v, %v), want (%v, %v)", gotBuffer, gotArena, tc.wantBuffer, tc.wantArena) + } + }) + } +} + func TestModuleModeLookup(t *testing.T) { bufferArenaModules := []string{"cachestat", "dc", "fd", "oomkill", "process", "shm", "swap", "vfs", "dns", "socket"} for _, name := range bufferArenaModules { @@ -896,45 +950,45 @@ func TestCandidateVersionIndex(t *testing.T) { wantIndex int }{ { - name: "rhf 5.14 matches at index 7", + name: "rhf 5.14 matches at index 7", filename: "pnetdata_ebpf_swap.5.14.rhf.o", - module: "swap", rhf: 1, kernels: netdataV514, maxIndex: 7, + module: "swap", rhf: 1, kernels: netdataV514, maxIndex: 7, wantIndex: 7, }, { - name: "non-rhf masks out V514", + name: "non-rhf masks out V514", filename: "pnetdata_ebpf_swap.5.14.rhf.o", - module: "swap", rhf: -1, kernels: netdataV514, maxIndex: 10, + module: "swap", rhf: -1, kernels: netdataV514, maxIndex: 10, wantIndex: -1, }, { - name: "non-rhf 6.8 matches at index 10", + name: "non-rhf 6.8 matches at index 10", filename: "pnetdata_ebpf_swap.6.8.o", - module: "swap", rhf: -1, kernels: netdataV68, maxIndex: 10, + module: "swap", rhf: -1, kernels: netdataV68, maxIndex: 10, wantIndex: 10, }, { - name: "picks file version from multi-version kernel set", + name: "picks file version from multi-version kernel set", filename: "pnetdata_ebpf_swap.5.4.o", - module: "swap", rhf: -1, kernels: netdataV54 | netdataV68, maxIndex: 10, + module: "swap", rhf: -1, kernels: netdataV54 | netdataV68, maxIndex: 10, wantIndex: 4, }, { - name: "wrong module name returns -1", + name: "wrong module name returns -1", filename: "pnetdata_ebpf_process.6.8.o", - module: "swap", rhf: -1, kernels: netdataV68, maxIndex: 10, + module: "swap", rhf: -1, kernels: netdataV68, maxIndex: 10, wantIndex: -1, }, { - name: "arena file matches with arenaMode enabled", + name: "arena file matches with arenaMode enabled", filename: "pnetdata_ebpf_swap_arena.6.12.o", - module: "swap", rhf: -1, kernels: netdataV612, maxIndex: 11, arenaMode: true, + module: "swap", rhf: -1, kernels: netdataV612, maxIndex: 11, arenaMode: true, wantIndex: 11, }, { - name: "arena file rejected without arenaMode", + name: "arena file rejected without arenaMode", filename: "pnetdata_ebpf_swap_arena.6.12.o", - module: "swap", rhf: -1, kernels: netdataV612, maxIndex: 11, arenaMode: false, + module: "swap", rhf: -1, kernels: netdataV612, maxIndex: 11, arenaMode: false, wantIndex: -1, }, } diff --git a/tests/tester_user.c b/tests/tester_user.c index 277bfe71..af90c1a9 100644 --- a/tests/tester_user.c +++ b/tests/tester_user.c @@ -490,7 +490,7 @@ static void ebpf_free_candidate_list(ebpf_candidate_list_t *list) memset(list, 0, sizeof(*list)); } -static const char *ebpf_mode_suffix(void) +static const char *ebpf_mode_suffix(int buffer_mode, int arena_mode) { if (arena_mode) return "_arena"; @@ -500,7 +500,7 @@ static const char *ebpf_mode_suffix(void) } static int ebpf_candidate_matches(const char *filename, const char *name, int is_return, - const char *version, int rhf_version) + const char *version, int rhf_version, int buffer_mode, int arena_mode) { char prefix[128]; size_t prefix_len; @@ -509,7 +509,8 @@ static int ebpf_candidate_matches(const char *filename, const char *name, int is const char *rest; int has_rhf; - snprintf(prefix, sizeof(prefix), "%cnetdata_ebpf_%s%s.", (is_return) ? 'r' : 'p', name, ebpf_mode_suffix()); + snprintf(prefix, sizeof(prefix), "%cnetdata_ebpf_%s%s.", (is_return) ? 'r' : 'p', name, + ebpf_mode_suffix(buffer_mode, arena_mode)); prefix_len = strlen(prefix); if (filename_len <= prefix_len + 2) return 0; @@ -532,7 +533,8 @@ static int ebpf_candidate_matches(const char *filename, const char *name, int is } static int ebpf_candidate_version_index(const char *filename, const char *name, int is_return, - int rhf_version, uint32_t kernels, uint32_t max_index) + int rhf_version, uint32_t kernels, uint32_t max_index, + int buffer_mode, int arena_mode) { int idx; @@ -543,7 +545,8 @@ static int ebpf_candidate_version_index(const char *filename, const char *name, if (!(kernels & (1U << idx))) continue; - if (ebpf_candidate_matches(filename, name, is_return, ebpf_kernel_names[idx], rhf_version)) + if (ebpf_candidate_matches(filename, name, is_return, ebpf_kernel_names[idx], rhf_version, + buffer_mode, arena_mode)) return idx; } @@ -551,7 +554,8 @@ static int ebpf_candidate_version_index(const char *filename, const char *name, } static void ebpf_discover_candidates(ebpf_candidate_list_t *list, const char *name, int is_return, - uint32_t kernels, uint32_t max_index, int rhf_version) + uint32_t kernels, uint32_t max_index, int rhf_version, + int buffer_mode, int arena_mode) { char *path = ebpf_resolve_binary_directory(); DIR *dir; @@ -576,7 +580,8 @@ static void ebpf_discover_candidates(ebpf_candidate_list_t *list, const char *na continue; candidate_index = ebpf_candidate_version_index(entry->d_name, name, is_return, - rhf_version, kernels, max_index); + rhf_version, kernels, max_index, + buffer_mode, arena_mode); if (candidate_index < 0) continue; @@ -1109,7 +1114,8 @@ static void ebpf_start_netdata_json(char *filename, int is_return) * @param is_return is return or entry ? * @param rhf_version Red Hat version. */ -static void ebpf_mount_name(char *out, size_t len, uint32_t kver, char *name, int is_return, int rhf_version) +static void ebpf_mount_name(char *out, size_t len, uint32_t kver, char *name, int is_return, int rhf_version, + int buffer_mode, int arena_mode) { char *version = ebpf_select_kernel_name(kver); char *path = ebpf_resolve_binary_directory(); @@ -1120,7 +1126,7 @@ static void ebpf_mount_name(char *out, size_t len, uint32_t kver, char *name, in path, (is_return) ? 'r' : 'p', name, - ebpf_mode_suffix(), + ebpf_mode_suffix(buffer_mode, arena_mode), version, (rhf_version != -1) ? ".rhf" : ""); free(path); @@ -1909,12 +1915,20 @@ static void ebpf_run_netdata_tests(int rhf_version, uint32_t kver, int is_return ebpf_detect_map_support(&map_support, rhf_version, kver); while (ebpf_modules[i].name) { - if (arena_mode && !ebpf_module_has_arena(ebpf_modules[i].name)) { + int use_buffer_mode = buffer_mode; + int use_arena_mode = arena_mode; + + if (!use_buffer_mode && !use_arena_mode && + !strcmp(ebpf_modules[i].name, "cachestat") && kver >= NETDATA_EBPF_KERNEL_5_10) { + use_buffer_mode = 1; + } + + if (use_arena_mode && !ebpf_module_has_arena(ebpf_modules[i].name)) { i++; continue; } - if (buffer_mode && !ebpf_module_has_buffer(ebpf_modules[i].name)) { + if (use_buffer_mode && !ebpf_module_has_buffer(ebpf_modules[i].name)) { i++; continue; } @@ -1925,15 +1939,15 @@ static void ebpf_run_netdata_tests(int rhf_version, uint32_t kver, int is_return char *first_incompatible = NULL; int unsupported_type = 0; size_t j; - uint32_t kernels_to_use = (arena_mode && ebpf_modules[i].arena_kernels) ? + uint32_t kernels_to_use = (use_arena_mode && ebpf_modules[i].arena_kernels) ? ebpf_modules[i].arena_kernels : - ((buffer_mode && ebpf_modules[i].buffer_kernels) ? + ((use_buffer_mode && ebpf_modules[i].buffer_kernels) ? ebpf_modules[i].buffer_kernels : ebpf_modules[i].kernels); uint32_t max_idx = ebpf_select_max_index(rhf_version, kver); uint32_t idx = ebpf_select_index(kernels_to_use, rhf_version, kver); ebpf_discover_candidates(&candidates, ebpf_modules[i].name, is_return, - kernels_to_use, max_idx, rhf_version); + kernels_to_use, max_idx, rhf_version, use_buffer_mode, use_arena_mode); for (j = 0; j < candidates.size; j++) { struct bpf_object *obj = bpf_object__open_file(candidates.files[j], NULL); if (libbpf_get_error(obj)) { @@ -1971,7 +1985,8 @@ NETDATA_LOG_THREAD_SAFE(" },\n \"Status\" : \"%s\"\n},\n", result); ebpf_write_map_compatibility_debug(unsupported_type, &map_support); NETDATA_LOG_THREAD_SAFE(" },\n \"Status\" : \"%s\"\n},\n", "Fail"); } else { - ebpf_mount_name(load, FILENAME_MAX - 1, idx, ebpf_modules[i].name, is_return, rhf_version); + ebpf_mount_name(load, FILENAME_MAX - 1, idx, ebpf_modules[i].name, is_return, rhf_version, + use_buffer_mode, use_arena_mode); ebpf_start_netdata_json(load, is_return); { char *result = ebpf_tester(load, ebpf_modules[i].update_names, flags & NETDATA_FLAG_CONTENT, From b88836bffdc2b0e175d261e82dfcb343df87f5aa Mon Sep 17 00:00:00 2001 From: thiagoftsm Date: Mon, 25 May 2026 01:17:35 +0000 Subject: [PATCH 02/12] improve_algs: Adjust other eBPF programs --- gotests/main.go | 44 ++++++++++++++++++++++++++++++++++++++---- gotests/main_test.go | 18 +++++++++++++++-- tests/tester_user.c | 46 +++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 99 insertions(+), 9 deletions(-) diff --git a/gotests/main.go b/gotests/main.go index d96b7091..00a1f7d2 100644 --- a/gotests/main.go +++ b/gotests/main.go @@ -436,6 +436,25 @@ func parseOSRelease(content string) int { return -1 } +func IsDebianFlavor() bool { + data, err := os.ReadFile("/etc/os-release") + if err != nil { + return false + } + + for _, line := range strings.Split(string(data), "\n") { + if !strings.HasPrefix(line, "ID=") { + continue + } + + value := strings.TrimSpace(strings.TrimPrefix(line, "ID=")) + value = strings.Trim(value, "\"'") + return value == "debian" + } + + return false +} + func parseRedHatRelease(release string) int { major := 0 minor := -1 @@ -724,12 +743,28 @@ func modeSuffix(bufferMode bool, arenaMode bool) string { return "" } -func effectiveModeFlags(moduleName string, kernelVersion int, bufferMode bool, arenaMode bool) (bool, bool) { - if moduleName == "cachestat" && !bufferMode && !arenaMode && kernelVersion >= netdataEBPFKernel510 { +func effectiveModeFlags(moduleName string, kernelVersion int, isDebian bool, bufferMode bool, arenaMode bool) (bool, bool) { + if bufferMode || arenaMode { + return bufferMode, arenaMode + } + + if moduleName == "cachestat" { + if kernelVersion >= netdataEBPFKernel510 { + return true, false + } + + return false, false + } + + if moduleHasArena(moduleName) && kernelVersion >= netdataEBPFKernel612 && !isDebian { + return false, true + } + + if moduleHasBuffer(moduleName) && kernelVersion >= netdataEBPFKernel510 { return true, false } - return bufferMode, arenaMode + return false, false } func candidateMatches(filename string, moduleName string, isReturn bool, version string, rhfVersion int, bufferMode bool, arenaMode bool) bool { @@ -1058,9 +1093,10 @@ func moduleHasArena(name string) bool { func runNetdataTests(w io.Writer, rhfVersion int, kernelVersion int, isReturn bool, opts options, nprocesses int) { supportedMapTypes := detectSupportedMapTypes(rhfVersion, kernelVersion) + isDebian := IsDebianFlavor() for _, mod := range ebpfModules { - bufferMode, arenaMode := effectiveModeFlags(mod.name, kernelVersion, opts.bufferMode, opts.arenaMode) + bufferMode, arenaMode := effectiveModeFlags(mod.name, kernelVersion, isDebian, opts.bufferMode, opts.arenaMode) if opts.flags&mod.flags == 0 { continue diff --git a/gotests/main_test.go b/gotests/main_test.go index daade5c8..62105b92 100644 --- a/gotests/main_test.go +++ b/gotests/main_test.go @@ -767,6 +767,7 @@ func TestEffectiveModeFlags(t *testing.T) { tests := map[string]struct { module string kernelVersion int + isDebian bool bufferMode bool arenaMode bool wantBuffer bool @@ -778,6 +779,19 @@ func TestEffectiveModeFlags(t *testing.T) { wantBuffer: true, wantArena: false, }, + "process-defaults-to-arena-on-6-12-nondebian": { + module: "process", + kernelVersion: netdataEBPFKernel612, + wantBuffer: false, + wantArena: true, + }, + "process-stays-buffer-on-debian": { + module: "process", + kernelVersion: netdataEBPFKernel612, + isDebian: true, + wantBuffer: true, + wantArena: false, + }, "cachestat-keeps-tracing-before-buffer-support": { module: "cachestat", kernelVersion: netdataEBPFKernel414, @@ -803,13 +817,13 @@ func TestEffectiveModeFlags(t *testing.T) { module: "swap", kernelVersion: netdataEBPFKernel612, wantBuffer: false, - wantArena: false, + wantArena: true, }, } for name, tc := range tests { t.Run(name, func(t *testing.T) { - gotBuffer, gotArena := effectiveModeFlags(tc.module, tc.kernelVersion, tc.bufferMode, tc.arenaMode) + gotBuffer, gotArena := effectiveModeFlags(tc.module, tc.kernelVersion, tc.isDebian, tc.bufferMode, tc.arenaMode) if gotBuffer != tc.wantBuffer || gotArena != tc.wantArena { t.Fatalf("effectiveModeFlags() = (%v, %v), want (%v, %v)", gotBuffer, gotArena, tc.wantBuffer, tc.wantArena) } diff --git a/tests/tester_user.c b/tests/tester_user.c index af90c1a9..e4341165 100644 --- a/tests/tester_user.c +++ b/tests/tester_user.c @@ -969,6 +969,37 @@ int ebpf_get_redhat_release() return ebpf_get_rh_from_os_release(); } +static int ebpf_is_debian_flavor(void) +{ + FILE *fp = fopen("/etc/os-release", "r"); + if (!fp) + return 0; + + char line[VERSION_STRING_LEN + 1]; + int is_debian = 0; + + while (fgets(line, sizeof(line), fp)) { + if (strncmp(line, "ID=", 3) != 0) + continue; + + char *value = line + 3; + if (*value == '"' || *value == '\'') + value++; + + char *end = strpbrk(value, "\"\n"); + if (end) + *end = '\0'; + + if (!strcmp(value, "debian")) { + is_debian = 1; + break; + } + } + + fclose(fp); + return is_debian; +} + /** * Kernel Name * @@ -1912,15 +1943,24 @@ static void ebpf_run_netdata_tests(int rhf_version, uint32_t kver, int is_return ebpf_map_support_t map_support; char load[FILENAME_MAX]; int i = 0; + int is_debian = ebpf_is_debian_flavor(); ebpf_detect_map_support(&map_support, rhf_version, kver); while (ebpf_modules[i].name) { int use_buffer_mode = buffer_mode; int use_arena_mode = arena_mode; - if (!use_buffer_mode && !use_arena_mode && - !strcmp(ebpf_modules[i].name, "cachestat") && kver >= NETDATA_EBPF_KERNEL_5_10) { - use_buffer_mode = 1; + if (!use_buffer_mode && !use_arena_mode) { + if (!strcmp(ebpf_modules[i].name, "cachestat")) { + if (kver >= NETDATA_EBPF_KERNEL_5_10) + use_buffer_mode = 1; + } else if (ebpf_module_has_arena(ebpf_modules[i].name) && + kver >= NETDATA_EBPF_KERNEL_6_12 && !is_debian) { + use_arena_mode = 1; + } else if (ebpf_module_has_buffer(ebpf_modules[i].name) && + kver >= NETDATA_EBPF_KERNEL_5_10) { + use_buffer_mode = 1; + } } if (use_arena_mode && !ebpf_module_has_arena(ebpf_modules[i].name)) { From 4570ee7eab14ae551808f8029bfaabcd8a749cb7 Mon Sep 17 00:00:00 2001 From: thiagoftsm Date: Tue, 15 Sep 2026 18:32:06 +0000 Subject: [PATCH 03/12] improve_algs: Improve detection of threads and process creation --- includes/netdata_process.h | 18 ------------------ kernel/process_buffer_kern.c | 34 +++++++++++++++------------------ kernel/process_kern.c | 37 +++++++++++++++--------------------- 3 files changed, 30 insertions(+), 59 deletions(-) diff --git a/includes/netdata_process.h b/includes/netdata_process.h index aed0eff3..9687afa2 100644 --- a/includes/netdata_process.h +++ b/includes/netdata_process.h @@ -11,23 +11,6 @@ typedef struct netdata_sched_process_exit { int prio; // offset:28; size:4; signed:1; } netdata_sched_process_exit_t; -// /sys/kernel/tracing/events/sched/sched_process_fork/format -typedef struct netdata_sched_process_fork { - __u64 pad; // This is not used with eBPF - char parent_comm[16]; // offset:8; size:16; signed:1; - int parent_pid; // offset:24; size:4; signed:1; - char child_comm[16]; // offset:28; size:16; signed:1; - int child_pid; // offset:44; size:4; signed:1; -} netdata_sched_process_fork_t; - -typedef struct netdata_sched_process_fork_v2 { - __u64 pad; // This is not used with eBPF - char parent_comm[4]; // offset:8; size:4; signed:1; - int parent_pid; // offset:12; size:4; signed:1; - char child_comm[4]; // offset:16; size:4; signed:1; - int child_pid; // offset:20; size:4; signed:1; -} netdata_sched_process_fork_v2_t; - // /sys/kernel/tracing/events/sched/sched_process_exec/format typedef struct netdata_sched_process_exec { __u64 pad; // This is not used with eBPF @@ -70,4 +53,3 @@ enum process_counters { }; #endif /* _NETDATA_EBPF_PROCESS_H_ */ - diff --git a/kernel/process_buffer_kern.c b/kernel/process_buffer_kern.c index a1e6f1e8..a40db370 100644 --- a/kernel/process_buffer_kern.c +++ b/kernel/process_buffer_kern.c @@ -120,28 +120,24 @@ int netdata_tracepoint_sched_process_exec_buffer(struct netdata_sched_process_ex return 0; } -SEC("tracepoint/sched/sched_process_fork") -int netdata_tracepoint_sched_process_fork_buffer(void *ctx) +SEC("kprobe/wake_up_new_task") +int netdata_wake_up_new_task_buffer(struct pt_regs *ctx) { - libnetdata_update_global(&tbl_total_stats, NETDATA_KEY_CALLS_PROCESS, 1); + struct task_struct *child = (struct task_struct *)PT_REGS_PARM1(ctx); + __u32 child_pid = 0; + __u32 child_tgid = 0; - /* - * Read parent_pid and child_pid via byte offsets to avoid direct typed-context - * dereference, which can interfere with user-ring metadata consumers on newer kernels. - * Offsets come from /sys/kernel/tracing/events/sched/sched_process_fork/format. - */ - int parent_pid = 0, child_pid = 0; -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6,16,0)) - /* fork_v2: u64 pad(0) + char[4](8) + int parent_pid(12) + char[4](16) + int child_pid(20) */ - bpf_probe_read(&parent_pid, sizeof(parent_pid), (char *)ctx + 12); - bpf_probe_read(&child_pid, sizeof(child_pid), (char *)ctx + 20); -#else - /* fork_v1: u64 pad(0) + char[16](8) + int parent_pid(24) + char[16](28) + int child_pid(44) */ - bpf_probe_read(&parent_pid, sizeof(parent_pid), (char *)ctx + 24); - bpf_probe_read(&child_pid, sizeof(child_pid), (char *)ctx + 44); -#endif + if (!child) + return 0; + + /* pid == tgid identifies a process; a thread has a distinct task ID. */ + if (bpf_probe_read(&child_pid, sizeof(child_pid), &child->pid) || + bpf_probe_read(&child_tgid, sizeof(child_tgid), &child->tgid)) + return 0; + + libnetdata_update_global(&tbl_total_stats, NETDATA_KEY_CALLS_PROCESS, 1); - __u8 is_thread = (parent_pid != child_pid && parent_pid != 1) ? 1 : 0; + __u8 is_thread = (child_pid != child_tgid) ? 1 : 0; if (is_thread) libnetdata_update_global(&tbl_total_stats, NETDATA_KEY_CALLS_THREAD, 1); diff --git a/kernel/process_kern.c b/kernel/process_kern.c index 3ed4cef1..dbbc71a9 100644 --- a/kernel/process_kern.c +++ b/kernel/process_kern.c @@ -124,33 +124,27 @@ int netdata_tracepoint_sched_process_exec(struct netdata_sched_process_exec *ptr return 0; } -SEC("tracepoint/sched/sched_process_fork") -int netdata_tracepoint_sched_process_fork(void *ctx) +SEC("kprobe/wake_up_new_task") +int netdata_wake_up_new_task(struct pt_regs *ctx) { + struct task_struct *child = (struct task_struct *)PT_REGS_PARM1(ctx); + __u32 child_pid = 0; + __u32 child_tgid = 0; struct netdata_pid_stat_t data = { }; struct netdata_pid_stat_t *fill; - libnetdata_update_global(&tbl_total_stats, NETDATA_KEY_CALLS_PROCESS, 1); + if (!child) + return 0; - /* - * Read parent_pid and child_pid via byte offsets to avoid direct typed-context - * dereference, which can interfere with user-ring metadata consumers on newer kernels. - * Offsets come from /sys/kernel/tracing/events/sched/sched_process_fork/format. - */ - int parent_pid = 0, child_pid = 0; -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6,16,0)) - /* fork_v2: u64 pad(0) + char[4](8) + int parent_pid(12) + char[4](16) + int child_pid(20) */ - bpf_probe_read(&parent_pid, sizeof(parent_pid), (char *)ctx + 12); - bpf_probe_read(&child_pid, sizeof(child_pid), (char *)ctx + 20); -#else - /* fork_v1: u64 pad(0) + char[16](8) + int parent_pid(24) + char[16](28) + int child_pid(44) */ - bpf_probe_read(&parent_pid, sizeof(parent_pid), (char *)ctx + 24); - bpf_probe_read(&child_pid, sizeof(child_pid), (char *)ctx + 44); -#endif + /* pid == tgid identifies a process; a thread has a distinct task ID. */ + if (bpf_probe_read(&child_pid, sizeof(child_pid), &child->pid) || + bpf_probe_read(&child_tgid, sizeof(child_tgid), &child->tgid)) + return 0; + + libnetdata_update_global(&tbl_total_stats, NETDATA_KEY_CALLS_PROCESS, 1); - int thread = 0; - if (parent_pid != child_pid && parent_pid != 1) { - thread = 1; + int thread = (child_pid != child_tgid) ? 1 : 0; + if (thread) { libnetdata_update_global(&tbl_total_stats, NETDATA_KEY_CALLS_THREAD, 1); } @@ -293,4 +287,3 @@ int netdata_sys_clone(struct pt_regs *ctx) #endif char _license[] SEC("license") = "GPL"; - From b1a2a3b546afac88a72a4d79ee80f6cfcabb1f4d Mon Sep 17 00:00:00 2001 From: thiagoftsm Date: Tue, 15 Sep 2026 19:08:44 +0000 Subject: [PATCH 04/12] improve_algs: Improve collection in different collectors --- includes/netdata_disk.h | 5 +++ kernel/cachestat_kern.c | 14 +++---- kernel/dc_kern.c | 8 ++-- kernel/disk_kern.c | 77 ++++++++++++++++++++++++++++-------- kernel/hardirq_kern.c | 4 +- kernel/network_viewer_kern.c | 5 --- kernel/vfs_buffer_kern.c | 20 ++++++---- kernel/vfs_kern.c | 8 ++++ 8 files changed, 97 insertions(+), 44 deletions(-) diff --git a/includes/netdata_disk.h b/includes/netdata_disk.h index 01bece1e..8188e89d 100644 --- a/includes/netdata_disk.h +++ b/includes/netdata_disk.h @@ -54,6 +54,11 @@ typedef struct netdata_disk_key { sector_t sector; } netdata_disk_key_t; +typedef struct netdata_disk_inflight { + __u64 timestamp; + netdata_disk_key_t key; +} netdata_disk_inflight_t; + typedef struct block_key { __u32 bin; u32 dev; diff --git a/kernel/cachestat_kern.c b/kernel/cachestat_kern.c index 3ad32c65..62d3913a 100644 --- a/kernel/cachestat_kern.c +++ b/kernel/cachestat_kern.c @@ -23,7 +23,7 @@ static __always_inline void netdata_cachestat_update_existing(__u32 *field) libnetdata_update_u32(field, 1); } -static __always_inline void netdata_cachestat_create_new_entry(__u32 *field, __u32 tgid) +static __always_inline void netdata_cachestat_create_new_entry(__u32 *field, __u32 key, __u32 tgid) { netdata_cachestat_t data = {}; @@ -37,7 +37,6 @@ static __always_inline void netdata_cachestat_create_new_entry(__u32 *field, __u data.name[0] = '\0'; #endif - __u32 key = 0; if (field) *field = 1; bpf_map_update_elem(&cstat_pid, &key, &data, BPF_ANY); @@ -70,7 +69,7 @@ int netdata_add_to_page_cache_lru(struct pt_regs* ctx) } netdata_cachestat_t data = {}; - netdata_cachestat_create_new_entry(&data.add_to_page_cache_lru, tgid); + netdata_cachestat_create_new_entry(&data.add_to_page_cache_lru, key, tgid); return 0; } @@ -94,7 +93,7 @@ int netdata_mark_page_accessed(struct pt_regs* ctx) } netdata_cachestat_t data = {}; - netdata_cachestat_create_new_entry(&data.mark_page_accessed, tgid); + netdata_cachestat_create_new_entry(&data.mark_page_accessed, key, tgid); return 0; } @@ -132,7 +131,7 @@ int netdata_set_page_dirty(struct pt_regs* ctx) } netdata_cachestat_t data = {}; - netdata_cachestat_create_new_entry(&data.account_page_dirtied, tgid); + netdata_cachestat_create_new_entry(&data.account_page_dirtied, key, tgid); return 0; } @@ -160,7 +159,7 @@ int netdata_account_page_dirtied(struct pt_regs* ctx) } netdata_cachestat_t data = {}; - netdata_cachestat_create_new_entry(&data.account_page_dirtied, tgid); + netdata_cachestat_create_new_entry(&data.account_page_dirtied, key, tgid); return 0; } @@ -185,10 +184,9 @@ int netdata_mark_buffer_dirty(struct pt_regs* ctx) } netdata_cachestat_t data = {}; - netdata_cachestat_create_new_entry(&data.mark_buffer_dirty, tgid); + netdata_cachestat_create_new_entry(&data.mark_buffer_dirty, key, tgid); return 0; } char _license[] SEC("license") = "GPL"; - diff --git a/kernel/dc_kern.c b/kernel/dc_kern.c index a2f9c6c3..132e25ac 100644 --- a/kernel/dc_kern.c +++ b/kernel/dc_kern.c @@ -25,7 +25,7 @@ static __always_inline void netdata_dc_update_existing(__u32 *field) libnetdata_update_u32(field, 1); } -static __always_inline void netdata_dc_create_new_entry(__u32 *field, __u32 tgid) +static __always_inline void netdata_dc_create_new_entry(__u32 *field, __u32 key, __u32 tgid) { netdata_dc_stat_t data = {}; @@ -39,7 +39,6 @@ static __always_inline void netdata_dc_create_new_entry(__u32 *field, __u32 tgid data.name[0] = '\0'; #endif - __u32 key = 0; if (field) *field = 1; bpf_map_update_elem(&dcstat_pid, &key, &data, BPF_ANY); @@ -72,7 +71,7 @@ int netdata_lookup_fast(struct pt_regs* ctx) } netdata_dc_stat_t data = {}; - netdata_dc_create_new_entry(&data.references, tgid); + netdata_dc_create_new_entry(&data.references, key, tgid); return 0; } @@ -96,7 +95,7 @@ int netdata_d_lookup(struct pt_regs* ctx) netdata_dc_update_existing(&fill->slow); } else { netdata_dc_stat_t data = {}; - netdata_dc_create_new_entry(&data.slow, tgid); + netdata_dc_create_new_entry(&data.slow, key, tgid); } if (ret == 0) { @@ -110,4 +109,3 @@ int netdata_d_lookup(struct pt_regs* ctx) } char _license[] SEC("license") = "GPL"; - diff --git a/kernel/disk_kern.c b/kernel/disk_kern.c index c7811f2a..8851db17 100644 --- a/kernel/disk_kern.c +++ b/kernel/disk_kern.c @@ -1,5 +1,6 @@ #define KBUILD_MODNAME "disk_netdata" #include +#include #if (LINUX_VERSION_CODE < KERNEL_VERSION(5,18,0)) #include #endif @@ -18,7 +19,7 @@ #include "netdata_ebpf.h" NETDATA_BPF_PERCPU_HASH_DEF(tbl_disk_iocall, block_key_t, __u64, NETDATA_DISK_HISTOGRAM_LENGTH); -NETDATA_BPF_HASH_DEF(tmp_disk_tp_stat, netdata_disk_key_t, __u64, 8192); +NETDATA_BPF_HASH_DEF(tmp_disk_tp_stat, __u64, netdata_disk_inflight_t, 8192); NETDATA_BPF_ARRAY_DEF(disk_ctrl, __u32, __u64, NETDATA_CONTROLLER_END); /************************************************************************************ @@ -38,43 +39,80 @@ static __always_inline netdata_disk_key_t netdata_disk_key(void *ptr) return key; } +static __always_inline int netdata_disk_request_key(struct request *rq, netdata_disk_key_t *key) +{ + struct request_queue *queue = NULL; + struct gendisk *disk = NULL; + struct block_device *part = NULL; + + if (!rq) + return 0; + + bpf_probe_read(&queue, sizeof(queue), &rq->q); + if (!queue) + return 0; + bpf_probe_read(&disk, sizeof(disk), &queue->disk); + if (!disk) + return 0; + bpf_probe_read(&part, sizeof(part), &disk->part0); + if (!part) + return 0; + + key->dev = 0; + key->pad = 0; + key->sector = 0; + bpf_probe_read(&key->dev, sizeof(key->dev), &part->bd_dev); + bpf_probe_read(&key->sector, sizeof(key->sector), &rq->__sector); + if (!key->dev) + return 0; + if ((s64)key->sector < 0) + key->sector = 0; + return 1; +} + /************************************************************************************ * - * Tracepoints + * Request Probes * ***********************************************************************************/ -SEC("tracepoint/block/block_rq_issue") -int netdata_block_rq_issue(struct netdata_block_rq_issue *ptr) +SEC("kprobe/blk_mq_start_request") +int netdata_block_rq_issue(struct pt_regs *ctx) { - if (!ptr->dev) + struct request *rq = (struct request *)PT_REGS_PARM1(ctx); + netdata_disk_key_t disk_key = { }; + if (!netdata_disk_request_key(rq, &disk_key)) return 0; - netdata_disk_key_t key = netdata_disk_key(ptr); - - __u64 value = bpf_ktime_get_ns(); - bpf_map_update_elem(&tmp_disk_tp_stat, &key, &value, BPF_ANY); + __u64 request_key = (__u64)rq; + netdata_disk_inflight_t value = { + .timestamp = bpf_ktime_get_ns(), + .key = disk_key, + }; + if (bpf_map_update_elem(&tmp_disk_tp_stat, &request_key, &value, BPF_ANY)) + return 0; libnetdata_update_global(&disk_ctrl, NETDATA_CONTROLLER_PID_TABLE_ADD, 1); return 0; } -SEC("tracepoint/block/block_rq_complete") -int netdata_block_rq_complete(struct netdata_block_rq_complete *ptr) +SEC("kprobe/blk_mq_end_request") +int netdata_block_rq_complete(struct pt_regs *ctx) { - netdata_disk_key_t key = netdata_disk_key(ptr); + struct request *rq = (struct request *)PT_REGS_PARM1(ctx); + __u64 request_key = (__u64)rq; - __u64 *fill = bpf_map_lookup_elem(&tmp_disk_tp_stat, &key); + netdata_disk_inflight_t *fill = bpf_map_lookup_elem(&tmp_disk_tp_stat, &request_key); if (!fill) return 0; - __u64 curr = bpf_ktime_get_ns() - *fill; + __u64 curr = bpf_ktime_get_ns() - fill->timestamp; curr /= 1000; block_key_t blk = { .bin = libnetdata_select_idx(curr, NETDATA_FS_MAX_BINS_POS), - .dev = netdata_new_encode_dev(ptr->dev) + .dev = netdata_new_encode_dev(fill->key.dev) }; __u64 *update = bpf_map_lookup_elem(&tbl_disk_iocall, &blk); @@ -84,11 +122,18 @@ int netdata_block_rq_complete(struct netdata_block_rq_complete *ptr) bpf_map_update_elem(&tbl_disk_iocall, &blk, &(__u64){1}, BPF_ANY); } - bpf_map_delete_elem(&tmp_disk_tp_stat, &key); + bpf_map_delete_elem(&tmp_disk_tp_stat, &request_key); libnetdata_update_global(&disk_ctrl, NETDATA_CONTROLLER_PID_TABLE_DEL, 1); return 0; } +/* Legacy request queues complete through this non-exported block-layer path. */ +SEC("kprobe/blk_complete_request") +int netdata_blk_complete_request(struct pt_regs *ctx) +{ + return netdata_block_rq_complete(ctx); +} + char _license[] SEC("license") = "GPL"; diff --git a/kernel/hardirq_kern.c b/kernel/hardirq_kern.c index 594f8c11..872f92a5 100644 --- a/kernel/hardirq_kern.c +++ b/kernel/hardirq_kern.c @@ -14,8 +14,8 @@ * MAPS ***********************************************************************************/ -NETDATA_BPF_HASH_DEF(tbl_hardirq, hardirq_key_t, hardirq_val_t, NETDATA_HARDIRQ_MAX_IRQS); -NETDATA_BPF_ARRAY_DEF(tbl_hardirq_static, __u32, hardirq_val_t, NETDATA_HARDIRQ_STATIC_END); +NETDATA_BPF_PERCPU_HASH_DEF(tbl_hardirq, hardirq_key_t, hardirq_val_t, NETDATA_HARDIRQ_MAX_IRQS); +NETDATA_BPF_PERCPU_ARRAY_DEF(tbl_hardirq_static, __u32, hardirq_val_t, NETDATA_HARDIRQ_STATIC_END); /************************************************************************************ * HARDIRQ SECTION diff --git a/kernel/network_viewer_kern.c b/kernel/network_viewer_kern.c index 6bbdf96d..d0d40578 100644 --- a/kernel/network_viewer_kern.c +++ b/kernel/network_viewer_kern.c @@ -464,9 +464,7 @@ int trace_udp_recvmsg(struct pt_regs* ctx) NETDATA_SOCKET_DIRECTION direction = NETDATA_SOCKET_DIRECTION_NONE; netdata_nv_data_t *val = (netdata_nv_data_t *) bpf_map_lookup_elem(&tbl_nv_socket, &idx); if (val) { - direction = NETDATA_SOCKET_DIRECTION_OUTBOUND; set_common_udp_nv_data(val, sk, family, direction); - val->closed = 1; return 0; } @@ -498,9 +496,7 @@ int trace_udp_sendmsg(struct pt_regs* ctx) NETDATA_SOCKET_DIRECTION direction = NETDATA_SOCKET_DIRECTION_NONE; netdata_nv_data_t *val = (netdata_nv_data_t *) bpf_map_lookup_elem(&tbl_nv_socket, &idx); if (val) { - direction = NETDATA_SOCKET_DIRECTION_INBOUND; set_common_udp_nv_data(val, sk, family, direction); - val->closed = 1; return 0; } @@ -519,4 +515,3 @@ int trace_udp_sendmsg(struct pt_regs* ctx) } char _license[] SEC("license") = "GPL"; - diff --git a/kernel/vfs_buffer_kern.c b/kernel/vfs_buffer_kern.c index 3b26eeb3..041e7b93 100644 --- a/kernel/vfs_buffer_kern.c +++ b/kernel/vfs_buffer_kern.c @@ -70,10 +70,11 @@ SEC("kprobe/vfs_write") #endif int netdata_sys_write_buffer(struct pt_regs *ctx) { - ssize_t bytes = (ssize_t)PT_REGS_PARM3(ctx); #if NETDATASEL < 2 - __u8 err = ((ssize_t)PT_REGS_RC(ctx) < 0) ? 1 : 0; + ssize_t bytes = (ssize_t)PT_REGS_RC(ctx); + __u8 err = (bytes < 0) ? 1 : 0; #else + ssize_t bytes = (ssize_t)PT_REGS_PARM3(ctx); __u8 err = 0; #endif @@ -107,10 +108,11 @@ SEC("kprobe/vfs_writev") #endif int netdata_sys_writev_buffer(struct pt_regs *ctx) { - ssize_t bytes = (ssize_t)PT_REGS_PARM3(ctx); #if NETDATASEL < 2 - __u8 err = ((ssize_t)PT_REGS_RC(ctx) < 0) ? 1 : 0; + ssize_t bytes = (ssize_t)PT_REGS_RC(ctx); + __u8 err = (bytes < 0) ? 1 : 0; #else + ssize_t bytes = (ssize_t)PT_REGS_PARM3(ctx); __u8 err = 0; #endif @@ -144,10 +146,11 @@ SEC("kprobe/vfs_read") #endif int netdata_sys_read_buffer(struct pt_regs *ctx) { - ssize_t bytes = (ssize_t)PT_REGS_PARM3(ctx); #if NETDATASEL < 2 - __u8 err = ((ssize_t)PT_REGS_RC(ctx) < 0) ? 1 : 0; + ssize_t bytes = (ssize_t)PT_REGS_RC(ctx); + __u8 err = (bytes < 0) ? 1 : 0; #else + ssize_t bytes = (ssize_t)PT_REGS_PARM3(ctx); __u8 err = 0; #endif @@ -181,10 +184,11 @@ SEC("kprobe/vfs_readv") #endif int netdata_sys_readv_buffer(struct pt_regs *ctx) { - ssize_t bytes = (ssize_t)PT_REGS_PARM3(ctx); #if NETDATASEL < 2 - __u8 err = ((ssize_t)PT_REGS_RC(ctx) < 0) ? 1 : 0; + ssize_t bytes = (ssize_t)PT_REGS_RC(ctx); + __u8 err = (bytes < 0) ? 1 : 0; #else + ssize_t bytes = (ssize_t)PT_REGS_PARM3(ctx); __u8 err = 0; #endif diff --git a/kernel/vfs_kern.c b/kernel/vfs_kern.c index b375752e..652df4ae 100644 --- a/kernel/vfs_kern.c +++ b/kernel/vfs_kern.c @@ -87,7 +87,9 @@ int netdata_sys_write(struct pt_regs* ctx) if (ret < 0) libnetdata_update_global(&tbl_vfs_stats, NETDATA_KEY_ERROR_VFS_WRITE, 1); #endif +#if NETDATASEL >= 2 ret = (ssize_t)PT_REGS_PARM3(ctx); +#endif tot = libnetdata_log2l(ret); libnetdata_update_global(&tbl_vfs_stats, NETDATA_KEY_BYTES_VFS_WRITE, tot); @@ -129,7 +131,9 @@ int netdata_sys_writev(struct pt_regs* ctx) if (ret < 0) libnetdata_update_global(&tbl_vfs_stats, NETDATA_KEY_ERROR_VFS_WRITEV, 1); #endif +#if NETDATASEL >= 2 ret = (ssize_t)PT_REGS_PARM3(ctx); +#endif tot = libnetdata_log2l(ret); libnetdata_update_global(&tbl_vfs_stats, NETDATA_KEY_BYTES_VFS_WRITEV, tot); @@ -171,7 +175,9 @@ int netdata_sys_read(struct pt_regs* ctx) if (ret < 0) libnetdata_update_global(&tbl_vfs_stats, NETDATA_KEY_ERROR_VFS_READ, 1); #endif +#if NETDATASEL >= 2 ret = (ssize_t)PT_REGS_PARM3(ctx); +#endif tot = libnetdata_log2l(ret); libnetdata_update_global(&tbl_vfs_stats, NETDATA_KEY_BYTES_VFS_READ, tot); @@ -213,7 +219,9 @@ int netdata_sys_readv(struct pt_regs* ctx) if (ret < 0) libnetdata_update_global(&tbl_vfs_stats, NETDATA_KEY_ERROR_VFS_READV, 1); #endif +#if NETDATASEL >= 2 ret = (ssize_t)PT_REGS_PARM3(ctx); +#endif tot = libnetdata_log2l(ret); libnetdata_update_global(&tbl_vfs_stats, NETDATA_KEY_BYTES_VFS_READV, tot); From 2504f3339c6b0f6edadb92f91835c88350aef085 Mon Sep 17 00:00:00 2001 From: thiagoftsm Date: Wed, 16 Sep 2026 00:54:08 +0000 Subject: [PATCH 05/12] improve_algs: Fix wrong code made by AI --- kernel/disk_kern.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/kernel/disk_kern.c b/kernel/disk_kern.c index 8851db17..efaaaacd 100644 --- a/kernel/disk_kern.c +++ b/kernel/disk_kern.c @@ -41,17 +41,22 @@ static __always_inline netdata_disk_key_t netdata_disk_key(void *ptr) static __always_inline int netdata_disk_request_key(struct request *rq, netdata_disk_key_t *key) { - struct request_queue *queue = NULL; struct gendisk *disk = NULL; struct block_device *part = NULL; if (!rq) return 0; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(6,0,0)) + /* request_queue::disk is not present on 5.4; requests carry rq_disk. */ + bpf_probe_read(&disk, sizeof(disk), &rq->rq_disk); +#else + struct request_queue *queue = NULL; bpf_probe_read(&queue, sizeof(queue), &rq->q); if (!queue) return 0; bpf_probe_read(&disk, sizeof(disk), &queue->disk); +#endif if (!disk) return 0; bpf_probe_read(&part, sizeof(part), &disk->part0); From d8531fcf2b35195cf134017af70267f945b90027 Mon Sep 17 00:00:00 2001 From: thiagoftsm Date: Wed, 16 Sep 2026 01:29:33 +0000 Subject: [PATCH 06/12] improve_algs: Fix ARENA loading --- includes/netdata_arena_common.h | 20 +++++++++++++++++--- kernel/disk_kern.c | 11 ++++++++--- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/includes/netdata_arena_common.h b/includes/netdata_arena_common.h index 3cde8376..db24defd 100644 --- a/includes/netdata_arena_common.h +++ b/includes/netdata_arena_common.h @@ -24,6 +24,10 @@ #define NETDATA_ARENA_MAP_PAGES 256 #define NETDATA_ARENA_EVENT_SLOTS 1024 +struct netdata_arena_lock_t { + struct bpf_spin_lock lock; +}; + #define NETDATA_BPF_ARENA_DEF(NAME, MAX_ENTRIES) \ struct { \ __uint(type, BPF_MAP_TYPE_ARENA); \ @@ -33,15 +37,25 @@ } NAME SEC(".maps") #define NETDATA_ARENA_QUEUE_DECL(PREFIX, EVENT_TYPE, SLOT_COUNT) \ + struct { \ + __uint(type, BPF_MAP_TYPE_ARRAY); \ + __type(key, __u32); \ + __type(value, struct netdata_arena_lock_t); \ + __uint(max_entries, 1); \ + } netdata_##PREFIX##_arena_lock SEC(".maps"); \ struct netdata_##PREFIX##_arena_state_t { \ __u32 head; \ EVENT_TYPE events[SLOT_COUNT]; \ }; \ extern __arena struct netdata_##PREFIX##_arena_state_t PREFIX##_arena_state; \ static __always_inline __arena EVENT_TYPE *netdata_##PREFIX##_arena_reserve(void) { \ - /* BPF backend rejects using the XADD return value directly. */ \ - __sync_fetch_and_add(&PREFIX##_arena_state.head, 1); \ - __u32 idx = PREFIX##_arena_state.head - 1; \ + __u32 key = 0; \ + struct netdata_arena_lock_t *lock = bpf_map_lookup_elem(&netdata_##PREFIX##_arena_lock, &key); \ + if (!lock) \ + return NULL; \ + bpf_spin_lock(&lock->lock); \ + __u32 idx = PREFIX##_arena_state.head++; \ + bpf_spin_unlock(&lock->lock); \ return &PREFIX##_arena_state.events[idx % SLOT_COUNT]; \ } \ static __always_inline void netdata_##PREFIX##_arena_submit(__arena EVENT_TYPE *ev) { \ diff --git a/kernel/disk_kern.c b/kernel/disk_kern.c index efaaaacd..6ca068bd 100644 --- a/kernel/disk_kern.c +++ b/kernel/disk_kern.c @@ -102,8 +102,7 @@ int netdata_block_rq_issue(struct pt_regs *ctx) return 0; } -SEC("kprobe/blk_mq_end_request") -int netdata_block_rq_complete(struct pt_regs *ctx) +static __always_inline int netdata_block_rq_complete_impl(struct pt_regs *ctx) { struct request *rq = (struct request *)PT_REGS_PARM1(ctx); __u64 request_key = (__u64)rq; @@ -138,7 +137,13 @@ int netdata_block_rq_complete(struct pt_regs *ctx) SEC("kprobe/blk_complete_request") int netdata_blk_complete_request(struct pt_regs *ctx) { - return netdata_block_rq_complete(ctx); + return netdata_block_rq_complete_impl(ctx); +} + +SEC("kprobe/blk_mq_end_request") +int netdata_block_rq_complete(struct pt_regs *ctx) +{ + return netdata_block_rq_complete_impl(ctx); } char _license[] SEC("license") = "GPL"; From a4c100748c13133534d1f379b1c724d4f092a85d Mon Sep 17 00:00:00 2001 From: thiagoftsm Date: Wed, 16 Sep 2026 12:30:36 +0000 Subject: [PATCH 07/12] improve_algs: Try to fix issues with recent kernel --- includes/netdata_arena_common.h | 16 ---------------- kernel/disk_kern.c | 2 ++ 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/includes/netdata_arena_common.h b/includes/netdata_arena_common.h index db24defd..425f1d8d 100644 --- a/includes/netdata_arena_common.h +++ b/includes/netdata_arena_common.h @@ -24,10 +24,6 @@ #define NETDATA_ARENA_MAP_PAGES 256 #define NETDATA_ARENA_EVENT_SLOTS 1024 -struct netdata_arena_lock_t { - struct bpf_spin_lock lock; -}; - #define NETDATA_BPF_ARENA_DEF(NAME, MAX_ENTRIES) \ struct { \ __uint(type, BPF_MAP_TYPE_ARENA); \ @@ -37,25 +33,13 @@ struct netdata_arena_lock_t { } NAME SEC(".maps") #define NETDATA_ARENA_QUEUE_DECL(PREFIX, EVENT_TYPE, SLOT_COUNT) \ - struct { \ - __uint(type, BPF_MAP_TYPE_ARRAY); \ - __type(key, __u32); \ - __type(value, struct netdata_arena_lock_t); \ - __uint(max_entries, 1); \ - } netdata_##PREFIX##_arena_lock SEC(".maps"); \ struct netdata_##PREFIX##_arena_state_t { \ __u32 head; \ EVENT_TYPE events[SLOT_COUNT]; \ }; \ extern __arena struct netdata_##PREFIX##_arena_state_t PREFIX##_arena_state; \ static __always_inline __arena EVENT_TYPE *netdata_##PREFIX##_arena_reserve(void) { \ - __u32 key = 0; \ - struct netdata_arena_lock_t *lock = bpf_map_lookup_elem(&netdata_##PREFIX##_arena_lock, &key); \ - if (!lock) \ - return NULL; \ - bpf_spin_lock(&lock->lock); \ __u32 idx = PREFIX##_arena_state.head++; \ - bpf_spin_unlock(&lock->lock); \ return &PREFIX##_arena_state.events[idx % SLOT_COUNT]; \ } \ static __always_inline void netdata_##PREFIX##_arena_submit(__arena EVENT_TYPE *ev) { \ diff --git a/kernel/disk_kern.c b/kernel/disk_kern.c index 6ca068bd..a75a4c55 100644 --- a/kernel/disk_kern.c +++ b/kernel/disk_kern.c @@ -133,12 +133,14 @@ static __always_inline int netdata_block_rq_complete_impl(struct pt_regs *ctx) return 0; } +#if (LINUX_VERSION_CODE < KERNEL_VERSION(6,0,0)) /* Legacy request queues complete through this non-exported block-layer path. */ SEC("kprobe/blk_complete_request") int netdata_blk_complete_request(struct pt_regs *ctx) { return netdata_block_rq_complete_impl(ctx); } +#endif SEC("kprobe/blk_mq_end_request") int netdata_block_rq_complete(struct pt_regs *ctx) From 96548009ff27239e461830edbd9cf2da3e7fa9a6 Mon Sep 17 00:00:00 2001 From: thiagoftsm Date: Wed, 16 Sep 2026 13:15:14 +0000 Subject: [PATCH 08/12] improve_algs: Fix compilation --- kernel/Makefile | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/kernel/Makefile b/kernel/Makefile index 605c697e..1f96747d 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -29,6 +29,10 @@ CLANG_MAJOR_VERSION := $(shell echo $(CLANG_VERSION) | cut -d. -f1 ) LLVM_INCLUDES = $(shell [ -d /usr/lib/clang ] && echo "-I/usr/lib/clang/$(CLANG_VERSION)/include" || echo "-I/usr/lib64/clang/$(CLANG_VERSION)/include") LLVM_INCLUDES += -I/opt/rh/llvm-toolset-7.0/root/usr/lib64/clang/$(CLANG_VERSION)/include +# Kernel sources include shared collector headers directly. Keep generated +# objects in sync when one of those headers changes. +NETDATA_KERNEL_HEADERS := $(wildcard ../includes/*.h) + #KERNEL_VERSION="$(shell basename $(realpath $(KERNELSOURCE)) | cut -f 2 -d '-')" KERNEL_VERSION="$(shell cat $(KERNELSOURCE)/include/config/kernel.release)" @@ -154,7 +158,7 @@ libbpf: # -fPIE added to be compatible with olders clang/gcc cd $(LIBBPF)/src && /bin/bash ../../.dockerfiles/change_libbpf.sh $(VER_MAJOR) $(VER_MINOR) && $(MAKE) CC=$(CC_LIBBPF) CFLAGS="-fPIE" BUILD_STATIC_ONLY=1 DESTDIR=../../.local_libbpf INCLUDEDIR= LIBDIR= UAPIDIR= install \ -%_kern.o: %_kern.c libbpf +%_kern.o: %_kern.c $(NETDATA_KERNEL_HEADERS) libbpf $(CLANG) $(EXTRA_CFLAGS) -S -nostdinc $(LINUXINCLUDE) $(LLVM_INCLUDES) \ -D__KERNEL__ -D__ASM_SYSREG_H -Wno-unused-value -Wno-pointer-sign \ -Wno-compare-distinct-pointer-types \ From 0a5cef46e5326c0aeae5f6a16962c06a5a740316 Mon Sep 17 00:00:00 2001 From: thiagoftsm Date: Wed, 16 Sep 2026 14:36:51 +0000 Subject: [PATCH 09/12] improve_algs: Fix runtiime --- gotests/cgo_helpers.go | 4 ++ gotests/main.go | 108 +++++++++++++++++++++++++++++++++------- gotests/main_test.go | 34 +++++++++++++ includes/netdata_ebpf.h | 2 +- 4 files changed, 130 insertions(+), 18 deletions(-) diff --git a/gotests/cgo_helpers.go b/gotests/cgo_helpers.go index 3a932aae..16b8a0f0 100644 --- a/gotests/cgo_helpers.go +++ b/gotests/cgo_helpers.go @@ -427,6 +427,10 @@ func (p *bpfProgram) name() string { return C.GoString(C.bpf_program__name(p.ptr)) } +func (p *bpfProgram) sectionName() string { + return C.GoString(C.bpf_program__section_name(p.ptr)) +} + func (p *bpfProgram) progType() uint32 { return uint32(C.bpf_program__type(p.ptr)) } diff --git a/gotests/main.go b/gotests/main.go index 00a1f7d2..5b43fdbe 100644 --- a/gotests/main.go +++ b/gotests/main.go @@ -150,6 +150,21 @@ type tableData struct { var ( testsStarted = 0 + netdataSyscalls = map[string]struct{}{ + "fdatasync": {}, + "fsync": {}, + "mount": {}, + "msync": {}, + "shmat": {}, + "shmctl": {}, + "shmdt": {}, + "shmget": {}, + "sync": {}, + "sync_file_range": {}, + "syncfs": {}, + "umount": {}, + } + dcOptionalNames = []specifyName{ { programName: "netdata_lookup_fast", @@ -318,7 +333,7 @@ func run() int { runNetdataTests(writer, rhfVersion, kernelVersion, false, opts, nprocesses) } else if opts.specificEBPF != "" { startExternalJSON(writer, opts.specificEBPF) - result := ebpfTester(writer, opts.specificEBPF, nil, opts.flags&flagContent != 0, "", opts, nprocesses) + result := ebpfTester(writer, opts.specificEBPF, nil, opts.flags&flagContent != 0, "", opts, nprocesses, kernelVersion) fmt.Fprintf(writer, " },\n \"Status\" : \"%s\"\n},\n", result) } @@ -1134,7 +1149,7 @@ func runNetdataTests(w io.Writer, rhfVersion int, kernelVersion int, isReturn bo for _, filename := range compatible { startNetdataJSON(w, filename, isReturn) - result := ebpfTester(w, filename, mod.updateNames, opts.flags&flagContent != 0, mod.ctrlTable, opts, nprocesses) + result := ebpfTester(w, filename, mod.updateNames, opts.flags&flagContent != 0, mod.ctrlTable, opts, nprocesses, kernelVersion) fmt.Fprintf(w, " },\n \"Status\" : \"%s\"\n},\n", result) } } @@ -1240,7 +1255,7 @@ func startNetdataJSON(w io.Writer, filename string, isReturn bool) { fmt.Fprintf(w, "\"%s\" : {\n \"Test\" : \"%s\",\n \"Tables\" : {\n", filename, testType) } -func ebpfTester(w io.Writer, filename string, names *[]specifyName, maps bool, ctrl string, opts options, nprocesses int) string { +func ebpfTester(w io.Writer, filename string, names *[]specifyName, maps bool, ctrl string, opts options, nprocesses int, kernelVersion int) string { const ( success = "Success" failure = "Fail" @@ -1267,7 +1282,7 @@ func ebpfTester(w io.Writer, filename string, names *[]specifyName, maps bool, c return failure } - summary := attachPrograms(obj, names) + summary := attachPrograms(obj, names, kernelVersion) if summary.fail > 0 { writeFailureDebug(w, obj, "attach_programs", summary.lastError, socketFilterDetected, total, summary) } @@ -1294,7 +1309,7 @@ func ebpfTester(w io.Writer, filename string, names *[]specifyName, maps bool, c return failure } -func attachPrograms(obj *bpfObject, names *[]specifyName) attachSummary { +func attachPrograms(obj *bpfObject, names *[]specifyName, kernelVersion int) attachSummary { var summary attachSummary for prog := obj.firstProgram(); prog != nil; prog = obj.nextProgram(prog) { @@ -1303,19 +1318,23 @@ func attachPrograms(obj *bpfObject, names *[]specifyName) attachSummary { err int ) - override := findOptionalName(names, prog.name()) - if override != nil && prog.progType() == bpfProgTypeKprobe { - target := override.optional - if target == "" && override.required { - target = override.functionToAttach - } - if target == "" { - summary.skipped++ - continue - } - link, err = prog.attachKprobe(override.retprobe, target) + if retprobe, target, ok := syscallAttachTarget(prog.sectionName(), kernelVersion); ok { + link, err = prog.attachKprobe(retprobe, target) } else { - link, err = prog.attach() + override := findOptionalName(names, prog.name()) + if override != nil && prog.progType() == bpfProgTypeKprobe { + target := override.optional + if target == "" && override.required { + target = override.functionToAttach + } + if target == "" { + summary.skipped++ + continue + } + link, err = prog.attachKprobe(override.retprobe, target) + } else { + link, err = prog.attach() + } } if err != 0 { @@ -1333,6 +1352,61 @@ func attachPrograms(obj *bpfObject, names *[]specifyName) attachSummary { return summary } +func syscallAttachTarget(section string, kernelVersion int) (bool, string, bool) { + retprobe := false + target := "" + + switch { + case strings.HasPrefix(section, "ksyscall/"): + target = strings.TrimPrefix(section, "ksyscall/") + case strings.HasPrefix(section, "kretsyscall/"): + retprobe = true + target = strings.TrimPrefix(section, "kretsyscall/") + case strings.HasPrefix(section, "kprobe/"): + target = strings.TrimPrefix(section, "kprobe/") + case strings.HasPrefix(section, "kretprobe/"): + retprobe = true + target = strings.TrimPrefix(section, "kretprobe/") + default: + return false, "", false + } + + syscallName := "" + for _, prefix := range []string{"__x64_sys_", "__arm64_sys_", "__s390x_", "sys_"} { + if strings.HasPrefix(target, prefix) { + syscallName = strings.TrimPrefix(target, prefix) + break + } + } + if syscallName == "" { + // kprobe sections for ordinary kernel functions must use libbpf's + // normal auto-attach path. + if strings.HasPrefix(section, "ksyscall/") || strings.HasPrefix(section, "kretsyscall/") { + syscallName = target + } else { + return false, "", false + } + } + if _, ok := netdataSyscalls[syscallName]; !ok && + !strings.HasPrefix(section, "ksyscall/") && !strings.HasPrefix(section, "kretsyscall/") { + return false, "", false + } + + prefix := "sys_" + if kernelVersion >= netdataEBPFKernel417 { + switch runtime.GOARCH { + case "amd64": + prefix = "__x64_sys_" + case "arm64": + prefix = "__arm64_sys_" + case "s390x": + prefix = "__s390x_" + } + } + + return retprobe, prefix + syscallName, true +} + func findOptionalName(names *[]specifyName, programName string) *specifyName { if names == nil { return nil diff --git a/gotests/main_test.go b/gotests/main_test.go index 62105b92..d3c79856 100644 --- a/gotests/main_test.go +++ b/gotests/main_test.go @@ -873,6 +873,40 @@ func TestFindOptionalName(t *testing.T) { } } +func TestSyscallAttachTarget(t *testing.T) { + tests := []struct { + name string + section string + version int + retprobe bool + targetPart string + want bool + }{ + {name: "legacy syscall section", section: "kprobe/sys_fsync", version: netdataEBPFKernel415, targetPart: "fsync", want: true}, + {name: "modern syscall section", section: "ksyscall/fsync", version: netdataEBPFKernel417, targetPart: "fsync", want: true}, + {name: "legacy return syscall section", section: "kretprobe/sys_mount", version: netdataEBPFKernel415, retprobe: true, targetPart: "mount", want: true}, + {name: "ordinary kprobe", section: "kprobe/lookup_fast", version: netdataEBPFKernel612, want: false}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + retprobe, target, ok := syscallAttachTarget(tc.section, tc.version) + if ok != tc.want { + t.Fatalf("syscallAttachTarget() recognized=%v, want %v", ok, tc.want) + } + if !ok { + return + } + if retprobe != tc.retprobe { + t.Fatalf("retprobe=%v, want %v", retprobe, tc.retprobe) + } + if !strings.HasSuffix(target, tc.targetPart) { + t.Fatalf("target=%q, want suffix %q", target, tc.targetPart) + } + }) + } +} + func TestSetCommonFlag(t *testing.T) { got := setCommonFlag() diff --git a/includes/netdata_ebpf.h b/includes/netdata_ebpf.h index 33f1a761..08cf3919 100644 --- a/includes/netdata_ebpf.h +++ b/includes/netdata_ebpf.h @@ -13,6 +13,7 @@ This header has the common definitions for all `.c` files. #include #include +#include "libbpf_version.h" #include "netdata_common.h" #include "netdata_cache.h" #include "netdata_dc.h" @@ -32,4 +33,3 @@ This header has the common definitions for all `.c` files. #include "netdata_vfs.h" #endif /* _NETDATA_EBPF_ */ - From 44c6b3b942f22d1ed90b45929fe6dcb97870be5a Mon Sep 17 00:00:00 2001 From: thiagoftsm Date: Wed, 16 Sep 2026 19:04:08 +0000 Subject: [PATCH 10/12] improve_algs: Try to fix runtime (again) --- gotests/main.go | 10 +++++++++ gotests/main_test.go | 21 +++++++++++++++++++ includes/netdata_arena_common.h | 37 +++++++++++++++++++++++++++++---- 3 files changed, 64 insertions(+), 4 deletions(-) diff --git a/gotests/main.go b/gotests/main.go index 5b43fdbe..079666d5 100644 --- a/gotests/main.go +++ b/gotests/main.go @@ -31,6 +31,7 @@ const ( netdataEBPFKernel417 = 266496 netdataEBPFKernel54 = 328704 netdataEBPFKernel58 = 329728 + netdataEBPFKernel60 = 393216 netdataEBPFKernel510 = 330240 netdataEBPFKernel511 = 330496 netdataEBPFKernel514 = 331264 @@ -1313,6 +1314,11 @@ func attachPrograms(obj *bpfObject, names *[]specifyName, kernelVersion int) att var summary attachSummary for prog := obj.firstProgram(); prog != nil; prog = obj.nextProgram(prog) { + if skipProgramForKernel(prog.sectionName(), kernelVersion) { + summary.skipped++ + continue + } + var ( link *bpfLink err int @@ -1352,6 +1358,10 @@ func attachPrograms(obj *bpfObject, names *[]specifyName, kernelVersion int) att return summary } +func skipProgramForKernel(section string, kernelVersion int) bool { + return kernelVersion >= netdataEBPFKernel60 && section == "kprobe/blk_complete_request" +} + func syscallAttachTarget(section string, kernelVersion int) (bool, string, bool) { retprobe := false target := "" diff --git a/gotests/main_test.go b/gotests/main_test.go index d3c79856..6e1d9456 100644 --- a/gotests/main_test.go +++ b/gotests/main_test.go @@ -907,6 +907,27 @@ func TestSyscallAttachTarget(t *testing.T) { } } +func TestSkipProgramForKernel(t *testing.T) { + tests := []struct { + name string + section string + version int + want bool + }{ + {name: "legacy disk completion on 5.4", section: "kprobe/blk_complete_request", version: netdataEBPFKernel54, want: false}, + {name: "legacy disk completion on 6.0", section: "kprobe/blk_complete_request", version: netdataEBPFKernel60, want: true}, + {name: "modern disk completion", section: "kprobe/blk_mq_end_request", version: netdataEBPFKernel612, want: false}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + if got := skipProgramForKernel(tc.section, tc.version); got != tc.want { + t.Fatalf("skipProgramForKernel() = %v, want %v", got, tc.want) + } + }) + } +} + func TestSetCommonFlag(t *testing.T) { got := setCommonFlag() diff --git a/includes/netdata_arena_common.h b/includes/netdata_arena_common.h index 425f1d8d..53da1229 100644 --- a/includes/netdata_arena_common.h +++ b/includes/netdata_arena_common.h @@ -3,9 +3,13 @@ #ifndef _NETDATA_ARENA_COMMON_ #define _NETDATA_ARENA_COMMON_ 1 -#if defined(__BPF_FEATURE_ADDR_SPACE_CAST) +/* Force the explicit form because the compiler form does not reliably cast + * pointers loaded from global arena data before tracing-program accesses. */ +#define NETDATA_ARENA_FORCE_ASM 1 + +#if defined(__BPF_FEATURE_ADDR_SPACE_CAST) && !defined(NETDATA_ARENA_FORCE_ASM) #define __arena __attribute__((address_space(1))) -#define __arena_global __attribute__((address_space(1))) SEC(".addr_space.1") +#define __arena_global __attribute__((address_space(1))) #else #define __arena #define __arena_global SEC(".addr_space.1") @@ -24,6 +28,29 @@ #define NETDATA_ARENA_MAP_PAGES 256 #define NETDATA_ARENA_EVENT_SLOTS 1024 +/* LLVM does not consistently emit the BPF arena address-space cast when a + * global arena object is dereferenced from a tracing program. */ +#ifndef netdata_bpf_addr_space_cast +#define netdata_bpf_addr_space_cast(var, dst_as, src_as) \ + asm volatile(\ + ".byte 0xBF; \ + .ifc %[reg], r0; .byte 0x00; .endif; \ + .ifc %[reg], r1; .byte 0x11; .endif; \ + .ifc %[reg], r2; .byte 0x22; .endif; \ + .ifc %[reg], r3; .byte 0x33; .endif; \ + .ifc %[reg], r4; .byte 0x44; .endif; \ + .ifc %[reg], r5; .byte 0x55; .endif; \ + .ifc %[reg], r6; .byte 0x66; .endif; \ + .ifc %[reg], r7; .byte 0x77; .endif; \ + .ifc %[reg], r8; .byte 0x88; .endif; \ + .ifc %[reg], r9; .byte 0x99; .endif; \ + .short %[off]; \ + .long %[as]" \ + : [reg] "+r"(var) \ + : [off] "i"(BPF_ADDR_SPACE_CAST), \ + [as] "i"(((dst_as) << 16) | (src_as))) +#endif + #define NETDATA_BPF_ARENA_DEF(NAME, MAX_ENTRIES) \ struct { \ __uint(type, BPF_MAP_TYPE_ARENA); \ @@ -39,8 +66,10 @@ }; \ extern __arena struct netdata_##PREFIX##_arena_state_t PREFIX##_arena_state; \ static __always_inline __arena EVENT_TYPE *netdata_##PREFIX##_arena_reserve(void) { \ - __u32 idx = PREFIX##_arena_state.head++; \ - return &PREFIX##_arena_state.events[idx % SLOT_COUNT]; \ + __arena struct netdata_##PREFIX##_arena_state_t *state = &PREFIX##_arena_state; \ + netdata_bpf_addr_space_cast(state, 0, 1); \ + __u32 idx = state->head++; \ + return &state->events[idx % SLOT_COUNT]; \ } \ static __always_inline void netdata_##PREFIX##_arena_submit(__arena EVENT_TYPE *ev) { \ (void)ev; \ From d61bee585be3db0403b9ea528ef468497bd3d961 Mon Sep 17 00:00:00 2001 From: thiagoftsm Date: Thu, 17 Sep 2026 01:47:45 +0000 Subject: [PATCH 11/12] improve_algs: Fix issues --- gotests/main.go | 3 +++ gotests/main_test.go | 4 ++++ includes/netdata_arena_common.h | 13 ++----------- kernel/Makefile | 5 ----- 4 files changed, 9 insertions(+), 16 deletions(-) diff --git a/gotests/main.go b/gotests/main.go index 079666d5..342c48ac 100644 --- a/gotests/main.go +++ b/gotests/main.go @@ -866,6 +866,7 @@ func detectSupportedMapTypes(rhfVersion int, kernelVersion int) map[uint32]bool bpfMapTypePerCPUArray: fallbackPerCPUMapSupport(rhfVersion, kernelVersion), bpfMapTypeRingBuf: false, bpfMapTypeUserRingBuf: false, + bpfMapTypeArena: false, } for _, mapType := range []uint32{ @@ -875,6 +876,7 @@ func detectSupportedMapTypes(rhfVersion int, kernelVersion int) map[uint32]bool bpfMapTypePerCPUArray, bpfMapTypeRingBuf, bpfMapTypeUserRingBuf, + bpfMapTypeArena, } { if probe := probeMapTypeSupport(mapType); probe >= 0 { supported[mapType] = probe > 0 @@ -914,6 +916,7 @@ func writeSupportedMapTypes(w io.Writer, supported map[uint32]bool) { bpfMapTypePerCPUArray, bpfMapTypeRingBuf, bpfMapTypeUserRingBuf, + bpfMapTypeArena, } { if supported[mapType] { names = append(names, fmt.Sprintf("\"%s\"", mapTypeName(mapType))) diff --git a/gotests/main_test.go b/gotests/main_test.go index 6e1d9456..78731578 100644 --- a/gotests/main_test.go +++ b/gotests/main_test.go @@ -984,6 +984,7 @@ func TestWriteSupportedMapTypes(t *testing.T) { bpfMapTypePerCPUArray: false, bpfMapTypeRingBuf: false, bpfMapTypeUserRingBuf: false, + bpfMapTypeArena: true, } var out bytes.Buffer @@ -1005,6 +1006,9 @@ func TestWriteSupportedMapTypes(t *testing.T) { if strings.Contains(got, `"ringbuf"`) { t.Fatalf("ringbuf must not appear (disabled): %s", got) } + if !strings.Contains(got, `"arena"`) { + t.Fatalf("arena must appear (enabled): %s", got) + } } func TestCandidateVersionIndex(t *testing.T) { diff --git a/includes/netdata_arena_common.h b/includes/netdata_arena_common.h index 53da1229..126fbd45 100644 --- a/includes/netdata_arena_common.h +++ b/includes/netdata_arena_common.h @@ -3,17 +3,9 @@ #ifndef _NETDATA_ARENA_COMMON_ #define _NETDATA_ARENA_COMMON_ 1 -/* Force the explicit form because the compiler form does not reliably cast - * pointers loaded from global arena data before tracing-program accesses. */ -#define NETDATA_ARENA_FORCE_ASM 1 - -#if defined(__BPF_FEATURE_ADDR_SPACE_CAST) && !defined(NETDATA_ARENA_FORCE_ASM) -#define __arena __attribute__((address_space(1))) -#define __arena_global __attribute__((address_space(1))) -#else +/* The native-target LLVM IR build uses an explicit BPF cast at reservation. */ #define __arena #define __arena_global SEC(".addr_space.1") -#endif #ifndef __arg_arena #define __arg_arena __attribute__((btf_decl_tag("arg:arena"))) @@ -28,8 +20,7 @@ #define NETDATA_ARENA_MAP_PAGES 256 #define NETDATA_ARENA_EVENT_SLOTS 1024 -/* LLVM does not consistently emit the BPF arena address-space cast when a - * global arena object is dereferenced from a tracing program. */ +/* Mark the relocated userspace address as PTR_TO_ARENA before dereferencing. */ #ifndef netdata_bpf_addr_space_cast #define netdata_bpf_addr_space_cast(var, dst_as, src_as) \ asm volatile(\ diff --git a/kernel/Makefile b/kernel/Makefile index 1f96747d..25f5491d 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -145,11 +145,6 @@ else NETDATA_ARENA_APPS= endif -# Arena objects need address-space-cast support so the verifier sees PTR_TO_ARENA -ifneq ($(NETDATA_ARENA_APPS),) -$(addsuffix _kern.o,$(NETDATA_ARENA_APPS)): EXTRA_CFLAGS += -D__BPF_FEATURE_ADDR_SPACE_CAST -Wno-address-space-conversion -endif - all: $(NETDATA_APPS) $(NETDATA_RINGBUF_APPS) $(NETDATA_ARENA_APPS) dev: ${NETDATA_ALL_APPS} ${NETDATA_RINGBUF_APPS} ${NETDATA_ARENA_APPS} From e95606613eabacb5036c3bec7ace53a4999f53d9 Mon Sep 17 00:00:00 2001 From: thiagoftsm Date: Fri, 18 Sep 2026 02:32:15 +0000 Subject: [PATCH 12/12] improve_algs: Fix runtime on kernel 4.19 --- gotests/main.go | 10 +++++++++- gotests/main_test.go | 1 + 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/gotests/main.go b/gotests/main.go index 342c48ac..ad1dc013 100644 --- a/gotests/main.go +++ b/gotests/main.go @@ -29,6 +29,7 @@ const ( netdataEBPFKernel414 = 265728 netdataEBPFKernel415 = 265984 netdataEBPFKernel417 = 266496 + netdataEBPFKernel418 = 266752 netdataEBPFKernel54 = 328704 netdataEBPFKernel58 = 329728 netdataEBPFKernel60 = 393216 @@ -1362,7 +1363,14 @@ func attachPrograms(obj *bpfObject, names *[]specifyName, kernelVersion int) att } func skipProgramForKernel(section string, kernelVersion int) bool { - return kernelVersion >= netdataEBPFKernel60 && section == "kprobe/blk_complete_request" + if section != "kprobe/blk_complete_request" { + return false + } + + // blk_complete_request is not available in the older kernels covered by + // the 4.x artifacts, and was removed from newer kernels in favor of the + // blk_mq completion path. + return kernelVersion < netdataEBPFKernel54 || kernelVersion >= netdataEBPFKernel60 } func syscallAttachTarget(section string, kernelVersion int) (bool, string, bool) { diff --git a/gotests/main_test.go b/gotests/main_test.go index 78731578..2533d1f3 100644 --- a/gotests/main_test.go +++ b/gotests/main_test.go @@ -915,6 +915,7 @@ func TestSkipProgramForKernel(t *testing.T) { want bool }{ {name: "legacy disk completion on 5.4", section: "kprobe/blk_complete_request", version: netdataEBPFKernel54, want: false}, + {name: "legacy disk completion on 4.18", section: "kprobe/blk_complete_request", version: netdataEBPFKernel418, want: true}, {name: "legacy disk completion on 6.0", section: "kprobe/blk_complete_request", version: netdataEBPFKernel60, want: true}, {name: "modern disk completion", section: "kprobe/blk_mq_end_request", version: netdataEBPFKernel612, want: false}, }