Skip to content

Commit 50e4957

Browse files
author
Alexei Starovoitov
committed
Merge branch 'bpf-fix-a-couple-of-test-failures-with-lto-kernel'
Yonghong Song says: ==================== bpf: Fix a couple of test failures with LTO kernel With a LTO kernel built with clang, with one of earlier version of kernel, I encountered two test failures, ksyms and kprobe_multi_bench_attach/kernel. Now with latest bpf-next, only kprobe_multi_bench_attach/kernel failed. But it is possible in the future ksyms selftest may fail again. Both test failures are due to static variable/function renaming due to cross-file inlining. For Ksyms failure, the solution is to strip .llvm.<hash> suffixes for symbols in /proc/kallsyms before comparing against the ksym in bpf program. For kprobe_multi_bench_attach/kernel failure, the solution is to either provide names in /proc/kallsyms to the kernel or ignore those names who have .llvm.<hash> suffix since the kernel sym name comparison is against /proc/kallsyms. Please see each individual patches for details. Changelogs: v2 -> v3: - no need to check config file, directly so strstr with '.llvm.'. - for kprobe_multi_bench with syms, instead of skipping the syms, consult /proc/kallyms to find corresponding names. - add a test with populating addrs to the kernel for kprobe multi attach. v1 -> v2: - Let libbpf handle .llvm.<hash suffixes since it may impact bpf program ksym. ==================== Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Alexei Starovoitov <[email protected]>
2 parents 74c8edc + aa17f46 commit 50e4957

File tree

6 files changed

+281
-81
lines changed

6 files changed

+281
-81
lines changed

tools/lib/bpf/libbpf.c

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1966,6 +1966,20 @@ static struct extern_desc *find_extern_by_name(const struct bpf_object *obj,
19661966
return NULL;
19671967
}
19681968

1969+
static struct extern_desc *find_extern_by_name_with_len(const struct bpf_object *obj,
1970+
const void *name, int len)
1971+
{
1972+
const char *ext_name;
1973+
int i;
1974+
1975+
for (i = 0; i < obj->nr_extern; i++) {
1976+
ext_name = obj->externs[i].name;
1977+
if (strlen(ext_name) == len && strncmp(ext_name, name, len) == 0)
1978+
return &obj->externs[i];
1979+
}
1980+
return NULL;
1981+
}
1982+
19691983
static int set_kcfg_value_tri(struct extern_desc *ext, void *ext_val,
19701984
char value)
19711985
{
@@ -7982,7 +7996,10 @@ static int bpf_object__sanitize_maps(struct bpf_object *obj)
79827996
return 0;
79837997
}
79847998

