selftests/bpf: Add kfunc_call test for simple dtor in bpf_testmod

add simple kfuncs to create/destroy a context type to bpf_testmod,
register them and add a kfunc_call test to use them.  This provides
test coverage for registration of dtor kfuncs from modules.

By transferring the context pointer to a map value as a __kptr
we also trigger the map-based dtor cleanup logic, improving test
coverage.

Suggested-by: Eduard Zingerman <eddyz87@gmail.com>
Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/bpf/20240620091733.1967885-7-alan.maguire@oracle.com
This commit is contained in:
Alan Maguire 2024-06-20 10:17:33 +01:00 committed by Andrii Nakryiko
parent 46fb0b62ea
commit 47a8cf0c5b
4 changed files with 93 additions and 0 deletions

View File

@ -159,6 +159,37 @@ __bpf_kfunc void bpf_kfunc_dynptr_test(struct bpf_dynptr *ptr,
{
}
__bpf_kfunc struct bpf_testmod_ctx *
bpf_testmod_ctx_create(int *err)
{
struct bpf_testmod_ctx *ctx;
ctx = kzalloc(sizeof(*ctx), GFP_ATOMIC);
if (!ctx) {
*err = -ENOMEM;
return NULL;
}
refcount_set(&ctx->usage, 1);
return ctx;
}
static void testmod_free_cb(struct rcu_head *head)
{
struct bpf_testmod_ctx *ctx;
ctx = container_of(head, struct bpf_testmod_ctx, rcu);
kfree(ctx);
}
__bpf_kfunc void bpf_testmod_ctx_release(struct bpf_testmod_ctx *ctx)
{
if (!ctx)
return;
if (refcount_dec_and_test(&ctx->usage))
call_rcu(&ctx->rcu, testmod_free_cb);
}
struct bpf_testmod_btf_type_tag_1 {
int a;
};
@ -369,8 +400,14 @@ BTF_ID_FLAGS(func, bpf_iter_testmod_seq_next, KF_ITER_NEXT | KF_RET_NULL)
BTF_ID_FLAGS(func, bpf_iter_testmod_seq_destroy, KF_ITER_DESTROY)
BTF_ID_FLAGS(func, bpf_kfunc_common_test)
BTF_ID_FLAGS(func, bpf_kfunc_dynptr_test)
BTF_ID_FLAGS(func, bpf_testmod_ctx_create, KF_ACQUIRE | KF_RET_NULL)
BTF_ID_FLAGS(func, bpf_testmod_ctx_release, KF_RELEASE)
BTF_KFUNCS_END(bpf_testmod_common_kfunc_ids)
BTF_ID_LIST(bpf_testmod_dtor_ids)
BTF_ID(struct, bpf_testmod_ctx)
BTF_ID(func, bpf_testmod_ctx_release)
static const struct btf_kfunc_id_set bpf_testmod_common_kfunc_set = {
.owner = THIS_MODULE,
.set = &bpf_testmod_common_kfunc_ids,
@ -904,6 +941,12 @@ extern int bpf_fentry_test1(int a);
static int bpf_testmod_init(void)
{
const struct btf_id_dtor_kfunc bpf_testmod_dtors[] = {
{
.btf_id = bpf_testmod_dtor_ids[0],
.kfunc_btf_id = bpf_testmod_dtor_ids[1]
},
};
int ret;
ret = register_btf_kfunc_id_set(BPF_PROG_TYPE_UNSPEC, &bpf_testmod_common_kfunc_set);
@ -912,6 +955,9 @@ static int bpf_testmod_init(void)
ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_SYSCALL, &bpf_testmod_kfunc_set);
ret = ret ?: register_bpf_struct_ops(&bpf_bpf_testmod_ops, bpf_testmod_ops);
ret = ret ?: register_bpf_struct_ops(&bpf_testmod_ops2, bpf_testmod_ops2);
ret = ret ?: register_btf_id_dtor_kfuncs(bpf_testmod_dtors,
ARRAY_SIZE(bpf_testmod_dtors),
THIS_MODULE);
if (ret < 0)
return ret;
if (bpf_fentry_test1(0) < 0)

View File

@ -80,6 +80,11 @@ struct sendmsg_args {
int msglen;
};
struct bpf_testmod_ctx {
struct callback_head rcu;
refcount_t usage;
};
struct prog_test_ref_kfunc *
bpf_kfunc_call_test_acquire(unsigned long *scalar_ptr) __ksym;
void bpf_kfunc_call_test_release(struct prog_test_ref_kfunc *p) __ksym;
@ -135,4 +140,8 @@ int bpf_kfunc_call_kernel_getsockname(struct addr_args *args) __ksym;
int bpf_kfunc_call_kernel_getpeername(struct addr_args *args) __ksym;
void bpf_kfunc_dynptr_test(struct bpf_dynptr *ptr, struct bpf_dynptr *ptr__nullable) __ksym;
struct bpf_testmod_ctx *bpf_testmod_ctx_create(int *err) __ksym;
void bpf_testmod_ctx_release(struct bpf_testmod_ctx *ctx) __ksym;
#endif /* _BPF_TESTMOD_KFUNC_H */

View File

@ -78,6 +78,7 @@ static struct kfunc_test_params kfunc_tests[] = {
SYSCALL_TEST(kfunc_syscall_test, 0),
SYSCALL_NULL_CTX_TEST(kfunc_syscall_test_null, 0),
TC_TEST(kfunc_call_test_static_unused_arg, 0),
TC_TEST(kfunc_call_ctx, 0),
};
struct syscall_test_args {

View File

@ -177,4 +177,41 @@ int kfunc_call_test_static_unused_arg(struct __sk_buff *skb)
return actual != expected ? -1 : 0;
}
struct ctx_val {
struct bpf_testmod_ctx __kptr *ctx;
};
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__uint(max_entries, 1);
__type(key, int);
__type(value, struct ctx_val);
} ctx_map SEC(".maps");
SEC("tc")
int kfunc_call_ctx(struct __sk_buff *skb)
{
struct bpf_testmod_ctx *ctx;
int err = 0;
ctx = bpf_testmod_ctx_create(&err);
if (!ctx && !err)
err = -1;
if (ctx) {
int key = 0;
struct ctx_val *ctx_val = bpf_map_lookup_elem(&ctx_map, &key);
/* Transfer ctx to map to be freed via implicit dtor call
* on cleanup.
*/
if (ctx_val)
ctx = bpf_kptr_xchg(&ctx_val->ctx, ctx);
if (ctx) {
bpf_testmod_ctx_release(ctx);
err = -1;
}
}
return err;
}
char _license[] SEC("license") = "GPL";