Skip to content

Commit 0270d69

Browse files
committed
Merge branch 'create-shadow-types-for-struct_ops-maps-in-skeletons'
Kui-Feng Lee says: ==================== Create shadow types for struct_ops maps in skeletons 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 my_skel__testmod_1__bpf_testmod_ops { const struct bpf_program *test_1; const struct bpf_program *test_2; const struct bpf_program *test_maybe_null; char onebyte; char __padding_4[3]; char __unsupported_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 v5: - Generate names for shadow types. - Check btf and the number of struct_ops maps in gen_st_ops_shadow() and gen_st_ops_shadow_init() instead of do_skeleton() and do_subskeleton(). - Name unsupported fields in the pattern __unsupported_*. - Have a padding field for a unsupported fields as well if necessary. - Implement resolve_func_ptr() in gen.c instead of reusing the one in libbpf. (Remove the part 1 in v4.) - Fix stylistic issues. Changes from v4: - Convert function pointers to the pointers to struct bpf_program in bpf_object__collect_st_ops_relos(). 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. v5: https://lore.kernel.org/all/20240227010432.714127-1-thinker.li@gmail.com/ v4: https://lore.kernel.org/all/20240222222624.1163754-1-thinker.li@gmail.com/ v3: https://lore.kernel.org/all/20240221012329.1387275-1-thinker.li@gmail.com/ v2: https://lore.kernel.org/all/20240214020836.1845354-1-thinker.li@gmail.com/ v1: https://lore.kernel.org/all/20240124224130.859921-1-thinker.li@gmail.com/ Kui-Feng Lee (5): 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 | 237 +++++++++++++++++- tools/lib/bpf/libbpf.c | 50 +++- .../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 + 7 files changed, 377 insertions(+), 14 deletions(-) ==================== Link: https://lore.kernel.org/r/20240229064523.2091270-1-thinker.li@gmail.com Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
2 parents 896880f + 0623e73 commit 0270d69

File tree

7 files changed

+372
-14
lines changed

7 files changed

+372
-14
lines changed

tools/bpf/bpftool/Documentation/bpftool-gen.rst

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -257,18 +257,48 @@ EXAMPLES
257257
return 0;
258258
}
259259

260-
This is example BPF application with two BPF programs and a mix of BPF maps
261-
and global variables. Source code is split across two source code files.
260+
**$ cat example3.bpf.c**
261+
262+
::
263+
264+
#include <linux/ptrace.h>
265+
#include <linux/bpf.h>
266+
#include <bpf/bpf_helpers.h>
267+
/* This header file is provided by the bpf_testmod module. */
268+
#include "bpf_testmod.h"
269+
270+
int test_2_result = 0;
271+
272+
/* bpf_Testmod.ko calls this function, passing a "4"
273+
* and testmod_map->data.
274+
*/
275+
SEC("struct_ops/test_2")
276+
void BPF_PROG(test_2, int a, int b)
277+
{
278+
test_2_result = a + b;
279+
}
280+
281+
SEC(".struct_ops")
282+
struct bpf_testmod_ops testmod_map = {
283+
.test_2 = (void *)test_2,
284+
.data = 0x1,
285+
};
286+
287+
This is example BPF application with three BPF programs and a mix of BPF
288+
maps and global variables. Source code is split across three source code
289+
files.
262290

263291
**$ clang --target=bpf -g example1.bpf.c -o example1.bpf.o**
264292

265293
**$ clang --target=bpf -g example2.bpf.c -o example2.bpf.o**
266294

267-
**$ bpftool gen object example.bpf.o example1.bpf.o example2.bpf.o**
295+
**$ clang --target=bpf -g example3.bpf.c -o example3.bpf.o**
296+
297+
**$ bpftool gen object example.bpf.o example1.bpf.o example2.bpf.o example3.bpf.o**
268298

269-
This set of commands compiles *example1.bpf.c* and *example2.bpf.c*
270-
individually and then statically links respective object files into the final
271-
BPF ELF object file *example.bpf.o*.
299+
This set of commands compiles *example1.bpf.c*, *example2.bpf.c* and
300+
*example3.bpf.c* individually and then statically links respective object
301+
files into the final BPF ELF object file *example.bpf.o*.
272302