7985-
int libbpf_kallsyms_parse(kallsyms_cb_t cb, void *ctx)
7999+
typedef int (*kallsyms_cb_t)(unsigned long long sym_addr, char sym_type,
8000+
const char *sym_name, void *ctx);
8001+
8002+
static int libbpf_kallsyms_parse(kallsyms_cb_t cb, void *ctx)
79868003
{
79878004
char sym_type, sym_name[500];
79888005
unsigned long long sym_addr;
@@ -8022,8 +8039,13 @@ static int kallsyms_cb(unsigned long long sym_addr, char sym_type,
80228039
struct bpf_object *obj = ctx;
80238040
const struct btf_type *t;
80248041
struct extern_desc *ext;
8042+
char *res;
80258043

8026-
ext = find_extern_by_name(obj, sym_name);
8044+
res = strstr(sym_name, ".llvm.");
8045+
if (sym_type == 'd' && res)
8046+
ext = find_extern_by_name_with_len(obj, sym_name, res - sym_name);
8047+
else
8048+
ext = find_extern_by_name(obj, sym_name);
80278049
if (!ext || ext->type != EXT_KSYM)
80288050
return 0;
80298051

tools/lib/bpf/libbpf_internal.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -518,11 +518,6 @@ int btf_ext_visit_str_offs(struct btf_ext *btf_ext, str_off_visit_fn visit, void
518518
__s32 btf__find_by_name_kind_own(const struct btf *btf, const char *type_name,
519519
__u32 kind);
520520

521-
typedef int (*kallsyms_cb_t)(unsigned long long sym_addr, char sym_type,
522-
const char *sym_name, void *ctx);
523-
524-
int libbpf_kallsyms_parse(kallsyms_cb_t cb, void *arg);
525-
526521
/* handle direct returned errors */
527522
static inline int libbpf_err(int ret)
528523
{

tools/testing/selftests/bpf/prog_tests/kprobe_multi_test.c

Lines changed: 200 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -336,15 +336,80 @@ static bool symbol_equal(long key1, long key2, void *ctx __maybe_unused)
336336
return strcmp((const char *) key1, (const char *) key2) == 0;
337337
}
338338

339+
static bool is_invalid_entry(char *buf, bool kernel)
340+
{
341+
if (kernel && strchr(buf, '['))
342+
return true;
343+
if (!kernel && !strchr(buf, '['))
344+
return true;
345+
return false;
346+
}
347+
348+
static bool skip_entry(char *name)
349+
{
350+
/*
351+
* We attach to almost all kernel functions and some of them
352+
* will cause 'suspicious RCU usage' when fprobe is attached
353+
* to them. Filter out the current culprits - arch_cpu_idle
354+
* default_idle and rcu_* functions.
355+
*/
356+
if (!strcmp(name, "arch_cpu_idle"))
357+
return true;
358+
if (!strcmp(name, "default_idle"))
359+
return true;
360+
if (!strncmp(name, "rcu_", 4))
361+
return true;
362+
if (!strcmp(name, "bpf_dispatcher_xdp_func"))
363+
return true;
364+
if (!strncmp(name, "__ftrace_invalid_address__",
365+
sizeof("__ftrace_invalid_address__") - 1))
366+
return true;
367+
return false;
368+
}
369+
370+
/* Do comparision by ignoring '.llvm.<hash>' suffixes. */
371+
static int compare_name(const char *name1, const char *name2)
372+
{
373+
const char *res1, *res2;
374+
int len1, len2;
375+
376+
res1 = strstr(name1, ".llvm.");
377+
res2 = strstr(name2, ".llvm.");
378+
len1 = res1 ? res1 - name1 : strlen(name1);
379+
len2 = res2 ? res2 - name2 : strlen(name2);
380+
381+
if (len1 == len2)
382+
return strncmp(name1, name2, len1);
383+
if (len1 < len2)
384+
return strncmp(name1, name2, len1) <= 0 ? -1 : 1;
385+
return strncmp(name1, name2, len2) >= 0 ? 1 : -1;
386+
}
387+
388+
static int load_kallsyms_compare(const void *p1, const void *p2)
389+
{
390+
return compare_name(((const struct ksym *)p1)->name, ((const struct ksym *)p2)->name);
391+
}
392+
393+
static int search_kallsyms_compare(const void *p1, const struct ksym *p2)
394+
{
395+
return compare_name(p1, p2->name);
396+
}
397+
339398
static int get_syms(char ***symsp, size_t *cntp, bool kernel)
340399
{
341-
size_t cap = 0, cnt = 0, i;
342-
char *name = NULL, **syms = NULL;
400+
size_t cap = 0, cnt = 0;
401+
char *name = NULL, *ksym_name, **syms = NULL;
343402
struct hashmap *map;
403+
struct ksyms *ksyms;
404+
struct ksym *ks;
344405
char buf[256];
345406
FILE *f;
346407
int err = 0;
347408

409+
ksyms = load_kallsyms_custom_local(load_kallsyms_compare);
410+
if (!ASSERT_OK_PTR(ksyms, "load_kallsyms_custom_local"))
411+
return -EINVAL;
412+
348413
/*
349414
* The available_filter_functions contains many duplicates,
350415
* but other than that all symbols are usable in kprobe multi
@@ -368,33 +433,23 @@ static int get_syms(char ***symsp, size_t *cntp, bool kernel)
368433
}
369434

370435
while (fgets(buf, sizeof(buf), f)) {
371-
if (kernel && strchr(buf, '['))
372-
continue;
373-
if (!kernel && !strchr(buf, '['))
436+
if (is_invalid_entry(buf, kernel))
374437
continue;
375438

376439
free(name);
377440
if (sscanf(buf, "%ms$*[^\n]\n", &name) != 1)
378441
continue;
379-
/*
380-
* We attach to almost all kernel functions and some of them
381-
* will cause 'suspicious RCU usage' when fprobe is attached
382-
* to them. Filter out the current culprits - arch_cpu_idle
383-
* default_idle and rcu_* functions.
384-
*/
385-
if (!strcmp(name, "arch_cpu_idle"))
386-
continue;
387-
if (!strcmp(name, "default_idle"))
388-
continue;
389-
if (!strncmp(name, "rcu_", 4))
390-
continue;
391-
if (!strcmp(name, "bpf_dispatcher_xdp_func"))
392-
continue;
393-
if (!strncmp(name, "__ftrace_invalid_address__",
394-
sizeof("__ftrace_invalid_address__") - 1))
442+
if (skip_entry(name))
395443
continue;
396444

397-
err = hashmap__add(map, name, 0);
445+
ks = search_kallsyms_custom_local(ksyms, name, search_kallsyms_compare);
446+
if (!ks) {
447+
err = -EINVAL;
448+
goto error;
449+
}
450+
451+
ksym_name = ks->name;
452+
err = hashmap__add(map, ksym_name, 0);
398453
if (err == -EEXIST) {
399454
err = 0;
400455
continue;
@@ -407,8 +462,7 @@ static int get_syms(char ***symsp, size_t *cntp, bool kernel)
407462
if (err)
408463
goto error;
409464

410-
syms[cnt++] = name;
411-
name = NULL;
465+
syms[cnt++] = ksym_name;
412466
}
413467

414468
*symsp = syms;
@@ -418,42 +472,88 @@ static int get_syms(char ***symsp, size_t *cntp, bool kernel)
418472
free(name);
419473
fclose(f);
420474
hashmap__free(map);
421-
if (err) {
422-
for (i = 0; i < cnt; i++)
423-
free(syms[i]);
475+
if (err)
424476
free(syms);
477+
return err;
478+
}
479+
480+
static int get_addrs(unsigned long **addrsp, size_t *cntp, bool kernel)
481+
{
482+
unsigned long *addr, *addrs, *tmp_addrs;
483+
int err = 0, max_cnt, inc_cnt;
484+
char *name = NULL;
485+
size_t cnt = 0;
486+
char buf[256];
487+
FILE *f;
488+
489+
if (access("/sys/kernel/tracing/trace", F_OK) == 0)
490+
f = fopen("/sys/kernel/tracing/available_filter_functions_addrs", "r");
491+
else
492+
f = fopen("/sys/kernel/debug/tracing/available_filter_functions_addrs", "r");
493+
494+
if (!f)
495+
return -ENOENT;
496+
497+
/* In my local setup, the number of entries is 50k+ so Let us initially
498+
* allocate space to hold 64k entries. If 64k is not enough, incrementally
499+
* increase 1k each time.
500+
*/
501+
max_cnt = 65536;
502+
inc_cnt = 1024;
503+
addrs = malloc(max_cnt * sizeof(long));
504+
if (addrs == NULL) {
505+
err = -ENOMEM;
506+
goto error;
425507
}
508+
509+
while (fgets(buf, sizeof(buf), f)) {
510+
if (is_invalid_entry(buf, kernel))
511+
continue;
512+
513+
free(name);
514+
if (sscanf(buf, "%p %ms$*[^\n]\n", &addr, &name) != 2)
515+
continue;
516+
if (skip_entry(name))
517+
continue;
518+
519+
if (cnt == max_cnt) {
520+
max_cnt += inc_cnt;
521+
tmp_addrs = realloc(addrs, max_cnt);
522+
if (!tmp_addrs) {
523+
err = -ENOMEM;
524+
goto error;
525+
}
526+
addrs = tmp_addrs;
527+
}
528+
529+
addrs[cnt++] = (unsigned long)addr;
530+
}
531+
532+
*addrsp = addrs;
533+
*cntp = cnt;
534+
535+
error:
536+
free(name);
537+
fclose(f);
538+
if (err)
539+
free(addrs);
426540
return err;
427541
}
428542

429-
static void test_kprobe_multi_bench_attach(bool kernel)
543+
static void do_bench_test(struct kprobe_multi_empty *skel, struct bpf_kprobe_multi_opts *opts)
430544
{
431-
LIBBPF_OPTS(bpf_kprobe_multi_opts, opts);
432-
struct kprobe_multi_empty *skel = NULL;
433545
long attach_start_ns, attach_end_ns;
434546
long detach_start_ns, detach_end_ns;
435547
double attach_delta, detach_delta;
436548
struct bpf_link *link = NULL;
437-
char **syms = NULL;
438-
size_t cnt = 0, i;
439-
440-
if (!ASSERT_OK(get_syms(&syms, &cnt, kernel), "get_syms"))
441-
return;
442-
443-
skel = kprobe_multi_empty__open_and_load();
444-
if (!ASSERT_OK_PTR(skel, "kprobe_multi_empty__open_and_load"))
445-
goto cleanup;
446-
447-
opts.syms = (const char **) syms;
448-
opts.cnt = cnt;
449549

450550
attach_start_ns = get_time_ns();
451551
link = bpf_program__attach_kprobe_multi_opts(skel->progs.test_kprobe_empty,
452-
NULL, &opts);
552+
NULL, opts);
453553
attach_end_ns = get_time_ns();
454554

455555
if (!ASSERT_OK_PTR(link, "bpf_program__attach_kprobe_multi_opts"))
456-
goto cleanup;
556+
return;
457557

458558
detach_start_ns = get_time_ns();
459559
bpf_link__destroy(link);
@@ -462,17 +562,65 @@ static void test_kprobe_multi_bench_attach(bool kernel)
462562
attach_delta = (attach_end_ns - attach_start_ns) / 1000000000.0;
463563
detach_delta = (detach_end_ns - detach_start_ns) / 1000000000.0;
464564

465-
printf("%s: found %lu functions\n", __func__, cnt);
565+
printf("%s: found %lu functions\n", __func__, opts->cnt);
466566
printf("%s: attached in %7.3lfs\n", __func__, attach_delta);
467567
printf("%s: detached in %7.3lfs\n", __func__, detach_delta);
568+
}
569+
570+
static void test_kprobe_multi_bench_attach(bool kernel)
571+
{
572+
LIBBPF_OPTS(bpf_kprobe_multi_opts, opts);
573+
struct kprobe_multi_empty *skel = NULL;
574+
char **syms = NULL;
575+
size_t cnt = 0;
576+
577+
if (!ASSERT_OK(get_syms(&syms, &cnt, kernel), "get_syms"))
578+
return;
579+
580+
skel = kprobe_multi_empty__open_and_load();
581+
if (!ASSERT_OK_PTR(skel, "kprobe_multi_empty__open_and_load"))
582+
goto cleanup;
583+
584+
opts.syms = (const char **) syms;
585+
opts.cnt = cnt;
586+
587+
do_bench_test(skel, &opts);
468588

469589
cleanup:
470590
kprobe_multi_empty__destroy(skel);
471-
if (syms) {
472-
for (i = 0; i < cnt; i++)
473-
free(syms[i]);
591+
if (syms)
474592
free(syms);
593+
}
594+
595+
static void test_kprobe_multi_bench_attach_addr(bool kernel)
596+
{
597+
LIBBPF_OPTS(bpf_kprobe_multi_opts, opts);
598+
struct kprobe_multi_empty *skel = NULL;
599+
unsigned long *addrs = NULL;
600+
size_t cnt = 0;
601+
int err;
602+
603+
err = get_addrs(&addrs, &cnt, kernel);
604+
if (err == -ENOENT) {
605+
test__skip();
606+
return;
475607
}
608+
609+
if (!ASSERT_OK(err, "get_addrs"))
610+
return;
611+
612+
skel = kprobe_multi_empty__open_and_load();
613+
if (!ASSERT_OK_PTR(skel, "kprobe_multi_empty__open_and_load"))
614+
goto cleanup;
615+
616+
opts.addrs = addrs;
617+
opts.cnt = cnt;
618+
619+
do_bench_test(skel, &opts);
620+
621+
cleanup:
622+
kprobe_multi_empty__destroy(skel);
623+
free(addrs);
476624
}
477625

478626
static void test_attach_override(void)
@@ -515,6 +663,10 @@ void serial_test_kprobe_multi_bench_attach(void)
515663
test_kprobe_multi_bench_attach(true);
516664
if (test__start_subtest("modules"))
517665
test_kprobe_multi_bench_attach(false);
666+
if (test__start_subtest("kernel"))
667+
test_kprobe_multi_bench_attach_addr(true);
668+
if (test__start_subtest("modules"))
669+
test_kprobe_multi_bench_attach_addr(false);
518670
}
519671

520672
void test_kprobe_multi_test(void)

0 commit comments

Comments
 (0)