Registrating struct_ops types from modules
Subsystems (8)
v4 cover Registrating struct_ops types from modules
From: Kui-Feng Lee <[email protected]>
Given the current constraints of the current implementation, struct_ops cannot be registered dynamically. This presents a significant limitation for modules like coming fuse-bpf, which seeks to implement a new struct_ops type. To address this issue, a new API is introduced that allows the registration of new struct_ops types from modules.
Previously, struct_ops types were defined in bpf_struct_ops_types.h and collected as a static array. The new API lets callers add new struct_ops types dynamically. The static array has been removed and replaced by the per-btf struct_ops_tab.
The struct_ops subsystem relies on BTF to determine the layout of values in a struct_ops map and identify the subsystem that the struct_ops map registers to. However, the kernel BTF does not include the type information of struct_ops types defined by a module. The struct_ops subsystem requires knowledge of the corresponding module for a given struct_ops map and the utilization of BTF information from that module. We empower libbpf to determine the correct module for accessing the BTF information and pass an identity (FD) of the module btf to the kernel. The kernel looks up type information and registered struct_ops types directly from the given btf.
If a module exits while one or more struct_ops maps still refer to a struct_ops type defined by the module, it can lead to unforeseen complications. Therefore, it is crucial to ensure that a module remains intact as long as any struct_ops map is still linked to a struct_ops type defined by the module. To achieve this, every struct_ops map holds a reference to the module for each struct_ops map.
Changes from v3:
- Fix according to the feedback for v3.
- Change of the order of arguments to make btf as the first argument.
- Use btf_try_get_module() instead of try_get_module() since the module pointed by st_ops->owner can gone while some one is still holding its btf.
- Move variables defined by BPF_STRUCT_OPS_COMMON_VALUE to struct bpf_struct_ops_common_value to validation easier.
- Register the struct_ops type defined by bpf_testmod in its init function.
- Rename field name to 'value_type_btf_obj_fd' to make it explicit.
- Fix leaking of btf objects on error.
- st_maps hold their modules to keep modules alive and prevent they from unloading.
- bpf_map of libbpf keeps mod_btf_fd instead of a pointer to module_btf.
- Do call_rcu_tasks_trace() in kern_sync_rcu() to ensure the bpf_testmod is unloaded properly. It uses rcu_tasks_trace_gp to trigger call_rcu_tasks_trace() in the kernel.
- Merge and reorder patches in a reasonable order.
-
Changes from v2:
- Remove struct_ops array, and add a per-btf (module) struct_ops_tab to collect registered struct_ops types.
- Validate value_type by checking member names and types.
---
v3: https://lore.kernel.org/all/[email protected]/
v2: https://lore.kernel.org/all/[email protected]/
Kui-Feng Lee (9):
bpf: refactory struct_ops type initialization to a function.
bpf: add struct_ops_tab to btf.
bpf: hold module for bpf_struct_ops_map.
bpf: validate value_type
bpf: pass attached BTF to the bpf_struct_ops subsystem bpf, net: switch to dynamic registration
libbpf: Find correct module BTFs for struct_ops maps and progs.
bpf: export btf_ctx_access to modules.
selftests/bpf: test case for register_bpf_struct_ops().
include/linux/bpf.h | 8 +- include/linux/btf.h | 35 ++ include/uapi/linux/bpf.h | 5 + kernel/bpf/bpf_struct_ops.c | 375 +++++++++++------- kernel/bpf/bpf_struct_ops_types.h | 12 - kernel/bpf/btf.c | 73 +++- kernel/bpf/syscall.c | 2 +- kernel/bpf/verifier.c | 4 +- net/bpf/bpf_dummy_struct_ops.c | 14 +- net/ipv4/bpf_tcp_ca.c | 16 +- tools/include/uapi/linux/bpf.h | 5 + tools/lib/bpf/bpf.c | 4 +- tools/lib/bpf/bpf.h | 5 +- tools/lib/bpf/libbpf.c | 68 ++-- .../selftests/bpf/bpf_testmod/bpf_testmod.c | 59 +++ .../selftests/bpf/bpf_testmod/bpf_testmod.h | 5 + .../bpf/prog_tests/test_struct_ops_module.c | 38 ++ .../selftests/bpf/progs/struct_ops_module.c | 30 ++ tools/testing/selftests/bpf/testing_helpers.c | 35 ++ 19 files changed, 602 insertions(+), 191 deletions(-) delete mode 100644 kernel/bpf/bpf_struct_ops_types.h create mode 100644 tools/testing/selftests/bpf/prog_tests/test_struct_ops_module.c create mode 100644 tools/testing/selftests/bpf/progs/struct_ops_module.c
Signature · 1 line
2.34.1
PATCH v4 1/9 bpf: refactory struct_ops type initialization to a function.
From: Kui-Feng Lee <[email protected]>
Move the majority of the code to bpf_struct_ops_init_one(), which can then be utilized for the initialization of newly registered dynamically allocated struct_ops types in the following patches.
--- kernel/bpf/bpf_struct_ops.c | 157 +++++++++++++++++++----------------- 1 file changed, 83 insertions(+), 74 deletions(-)
kernel/bpf/bpf_struct_ops.c +83 -74
@@ -110,102 +110,111 @@ const struct bpf_prog_ops bpf_struct_ops_prog_ops = {static const struct btf_type *module_type; -void bpf_struct_ops_init (struct btf *btf,struct bpf_verifier_log *log) +static void bpf_struct_ops_init_one (struct bpf_struct_ops *st_ops, +struct btf *btf, +struct bpf_verifier_log *log) { -s32 type_id, value_id, module_id;const struct btf_member *member; -struct bpf_struct_ops *st_ops;const struct btf_type *t; +s32 type_id, value_id;char value_name[128 ];const char *mname; -u32 i, j; +int i; -/* Ensure BTF type is emitted for "struct bpf_struct_ops_##_name" */ -#define BPF_STRUCT_OPS_TYPE (_name) BTF_TYPE_EMIT(struct bpf_struct_ops_##_name); -#include "bpf_struct_ops_types.h" -#undef BPF_STRUCT_OPS_TYPE +if (strlen (st_ops->name) +VALUE_PREFIX_LEN >= +sizeof (value_name)) { +pr_warn ("struct_ops name %s is too long\n" , + st_ops->name); +return ; + } +sprintf (value_name,"%s%s" ,VALUE_PREFIX , st_ops->name); - module_id =btf_find_by_name_kind (btf,"module" ,BTF_KIND_STRUCT ); -if (module_id <0 ) { -pr_warn ("Cannot find struct module in btf_vmlinux\n" ); + value_id =btf_find_by_name_kind (btf, value_name, +BTF_KIND_STRUCT ); +if (value_id <0 ) { +pr_warn ("Cannot find struct %s in btf_vmlinux\n" , + value_name);return ; } - module_type =btf_type_by_id (btf, module_id); -for (i =0 ; i <ARRAY_SIZE (bpf_struct_ops); i++) { - st_ops = bpf_struct_ops[i]; + type_id =btf_find_by_name_kind (btf, st_ops->name, +BTF_KIND_STRUCT ); +if (type_id <0 ) { +pr_warn ("Cannot find struct %s in btf_vmlinux\n" , + st_ops->name); +return ; + } + t =btf_type_by_id (btf, type_id); +if (btf_type_vlen (t) >BPF_STRUCT_OPS_MAX_NR_MEMBERS ) { +pr_warn ("Cannot support #%u members in struct %s\n" , +btf_type_vlen (t), st_ops->name); +return ; + } -if (strlen (st_ops->name) +VALUE_PREFIX_LEN >= -sizeof (value_name)) { -pr_warn ("struct_ops name %s is too long\n" , +for_each_member (i, t, member) { +const struct btf_type *func_proto; + + mname =btf_name_by_offset (btf, member->name_off); +if (!*mname) { +pr_warn ("anon member in struct %s is not supported\n" , st_ops->name); -continue ; +break ; } -sprintf (value_name,"%s%s" ,VALUE_PREFIX , st_ops->name); - value_id =btf_find_by_name_kind (btf, value_name, -BTF_KIND_STRUCT ); -if (value_id <0 ) { -pr_warn ("Cannot find struct %s in btf_vmlinux\n" , - value_name); -continue ; +if (__btf_member_bitfield_size (t, member)) { +pr_warn ("bit field member %s in struct %s is not supported\n" , + mname, st_ops->name); +break ; } - type_id =btf_find_by_name_kind (btf, st_ops->name, -BTF_KIND_STRUCT ); -if (type_id <0 ) { -pr_warn ("Cannot find struct %s in btf_vmlinux\n" , - st_ops->name); -continue ; - } - t =btf_type_by_id (btf, type_id); -if (btf_type_vlen (t) >BPF_STRUCT_OPS_MAX_NR_MEMBERS ) { -pr_warn ("Cannot support #%u members in struct %s\n" , -btf_type_vlen (t), st_ops->name); -continue ; + func_proto =btf_type_resolve_func_ptr (btf, + member->type, +NULL ); +if (func_proto && +btf_distill_func_proto (log, btf, + func_proto, mname, + &st_ops->func_models[i])) { +pr_warn ("Error in parsing func ptr %s in struct %s\n" , + mname, st_ops->name); +break ; } + } -for_each_member (j, t, member) { -const struct btf_type *func_proto; +if (i ==btf_type_vlen (t)) { +if (st_ops->init (btf)) { +pr_warn ("Error in init bpf_struct_ops %s\n" , + st_ops->name); + }else { + st_ops->type_id = type_id; + st_ops->type = t; + st_ops->value_id = value_id; + st_ops->value_type =btf_type_by_id (btf, + value_id); + } + } +} - mname =btf_name_by_offset (btf, member->name_off); -if (!*mname) { -pr_warn ("anon member in struct %s is not supported\n" , - st_ops->name); -break ; - } +void bpf_struct_ops_init (struct btf *btf,struct bpf_verifier_log *log) +{ +struct bpf_struct_ops *st_ops; +s32 module_id; +u32 i; -if (__btf_member_bitfield_size (t, member)) { -pr_warn ("bit field member %s in struct %s is not supported\n" , - mname, st_ops->name); -break ; - } +/* Ensure BTF type is emitted for "struct bpf_struct_ops_##_name" */ +#define BPF_STRUCT_OPS_TYPE (_name) BTF_TYPE_EMIT(struct bpf_struct_ops_##_name); +#include "bpf_struct_ops_types.h" +#undef BPF_STRUCT_OPS_TYPE - func_proto =btf_type_resolve_func_ptr (btf, - member->type, -NULL ); -if (func_proto && -btf_distill_func_proto (log, btf, - func_proto, mname, - &st_ops->func_models[j])) { -pr_warn ("Error in parsing func ptr %s in struct %s\n" , - mname, st_ops->name); -break ; - } - } + module_id =btf_find_by_name_kind (btf,"module" ,BTF_KIND_STRUCT ); +if (module_id <0 ) { +pr_warn ("Cannot find struct module in btf_vmlinux\n" ); +return ; + } + module_type =btf_type_by_id (btf, module_id); -if (j ==btf_type_vlen (t)) { -if (st_ops->init (btf)) { -pr_warn ("Error in init bpf_struct_ops %s\n" , - st_ops->name); - }else { - st_ops->type_id = type_id; - st_ops->type = t; - st_ops->value_id = value_id; - st_ops->value_type =btf_type_by_id (btf, - value_id); - } - } +for (i =0 ; i <ARRAY_SIZE (bpf_struct_ops); i++) { + st_ops = bpf_struct_ops[i]; +bpf_struct_ops_init_one (st_ops, btf, log); } }
Signature · 1 line
2.34.1
PATCH v4 2/9 bpf: add struct_ops_tab to btf.
In reply to [email protected]From: Kui-Feng Lee <[email protected]>
Maintain a registry of registered struct_ops types in the per-btf (module) struct_ops_tab. This registry allows for easy lookup of struct_ops types that are registered by a specific module.
Every struct_ops type should have an associated module BTF to provide type information since we are going to allow modules to define and register new struct_ops types. Once this change is made, the bpf_struct_ops subsystem knows where to look up type info with just a bpf_struct_ops.
The subsystem looks up struct_ops types from a given module BTF although it is always btf_vmlinux now. Once start using struct_ops_tab, btfs other than btf_vmlinux can be used as well.
--- include/linux/bpf.h | 5 +-- include/linux/btf.h | 6 ++++ kernel/bpf/bpf_struct_ops.c | 17 ++++----- kernel/bpf/btf.c | 70 +++++++++++++++++++++++++++++++++++++ kernel/bpf/verifier.c | 2 +- 5 files changed, 89 insertions(+), 11 deletions(-)
include/linux/bpf.h +3 -2
@@ -1626,6 +1626,7 @@ struct bpf_struct_ops {void (*unreg)(void *kdata);int (*update)(void *kdata,void *old_kdata);int (*validate)(void *kdata); +struct btf *btf;const struct btf_type *type;const struct btf_type *value_type;const char *name;
@@ -1636,7 +1637,7 @@ struct bpf_struct_ops {#if defined(CONFIG_BPF_JIT ) && defined(CONFIG_BPF_SYSCALL )#define BPF_MODULE_OWNER ((void *)((0xeB9FUL << 2) + POISON_POINTER_DELTA)) -const struct bpf_struct_ops *bpf_struct_ops_find (u32 type_id); +const struct bpf_struct_ops *bpf_struct_ops_find (struct btf *btf,u32 type_id);void bpf_struct_ops_init (struct btf *btf,struct bpf_verifier_log *log);bool bpf_struct_ops_get (const void *kdata);void bpf_struct_ops_put (const void *kdata);
@@ -1679,7 +1680,7 @@ int bpf_struct_ops_test_run(struct bpf_prog *prog, const union bpf_attr *kattr, union bpf_attr __user *uattr);#endif #else -static inline const struct bpf_struct_ops *bpf_struct_ops_find (u32 type_id) +static inline const struct bpf_struct_ops *bpf_struct_ops_find (struct btf *btf,u32 type_id) {return NULL ; }
include/linux/btf.h +6 -0
@@ -571,4 +571,10 @@ static inline bool btf_type_is_struct_ptr(struct btf *btf, const struct btf_type return btf_type_is_struct(t); } +struct bpf_struct_ops ; + +int btf_add_struct_ops (struct bpf_struct_ops *st_ops); +const struct bpf_struct_ops ** +btf_get_struct_ops (struct btf *btf,u32 *ret_cnt); +#endif
kernel/bpf/bpf_struct_ops.c +9 -8
@@ -185,6 +185,7 @@ static void bpf_struct_ops_init_one(struct bpf_struct_ops *st_ops,pr_warn ("Error in init bpf_struct_ops %s\n" , st_ops->name); }else { + st_ops->btf = btf; st_ops->type_id = type_id; st_ops->type = t; st_ops->value_id = value_id;
@@ -221,7 +222,7 @@ void bpf_struct_ops_init(struct btf *btf, struct bpf_verifier_log *log)extern struct btf *btf_vmlinux;static const struct bpf_struct_ops * -bpf_struct_ops_find_value (u32 value_id) +bpf_struct_ops_find_value (struct btf *btf,u32 value_id) {unsigned int i;
@@ -236,7 +237,7 @@ bpf_struct_ops_find_value(u32 value_id)return NULL ; } -const struct bpf_struct_ops *bpf_struct_ops_find (u32 type_id) +const struct bpf_struct_ops *bpf_struct_ops_find (struct btf *btf,u32 type_id) {unsigned int i;
@@ -316,7 +317,7 @@ static void bpf_struct_ops_map_put_progs(struct bpf_struct_ops_map *st_map) } } -static int check_zero_holes (const struct btf_type *t,void *data) +static int check_zero_holes (const struct btf *btf,const struct btf_type *t,void *data) {const struct btf_member *member;u32 i, moff, msize, prev_mend =0 ;
@@ -328,8 +329,8 @@ static int check_zero_holes(const struct btf_type *t, void *data) memchr_inv(data + prev_mend, 0, moff - prev_mend))return -EINVAL ; - mtype =btf_type_by_id (btf_vmlinux, member->type); - mtype =btf_resolve_size (btf_vmlinux, mtype, &msize); + mtype =btf_type_by_id (btf, member->type); + mtype =btf_resolve_size (btf, mtype, &msize);if (IS_ERR (mtype))return PTR_ERR (mtype); prev_mend = moff + msize;
@@ -395,12 +396,12 @@ static long bpf_struct_ops_map_update_elem(struct bpf_map *map, void *key,if (*(u32 *)key !=0 )return -E2BIG ; - err =check_zero_holes (st_ops->value_type, value); + err =check_zero_holes (st_ops->btf, st_ops->value_type, value);if (err)return err; uvalue = value; - err =check_zero_holes (t, uvalue->data); + err =check_zero_holes (st_ops->btf, t, uvalue->data);if (err)return err;
@@ -671,7 +672,7 @@ static struct bpf_map *bpf_struct_ops_map_alloc(union bpf_attr *attr)struct bpf_map *map;int ret; - st_ops =bpf_struct_ops_find_value (attr->btf_vmlinux_value_type_id); + st_ops =bpf_struct_ops_find_value (attr->btf_vmlinux_value_type_id, btf_vmlinux);if (!st_ops)return ERR_PTR (-ENOTSUPP );
kernel/bpf/btf.c +70 -0
96 lines not on this page · raw message
kernel/bpf/verifier.c +1 -1
@@ -19631,7 +19631,7 @@ static int check_struct_ops_btf_id(struct bpf_verifier_env *env) } btf_id = prog->aux->attach_btf_id; - st_ops =bpf_struct_ops_find (btf_id); + st_ops =bpf_struct_ops_find (btf_vmlinux, btf_id);if (!st_ops) {verbose (env,"attach_btf_id %u is not a supported struct\n" , btf_id);
Signature · 1 line
2.34.1
PATCH v4 3/9 bpf: hold module for bpf_struct_ops_map.
In reply to [email protected]From: Kui-Feng Lee <[email protected]>
To ensure that a module remains accessible whenever a struct_ops object of a struct_ops type provided by the module is still in use.
--- include/linux/bpf.h | 1 + kernel/bpf/bpf_struct_ops.c | 21 ++++++++++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-)
include/linux/bpf.h +1 -0
@@ -1627,6 +1627,7 @@ struct bpf_struct_ops {int (*update)(void *kdata,void *old_kdata);int (*validate)(void *kdata);struct btf *btf; +struct module *owner;const struct btf_type *type;const struct btf_type *value_type;const char *name;
kernel/bpf/bpf_struct_ops.c +18 -3
@@ -112,6 +112,7 @@ static const struct btf_type *module_type;static void bpf_struct_ops_init_one (struct bpf_struct_ops *st_ops,struct btf *btf, +struct module *owner,struct bpf_verifier_log *log) {const struct btf_member *member;
@@ -186,6 +187,7 @@ static void bpf_struct_ops_init_one(struct bpf_struct_ops *st_ops, st_ops->name); }else { st_ops->btf = btf; + st_ops->owner = owner; st_ops->type_id = type_id; st_ops->type = t; st_ops->value_id = value_id;
@@ -193,6 +195,7 @@ static void bpf_struct_ops_init_one(struct bpf_struct_ops *st_ops, value_id); } } + }void bpf_struct_ops_init (struct btf *btf,struct bpf_verifier_log *log)
@@ -215,7 +218,7 @@ void bpf_struct_ops_init(struct btf *btf, struct bpf_verifier_log *log)for (i =0 ; i <ARRAY_SIZE (bpf_struct_ops); i++) { st_ops = bpf_struct_ops[i]; -bpf_struct_ops_init_one (st_ops, btf, log); +bpf_struct_ops_init_one (st_ops, btf,NULL , log); } }
@@ -630,6 +633,7 @@ static void __bpf_struct_ops_map_free(struct bpf_map *map)bpf_jit_uncharge_modmem (PAGE_SIZE ); }bpf_map_area_free (st_map->uvalue); +module_put (st_map->st_ops->owner);bpf_map_area_free (st_map); }
@@ -676,9 +680,18 @@ static struct bpf_map *bpf_struct_ops_map_alloc(union bpf_attr *attr)if (!st_ops)return ERR_PTR (-ENOTSUPP ); +/* If st_ops->owner is NULL, it means the struct_ops is +* statically defined in the kernel. We don't need to +* take a refcount on it. +*/ +if (st_ops->owner && !btf_try_get_module (st_ops->btf)) +return ERR_PTR (-EINVAL ); + vt = st_ops->value_type; -if (attr->value_size != vt->size) +if (attr->value_size != vt->size) { +module_put (st_ops->owner);return ERR_PTR (-EINVAL ); + } t = st_ops->type;
@@ -689,8 +702,10 @@ static struct bpf_map *bpf_struct_ops_map_alloc(union bpf_attr *attr) (vt->size -sizeof (struct bpf_struct_ops_value )); st_map =bpf_map_area_alloc (st_map_size,NUMA_NO_NODE ); -if (!st_map) +if (!st_map) { +module_put (st_ops->owner);return ERR_PTR (-ENOMEM ); + } st_map->st_ops = st_ops; map = &st_map->map;
Signature · 1 line
2.34.1
PATCH v4 4/9 bpf: validate value_type
In reply to [email protected]From: Kui-Feng Lee <[email protected]>
A value_type should consist of three components: refcnt, state, and data. refcnt and state has been move to struct bpf_struct_ops_common_value to make it easier to check the value type.
--- kernel/bpf/bpf_struct_ops.c | 88 +++++++++++++++++++++++++++---------- 1 file changed, 66 insertions(+), 22 deletions(-)
kernel/bpf/bpf_struct_ops.c +66 -22
@@ -20,9 +20,11 @@ enum bpf_struct_ops_state { BPF_STRUCT_OPS_STATE_READY, }; -#define BPF_STRUCT_OPS_COMMON_VALUE \ - refcount_t refcnt; \ - enum bpf_struct_ops_state state +struct bpf_struct_ops_common_value { +refcount_t refcnt; +enum bpf_struct_ops_state state; +}; +#define BPF_STRUCT_OPS_COMMON_VALUE struct bpf_struct_ops_common_value commonstruct bpf_struct_ops_value {BPF_STRUCT_OPS_COMMON_VALUE ;
@@ -109,6 +111,38 @@ const struct bpf_prog_ops bpf_struct_ops_prog_ops = { };static const struct btf_type *module_type; +static const struct btf_type *common_value_type; + +static bool is_valid_value_type (struct btf *btf,s32 value_id, +const struct btf_type *type, +const char *value_name) +{ +const struct btf_member *member; +const struct btf_type *vt, *mt; + + vt =btf_type_by_id (btf, value_id); +if (btf_vlen (vt) !=2 ) { +pr_warn ("The number of %s's members should be 2, but we get %d\n" , + value_name,btf_vlen (vt)); +return false; + } + member =btf_type_member (vt); + mt =btf_type_by_id (btf, member->type); +if (mt != common_value_type) { +pr_warn ("The first member of %s should be bpf_struct_ops_common_value\n" , + value_name); +return false; + } + member++; + mt =btf_type_by_id (btf, member->type); +if (mt != type) { +pr_warn ("The second member of %s should be %s\n" , + value_name,btf_name_by_offset (btf, type->name_off)); +return false; + } + +return true; +} static void bpf_struct_ops_init_one(struct bpf_struct_ops *st_ops, struct btf *btf,
@@ -130,14 +164,6 @@ static void bpf_struct_ops_init_one(struct bpf_struct_ops *st_ops, }sprintf (value_name,"%s%s" ,VALUE_PREFIX , st_ops->name); - value_id =btf_find_by_name_kind (btf, value_name, -BTF_KIND_STRUCT ); -if (value_id <0 ) { -pr_warn ("Cannot find struct %s in btf_vmlinux\n" , - value_name); -return ; - } - type_id =btf_find_by_name_kind (btf, st_ops->name,BTF_KIND_STRUCT );if (type_id <0 ) {
@@ -152,6 +178,16 @@ static void bpf_struct_ops_init_one(struct bpf_struct_ops *st_ops,return ; } + value_id =btf_find_by_name_kind (btf, value_name, +BTF_KIND_STRUCT ); +if (value_id <0 ) { +pr_warn ("Cannot find struct %s in btf_vmlinux\n" , + value_name); +return ; + } +if (!is_valid_value_type (btf, value_id, t, value_name)) +return ; +for_each_member (i, t, member) {const struct btf_type *func_proto;
@@ -201,7 +237,7 @@ static void bpf_struct_ops_init_one(struct bpf_struct_ops *st_ops,void bpf_struct_ops_init (struct btf *btf,struct bpf_verifier_log *log) {struct bpf_struct_ops *st_ops; -s32 module_id; +s32 module_id, common_value_id;u32 i;/* Ensure BTF type is emitted for "struct bpf_struct_ops_##_name" */
@@ -215,6 +251,14 @@ void bpf_struct_ops_init(struct btf *btf, struct bpf_verifier_log *log)return ; } module_type =btf_type_by_id (btf, module_id); + common_value_id =btf_find_by_name_kind (btf, +"bpf_struct_ops_common_value" , +BTF_KIND_STRUCT ); +if (common_value_id <0 ) { +pr_warn ("Cannot find struct common_value in btf_vmlinux\n" ); +return ; + } + common_value_type =btf_type_by_id (btf, common_value_id);for (i =0 ; i <ARRAY_SIZE (bpf_struct_ops); i++) { st_ops = bpf_struct_ops[i];
@@ -278,7 +322,7 @@ int bpf_struct_ops_map_sys_lookup_elem(struct bpf_map *map, void *key, kvalue = &st_map->kvalue;/* Pair with smp_store_release() during map_update */ - state =smp_load_acquire (&kvalue->state); + state =smp_load_acquire (&kvalue->common.state);if (state ==BPF_STRUCT_OPS_STATE_INIT ) {memset (value,0 , map->value_size);return 0 ;
@@ -289,7 +333,7 @@ int bpf_struct_ops_map_sys_lookup_elem(struct bpf_map *map, void *key,*/ uvalue = value;memcpy (uvalue, st_map->uvalue, map->value_size); - uvalue->state = state; + uvalue->common.state = state;/* This value offers the user space a general estimate of how * many sockets are still utilizing this struct_ops for TCP
@@ -297,7 +341,7 @@ int bpf_struct_ops_map_sys_lookup_elem(struct bpf_map *map, void *key,* should sufficiently meet our present goals. */ refcnt =atomic64_read (&map->refcnt) -atomic64_read (&map->usercnt); -refcount_set (&uvalue->refcnt,max_t (s64, refcnt,0 )); +refcount_set (&uvalue->common.refcnt,max_t (s64, refcnt,0 ));return 0 ; }
@@ -408,7 +452,7 @@ static long bpf_struct_ops_map_update_elem(struct bpf_map *map, void *key,if (err)return err; -if (uvalue->state ||refcount_read (&uvalue->refcnt)) +if (uvalue->common.state ||refcount_read (&uvalue->common.refcnt))return -EINVAL ; tlinks =kcalloc (BPF_TRAMP_MAX ,sizeof (*tlinks),GFP_KERNEL );
@@ -420,7 +464,7 @@ static long bpf_struct_ops_map_update_elem(struct bpf_map *map, void *key,mutex_lock (&st_map->lock); -if (kvalue->state !=BPF_STRUCT_OPS_STATE_INIT ) { +if (kvalue->common.state !=BPF_STRUCT_OPS_STATE_INIT ) { err = -EBUSY ; goto unlock; }
@@ -533,7 +577,7 @@ static long bpf_struct_ops_map_update_elem(struct bpf_map *map, void *key,* * Pair with smp_load_acquire() during lookup_elem(). */ -smp_store_release (&kvalue->state,BPF_STRUCT_OPS_STATE_READY ); +smp_store_release (&kvalue->common.state,BPF_STRUCT_OPS_STATE_READY ); goto unlock; }
@@ -551,7 +595,7 @@ static long bpf_struct_ops_map_update_elem(struct bpf_map *map, void *key,* It ensures the above udata updates (e.g. prog->aux->id) * can be seen once BPF_STRUCT_OPS_STATE_INUSE is set. */ -smp_store_release (&kvalue->state,BPF_STRUCT_OPS_STATE_INUSE ); +smp_store_release (&kvalue->common.state,BPF_STRUCT_OPS_STATE_INUSE ); goto unlock; }
@@ -582,7 +626,7 @@ static long bpf_struct_ops_map_delete_elem(struct bpf_map *map, void *key)if (st_map->map.map_flags &BPF_F_LINK )return -EOPNOTSUPP ; - prev_state =cmpxchg (&st_map->kvalue.state, + prev_state =cmpxchg (&st_map->kvalue.common.state,BPF_STRUCT_OPS_STATE_INUSE ,BPF_STRUCT_OPS_STATE_TOBEFREE );switch (prev_state) {
@@ -676,7 +720,7 @@ static struct bpf_map *bpf_struct_ops_map_alloc(union bpf_attr *attr)struct bpf_map *map;int ret; - st_ops =bpf_struct_ops_find_value (attr->btf_vmlinux_value_type_id, btf_vmlinux); + st_ops =bpf_struct_ops_find_value (btf_vmlinux, attr->btf_vmlinux_value_type_id);if (!st_ops)return ERR_PTR (-ENOTSUPP );
@@ -805,7 +849,7 @@ static bool bpf_struct_ops_valid_to_reg(struct bpf_map *map)return map->map_type ==BPF_MAP_TYPE_STRUCT_OPS && map->map_flags &BPF_F_LINK &&/* Pair with smp_store_release() during map_update */ -smp_load_acquire (&st_map->kvalue.state) ==BPF_STRUCT_OPS_STATE_READY ; +smp_load_acquire (&st_map->kvalue.common.state) ==BPF_STRUCT_OPS_STATE_READY ; }static void bpf_struct_ops_map_link_dealloc (struct bpf_link *link)
Signature · 1 line
2.34.1
PATCH v4 5/9 bpf: pass attached BTF to the bpf_struct_ops subsystem
In reply to [email protected]From: Kui-Feng Lee <[email protected]>
Giving a BTF, the bpf_struct_ops knows the right place to look up type info associated with a type ID. This enables a user space program to load a struct_ops object linked to a struct_ops type defined by a module, by providing the module BTF (fd).
The bpf_prog includes attach_btf in aux which is passed along with the bpf_attr when loading the program. The purpose of attach_btf is to determine the btf type of attach_btf_id. The attach_btf_id is then used to identify the traced function for a trace program. In the case of struct_ops programs, it is used to identify the struct_ops type of the struct_ops object that a program is attached to.
--- include/uapi/linux/bpf.h | 5 +++++ kernel/bpf/bpf_struct_ops.c | 34 +++++++++++++++++++++++++++------- kernel/bpf/syscall.c | 2 +- kernel/bpf/verifier.c | 4 +++- tools/include/uapi/linux/bpf.h | 5 +++++ 5 files changed, 41 insertions(+), 9 deletions(-)
include/uapi/linux/bpf.h +5 -0
@@ -1390,6 +1390,11 @@ union bpf_attr {* to using 5 hash functions). */ __u64 map_extra; + +__u32 value_type_btf_obj_fd;/* fd pointing to a BTF +* type data for +* btf_vmlinux_value_type_id. +*/ };struct {/* anonymous struct used by BPF_MAP_*_ELEM commands */
kernel/bpf/bpf_struct_ops.c +27 -7
@@ -677,6 +677,7 @@ static void __bpf_struct_ops_map_free(struct bpf_map *map)bpf_jit_uncharge_modmem (PAGE_SIZE ); }bpf_map_area_free (st_map->uvalue); +btf_put (st_map->st_ops->btf);module_put (st_map->st_ops->owner);bpf_map_area_free (st_map); }
@@ -718,23 +719,36 @@ static struct bpf_map *bpf_struct_ops_map_alloc(union bpf_attr *attr)struct bpf_struct_ops_map *st_map;const struct btf_type *t, *vt;struct bpf_map *map; +struct btf *btf;int ret; - st_ops =bpf_struct_ops_find_value (btf_vmlinux, attr->btf_vmlinux_value_type_id); -if (!st_ops) +if (attr->value_type_btf_obj_fd) { + btf =btf_get_by_fd (attr->value_type_btf_obj_fd); +if (IS_ERR (btf)) +return ERR_PTR (PTR_ERR (btf)); + }else { + btf = btf_vmlinux; +btf_get (btf); + } + st_ops =bpf_struct_ops_find_value (btf, attr->btf_vmlinux_value_type_id); +if (!st_ops) { +btf_put (btf);return ERR_PTR (-ENOTSUPP ); + }/* If st_ops->owner is NULL, it means the struct_ops is * statically defined in the kernel. We don't need to * take a refcount on it. */ -if (st_ops->owner && !btf_try_get_module (st_ops->btf)) +if (st_ops->owner && !btf_try_get_module (st_ops->btf)) { +btf_put (btf);return ERR_PTR (-EINVAL ); + } vt = st_ops->value_type;if (attr->value_size != vt->size) { -module_put (st_ops->owner); -return ERR_PTR (-EINVAL ); + ret = -EINVAL ; + goto errout; } t = st_ops->type;
@@ -747,8 +761,8 @@ static struct bpf_map *bpf_struct_ops_map_alloc(union bpf_attr *attr) st_map =bpf_map_area_alloc (st_map_size,NUMA_NO_NODE );if (!st_map) { -module_put (st_ops->owner); -return ERR_PTR (-ENOMEM ); + ret = -ENOMEM ; + goto errout; } st_map->st_ops = st_ops;
@@ -784,6 +798,12 @@ static struct bpf_map *bpf_struct_ops_map_alloc(union bpf_attr *attr)bpf_map_init_from_attr (map, attr);return map; + +errout: +btf_put (btf); +module_put (st_ops->owner); + +return ERR_PTR (ret); }static u64 bpf_struct_ops_map_mem_usage (const struct bpf_map *map)
kernel/bpf/syscall.c +1 -1
@@ -1097,7 +1097,7 @@ static int map_check_btf(struct bpf_map *map, const struct btf *btf,return ret; } -#define BPF_MAP_CREATE_LAST_FIELD map_extra +#define BPF_MAP_CREATE_LAST_FIELD value_type_btf_obj_fd/* called via syscall */ static int map_create (union bpf_attr *attr) {
kernel/bpf/verifier.c +3 -1
@@ -19623,6 +19623,7 @@ static int check_struct_ops_btf_id(struct bpf_verifier_env *env)const struct btf_member *member;struct bpf_prog *prog = env->prog;u32 btf_id, member_idx; +struct btf *btf;const char *mname;if (!prog->gpl_compatible) {
@@ -19630,8 +19631,9 @@ static int check_struct_ops_btf_id(struct bpf_verifier_env *env)return -EINVAL ; } + btf = prog->aux->attach_btf; btf_id = prog->aux->attach_btf_id; - st_ops =bpf_struct_ops_find (btf_vmlinux, btf_id); + st_ops =bpf_struct_ops_find (btf, btf_id);if (!st_ops) {verbose (env,"attach_btf_id %u is not a supported struct\n" , btf_id);
tools/include/uapi/linux/bpf.h +5 -0
@@ -1390,6 +1390,11 @@ union bpf_attr {* to using 5 hash functions). */ __u64 map_extra; + +__u32 value_type_btf_obj_fd;/* fd pointing to a BTF +* type data for +* btf_vmlinux_value_type_id. +*/ };struct {/* anonymous struct used by BPF_MAP_*_ELEM commands */
Signature · 1 line
2.34.1
PATCH v4 6/9 bpf, net: switch to dynamic registration
In reply to [email protected]From: Kui-Feng Lee <[email protected]>
Replace the static list of struct_ops types with pre-btf struct_ops_tab to enable dynamic registration.
Both bpf_dummy_ops and bpf_tcp_ca now utilize the registration function instead of being listed in bpf_struct_ops_types.h.
--- include/linux/bpf.h | 2 + include/linux/btf.h | 29 +++++++ kernel/bpf/bpf_struct_ops.c | 124 +++++++++++++++--------------- kernel/bpf/bpf_struct_ops_types.h | 12 --- kernel/bpf/btf.c | 2 +- net/bpf/bpf_dummy_struct_ops.c | 14 +++- net/ipv4/bpf_tcp_ca.c | 16 +++- 7 files changed, 119 insertions(+), 80 deletions(-) delete mode 100644 kernel/bpf/bpf_struct_ops_types.h
include/linux/bpf.h +2 -0
@@ -3207,4 +3207,6 @@ static inline bool bpf_is_subprog(const struct bpf_prog *prog) return prog->aux->func_idx != 0; } +int register_bpf_struct_ops (struct bpf_struct_ops *st_ops); +#endif /* _LINUX_BPF_H */
include/linux/btf.h +29 -0
@@ -12,6 +12,8 @@#include <uapi/linux/bpf.h> #define BTF_TYPE_EMIT (type) ((void)(type *)0) +#define BTF_STRUCT_OPS_TYPE_EMIT (type) {((void)(struct type *)0); \ + ((void)(struct bpf_struct_ops_##type *)0); }#define BTF_TYPE_EMIT_ENUM (enum_val) ((void)enum_val)/* These need to be macros, as the expressions are used in assembler input */
@@ -200,6 +202,7 @@ u32 btf_obj_id(const struct btf *btf);bool btf_is_kernel (const struct btf *btf);bool btf_is_module (const struct btf *btf);struct module *btf_try_get_module (const struct btf *btf); +struct btf *btf_get_module_btf (const struct module *module);u32 btf_nr_types (const struct btf *btf); bool btf_member_is_reg_int(const struct btf *btf, const struct btf_type *s, const struct btf_member *m,
@@ -577,4 +580,30 @@ int btf_add_struct_ops(struct bpf_struct_ops *st_ops);const struct bpf_struct_ops **btf_get_struct_ops (struct btf *btf,u32 *ret_cnt); +enum bpf_struct_ops_state { +BPF_STRUCT_OPS_STATE_INIT , +BPF_STRUCT_OPS_STATE_INUSE , +BPF_STRUCT_OPS_STATE_TOBEFREE , +BPF_STRUCT_OPS_STATE_READY , +}; + +struct bpf_struct_ops_common_value { +refcount_t refcnt; +enum bpf_struct_ops_state state; +}; +#define BPF_STRUCT_OPS_COMMON_VALUE struct bpf_struct_ops_common_value common + +/* bpf_struct_ops_##_name (e.g. bpf_struct_ops_tcp_congestion_ops) is +* the map's value exposed to the userspace and its btf-type-id is +* stored at the map->btf_vmlinux_value_type_id. +* +*/ +#define DEFINE_STRUCT_OPS_VALUE_TYPE (_name) \ +extern struct bpf_struct_ops bpf_##_name; \ + \ +struct bpf_struct_ops_##_name { \ + BPF_STRUCT_OPS_COMMON_VALUE; \ + struct _name data ____cacheline_aligned_in_smp; \ +} +#endif
kernel/bpf/bpf_struct_ops.c +62 -62
175 lines not on this page · raw message
kernel/bpf/bpf_struct_ops_types.h deleted +0 -12
@@ -1,12 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* internal file - do not include directly */ - -#ifdef CONFIG_BPF_JIT -#ifdef CONFIG_NET -BPF_STRUCT_OPS_TYPE(bpf_dummy_ops) -#endif -#ifdef CONFIG_INET -#include <net/tcp.h> -BPF_STRUCT_OPS_TYPE(tcp_congestion_ops) -#endif -#endif
kernel/bpf/btf.c +1 -1
@@ -7532,7 +7532,7 @@ struct module *btf_try_get_module(const struct btf *btf)/* Returns struct btf corresponding to the struct module. * This function can return NULL or ERR_PTR. */ -static struct btf *btf_get_module_btf (const struct module *module) +struct btf *btf_get_module_btf (const struct module *module) {#ifdef CONFIG_DEBUG_INFO_BTF_MODULES struct btf_module *btf_mod, *tmp;
net/bpf/bpf_dummy_struct_ops.c +12 -2
@@ -7,7 +7,7 @@#include <linux/bpf.h> #include <linux/btf.h> -extern struct bpf_struct_ops bpf_bpf_dummy_ops; +static struct bpf_struct_ops bpf_bpf_dummy_ops;/* A common type for test_N with return value in bpf_dummy_ops */ typedef int (*dummy_ops_test_ret_fn )(struct bpf_dummy_ops_state *state, ...);
@@ -216,11 +216,13 @@ static int bpf_dummy_reg(void *kdata)return -EOPNOTSUPP ; } +DEFINE_STRUCT_OPS_VALUE_TYPE (bpf_dummy_ops); +static void bpf_dummy_unreg (void *kdata) { } -struct bpf_struct_ops bpf_bpf_dummy_ops = { +static struct bpf_struct_ops bpf_bpf_dummy_ops = { .verifier_ops = &bpf_dummy_verifier_ops, .init = bpf_dummy_init, .check_member = bpf_dummy_ops_check_member,
@@ -228,4 +230,12 @@ struct bpf_struct_ops bpf_bpf_dummy_ops = { .reg = bpf_dummy_reg, .unreg = bpf_dummy_unreg, .name = "bpf_dummy_ops", + .owner = THIS_MODULE, }; + +static int __init bpf_dummy_struct_ops_init (void ) +{ +BTF_STRUCT_OPS_TYPE_EMIT (bpf_dummy_ops); +return register_bpf_struct_ops (&bpf_bpf_dummy_ops); +} +late_initcall (bpf_dummy_struct_ops_init);
net/ipv4/bpf_tcp_ca.c +13 -3
@@ -12,7 +12,7 @@#include <net/bpf_sk_storage.h> /* "extern" is to avoid sparse warning. It is only used in bpf_struct_ops.c. */ -extern struct bpf_struct_ops bpf_tcp_congestion_ops; +static struct bpf_struct_ops bpf_tcp_congestion_ops;static u32 unsupported_ops[] = { offsetof(struct tcp_congestion_ops , get_info),
@@ -271,7 +271,9 @@ static int bpf_tcp_ca_validate(void *kdata)return tcp_validate_congestion_control (kdata); } -struct bpf_struct_ops bpf_tcp_congestion_ops = { +DEFINE_STRUCT_OPS_VALUE_TYPE (tcp_congestion_ops); + +static struct bpf_struct_ops bpf_tcp_congestion_ops = { .verifier_ops = &bpf_tcp_ca_verifier_ops, .reg = bpf_tcp_ca_reg, .unreg = bpf_tcp_ca_unreg,
@@ -281,10 +283,18 @@ struct bpf_struct_ops bpf_tcp_congestion_ops = { .init = bpf_tcp_ca_init, .validate = bpf_tcp_ca_validate, .name = "tcp_congestion_ops", + .owner = THIS_MODULE, };static int __init bpf_tcp_ca_kfunc_init (void ) { -return register_btf_kfunc_id_set (BPF_PROG_TYPE_STRUCT_OPS , &bpf_tcp_ca_kfunc_set); +int ret; + +BTF_STRUCT_OPS_TYPE_EMIT (tcp_congestion_ops); + + ret =register_btf_kfunc_id_set (BPF_PROG_TYPE_STRUCT_OPS , &bpf_tcp_ca_kfunc_set); + ret = ret ?:register_bpf_struct_ops (&bpf_tcp_congestion_ops); + +return ret; }late_initcall (bpf_tcp_ca_kfunc_init);
Signature · 1 line
2.34.1
PATCH v4 7/9 libbpf: Find correct module BTFs for struct_ops maps and progs.
In reply to [email protected]From: Kui-Feng Lee <[email protected]>
Locate the module BTFs for struct_ops maps and progs and pass them to the kernel. This ensures that the kernel correctly resolves type IDs from the appropriate module BTFs.
For the map of a struct_ops object, mod_btf is added to bpf_map to keep a reference to the module BTF. The FD of the module BTF is passed to the kernel as mod_btf_fd when the struct_ops object is loaded.
For a bpf_struct_ops prog, attach_btf_obj_fd of bpf_prog is the FD of a module BTF in the kernel.
--- tools/lib/bpf/bpf.c | 4 ++- tools/lib/bpf/bpf.h | 5 +++- tools/lib/bpf/libbpf.c | 68 +++++++++++++++++++++++++++--------------- 3 files changed, 51 insertions(+), 26 deletions(-)
tools/lib/bpf/bpf.c +3 -1
@@ -169,7 +169,8 @@ int bpf_map_create(enum bpf_map_type map_type,__u32 max_entries, const struct bpf_map_create_opts *opts) { - const size_t attr_sz = offsetofend(union bpf_attr, map_extra); + const size_t attr_sz = offsetofend(union bpf_attr, + value_type_btf_obj_fd);union bpf_attr attr;int fd;
@@ -191,6 +192,7 @@ int bpf_map_create(enum bpf_map_type map_type, attr.btf_key_type_id =OPTS_GET (opts, btf_key_type_id,0 ); attr.btf_value_type_id =OPTS_GET (opts, btf_value_type_id,0 ); attr.btf_vmlinux_value_type_id =OPTS_GET (opts, btf_vmlinux_value_type_id,0 ); + attr.value_type_btf_obj_fd =OPTS_GET (opts, value_type_btf_obj_fd,0 ); attr.inner_map_fd =OPTS_GET (opts, inner_map_fd,0 ); attr.map_flags =OPTS_GET (opts, map_flags,0 );
tools/lib/bpf/bpf.h +4 -1
@@ -51,8 +51,11 @@ struct bpf_map_create_opts {__u32 numa_node;__u32 map_ifindex; + +__u32 value_type_btf_obj_fd; + size_t:0; }; -#define bpf_map_create_opts__last_field map_ifindex +#define bpf_map_create_opts__last_field value_type_btf_obj_fd LIBBPF_API int bpf_map_create(enum bpf_map_type map_type, const char *map_name,
tools/lib/bpf/libbpf.c +44 -24
193 lines not on this page · raw message
Signature · 1 line
2.34.1
PATCH v4 8/9 bpf: export btf_ctx_access to modules.
In reply to [email protected]From: Kui-Feng Lee <[email protected]>
The module requires the use of btf_ctx_access() to invoke bpf_tracing_btf_ctx_access() from a module. This function is valuable for implementing validation functions that ensure proper access to ctx.
--- kernel/bpf/btf.c | 1 + 1 file changed, 1 insertion(+)
kernel/bpf/btf.c +1 -0
@@ -6141,6 +6141,7 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type, __btf_name_by_offset(btf, t->name_off));return true; } +EXPORT_SYMBOL_GPL (btf_ctx_access);enum bpf_struct_walk_result {/* < 0 error */
Signature · 1 line
2.34.1
PATCH v4 9/9 selftests/bpf: test case for register_bpf_struct_ops().
In reply to [email protected]From: Kui-Feng Lee <[email protected]>
Create a new struct_ops type called bpf_testmod_ops within the bpf_testmod module. When a struct_ops object is registered, the bpf_testmod module will invoke test_2 from the module.
--- .../selftests/bpf/bpf_testmod/bpf_testmod.c | 59 +++++++++++++++++++ .../selftests/bpf/bpf_testmod/bpf_testmod.h | 5 ++ .../bpf/prog_tests/test_struct_ops_module.c | 38 ++++++++++++ .../selftests/bpf/progs/struct_ops_module.c | 30 ++++++++++ tools/testing/selftests/bpf/testing_helpers.c | 35 +++++++++++ 5 files changed, 167 insertions(+) create mode 100644 tools/testing/selftests/bpf/prog_tests/test_struct_ops_module.c create mode 100644 tools/testing/selftests/bpf/progs/struct_ops_module.c
tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c +59 -0
@@ -1,5 +1,6 @@// SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2020 Facebook */ +#include <linux/bpf.h> #include <linux/btf.h> #include <linux/btf_ids.h> #include <linux/error-injection.h>
@@ -517,11 +518,66 @@ BTF_ID_FLAGS(func, bpf_kfunc_call_test_static_unused_arg) BTF_ID_FLAGS(func, bpf_kfunc_call_test_offset) BTF_SET8_END(bpf_testmod_check_kfunc_ids) +#ifdef CONFIG_DEBUG_INFO_BTF_MODULES + +DEFINE_STRUCT_OPS_VALUE_TYPE (bpf_testmod_ops); + +static int bpf_testmod_ops_init (struct btf *btf) +{ +return 0 ; +} + +static bool bpf_testmod_ops_is_valid_access (int off,int size, +enum bpf_access_type type, +const struct bpf_prog *prog, +struct bpf_insn_access_aux *info) +{ +return bpf_tracing_btf_ctx_access (off, size, type, prog, info); +} + +static int bpf_testmod_ops_init_member (const struct btf_type *t, +const struct btf_member *member, +void *kdata,const void *udata) +{ +return 0 ; +} +static const struct btf_kfunc_id_set bpf_testmod_kfunc_set = { .owner =THIS_MODULE , .set = &bpf_testmod_check_kfunc_ids, }; +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) +{ +struct bpf_testmod_ops *ops = kdata; +int r; + +BTF_STRUCT_OPS_TYPE_EMIT (bpf_testmod_ops); + r = ops->test_2 (4 ,3 ); + +return 0 ; +} + +static void bpf_dummy_unreg (void *kdata) +{ +} + +struct bpf_struct_ops bpf_bpf_testmod_ops = { + .verifier_ops = &bpf_testmod_verifier_ops, + .init = bpf_testmod_ops_init, + .init_member = bpf_testmod_ops_init_member, + .reg = bpf_dummy_reg, + .unreg = bpf_dummy_unreg, + .name ="bpf_testmod_ops" , + .owner =THIS_MODULE , +}; + +#endif /* CONFIG_DEBUG_INFO_BTF_MODULES */ +extern int bpf_fentry_test1 (int a);static int bpf_testmod_init (void )
@@ -532,6 +588,9 @@ static int bpf_testmod_init(void) ret = ret ?:register_btf_kfunc_id_set (BPF_PROG_TYPE_SCHED_CLS , &bpf_testmod_kfunc_set); ret = ret ?:register_btf_kfunc_id_set (BPF_PROG_TYPE_TRACING , &bpf_testmod_kfunc_set); ret = ret ?:register_btf_kfunc_id_set (BPF_PROG_TYPE_SYSCALL , &bpf_testmod_kfunc_set); +#ifdef CONFIG_DEBUG_INFO_BTF_MODULES + ret = ret ?:register_bpf_struct_ops (&bpf_bpf_testmod_ops); +#endif if (ret <0 )return ret;if (bpf_fentry_test1 (0 ) <0 )
tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.h +5 -0
@@ -28,4 +28,9 @@ struct bpf_iter_testmod_seq { int cnt; }; +struct bpf_testmod_ops { +int (*test_1)(void ); +int (*test_2)(int a,int b); +}; +#endif /* _BPF_TESTMOD_H */
tools/testing/selftests/bpf/prog_tests/test_struct_ops_module.c new file +38 -0
@@ -0,0 +1,38 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2023 Meta Platforms, Inc. and affiliates. */ +#include <test_progs.h> +#include <time.h> + +#include "rcu_tasks_trace_gp.skel.h" +#include "struct_ops_module.skel.h" + +static void test_regular_load (void ) +{ +struct struct_ops_module *skel; +struct bpf_link *link; +DECLARE_LIBBPF_OPTS (bpf_object_open_opts, opts); +int err; + + skel =struct_ops_module__open_opts (&opts); +if (!ASSERT_OK_PTR (skel,"struct_ops_module_open" )) +return ; + err =struct_ops_module__load (skel); +if (!ASSERT_OK (err,"struct_ops_module_load" )) +return ; + + link =bpf_map__attach_struct_ops (skel->maps.testmod_1); +ASSERT_OK_PTR (link,"attach_test_mod_1" ); + +ASSERT_EQ (skel->bss->test_2_result,7 ,"test_2_result" ); + +bpf_link__destroy (link); + +struct_ops_module__destroy (skel); +} + +void serial_test_struct_ops_module (void ) +{ +if (test__start_subtest ("regular_load" )) +test_regular_load (); +} +
tools/testing/selftests/bpf/progs/struct_ops_module.c new file +30 -0
@@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2023 Meta Platforms, Inc. and affiliates. */ +#include <vmlinux.h> +#include <bpf/bpf_helpers.h> +#include <bpf/bpf_tracing.h> +#include "../bpf_testmod/bpf_testmod.h" + +char _license[] SEC("license") = "GPL"; + +int test_2_result =0 ; + +SEC("struct_ops/test_1") +int BPF_PROG (test_1 ) +{ +return 0xdeadbeef ; +} + +SEC("struct_ops/test_2") +int BPF_PROG (test_2 ,int a,int b) +{ + test_2_result = a + b; +return a + b; +} + +SEC(".struct_ops.link") +struct bpf_testmod_ops testmod_1 = { + .test_1 = (void *)test_1, + .test_2 = (void *)test_2, +}; +
tools/testing/selftests/bpf/testing_helpers.c +35 -0
@@ -10,6 +10,7 @@#include "test_progs.h" #include "testing_helpers.h" #include <linux/membarrier.h> +#include "rcu_tasks_trace_gp.skel.h" int parse_num_list (const char *s,bool **num_set,int *num_set_len) {
@@ -380,10 +381,44 @@ int load_bpf_testmod(bool verbose)return 0 ; } +/* This function will trigger call_rcu_tasks_trace() in the kernel */ +static int kern_sync_rcu_tasks_trace (void ) +{ +struct rcu_tasks_trace_gp *rcu; +time_t start; +long gp_seq; +LIBBPF_OPTS (bpf_test_run_opts, opts); + + rcu =rcu_tasks_trace_gp__open_and_load (); +if (IS_ERR (rcu)) +return -EFAULT ; +if (rcu_tasks_trace_gp__attach (rcu)) +return -EFAULT ; + + gp_seq =READ_ONCE (rcu->bss->gp_seq); + +if (bpf_prog_test_run_opts (bpf_program__fd (rcu->progs.do_call_rcu_tasks_trace), + &opts)) +return -EFAULT ; +if (opts.retval !=0 ) +return -EFAULT ; + + start =time (NULL ); +while ((start +2 ) >time (NULL ) && + gp_seq ==READ_ONCE (rcu->bss->gp_seq)) +sched_yield (); + +rcu_tasks_trace_gp__destroy (rcu); + +return 0 ; +} +/* * Trigger synchronize_rcu() in kernel. */ int kern_sync_rcu (void ) { +if (kern_sync_rcu_tasks_trace ()) +return -EFAULT ;return syscall (__NR_membarrier,MEMBARRIER_CMD_SHARED ,0 ,0 ); }
Signature · 1 line
2.34.1
Just fixed a building dependency issue and sent v5. Please check v5.
Quoted 8 lines · On 10/13/23 15:42, [email protected] wrote:
From: Kui-Feng Lee <[email protected]>
Given the current constraints of the current implementation,
struct_opscannot be registered dynamically. This presents a significant limitation for modules like coming fuse-bpf, which seeks to implement a newstruct_opstype. To address this issue, a new API is introduced that allows the registration of newstruct_opstypes from modules.