273303
**$ bpftool gen skeleton example.bpf.o name example | tee example.skel.h**
274304

@@ -291,7 +321,15 @@ BPF ELF object file *example.bpf.o*.
291321
struct bpf_map *data;
292322
struct bpf_map *bss;
293323
struct bpf_map *my_map;
324+
struct bpf_map *testmod_map;
294325
} maps;
326+
struct {
327+
struct example__testmod_map__bpf_testmod_ops {
328+
const struct bpf_program *test_1;
329+
const struct bpf_program *test_2;
330+
int data;
331+
} *testmod_map;
332+
} struct_ops;
295333
struct {
296334
struct bpf_program *handle_sys_enter;
297335
struct bpf_program *handle_sys_exit;
@@ -304,6 +342,7 @@ BPF ELF object file *example.bpf.o*.
304342
struct {
305343
int x;
306344
} data;
345+
int test_2_result;
307346
} *bss;
308347
struct example__data {
309348
_Bool global_flag;
@@ -342,10 +381,16 @@ BPF ELF object file *example.bpf.o*.
342381

343382
skel->rodata->param1 = 128;
344383

384+
/* Change the value through the pointer of shadow type */
385+
skel->struct_ops.testmod_map->data = 13;
386+
345387
err = example__load(skel);
346388
if (err)
347389
goto cleanup;
348390

391+
/* The result of the function test_2() */
392+
printf("test_2_result: %d\n", skel->bss->test_2_result);
393+
349394
err = example__attach(skel);
350395
if (err)
351396
goto cleanup;
@@ -372,6 +417,7 @@ BPF ELF object file *example.bpf.o*.
372417

373418
::
374419

420+
test_2_result: 17
375421
my_map name: my_map
376422
sys_enter prog FD: 8
377423
my_static_var: 7

tools/bpf/bpftool/gen.c

Lines changed: 236 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,20 @@ static bool str_has_suffix(const char *str, const char *suffix)
5555
return true;
5656
}
5757

58+
static const struct btf_type *
59+
resolve_func_ptr(const struct btf *btf, __u32 id, __u32 *res_id)
60+
{
61+
const struct btf_type *t;
62+
63+
t = skip_mods_and_typedefs(btf, id, NULL);
64+
if (!btf_is_ptr(t))
65+
return NULL;
66+
67+
t = skip_mods_and_typedefs(btf, t->type, res_id);
68+
69+
return btf_is_func_proto(t) ? t : NULL;
70+
}
71+
5872
static void get_obj_name(char *name, const char *file)
5973
{
6074
char file_copy[PATH_MAX];
@@ -909,6 +923,207 @@ codegen_progs_skeleton(struct bpf_object *obj, size_t prog_cnt, bool populate_li
909923
}
910924
}
911925

