diff options
Diffstat (limited to 'tools/testing/selftests/bpf/bpf_testmod')
| -rw-r--r-- | tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c | 200 | ||||
| -rw-r--r-- | tools/testing/selftests/bpf/bpf_testmod/bpf_testmod_kfunc.h | 10 | 
2 files changed, 206 insertions, 4 deletions
| diff --git a/tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c b/tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c index 2a18bd320e92..fd28c1157bd3 100644 --- a/tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c +++ b/tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c @@ -18,6 +18,7 @@  #include <linux/in6.h>  #include <linux/un.h>  #include <net/sock.h> +#include <linux/namei.h>  #include "bpf_testmod.h"  #include "bpf_testmod_kfunc.h" @@ -53,6 +54,13 @@ struct bpf_testmod_struct_arg_4 {  	int b;  }; +struct bpf_testmod_struct_arg_5 { +	char a; +	short b; +	int c; +	long d; +}; +  __bpf_hook_start();  noinline int @@ -111,6 +119,15 @@ bpf_testmod_test_struct_arg_8(u64 a, void *b, short c, int d, void *e,  }  noinline int +bpf_testmod_test_struct_arg_9(u64 a, void *b, short c, int d, void *e, char f, +			      short g, struct bpf_testmod_struct_arg_5 h, long i) +{ +	bpf_testmod_test_struct_arg_result = a + (long)b + c + d + (long)e + +		f + g + h.a + h.b + h.c + h.d + i; +	return bpf_testmod_test_struct_arg_result; +} + +noinline int  bpf_testmod_test_arg_ptr_to_struct(struct bpf_testmod_struct_arg_1 *a) {  	bpf_testmod_test_struct_arg_result = a->a;  	return bpf_testmod_test_struct_arg_result; @@ -154,6 +171,42 @@ __bpf_kfunc void bpf_kfunc_common_test(void)  {  } +__bpf_kfunc void bpf_kfunc_dynptr_test(struct bpf_dynptr *ptr, +				       struct bpf_dynptr *ptr__nullable) +{ +} + +__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;  }; @@ -269,6 +322,7 @@ bpf_testmod_test_read(struct file *file, struct kobject *kobj,  	struct bpf_testmod_struct_arg_2 struct_arg2 = {2, 3};  	struct bpf_testmod_struct_arg_3 *struct_arg3;  	struct bpf_testmod_struct_arg_4 struct_arg4 = {21, 22}; +	struct bpf_testmod_struct_arg_5 struct_arg5 = {23, 24, 25, 26};  	int i = 1;  	while (bpf_testmod_return_ptr(i)) @@ -283,6 +337,8 @@ bpf_testmod_test_read(struct file *file, struct kobject *kobj,  					    (void *)20, struct_arg4);  	(void)bpf_testmod_test_struct_arg_8(16, (void *)17, 18, 19,  					    (void *)20, struct_arg4, 23); +	(void)bpf_testmod_test_struct_arg_9(16, (void *)17, 18, 19, (void *)20, +					    21, 22, struct_arg5, 27);  	(void)bpf_testmod_test_arg_ptr_to_struct(&struct_arg1_2); @@ -358,13 +414,133 @@ static struct bin_attribute bin_attr_bpf_testmod_file __ro_after_init = {  	.write = bpf_testmod_test_write,  }; +/* bpf_testmod_uprobe sysfs attribute is so far enabled for x86_64 only, + * please see test_uretprobe_regs_change test + */ +#ifdef __x86_64__ + +static int +uprobe_ret_handler(struct uprobe_consumer *self, unsigned long func, +		   struct pt_regs *regs) + +{ +	regs->ax  = 0x12345678deadbeef; +	regs->cx  = 0x87654321feebdaed; +	regs->r11 = (u64) -1; +	return true; +} + +struct testmod_uprobe { +	struct path path; +	loff_t offset; +	struct uprobe_consumer consumer; +}; + +static DEFINE_MUTEX(testmod_uprobe_mutex); + +static struct testmod_uprobe uprobe = { +	.consumer.ret_handler = uprobe_ret_handler, +}; + +static int testmod_register_uprobe(loff_t offset) +{ +	int err = -EBUSY; + +	if (uprobe.offset) +		return -EBUSY; + +	mutex_lock(&testmod_uprobe_mutex); + +	if (uprobe.offset) +		goto out; + +	err = kern_path("/proc/self/exe", LOOKUP_FOLLOW, &uprobe.path); +	if (err) +		goto out; + +	err = uprobe_register_refctr(d_real_inode(uprobe.path.dentry), +				     offset, 0, &uprobe.consumer); +	if (err) +		path_put(&uprobe.path); +	else +		uprobe.offset = offset; + +out: +	mutex_unlock(&testmod_uprobe_mutex); +	return err; +} + +static void testmod_unregister_uprobe(void) +{ +	mutex_lock(&testmod_uprobe_mutex); + +	if (uprobe.offset) { +		uprobe_unregister(d_real_inode(uprobe.path.dentry), +				  uprobe.offset, &uprobe.consumer); +		uprobe.offset = 0; +	} + +	mutex_unlock(&testmod_uprobe_mutex); +} + +static ssize_t +bpf_testmod_uprobe_write(struct file *file, struct kobject *kobj, +			 struct bin_attribute *bin_attr, +			 char *buf, loff_t off, size_t len) +{ +	unsigned long offset = 0; +	int err = 0; + +	if (kstrtoul(buf, 0, &offset)) +		return -EINVAL; + +	if (offset) +		err = testmod_register_uprobe(offset); +	else +		testmod_unregister_uprobe(); + +	return err ?: strlen(buf); +} + +static struct bin_attribute bin_attr_bpf_testmod_uprobe_file __ro_after_init = { +	.attr = { .name = "bpf_testmod_uprobe", .mode = 0666, }, +	.write = bpf_testmod_uprobe_write, +}; + +static int register_bpf_testmod_uprobe(void) +{ +	return sysfs_create_bin_file(kernel_kobj, &bin_attr_bpf_testmod_uprobe_file); +} + +static void unregister_bpf_testmod_uprobe(void) +{ +	testmod_unregister_uprobe(); +	sysfs_remove_bin_file(kernel_kobj, &bin_attr_bpf_testmod_uprobe_file); +} + +#else +static int register_bpf_testmod_uprobe(void) +{ +	return 0; +} + +static void unregister_bpf_testmod_uprobe(void) { } +#endif +  BTF_KFUNCS_START(bpf_testmod_common_kfunc_ids)  BTF_ID_FLAGS(func, bpf_iter_testmod_seq_new, KF_ITER_NEW)  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, @@ -820,7 +996,7 @@ static const struct bpf_verifier_ops bpf_testmod_verifier_ops = {  	.is_valid_access = bpf_testmod_ops_is_valid_access,  }; -static int bpf_dummy_reg(void *kdata) +static int bpf_dummy_reg(void *kdata, struct bpf_link *link)  {  	struct bpf_testmod_ops *ops = kdata; @@ -835,7 +1011,7 @@ static int bpf_dummy_reg(void *kdata)  	return 0;  } -static void bpf_dummy_unreg(void *kdata) +static void bpf_dummy_unreg(void *kdata, struct bpf_link *link)  {  } @@ -871,7 +1047,7 @@ struct bpf_struct_ops bpf_bpf_testmod_ops = {  	.owner = THIS_MODULE,  }; -static int bpf_dummy_reg2(void *kdata) +static int bpf_dummy_reg2(void *kdata, struct bpf_link *link)  {  	struct bpf_testmod_ops2 *ops = kdata; @@ -898,6 +1074,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); @@ -906,13 +1088,22 @@ 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)  		return -EINVAL;  	sock = NULL;  	mutex_init(&sock_lock); -	return sysfs_create_bin_file(kernel_kobj, &bin_attr_bpf_testmod_file); +	ret = sysfs_create_bin_file(kernel_kobj, &bin_attr_bpf_testmod_file); +	if (ret < 0) +		return ret; +	ret = register_bpf_testmod_uprobe(); +	if (ret < 0) +		return ret; +	return 0;  }  static void bpf_testmod_exit(void) @@ -927,6 +1118,7 @@ static void bpf_testmod_exit(void)  	bpf_kfunc_close_sock();  	sysfs_remove_bin_file(kernel_kobj, &bin_attr_bpf_testmod_file); +	unregister_bpf_testmod_uprobe();  }  module_init(bpf_testmod_init); diff --git a/tools/testing/selftests/bpf/bpf_testmod/bpf_testmod_kfunc.h b/tools/testing/selftests/bpf/bpf_testmod/bpf_testmod_kfunc.h index b0d586a6751f..e587a79f2239 100644 --- a/tools/testing/selftests/bpf/bpf_testmod/bpf_testmod_kfunc.h +++ b/tools/testing/selftests/bpf/bpf_testmod/bpf_testmod_kfunc.h @@ -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; @@ -134,4 +139,9 @@ int bpf_kfunc_call_sock_sendmsg(struct sendmsg_args *args) __ksym;  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 */ |