Create shadow types for struct_ops maps in skeletons
Subsystems (6)
v4 cover Create shadow types for struct_ops maps in skeletons
From: Kui-Feng Lee <[email protected]>
This patchset allows skeleton users to change the values of the fields in struct_ops maps at runtime. It will create a shadow type pointer in a skeleton for each struct_ops map, allowing users to access the values of fields through these pointers. For instance, if there is an integer field named "FOO" in a struct_ops map called "testmap", you can access the value of "FOO" in this way.
skel->struct_ops.testmap->FOO = 13;
With this feature, the users can pass flags or other data along with the map from the user space to the kernel without creating separate struct_ops map for different values in BPF.
== Shadow Type ==
The shadow type of a struct_ops map is a variant of the original struct type of the map. The code generator translates each field in the original struct type to a field in the shadow type. The type of a field in the shadow type may not be the same as the corresponding field in the original struct type. For example, modifiers like volatile, const, etc., are removed from the fields in a shadow type. Function pointers are translated to pointers of struct bpf_program.
Currently, only scalar types and function pointers are supported. Fields belonging to structs, unions, non-function pointers, arrays, or other types are not supported. For those unsupported fields, they are converted to arrays of characters to preserve their space within the original struct type.
The padding between consecutive fields is handled by padding fields (__padding_*). This helps to maintain the memory layout consistent with the original struct_type.
Here is an example of shadow types.
The origin struct type of a struct_ops map is
struct bpf_testmod_ops {
int (*test_1)(void);
void (*test_2)(int a, int b);
/* Used to test nullable arguments. */
int (*test_maybe_null)(int dummy, struct task_struct *task);
/* The following fields are used to test shadow copies. */
char onebyte;
struct {
int a;
int b;
} unsupported;
int data;
};The struct_ops map, named testmod_1, of this type will be translated to a pointer in the shadow type.
struct {
struct {
const struct bpf_program *test_1;
const struct bpf_program *test_2;
const struct bpf_program *test_maybe_null;
char onebyte;
char __padding_3[3];
char __padding_4[8];
int data;
} *testmod_1;
} struct_ops;== Convert st_ops->data to Shadow Type ==
libbpf converts st_ops->data to the format of the shadow type for each struct_ops map. This means that the bytes where function pointers are located are converted to the values of the pointers of struct bpf_program. The fields of other types are kept as they were.
Libbpf will synchronize the pointers of struct bpf_program with st_ops->progs[] so that users can change function pointers (bpf_program) before loading the map.
---
Changes from v3:
- Add comment to avoid people from removing resolve_func_ptr() from libbpf_internal.h
- Fix commit logs and comments.
- Add an example about using the pointers of shadow types for struct_ops maps to bpftool-gen.8.
v3: https://lore.kernel.org/all/[email protected]/
v2: https://lore.kernel.org/all/[email protected]/
v1: https://lore.kernel.org/all/[email protected]/
Kui-Feng Lee (6):
libbpf: expose resolve_func_ptr() through libbpf_internal.h.
libbpf: set btf_value_type_id of struct bpf_map for struct_ops.
libbpf: Convert st_ops->data to shadow type.
bpftool: generated shadow variables for struct_ops maps.
bpftool: Add an example for struct_ops map and shadow type. selftests/bpf: Test if shadow types work correctly.
.../bpf/bpftool/Documentation/bpftool-gen.rst | 58 ++++- tools/bpf/bpftool/gen.c | 235 +++++++++++++++++- tools/lib/bpf/libbpf.c | 60 ++++- tools/lib/bpf/libbpf_internal.h | 2 + .../selftests/bpf/bpf_testmod/bpf_testmod.c | 11 +- .../selftests/bpf/bpf_testmod/bpf_testmod.h | 8 + .../bpf/prog_tests/test_struct_ops_module.c | 19 +- .../selftests/bpf/progs/struct_ops_module.c | 8 + 8 files changed, 387 insertions(+), 14 deletions(-)
Signature · 1 line
2.34.1
PATCH v4 1/6 libbpf: expose resolve_func_ptr() through libbpf_internal.h.
From: Kui-Feng Lee <[email protected]>
bpftool is going to reuse this helper function to support shadow types of struct_ops maps.
--- tools/lib/bpf/libbpf.c | 2 +- tools/lib/bpf/libbpf_internal.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-)
tools/lib/bpf/libbpf.c +1 -1
@@ -2145,7 +2145,7 @@ skip_mods_and_typedefs(const struct btf *btf, __u32 id, __u32 *res_id)return t; } -static const struct btf_type * +const struct btf_type *resolve_func_ptr (const struct btf *btf,__u32 id,__u32 *res_id) {const struct btf_type *t;
tools/lib/bpf/libbpf_internal.h +2 -0
@@ -234,6 +234,8 @@ struct btf_type;struct btf_type *btf_type_by_id (const struct btf *btf,__u32 type_id);const char *btf_kind_str (const struct btf_type *t);const struct btf_type *skip_mods_and_typedefs (const struct btf *btf,__u32 id,__u32 *res_id); +/* This function is exposed to bpftool */ +const struct btf_type *resolve_func_ptr (const struct btf *btf,__u32 id,__u32 *res_id);static inline enum btf_func_linkage btf_func_linkage (const struct btf_type *t) {
Signature · 1 line
2.34.1
PATCH v4 2/6 libbpf: set btf_value_type_id of struct bpf_map for struct_ops.
In reply to [email protected]From: Kui-Feng Lee <[email protected]>
For a struct_ops map, btf_value_type_id is the type ID of it's struct type. This value is required by bpftool to generate skeleton including pointers of shadow types. The code generator gets the type ID from bpf_map__btf_vaule_type_id() in order to get the type information of the struct type of a map.
--- tools/lib/bpf/libbpf.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
tools/lib/bpf/libbpf.c +4 -1
@@ -1229,6 +1229,7 @@ static int init_struct_ops_maps(struct bpf_object *obj, const char *sec_name, map->name =strdup (var_name);if (!map->name)return -ENOMEM ; + map->btf_value_type_id = type_id; map->def.type =BPF_MAP_TYPE_STRUCT_OPS ; map->def.key_size =sizeof (int );
@@ -4818,7 +4819,9 @@ static int bpf_object__create_map(struct bpf_object *obj, struct bpf_map *map, bif (obj->btf &&btf__fd (obj->btf) >=0 ) { create_attr.btf_fd =btf__fd (obj->btf); create_attr.btf_key_type_id = map->btf_key_type_id; - create_attr.btf_value_type_id = map->btf_value_type_id; + create_attr.btf_value_type_id = + def->type !=BPF_MAP_TYPE_STRUCT_OPS ? + map->btf_value_type_id :0 ; }if (bpf_map_type__is_map_in_map (def->type)) {
Signature · 1 line
2.34.1
PATCH v4 3/6 libbpf: Convert st_ops->data to shadow type.
In reply to [email protected]From: Kui-Feng Lee <[email protected]>
Convert st_ops->data to the shadow type of the struct_ops map. The shadow type of a struct_ops type is a variant of the original struct type providing a way to access/change the values in the maps of the struct_ops type.
bpf_map__initial_value() will return st_ops->data for struct_ops types. The skeleton is going to use it as the pointer to the shadow type of the original struct type.
One of the main differences between the original struct type and the shadow type is that all function pointers of the shadow type are converted to pointers of struct bpf_program. Users can replace these bpf_program pointers with other BPF programs. The st_ops->progs[] will be updated before updating the value of a map to reflect the changes made by users.
--- tools/lib/bpf/libbpf.c | 53 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+)
tools/lib/bpf/libbpf.c +53 -0
@@ -1102,6 +1102,9 @@ static int bpf_map__init_kern_struct_ops(struct bpf_map *map)if (btf_is_ptr (mtype)) {struct bpf_program *prog; +/* Update the value from the shadow type */ + st_ops->progs[i] = *(struct bpf_program **)mdata; + prog = st_ops->progs[i];if (!prog)continue ;
@@ -1172,6 +1175,36 @@ static int bpf_object__init_kern_struct_ops_maps(struct bpf_object *obj)return 0 ; } +/* Convert the data of a struct_ops map to shadow type. +* +* The function pointers are replaced with the pointers of bpf_program in +* st_ops->progs[]. +*/ +static void struct_ops_convert_shadow (struct bpf_map *map, +const struct btf_type *t) +{ +struct btf *btf = map->obj->btf; +struct bpf_struct_ops *st_ops = map->st_ops; +const struct btf_member *m; +const struct btf_type *mtype; +char *data; +int i; + + data = st_ops->data; + +for (i =0 , m =btf_members (t); i <btf_vlen (t); i++, m++) { + mtype =skip_mods_and_typedefs (btf, m->type,NULL ); + +if (btf_kind (mtype) !=BTF_KIND_PTR ) +continue ; +if (!resolve_func_ptr (btf, m->type,NULL )) +continue ; + + *((struct bpf_program **)(data + m->offset /8 )) = + st_ops->progs[i]; + } +} +static int init_struct_ops_maps (struct bpf_object *obj,const char *sec_name,int shndx,Elf_Data *data,__u32 map_flags) {
@@ -7531,6 +7564,19 @@ static int bpf_object_init_progs(struct bpf_object *obj, const struct bpf_objectreturn 0 ; } +/* Convert the data to the shadow type for each struct_ops map. */ +static void bpf_object__init_shadow (struct bpf_object *obj) +{ +struct bpf_map *map; + +bpf_object__for_each_map (map, obj) { +if (!bpf_map__is_struct_ops (map)) +continue ; + +struct_ops_convert_shadow (map, map->st_ops->type); + } +} +static struct bpf_object *bpf_object_open (const char *path,const void *obj_buf,size_t obj_buf_sz,const struct bpf_object_open_opts *opts) {
@@ -7631,6 +7677,7 @@ static struct bpf_object *bpf_object_open(const char *path, const void *obj_buf, goto out;bpf_object__elf_finish (obj); +bpf_object__init_shadow (obj);return obj; out:
@@ -9880,6 +9927,12 @@ int bpf_map__set_initial_value(struct bpf_map *map,void *bpf_map__initial_value (struct bpf_map *map,size_t *psize) { +if (bpf_map__is_struct_ops (map)) { +if (psize) + *psize = map->def.value_size; +return map->st_ops->data; + } +if (!map->mmaped)return NULL ; *psize = map->def.value_size;
Signature · 1 line
2.34.1
Martin KaFai Lau
reviewerRe: [PATCH bpf-next v4 3/6] libbpf: Convert st_ops->data to shadow type.
Quoted 17 lines · On 2/22/24 2:26 PM, [email protected] wrote:
tools/lib/bpf/libbpf.c +17
-0+/* Convert the data of a struct_ops map to shadow type. +* +* The function pointers are replaced with the pointers of bpf_program in +* st_ops->progs[]. +*/ +static void struct_ops_convert_shadow (struct bpf_map *map, +const struct btf_type *t) +{ +struct btf *btf = map->obj->btf; +struct bpf_struct_ops *st_ops = map->st_ops; +const struct btf_member *m; +const struct btf_type *mtype; +char *data; +int i; + + data = st_ops->data; +
tools/lib/bpf/libbpf.c static int bpf_object__init_kern_struct_ops_maps(struct bpf_object *obj) · line 1204
Quoted 10 lines
tools/lib/bpf/libbpf.c +10
-0+for (i =0 , m =btf_members (t); i <btf_vlen (t); i++, m++) { + mtype =skip_mods_and_typedefs (btf, m->type,NULL ); + +if (btf_kind (mtype) !=BTF_KIND_PTR ) +continue ; +if (!resolve_func_ptr (btf, m->type,NULL )) +continue ; + + *((struct bpf_program **)(data + m->offset /8 )) = + st_ops->progs[i];
This is to initialize the bpf_program pointer in the st_ops->data. Can this be done directly at the bpf_object__collect_st_ops_relos()?
Quoted 3 lines
tools/lib/bpf/libbpf.c +3
-0+ } +} +
Re: [PATCH bpf-next v4 3/6] libbpf: Convert st_ops->data to shadow type.
Quoted 24 lines · Martin KaFai Lau
Quoted 23 lines · On 2/22/24 2:26 PM, [email protected] wrote:
+/* Convert the data of a
struct_opsmap to shadow type.
+ *
+ * The function pointers are replaced with the pointers ofbpf_programin
+ * st_ops->progs[].
+ */tools/lib/bpf/libbpf.c +17
-0+static void struct_ops_convert_shadow (struct bpf_map *map, + const struct btf_type *t) +{ + struct btf *btf = map->obj->btf; + struct bpf_struct_ops *st_ops = map->st_ops; + const struct btf_member *m; + const struct btf_type *mtype; + char *data; + int i; + + data = st_ops->data; + + for (i = 0, m = btf_members(t); i < btf_vlen(t); i++, m++) { + mtype = skip_mods_and_typedefs(btf, m->type, NULL); + + if (btf_kind(mtype) != BTF_KIND_PTR) + continue;
Quoted 8 lines
Quoted 5 lines
+ if (!resolve_func_ptr(btf,
m->type, NULL)) + continue;
+
+ *((structbpf_program**)(data +m->offset/ 8)) = + st_ops->progs[i];This is to initialize the
bpf_programpointer in thest_ops->data. Can this be done directly at thebpf_object__collect_st_ops_relos()?
No problem!
Quoted 5 lines
Quoted 4 lines
+ }
(quoted diff) +2
-0+} +
PATCH v4 4/6 bpftool: generated shadow variables for struct_ops maps.
In reply to [email protected]From: Kui-Feng Lee <[email protected]>
Declares and defines a pointer of the shadow type for each struct_ops map.
The code generator will create an anonymous struct type as the shadow type for each struct_ops map. The shadow type is translated from the original struct type of the map. The user of the skeleton use pointers of them to access the values of struct_ops maps.
However, shadow types only supports certain types of fields, including scalar types and function pointers. Any fields of unsupported types are translated into an array of characters to occupy the space of the original field. Function pointers are translated into pointers of the struct bpf_program. Additionally, padding fields are generated to occupy the space between two consecutive fields.
The pointers of shadow types of struct_osp maps are initialized when *__open_opts() in skeletons are called. For a map called FOO, the user can access it through the pointer at skel->struct_ops.FOO.
--- tools/bpf/bpftool/gen.c | 235 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 234 insertions(+), 1 deletion(-)
tools/bpf/bpftool/gen.c +234 -1
@@ -909,6 +909,207 @@ codegen_progs_skeleton(struct bpf_object *obj, size_t prog_cnt, bool populate_li } } +static int walk_st_ops_shadow_vars (struct btf *btf, +const char *ident, +const struct bpf_map *map) +{ +DECLARE_LIBBPF_OPTS (btf_dump_emit_type_decl_opts, opts, + .indent_level = 3, + ); +const struct btf_type *map_type, *member_type; +__u32 map_type_id, member_type_id; +__u32 offset, next_offset =0 ; +const struct btf_member *m; +const char *member_name; +struct btf_dump *d =NULL ; +int i, err =0 ; +int size, map_size; + + map_type_id =bpf_map__btf_value_type_id (map); +if (map_type_id ==0 ) +return -EINVAL ; + map_type =btf__type_by_id (btf, map_type_id); +if (!map_type) +return -EINVAL ; + + d =btf_dump__new (btf, codegen_btf_dump_printf,NULL ,NULL ); +if (!d) +return -errno; + +for (i =0 , m =btf_members (map_type); + i <btf_vlen (map_type); + i++, m++) { + member_type =skip_mods_and_typedefs (btf, m->type, + &member_type_id); +if (!member_type) { + err = -EINVAL ; + goto out; + } + + member_name =btf__name_by_offset (btf, m->name_off); +if (!member_name) { + err = -EINVAL ; + goto out; + } + + offset = m->offset /8 ; +if (next_offset != offset) { +printf ("\t\t\tchar __padding_%d[%d];\n" , + i -1 , offset - next_offset); + } + +switch (btf_kind (member_type)) { +case BTF_KIND_INT : +case BTF_KIND_FLOAT : +case BTF_KIND_ENUM : +case BTF_KIND_ENUM64 : +/* scalar type */ +printf ("\t\t\t" ); + opts.field_name = member_name; + err =btf_dump__emit_type_decl (d, member_type_id, + &opts); +if (err) + goto out; +printf (";\n" ); + + size =btf__resolve_size (btf, member_type_id); +if (size <0 ) { + err = size; + goto out; + } + + next_offset = offset + size; +break ; + +case BTF_KIND_PTR : +if (resolve_func_ptr (btf, m->type,NULL )) { +/* Function pointer */ +printf ("\t\t\tconst struct bpf_program *%s;\n" , + member_name); + + next_offset = offset +sizeof (void *); +break ; + } +/* All pointer types are unsupported except for +* function pointers. +*/ + fallthrough; + +default : +/* Unsupported types +* +* Types other than scalar types and function +* pointers are currently not supported in order to +* prevent conflicts in the generated code caused +* by multiple definitions. For instance, if the +* struct type FOO is used in a struct_ops map, +* bpftool has to generate definitions for FOO, +* which may result in conflicts if FOO is defined +* in different skeleton files. +*/ +if (i ==btf_vlen (map_type) -1 ) { + map_size =btf__resolve_size (btf, map_type_id); +if (map_size <0 ) +return -EINVAL ; + size = map_size - offset; + }else { + size = (m[1 ].offset - m->offset) /8 ; + } + +printf ("\t\t\tchar __padding_%d[%d];\n" , i, size); + + next_offset = offset + size; +break ; + } + } + +out: +btf_dump__free (d); + +return err; +} + +/* Generate the pointer of the shadow type for a struct_ops map. +* +* This function adds a pointer of the shadow type for a struct_ops map. +* The members of a struct_ops map can be exported through a pointer to a +* shadow type. The user can access these members through the pointer. +* +* A shadow type includes not all members, only members of some types. +* They are scalar types and function pointers. The function pointers are +* translated to the pointer of the struct bpf_program. The scalar types +* are translated to the original type without any modifiers. +* +* Unsupported types will be translated to a char array to occupy the same +* space as the original field. However, the user should not access them +* directly. These unsupported fields are also renamed as __padding_* +* . They may be reordered or shifted due to changes in the original struct +* type. Accessing them through the generated names may unintentionally +* corrupt data. +*/ +static int gen_st_ops_shadow_type (struct btf *btf,const char *ident, +const struct bpf_map *map) +{ +int err; + +printf ("\t\tstruct {\n" ); + + err =walk_st_ops_shadow_vars (btf, ident, map); +if (err) +return err; + +printf ("\t\t} *%s;\n" , ident); + +return 0 ; +} + +static int gen_st_ops_shadow (struct btf *btf,struct bpf_object *obj) +{ +struct bpf_map *map; +char ident[256 ]; +int err; + +/* Generate the pointers to shadow types of +* struct_ops maps. +*/ +printf ("\tstruct {\n" ); +bpf_object__for_each_map (map, obj) { +if (bpf_map__type (map) !=BPF_MAP_TYPE_STRUCT_OPS ) +continue ; +if (!get_map_ident (map, ident,sizeof (ident))) +continue ; + err =gen_st_ops_shadow_type (btf, ident, map); +if (err) +return err; + } +printf ("\t} struct_ops;\n" ); + +return 0 ; +} + +/* Generate the code to initialize the pointers of shadow types. */ +static void gen_st_ops_shadow_init (struct btf *btf,struct bpf_object *obj) +{ +struct bpf_map *map; +char ident[256 ]; + +/* Initialize the pointers to_ops shadow types of +* struct_ops maps. +*/ +bpf_object__for_each_map (map, obj) { +if (bpf_map__type (map) !=BPF_MAP_TYPE_STRUCT_OPS ) +continue ; +if (!get_map_ident (map, ident,sizeof (ident))) +continue ; +codegen ("\ +\n\ +obj->struct_ops.%1$s = \n\ +bpf_map__initial_value(obj->maps.%1$s, NULL);\n\ +\n\ +" , ident); + } +} +static int do_skeleton (int argc,char **argv) {char header_guard[MAX_OBJ_NAME_LEN +sizeof ("__SKEL_H__" )];
@@ -923,6 +1124,7 @@ static int do_skeleton(int argc, char **argv)struct bpf_map *map;struct btf *btf;struct stat st; +int st_ops_cnt =0 ;if (!REQ_ARGS (1 )) {usage ();
@@ -1039,6 +1241,8 @@ static int do_skeleton(int argc, char **argv) ); } + btf =bpf_object__btf (obj); +if (map_cnt) {printf ("\tstruct {\n" );bpf_object__for_each_map (map, obj) {
@@ -1048,8 +1252,15 @@ static int do_skeleton(int argc, char **argv)printf ("\t\tstruct bpf_map_desc %s;\n" , ident); else printf("\t\tstruct bpf_map *%s;\n", ident); +if (bpf_map__type (map) ==BPF_MAP_TYPE_STRUCT_OPS ) + st_ops_cnt++; }printf ("\t} maps;\n" ); +if (st_ops_cnt && btf) { + err =gen_st_ops_shadow (btf, obj); +if (err) + goto out; + } }if (prog_cnt) {
@@ -1075,7 +1286,6 @@ static int do_skeleton(int argc, char **argv)printf ("\t} links;\n" ); } - btf =bpf_object__btf (obj);if (btf) { err =codegen_datasecs (obj, obj_name);if (err)
@@ -1133,6 +1343,13 @@ static int do_skeleton(int argc, char **argv) if (err) \n\ goto err_out; \n\ \n\ + ", obj_name); + +if (st_ops_cnt && btf) +gen_st_ops_shadow_init (btf, obj); + + codegen("\ + \n\ return obj; \n\ err_out: \n\ %1$s__destroy(obj); \n\
@@ -1296,6 +1513,7 @@ static int do_subskeleton(int argc, char **argv)struct btf *btf;const struct btf_type *map_type, *var_type;const struct btf_var_secinfo *var; +int st_ops_cnt =0 ;struct stat st;if (!REQ_ARGS (1 )) {
@@ -1438,10 +1656,18 @@ static int do_subskeleton(int argc, char **argv)if (!get_map_ident (map, ident,sizeof (ident)))continue ;printf ("\t\tstruct bpf_map *%s;\n" , ident); +if (bpf_map__type (map) ==BPF_MAP_TYPE_STRUCT_OPS ) + st_ops_cnt++; }printf ("\t} maps;\n" ); } +if (st_ops_cnt && btf) { + err =gen_st_ops_shadow (btf, obj); +if (err) + goto out; + } +if (prog_cnt) {printf ("\tstruct {\n" );bpf_object__for_each_program (prog, obj) {
@@ -1553,6 +1779,13 @@ static int do_subskeleton(int argc, char **argv) if (err) \n\ goto err; \n\ \n\ + "); + +if (st_ops_cnt && btf) +gen_st_ops_shadow_init (btf, obj); + + codegen("\ + \n\ return obj; \n\ err: \n\ %1$s__destroy(obj); \n\
Signature · 1 line
2.34.1
PATCH v4 5/6 bpftool: Add an example for struct_ops map and shadow type.
In reply to [email protected]From: Kui-Feng Lee <[email protected]>
The example in bpftool-gen.8 explains how to use the pointer of the shadow type to change the value of a field of a struct_ops map.
--- .../bpf/bpftool/Documentation/bpftool-gen.rst | 58 +++++++++++++++++-- 1 file changed, 52 insertions(+), 6 deletions(-)
tools/bpf/bpftool/Documentation/bpftool-gen.rst +52 -6
@@ -257,18 +257,48 @@ EXAMPLES return 0; } -This is example BPF application with two BPF programs and a mix of BPF maps -and global variables. Source code is split across two source code files. +**$ cat example3.bpf.c** + +:: + + #include <linux/ptrace.h> + #include <linux/bpf.h> + #include <bpf/bpf_helpers.h> + /* This header file is provided by the bpf_testmod module. */ + #include "bpf_testmod.h" + + int test_2_result = 0; + + /* bpf_Testmod.ko calls this function, passing a "4" + * and testmod_map->data. + */ + SEC("struct_ops/test_2") + void BPF_PROG(test_2, int a, int b) + { + test_2_result = a + b; + } + + SEC(".struct_ops") + struct bpf_testmod_ops testmod_map = { + .test_2 = (void *)test_2, + .data = 0x1, + }; + +This is example BPF application with three BPF programs and a mix of BPF +maps and global variables. Source code is split across three source code +files. **$ clang --target=bpf -g example1.bpf.c -o example1.bpf.o** **$ clang --target=bpf -g example2.bpf.c -o example2.bpf.o** -**$ bpftool gen object example.bpf.o example1.bpf.o example2.bpf.o** +**$ clang --target=bpf -g example3.bpf.c -o example3.bpf.o** + +**$ bpftool gen object example.bpf.o example1.bpf.o example2.bpf.o example3.bpf.o** -This set of commands compiles *example1.bpf.c* and *example2.bpf.c* -individually and then statically links respective object files into the final -BPF ELF object file *example.bpf.o*. +This set of commands compiles *example1.bpf.c*, *example2.bpf.c* and +*example3.bpf.c* individually and then statically links respective object +files into the final BPF ELF object file *example.bpf.o*. **$ bpftool gen skeleton example.bpf.o name example | tee example.skel.h**
@@ -291,7 +321,15 @@ BPF ELF object file *example.bpf.o*. struct bpf_map *data; struct bpf_map *bss; struct bpf_map *my_map; + struct bpf_map *testmod_map; } maps; + struct { + struct { + const struct bpf_program *test_1; + const struct bpf_program *test_2; + int data; + } *testmod_map; + } struct_ops; struct { struct bpf_program *handle_sys_enter; struct bpf_program *handle_sys_exit;
@@ -304,6 +342,7 @@ BPF ELF object file *example.bpf.o*. struct { int x; } data; + int test_2_result; } *bss; struct example__data { _Bool global_flag;
@@ -342,10 +381,16 @@ BPF ELF object file *example.bpf.o*. skel->rodata->param1 = 128; + /* Change the value through the pointer of shadow type */ + skel->struct_ops.testmod_map->data = 13; + err = example__load(skel); if (err) goto cleanup; + /* The result of the function test_2() */ + printf("test_2_result: %d\n", skel->bss->test_2_result); + err = example__attach(skel); if (err) goto cleanup;
@@ -372,6 +417,7 @@ BPF ELF object file *example.bpf.o*. :: + test_2_result: 17 my_map name: my_map sys_enter prog FD: 8 my_static_var: 7
Signature · 1 line
2.34.1
PATCH v4 6/6 selftests/bpf: Test if shadow types work correctly.
In reply to [email protected]From: Kui-Feng Lee <[email protected]>
Change the values of fields, including scalar types and function pointers, and check if the struct_ops map works as expected.
The test changes the field "test_2" of "testmod_1" from the pointer to test_2() to pointer to test_3() and the field "data" to 13. The function test_2() and test_3() both compute a new value for "test_2_result", but in different way. By checking the value of "test_2_result", it ensures the struct_ops map works as expected with changes through shadow types.
--- .../selftests/bpf/bpf_testmod/bpf_testmod.c | 11 ++++++++++- .../selftests/bpf/bpf_testmod/bpf_testmod.h | 8 ++++++++ .../bpf/prog_tests/test_struct_ops_module.c | 19 +++++++++++++++---- .../selftests/bpf/progs/struct_ops_module.c | 8 ++++++++ 4 files changed, 41 insertions(+), 5 deletions(-)
tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c +10 -1
@@ -539,6 +539,15 @@ static int bpf_testmod_ops_init_member(const struct btf_type *t,const struct btf_member *member, void *kdata, const void *udata) { + if (member->offset == offsetof(struct bpf_testmod_ops, data) * 8) { +/* For data fields, this function has to copy it and return +* 1 to indicate that the data has been handled by the +* struct_ops type, or the verifier will reject the map if +* the value of the data field is not zero. +*/ + ((struct bpf_testmod_ops *)kdata)->data = ((struct bpf_testmod_ops *)udata)->data; +return 1 ; + }return 0 ; }
@@ -559,7 +568,7 @@ static int bpf_dummy_reg(void *kdata)* initialized, so we need to check for NULL. */ if (ops->test_2) - ops->test_2 (4 ,3 ); + ops->test_2 (4 , ops->data);return 0 ; }
tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.h +8 -0
@@ -35,6 +35,14 @@ struct bpf_testmod_ops {void (*test_2)(int a,int b);/* Used to test nullable arguments. */ int (*test_maybe_null)(int dummy,struct task_struct *task); + +/* The following fields are used to test shadow copies. */ +char onebyte; +struct { +int a; +int b; + } unsupported; + int data; };#endif /* _BPF_TESTMOD_H */
tools/testing/selftests/bpf/prog_tests/test_struct_ops_module.c +15 -4
@@ -32,17 +32,23 @@ static void check_map_info(struct bpf_map_info *info)static void test_struct_ops_load (void ) { -DECLARE_LIBBPF_OPTS (bpf_object_open_opts, opts);struct struct_ops_module *skel;struct bpf_map_info info = {};struct bpf_link *link;int err;u32 len; - skel =struct_ops_module__open_opts (&opts); + skel =struct_ops_module__open ();if (!ASSERT_OK_PTR (skel,"struct_ops_module_open" ))return ; + skel->struct_ops.testmod_1->data =13 ; + skel->struct_ops.testmod_1->test_2 = skel->progs.test_3; +/* Since test_2() is not being used, it should be disabled from +* auto-loading, or it will fail to load. +*/ +bpf_program__set_autoload (skel->progs.test_2, false); + err =struct_ops_module__load (skel);if (!ASSERT_OK (err,"struct_ops_module_load" )) goto cleanup;
@@ -56,8 +62,13 @@ static void test_struct_ops_load(void) link =bpf_map__attach_struct_ops (skel->maps.testmod_1);ASSERT_OK_PTR (link,"attach_test_mod_1" ); -/* test_2() will be called from bpf_dummy_reg() in bpf_testmod.c */ -ASSERT_EQ (skel->bss->test_2_result,7 ,"test_2_result" ); +/* test_3() will be called from bpf_dummy_reg() in bpf_testmod.c +* +* In bpf_testmod.c it will pass 4 and 13 (the value of data) to +* .test_2. So, the value of test_2_result should be 20 (4 + 13 + +* 3). +*/ +ASSERT_EQ (skel->bss->test_2_result,20 ,"check_shadow_variables" );bpf_link__destroy (link);
tools/testing/selftests/bpf/progs/struct_ops_module.c +8 -0
@@ -21,9 +21,17 @@ void BPF_PROG(test_2, int a, int b) test_2_result = a + b; } +SEC("struct_ops/test_3") +int BPF_PROG (test_3 ,int a,int b) +{ + test_2_result = a + b +3 ; +return a + b +3 ; +} + SEC(".struct_ops.link")struct bpf_testmod_ops testmod_1 = { .test_1 = (void *)test_1, .test_2 = (void *)test_2, + .data =0x1 , };
Signature · 1 line
2.34.1