926+
static int walk_st_ops_shadow_vars(struct btf *btf, const char *ident,
927+
const struct btf_type *map_type, __u32 map_type_id)
928+
{
929+
LIBBPF_OPTS(btf_dump_emit_type_decl_opts, opts, .indent_level = 3);
930+
const struct btf_type *member_type;
931+
__u32 offset, next_offset = 0;
932+
const struct btf_member *m;
933+
struct btf_dump *d = NULL;
934+
const char *member_name;
935+
__u32 member_type_id;
936+
int i, err = 0, n;
937+
int size;
938+
939+
d = btf_dump__new(btf, codegen_btf_dump_printf, NULL, NULL);
940+
if (!d)
941+
return -errno;
942+
943+
n = btf_vlen(map_type);
944+
for (i = 0, m = btf_members(map_type); i < n; i++, m++) {
945+
member_type = skip_mods_and_typedefs(btf, m->type, &member_type_id);
946+
member_name = btf__name_by_offset(btf, m->name_off);
947+
948+
offset = m->offset / 8;
949+
if (next_offset < offset)
950+
printf("\t\t\tchar __padding_%d[%d];\n", i, offset - next_offset);
951+
952+
switch (btf_kind(member_type)) {
953+
case BTF_KIND_INT:
954+
case BTF_KIND_FLOAT:
955+
case BTF_KIND_ENUM:
956+
case BTF_KIND_ENUM64:
957+
/* scalar type */
958+
printf("\t\t\t");
959+
opts.field_name = member_name;
960+
err = btf_dump__emit_type_decl(d, member_type_id, &opts);
961+
if (err) {
962+
p_err("Failed to emit type declaration for %s: %d", member_name, err);
963+
goto out;
964+
}
965+
printf(";\n");
966+
967+
size = btf__resolve_size(btf, member_type_id);
968+
if (size < 0) {
969+
p_err("Failed to resolve size of %s: %d\n", member_name, size);
970+
err = size;
971+
goto out;
972+
}
973+
974+
next_offset = offset + size;
975+
break;
976+
977+
case BTF_KIND_PTR:
978+
if (resolve_func_ptr(btf, m->type, NULL)) {
979+
/* Function pointer */
980+
printf("\t\t\tstruct bpf_program *%s;\n", member_name);
981+
982+
next_offset = offset + sizeof(void *);
983+
break;
984+
}
985+
/* All pointer types are unsupported except for
986+
* function pointers.
987+
*/
988+
fallthrough;
989+
990+
default:
991+
/* Unsupported types
992+
*
993+
* Types other than scalar types and function
994+
* pointers are currently not supported in order to
995+
* prevent conflicts in the generated code caused
996+
* by multiple definitions. For instance, if the
997+
* struct type FOO is used in a struct_ops map,
998+
* bpftool has to generate definitions for FOO,
999+
* which may result in conflicts if FOO is defined
1000+
* in different skeleton files.
1001+
*/
1002+
size = btf__resolve_size(btf, member_type_id);
1003+
if (size < 0) {
1004+
p_err("Failed to resolve size of %s: %d\n", member_name, size);
1005+
err = size;
1006+
goto out;
1007+
}
1008+
printf("\t\t\tchar __unsupported_%d[%d];\n", i, size);
1009+
1010+
next_offset = offset + size;
1011+
break;
1012+
}
1013+
}
1014+
1015+
/* Cannot fail since it must be a struct type */
1016+
size = btf__resolve_size(btf, map_type_id);
1017+
if (next_offset < (__u32)size)
1018+
printf("\t\t\tchar __padding_end[%d];\n", size - next_offset);
1019+
1020+
out:
1021+
btf_dump__free(d);
1022+
1023+
return err;
1024+
}
1025+
1026+
/* Generate the pointer of the shadow type for a struct_ops map.
1027+
*
1028+
* This function adds a pointer of the shadow type for a struct_ops map.
1029+
* The members of a struct_ops map can be exported through a pointer to a
1030+
* shadow type. The user can access these members through the pointer.
1031+
*
1032+
* A shadow type includes not all members, only members of some types.
1033+
* They are scalar types and function pointers. The function pointers are
1034+
* translated to the pointer of the struct bpf_program. The scalar types
1035+
* are translated to the original type without any modifiers.
1036+
*
1037+
* Unsupported types will be translated to a char array to occupy the same
1038+
* space as the original field, being renamed as __unsupported_*. The user
1039+
* should treat these fields as opaque data.
1040+
*/
1041+
static int gen_st_ops_shadow_type(const char *obj_name, struct btf *btf, const char *ident,
1042+
const struct bpf_map *map)
1043+
{
1044+
const struct btf_type *map_type;
1045+
const char *type_name;
1046+
__u32 map_type_id;
1047+
int err;
1048+
1049+
map_type_id = bpf_map__btf_value_type_id(map);
1050+
if (map_type_id == 0)
1051+
return -EINVAL;
1052+
map_type = btf__type_by_id(btf, map_type_id);
1053+
if (!map_type)
1054+
return -EINVAL;
1055+
1056+
type_name = btf__name_by_offset(btf, map_type->name_off);
1057+
1058+
printf("\t\tstruct %s__%s__%s {\n", obj_name, ident, type_name);
1059+
1060+
err = walk_st_ops_shadow_vars(btf, ident, map_type, map_type_id);
1061+
if (err)
1062+
return err;
1063+
1064+
printf("\t\t} *%s;\n", ident);
1065+
1066+
return 0;
1067+
}
1068+
1069+
static int gen_st_ops_shadow(const char *obj_name, struct btf *btf, struct bpf_object *obj)
1070+
{
1071+
int err, st_ops_cnt = 0;
1072+
struct bpf_map *map;
1073+
char ident[256];
1074+
1075+
if (!btf)
1076+
return 0;
1077+
1078+
/* Generate the pointers to shadow types of
1079+
* struct_ops maps.
1080+
*/
1081+
bpf_object__for_each_map(map, obj) {
1082+
if (bpf_map__type(map) != BPF_MAP_TYPE_STRUCT_OPS)
1083+
continue;
1084+
if (!get_map_ident(map, ident, sizeof(ident)))
1085+
continue;
1086+
1087+
if (st_ops_cnt == 0) /* first struct_ops map */
1088+
printf("\tstruct {\n");
1089+
st_ops_cnt++;
1090+
1091+
err = gen_st_ops_shadow_type(obj_name, btf, ident, map);
1092+
if (err)
1093+
return err;
1094+
}
1095+
1096+
if (st_ops_cnt)
1097+
printf("\t} struct_ops;\n");
1098+
1099+
return 0;
1100+
}
1101+
1102+
/* Generate the code to initialize the pointers of shadow types. */
1103+
static void gen_st_ops_shadow_init(struct btf *btf, struct bpf_object *obj)
1104+
{
1105+
struct bpf_map *map;
1106+
char ident[256];
1107+
1108+
if (!btf)
1109+
return;
1110+
1111+
/* Initialize the pointers to_ops shadow types of
1112+
* struct_ops maps.
1113+
*/
1114+
bpf_object__for_each_map(map, obj) {
1115+
if (bpf_map__type(map) != BPF_MAP_TYPE_STRUCT_OPS)
1116+
continue;
1117+
if (!get_map_ident(map, ident, sizeof(ident)))
1118+
continue;
1119+
codegen("\
1120+
\n\
1121+
obj->struct_ops.%1$s = bpf_map__initial_value(obj->maps.%1$s, NULL);\n\
1122+
\n\
1123+
", ident);
1124+
}
1125+
}
1126+
9121127
static int do_skeleton(int argc, char **argv)
9131128
{
9141129
char header_guard[MAX_OBJ_NAME_LEN + sizeof("__SKEL_H__")];
@@ -1052,6 +1267,11 @@ static int do_skeleton(int argc, char **argv)
10521267
printf("\t} maps;\n");
10531268
}
10541269

1270+
btf = bpf_object__btf(obj);
1271+
err = gen_st_ops_shadow(obj_name, btf, obj);
1272+
if (err)
1273+
goto out;
1274+
10551275
if (prog_cnt) {
10561276
printf("\tstruct {\n");
10571277
bpf_object__for_each_program(prog, obj) {
@@ -1075,7 +1295,6 @@ static int do_skeleton(int argc, char **argv)
10751295
printf("\t} links;\n");
10761296
}
10771297

1078-
btf = bpf_object__btf(obj);
10791298
if (btf) {
10801299
err = codegen_datasecs(obj, obj_name);
10811300
if (err)
@@ -1133,6 +1352,12 @@ static int do_skeleton(int argc, char **argv)
11331352
if (err) \n\
11341353
goto err_out; \n\
11351354
\n\
1355+
", obj_name);
1356+
1357+
gen_st_ops_shadow_init(btf, obj);
1358+
1359+
codegen("\
1360+
\n\
11361361
return obj; \n\
11371362
err_out: \n\
11381363
%1$s__destroy(obj); \n\
@@ -1442,6 +1667,10 @@ static int do_subskeleton(int argc, char **argv)
14421667
printf("\t} maps;\n");
14431668
}
14441669

1670+
err = gen_st_ops_shadow(obj_name, btf, obj);
1671+
if (err)
1672+
goto out;
1673+
14451674
if (prog_cnt) {
14461675
printf("\tstruct {\n");
14471676
bpf_object__for_each_program(prog, obj) {
@@ -1553,6 +1782,12 @@ static int do_subskeleton(int argc, char **argv)
15531782
if (err) \n\
15541783
goto err; \n\
15551784
\n\
1785+
");
1786+
1787+
gen_st_ops_shadow_init(btf, obj);
1788+
1789+
codegen("\
1790+
\n\
15561791
return obj; \n\
15571792
err: \n\
15581793
%1$s__destroy(obj); \n\

0 commit comments

Comments
 (0)