Skip to content

Commit f263336

Browse files
alan-maguireanakryiko
authored andcommitted
selftests/bpf: Add btf dedup test covering module BTF dedup
Recently issues were observed with module BTF deduplication failures [1]. Add a dedup selftest that ensures that core kernel types are referenced from split BTF as base BTF types. To do this use bpf_testmod functions which utilize core kernel types, specifically ssize_t bpf_testmod_test_write(struct file *file, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t len); __bpf_kfunc struct sock *bpf_kfunc_call_test3(struct sock *sk); __bpf_kfunc void bpf_kfunc_call_test_pass_ctx(struct __sk_buff *skb); For each of these ensure that the types they reference - struct file, struct kobject, struct bin_attr etc - are in base BTF. Note that because bpf_testmod.ko is built with distilled base BTF the associated reference types - i.e. the PTR that points at a "struct file" - will be in split BTF. As a result the test resolves typedef and pointer references and verifies the pointed-at or typedef'ed type is in base BTF. Because we use BTF from /sys/kernel/btf/bpf_testmod relocation has occurred for the referenced types and they will be base - not distilled base - types. For large-scale dedup issues, we see such types appear in split BTF and as a result this test fails. Hence it is proposed as a test which will fail when large-scale dedup issues have occurred. [1] https://lore.kernel.org/dwarves/CAADnVQL+-LiJGXwxD3jEUrOonO-fX0SZC8496dVzUXvfkB7gYQ@mail.gmail.com/ Signed-off-by: Alan Maguire <[email protected]> Signed-off-by: Andrii Nakryiko <[email protected]> Acked-by: Eduard Zingerman <[email protected]> Link: https://lore.kernel.org/bpf/[email protected]
1 parent 86870d0 commit f263336

File tree

1 file changed

+101
-0
lines changed

1 file changed

+101
-0
lines changed

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

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,105 @@ static void test_split_dup_struct_in_cu()
440440
btf__free(btf1);
441441
}
442442

443+
/* Ensure module split BTF dedup worked correctly; when dedup fails badly
444+
* core kernel types are in split BTF also, so ensure that references to
445+
* such types point at base - not split - BTF.
446+
*
447+
* bpf_testmod_test_write() has multiple core kernel type parameters;
448+
*
449+
* ssize_t
450+
* bpf_testmod_test_write(struct file *file, struct kobject *kobj,
451+
* struct bin_attribute *bin_attr,
452+
* char *buf, loff_t off, size_t len);
453+
*
454+
* Ensure each of the FUNC_PROTO params is a core kernel type.
455+
*
456+
* Do the same for
457+
*
458+
* __bpf_kfunc struct sock *bpf_kfunc_call_test3(struct sock *sk);
459+
*
460+
* ...and
461+
*
462+
* __bpf_kfunc void bpf_kfunc_call_test_pass_ctx(struct __sk_buff *skb);
463+
*
464+
*/
465+
const char *mod_funcs[] = {
466+
"bpf_testmod_test_write",
467+
"bpf_kfunc_call_test3",
468+
"bpf_kfunc_call_test_pass_ctx"
469+
};
470+
471+
static void test_split_module(void)
472+
{
473+
struct btf *vmlinux_btf, *btf1 = NULL;
474+
int i, nr_base_types;
475+
476+
vmlinux_btf = btf__load_vmlinux_btf();
477+
if (!ASSERT_OK_PTR(vmlinux_btf, "vmlinux_btf"))
478+
return;
479+
nr_base_types = btf__type_cnt(vmlinux_btf);
480+
if (!ASSERT_GT(nr_base_types, 0, "nr_base_types"))
481+
goto cleanup;
482+
483+
btf1 = btf__parse_split("/sys/kernel/btf/bpf_testmod", vmlinux_btf);
484+
if (!ASSERT_OK_PTR(btf1, "split_btf"))
485+
return;
486+
487+
for (i = 0; i < ARRAY_SIZE(mod_funcs); i++) {
488+
const struct btf_param *p;
489+
const struct btf_type *t;
490+
__u16 vlen;
491+
__u32 id;
492+
int j;
493+
494+
id = btf__find_by_name_kind(btf1, mod_funcs[i], BTF_KIND_FUNC);
495+
if (!ASSERT_GE(id, nr_base_types, "func_id"))
496+
goto cleanup;
497+
t = btf__type_by_id(btf1, id);
498+
if (!ASSERT_OK_PTR(t, "func_id_type"))
499+
goto cleanup;
500+
t = btf__type_by_id(btf1, t->type);
501+
if (!ASSERT_OK_PTR(t, "func_proto_id_type"))
502+
goto cleanup;
503+
if (!ASSERT_EQ(btf_is_func_proto(t), true, "is_func_proto"))
504+
goto cleanup;
505+
vlen = btf_vlen(t);
506+
507+
for (j = 0, p = btf_params(t); j < vlen; j++, p++) {
508+
/* bpf_testmod uses resilient split BTF, so any
509+
* reference types will be added to split BTF and their
510+
* associated targets will be base BTF types; for example
511+
* for a "struct sock *" the PTR will be in split BTF
512+
* while the "struct sock" will be in base.
513+
*
514+
* In some cases like loff_t we have to resolve
515+
* multiple typedefs hence the while() loop below.
516+
*
517+
* Note that resilient split BTF generation depends
518+
* on pahole version, so we do not assert that
519+
* reference types are in split BTF, as if pahole
520+
* does not support resilient split BTF they will
521+
* also be base BTF types.
522+
*/
523+
id = p->type;
524+
do {
525+
t = btf__type_by_id(btf1, id);
526+
if (!ASSERT_OK_PTR(t, "param_ref_type"))
527+
goto cleanup;
528+
if (!btf_is_mod(t) && !btf_is_ptr(t) && !btf_is_typedef(t))
529+
break;
530+
id = t->type;
531+
} while (true);
532+
533+
if (!ASSERT_LT(id, nr_base_types, "verify_base_type"))
534+
goto cleanup;
535+
}
536+
}
537+
cleanup:
538+
btf__free(btf1);
539+
btf__free(vmlinux_btf);
540+
}
541+
443542
void test_btf_dedup_split()
444543
{
445544
if (test__start_subtest("split_simple"))
@@ -450,4 +549,6 @@ void test_btf_dedup_split()
450549
test_split_fwd_resolve();
451550
if (test__start_subtest("split_dup_struct_in_cu"))
452551
test_split_dup_struct_in_cu();
552+
if (test__start_subtest("split_module"))
553+
test_split_module();
453554
}

0 commit comments

Comments
 (0)