Commit 47a8cf0c authored by Alan Maguire's avatar Alan Maguire Committed by Andrii Nakryiko
Browse files

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: default avatarEduard Zingerman <eddyz87@gmail.com>
Signed-off-by: default avatarAlan Maguire <alan.maguire@oracle.com>
Signed-off-by: default avatarAndrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/bpf/20240620091733.1967885-7-alan.maguire@oracle.com
parent 46fb0b62
Loading
Loading
Loading
Loading
+46 −0
Original line number Diff line number Diff line
@@ -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)
+9 −0
Original line number Diff line number Diff line
@@ -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 */
+1 −0
Original line number Diff line number Diff line
@@ -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 {
+37 −0
Original line number Diff line number Diff line
@@ -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";