diff options
Diffstat (limited to 'libnetdata/ebpf/ebpf.c')
-rw-r--r-- | libnetdata/ebpf/ebpf.c | 202 |
1 files changed, 172 insertions, 30 deletions
diff --git a/libnetdata/ebpf/ebpf.c b/libnetdata/ebpf/ebpf.c index c48f2f24e..382485e5f 100644 --- a/libnetdata/ebpf/ebpf.c +++ b/libnetdata/ebpf/ebpf.c @@ -239,7 +239,7 @@ static int kernel_is_rejected() } fclose(kernel_reject_list); - freez(reject_string); + free(reject_string); return 0; } @@ -279,7 +279,8 @@ static char *ebpf_select_kernel_name(uint32_t selector) { static char *kernel_names[] = { NETDATA_IDX_STR_V3_10, NETDATA_IDX_STR_V4_14, NETDATA_IDX_STR_V4_16, NETDATA_IDX_STR_V4_18, NETDATA_IDX_STR_V5_4, NETDATA_IDX_STR_V5_10, - NETDATA_IDX_STR_V5_11, NETDATA_IDX_STR_V5_15, NETDATA_IDX_STR_V5_16 + NETDATA_IDX_STR_V5_11, NETDATA_IDX_STR_V5_14, NETDATA_IDX_STR_V5_15, + NETDATA_IDX_STR_V5_16 }; return kernel_names[selector]; @@ -298,7 +299,11 @@ static char *ebpf_select_kernel_name(uint32_t selector) static int ebpf_select_max_index(int is_rhf, uint32_t kver) { if (is_rhf > 0) { // Is Red Hat family - if (kver >= NETDATA_EBPF_KERNEL_4_11) + if (kver >= NETDATA_EBPF_KERNEL_5_14) + return NETDATA_IDX_V5_14; + else if (kver >= NETDATA_EBPF_KERNEL_5_4 && kver < NETDATA_EBPF_KERNEL_5_5) // For Oracle Linux + return NETDATA_IDX_V5_4; + else if (kver >= NETDATA_EBPF_KERNEL_4_11) return NETDATA_IDX_V4_18; } else { // Kernels from kernel.org if (kver >= NETDATA_EBPF_KERNEL_5_16) @@ -334,6 +339,9 @@ static uint32_t ebpf_select_index(uint32_t kernels, int is_rhf, uint32_t kver) uint32_t start = ebpf_select_max_index(is_rhf, kver); uint32_t idx; + if (is_rhf == -1) + kernels &= ~NETDATA_V5_14; + for (idx = start; idx; idx--) { if (kernels & 1 << idx) break; @@ -438,9 +446,9 @@ void ebpf_update_stats(ebpf_plugin_stats_t *report, ebpf_module_t *em) // In theory the `else if` is useless, because when this function is called, the module should not stay in // EBPF_LOAD_PLAY_DICE. We have this additional condition to detect errors from developers. - if (em->load == EBPF_LOAD_LEGACY) + if (em->load & EBPF_LOAD_LEGACY) report->legacy++; - else if (em->load == EBPF_LOAD_CORE) + else if (em->load & EBPF_LOAD_CORE) report->core++; ebpf_stats_targets(report, em->targets); @@ -463,26 +471,42 @@ void ebpf_update_pid_table(ebpf_local_maps_t *pid, ebpf_module_t *em) * @param em the structure with information about how the module/thread is working. * @param map_name the name of the file used to log. */ -void ebpf_update_map_size(struct bpf_map *map, ebpf_local_maps_t *lmap, ebpf_module_t *em, const char *map_name) +void ebpf_update_map_size(struct bpf_map *map, ebpf_local_maps_t *lmap, ebpf_module_t *em, const char *map_name __maybe_unused) { + uint32_t define_size = 0; uint32_t apps_type = NETDATA_EBPF_MAP_PID | NETDATA_EBPF_MAP_RESIZABLE; if (lmap->user_input && lmap->user_input != lmap->internal_input) { + define_size = lmap->internal_input; #ifdef NETDATA_INTERNAL_CHECKS info("Changing map %s from size %u to %u ", map_name, lmap->internal_input, lmap->user_input); #endif -#ifdef LIBBPF_MAJOR_VERSION - bpf_map__set_max_entries(map, lmap->user_input); -#else - bpf_map__resize(map, lmap->user_input); -#endif } else if (((lmap->type & apps_type) == apps_type) && (!em->apps_charts) && (!em->cgroup_charts)) { lmap->user_input = ND_EBPF_DEFAULT_MIN_PID; + } else if (((em->apps_charts) || (em->cgroup_charts)) && (em->apps_level != NETDATA_APPS_NOT_SET)) { + switch (em->apps_level) { + case NETDATA_APPS_LEVEL_ALL: { + define_size = lmap->user_input; + break; + } + case NETDATA_APPS_LEVEL_PARENT: { + define_size = ND_EBPF_DEFAULT_PID_SIZE / 2; + break; + } + case NETDATA_APPS_LEVEL_REAL_PARENT: + default: { + define_size = ND_EBPF_DEFAULT_PID_SIZE / 3; + } + } + } + + if (!define_size) + return; + #ifdef LIBBPF_MAJOR_VERSION - bpf_map__set_max_entries(map, lmap->user_input); + bpf_map__set_max_entries(map, define_size); #else - bpf_map__resize(map, lmap->user_input); + bpf_map__resize(map, define_size); #endif - } } /** @@ -607,11 +631,18 @@ static void ebpf_update_maps(ebpf_module_t *em, struct bpf_object *obj) */ void ebpf_update_controller(int fd, ebpf_module_t *em) { - uint32_t key = NETDATA_CONTROLLER_APPS_ENABLED; - uint32_t value = (em->apps_charts & NETDATA_EBPF_APPS_FLAG_YES) | em->cgroup_charts; - int ret = bpf_map_update_elem(fd, &key, &value, 0); - if (ret) - error("Add key(%u) for controller table failed.", key); + uint32_t values[NETDATA_CONTROLLER_END] = { + (em->apps_charts & NETDATA_EBPF_APPS_FLAG_YES) | em->cgroup_charts, + em->apps_level + }; + uint32_t key; + uint32_t end = (em->apps_level != NETDATA_APPS_NOT_SET) ? NETDATA_CONTROLLER_END : NETDATA_CONTROLLER_APPS_LEVEL; + + for (key = NETDATA_CONTROLLER_APPS_ENABLED; key < end; key++) { + int ret = bpf_map_update_elem(fd, &key, &values[key], 0); + if (ret) + error("Add key(%u) for controller table failed.", key); + } } /** @@ -667,6 +698,10 @@ struct bpf_link **ebpf_load_program(char *plugins_dir, ebpf_module_t *em, int kv ebpf_mount_name(lpath, 4095, plugins_dir, idx, em->thread_name, em->mode); + // When this function is called ebpf.plugin is using legacy code, so we should reset the variable + em->load &= ~ NETDATA_EBPF_LOAD_METHODS; + em->load |= EBPF_LOAD_LEGACY; + *obj = bpf_object__open_file(lpath, NULL); if (libbpf_get_error(obj)) { error("Cannot open BPF object %s", lpath); @@ -797,15 +832,53 @@ netdata_ebpf_load_mode_t epbf_convert_string_to_load_mode(char *str) */ static char *ebpf_convert_load_mode_to_string(netdata_ebpf_load_mode_t mode) { - if (mode == EBPF_LOAD_CORE) + if (mode & EBPF_LOAD_CORE) return EBPF_CFG_CORE_PROGRAM; - else if (mode == EBPF_LOAD_LEGACY) + else if (mode & EBPF_LOAD_LEGACY) return EBPF_CFG_LEGACY_PROGRAM; return EBPF_CFG_DEFAULT_PROGRAM; } /** + * Convert collect pid to string + * + * @param level value that will select the string + * + * @return It returns the string associated to level. + */ +static char *ebpf_convert_collect_pid_to_string(netdata_apps_level_t level) +{ + if (level == NETDATA_APPS_LEVEL_REAL_PARENT) + return EBPF_CFG_PID_REAL_PARENT; + else if (level == NETDATA_APPS_LEVEL_PARENT) + return EBPF_CFG_PID_PARENT; + else if (level == NETDATA_APPS_LEVEL_ALL) + return EBPF_CFG_PID_ALL; + + return EBPF_CFG_PID_INTERNAL_USAGE; +} + +/** + * Convert string to apps level + * + * @param str the argument read from config files + * + * @return it returns the level associated to the string or default when it is a wrong value + */ +netdata_apps_level_t ebpf_convert_string_to_apps_level(char *str) +{ + if (!strcasecmp(str, EBPF_CFG_PID_REAL_PARENT)) + return NETDATA_APPS_LEVEL_REAL_PARENT; + else if (!strcasecmp(str, EBPF_CFG_PID_PARENT)) + return NETDATA_APPS_LEVEL_PARENT; + else if (!strcasecmp(str, EBPF_CFG_PID_ALL)) + return NETDATA_APPS_LEVEL_ALL; + + return NETDATA_APPS_NOT_SET; +} + +/** * CO-RE type * * Select the preferential type of CO-RE @@ -836,9 +909,11 @@ netdata_ebpf_program_loaded_t ebpf_convert_core_type(char *str, netdata_run_mode void ebpf_adjust_thread_load(ebpf_module_t *mod, struct btf *file) { if (!file) { - mod->load = EBPF_LOAD_LEGACY; + mod->load &= ~EBPF_LOAD_CORE; + mod->load |= EBPF_LOAD_LEGACY; } else if (mod->load == EBPF_LOAD_PLAY_DICE && file) { - mod->load = EBPF_LOAD_CORE; + mod->load &= ~EBPF_LOAD_LEGACY; + mod->load |= EBPF_LOAD_CORE; } } @@ -952,13 +1027,45 @@ static void ebpf_update_target_with_conf(ebpf_module_t *em, netdata_ebpf_program } /** + * Select Load Mode + * + * Select the load mode according the given inputs. + * + * @param btf_file a pointer to the loaded btf file. + * @parma load current value. + * @param btf_file a pointer to the loaded btf file. + * @param is_rhf is Red Hat family? + * + * @return it returns the new load mode. + */ +static netdata_ebpf_load_mode_t ebpf_select_load_mode(struct btf *btf_file, netdata_ebpf_load_mode_t load, + int kver, int is_rh) +{ +#ifdef LIBBPF_MAJOR_VERSION + if ((load & EBPF_LOAD_CORE) || (load & EBPF_LOAD_PLAY_DICE)) { + // Quick fix for Oracle linux 8.x + load = (!btf_file || (is_rh && (kver >= NETDATA_EBPF_KERNEL_5_4 && kver < NETDATA_EBPF_KERNEL_5_5))) ? + EBPF_LOAD_LEGACY : EBPF_LOAD_CORE; + } +#else + load = EBPF_LOAD_LEGACY; +#endif + + return load; +} + +/** * Update Module using config * * Update configuration for a specific thread. * * @param modules structure that will be updated + * @oaram origin specify the configuration file loaded + * @param btf_file a pointer to the loaded btf file. + * @param is_rhf is Red Hat family? */ -void ebpf_update_module_using_config(ebpf_module_t *modules) +void ebpf_update_module_using_config(ebpf_module_t *modules, netdata_ebpf_load_mode_t origin, struct btf *btf_file, + int kver, int is_rh) { char default_value[EBPF_MAX_MODE_LENGTH + 1]; ebpf_select_mode_string(default_value, EBPF_MAX_MODE_LENGTH, modules->mode); @@ -977,13 +1084,19 @@ void ebpf_update_module_using_config(ebpf_module_t *modules) modules->pid_map_size = (uint32_t)appconfig_get_number(modules->cfg, EBPF_GLOBAL_SECTION, EBPF_CFG_PID_SIZE, modules->pid_map_size); - value = ebpf_convert_load_mode_to_string(modules->load); + value = ebpf_convert_load_mode_to_string(modules->load & NETDATA_EBPF_LOAD_METHODS); value = appconfig_get(modules->cfg, EBPF_GLOBAL_SECTION, EBPF_CFG_TYPE_FORMAT, value); - modules->load = epbf_convert_string_to_load_mode(value); + netdata_ebpf_load_mode_t load = epbf_convert_string_to_load_mode(value); + load = ebpf_select_load_mode(btf_file, load, kver, is_rh); + modules->load = origin | load; value = appconfig_get(modules->cfg, EBPF_GLOBAL_SECTION, EBPF_CFG_CORE_ATTACH, EBPF_CFG_ATTACH_TRAMPOLINE); netdata_ebpf_program_loaded_t fill_lm = ebpf_convert_core_type(value, modules->mode); ebpf_update_target_with_conf(modules, fill_lm); + + value = ebpf_convert_collect_pid_to_string(modules->apps_level); + value = appconfig_get(modules->cfg, EBPF_GLOBAL_SECTION, EBPF_CFG_COLLECT_PID, value); + modules->apps_level = ebpf_convert_string_to_apps_level(value); } /** @@ -995,10 +1108,15 @@ void ebpf_update_module_using_config(ebpf_module_t *modules) * update the variables. * * @param em the module structure + * @param btf_file a pointer to the loaded btf file. + * @param is_rhf is Red Hat family? + * @param kver the kernel version */ -void ebpf_update_module(ebpf_module_t *em) +void ebpf_update_module(ebpf_module_t *em, struct btf *btf_file, int kver, int is_rh) { char filename[FILENAME_MAX+1]; + netdata_ebpf_load_mode_t origin; + ebpf_mount_config_name(filename, FILENAME_MAX, ebpf_user_config_dir, em->config_file); if (!ebpf_load_config(em->cfg, filename)) { ebpf_mount_config_name(filename, FILENAME_MAX, ebpf_stock_config_dir, em->config_file); @@ -1006,9 +1124,32 @@ void ebpf_update_module(ebpf_module_t *em) error("Cannot load the ebpf configuration file %s", em->config_file); return; } - } + // If user defined data globaly, we will have here EBPF_LOADED_FROM_USER, we need to consider this, to avoid + // forcing users to configure thread by thread. + origin = (!(em->load & NETDATA_EBPF_LOAD_SOURCE)) ? EBPF_LOADED_FROM_STOCK : em->load & NETDATA_EBPF_LOAD_SOURCE; + } else + origin = EBPF_LOADED_FROM_USER; - ebpf_update_module_using_config(em); + ebpf_update_module_using_config(em, origin, btf_file, kver, is_rh); +} + +/** + * Adjust Apps Cgroup + * + * Apps and cgroup has internal cleanup that needs attaching tracers to release_task, to avoid overload the function + * we will enable this integration by default, if and only if, we are running with trampolines. + * + * @param em a poiter to the main thread structure. + * @param mode is the mode used with different + */ +void ebpf_adjust_apps_cgroup(ebpf_module_t *em, netdata_ebpf_program_loaded_t mode) +{ + if ((em->load & EBPF_LOADED_FROM_STOCK) && + (em->apps_charts || em->cgroup_charts) && + mode != EBPF_LOAD_TRAMPOLINE) { + em->apps_charts = NETDATA_EBPF_APPS_FLAG_NO; + em->cgroup_charts = 0; + } } //---------------------------------------------------------------------------------------------------------------------- @@ -1282,4 +1423,5 @@ void ebpf_select_host_prefix(char *output, size_t length, char *syscall, int kve char *prefix = (arch == 32) ? "__ia32" : "__x64"; snprintfz(output, length, "%s_sys_%s", prefix, syscall); } -}
\ No newline at end of file +} + |