On 12/20, Quentin Monnet wrote: > Add probes to dump a number of options set (or not set) for compiling > the kernel image. These parameters provide information about what BPF > components should be available on the system. A number of them are not > directly related to eBPF, but are in fact used in the kernel as > conditions on which to compile, or not to compile, some of the eBPF > helper functions. > > Sample output: > > # bpftool feature probe kernel > Scanning system configuration... > ... > CONFIG_BPF is set to y > CONFIG_BPF_SYSCALL is set to y > CONFIG_HAVE_EBPF_JIT is set to y > ... > > # bpftool --pretty --json feature probe kernel > { > "system_config": { > ... > "CONFIG_BPF": "y", > "CONFIG_BPF_SYSCALL": "y", > "CONFIG_HAVE_EBPF_JIT": "y", > ... > } > } > > v2: > - Remove C-style macros output from this patch. > - NOT addressed: grouping of those config options into subsections > (I don't see an easy way of grouping them at the moment, please see > also the discussion on v1 thread). > > Signed-off-by: Quentin Monnet <quentin.mon...@netronome.com> > Reviewed-by: Jakub Kicinski <jakub.kicin...@netronome.com> > --- > tools/bpf/bpftool/feature.c | 137 ++++++++++++++++++++++++++++++++++++ > 1 file changed, 137 insertions(+) > > diff --git a/tools/bpf/bpftool/feature.c b/tools/bpf/bpftool/feature.c > index 410d35857cf4..238d7b80f426 100644 > --- a/tools/bpf/bpftool/feature.c > +++ b/tools/bpf/bpftool/feature.c > @@ -48,6 +48,30 @@ print_bool_feature(const char *feat_name, const char > *plain_name, bool res) > printf("%s is %savailable\n", plain_name, res ? "" : "NOT "); > } > > +static void print_kernel_option(const char *name, const char *value) > +{ > + char *endptr; > + int res; > + > + if (json_output) { > + if (!value) { > + jsonw_null_field(json_wtr, name); > + return; > + } > + errno = 0; > + res = strtol(value, &endptr, 0); > + if (!errno && *endptr == '\n') > + jsonw_int_field(json_wtr, name, res); > + else > + jsonw_string_field(json_wtr, name, value); > + } else { > + if (value) > + printf("%s is set to %s\n", name, value); > + else > + printf("%s is not set\n", name); > + } > +} > + > static void > print_start_section(const char *json_title, const char *plain_title) > { > @@ -190,6 +214,118 @@ static void probe_jit_kallsyms(void) > } > } > > +static char *get_kernel_config_option(FILE *fd, const char *option) > +{ > + size_t line_n = 0, optlen = strlen(option); > + char *res, *strval, *line = NULL; > + ssize_t n; > + > + rewind(fd); > + while ((n = getline(&line, &line_n, fd)) > 0) { > + if (strncmp(line, option, optlen)) > + continue; > + /* Check we have at least '=', value, and '\n' */ > + if (strlen(line) < optlen + 3) > + continue; > + if (*(line + optlen) != '=') > + continue; > + > + /* Trim ending '\n' */ > + line[strlen(line) - 1] = '\0'; > + > + /* Copy and return config option value */ > + strval = line + optlen + 1; > + res = strdup(strval); > + free(line); > + return res; > + } > + free(line); > + > + return NULL; > +} > + > +static void probe_kernel_image_config(void) > +{ > + const char * const options[] = { > + "CONFIG_BPF", > + "CONFIG_BPF_SYSCALL", > + "CONFIG_HAVE_EBPF_JIT", > + "CONFIG_BPF_JIT", > + "CONFIG_BPF_JIT_ALWAYS_ON", > + "CONFIG_NET", > + "CONFIG_XDP_SOCKETS", > + "CONFIG_CGROUPS", > + "CONFIG_CGROUP_BPF", > + "CONFIG_CGROUP_NET_CLASSID", > + "CONFIG_BPF_EVENTS", > + "CONFIG_LWTUNNEL_BPF", > + "CONFIG_NET_ACT_BPF", > + "CONFIG_NET_CLS_ACT", > + "CONFIG_NET_CLS_BPF", > + "CONFIG_NET_SCH_INGRESS", > + "CONFIG_XFRM", > + "CONFIG_SOCK_CGROUP_DATA", > + "CONFIG_IP_ROUTE_CLASSID", > + "CONFIG_IPV6_SEG6_BPF", > + "CONFIG_FUNCTION_ERROR_INJECTION", > + "CONFIG_BPF_KPROBE_OVERRIDE", > + "CONFIG_BPF_LIRC_MODE2", > + "CONFIG_NETFILTER_XT_MATCH_BPF", > + "CONFIG_TEST_BPF", > + "CONFIG_BPFILTER", > + "CONFIG_BPFILTER_UMH", > + "CONFIG_BPF_STREAM_PARSER", > + }; > + char *value, *buf = NULL; > + struct utsname utsn; > + char path[PATH_MAX]; > + size_t i, n; > + ssize_t ret; > + FILE *fd; > + > + if (uname(&utsn)) > + goto no_config; > + [..] > + snprintf(path, sizeof(path), "/boot/config-%s", utsn.release); > + > + fd = fopen(path, "r"); > + if (!fd && errno == ENOENT) { > + /* Sometimes config is at /proc/config */ > + fd = fopen("/proc/config", "r"); I wonder whether the order here should be reversed? 1. try /proc/config.gz (CONFIG_IKCONFIG_PROC) 2. if not avail, try /boot/config-$(uname -r)
Because, at the end, /proc/config.gz is the real source of truth (if available). What is /proc/config btw? I can see only /proc/config.gz being exported. > + } > + if (!fd) { > + p_err("can't open kernel config file: %s", strerror(errno)); > + goto no_config; > + } > + /* Sanity checks */ > + ret = getline(&buf, &n, fd); > + ret = getline(&buf, &n, fd); > + if (!buf || !ret) { > + p_err("can't read from kernel config file: %s", > + strerror(errno)); > + free(buf); > + goto no_config; > + } > + if (strcmp(buf, "# Automatically generated file; DO NOT EDIT.\n")) { > + p_err("can't find correct kernel config file"); > + free(buf); > + goto no_config; > + } > + free(buf); > + > + for (i = 0; i < ARRAY_SIZE(options); i++) { > + value = get_kernel_config_option(fd, options[i]); > + print_kernel_option(options[i], value); > + free(value); > + } > + fclose(fd); > + return; > + > +no_config: > + for (i = 0; i < ARRAY_SIZE(options); i++) > + print_kernel_option(options[i], NULL); > +} > + > static int probe_kernel_version(void) > { > int version, subversion, patchlevel, code = 0; > @@ -270,6 +406,7 @@ static int do_probe(int argc, char **argv) > } else { > p_info("/* procfs not mounted, skipping related probes > */"); > } > + probe_kernel_image_config(); > if (json_output) > jsonw_end_object(json_wtr); > else > -- > 2.17.1 >