libbpf: type suffixes and autocreate flag for struct_ops maps
Subsystems (5)
Eduard Zingerman
maintainerv4 cover libbpf: type suffixes and autocreate flag for struct_ops maps
Tweak struct_ops related APIs to allow the following features:
- specify version suffixes for stuct_ops map types;
- share same BPF program between several map definitions with different local BTF types, assuming only maps with same kernel BTF type would be selected for load;
- toggle autocreate flag for struct_ops maps;
- automatically toggle autoload for struct_ops programs referenced from struct_ops maps, depending on autocreate status of the corresponding map;
- use SEC("?.struct_ops") and SEC("?.struct_ops.link")
to define struct_ops maps with autocreate == false after object open.
This would allow loading programs like below:
SEC("struct_ops/foo") int BPF_PROG(foo) { ... }
SEC("struct_ops/bar") int BPF_PROG(bar) { ... }
struct bpf_testmod_ops___v1 {
int (*foo)(void);
};
struct bpf_testmod_ops___v2 {
int (*foo)(void);
int (*bar)(void);
};
/* Assume kernel type name to be 'test_ops' */
SEC(".struct_ops.link")
struct test_ops___v1 map_v1 = {
/* Program 'foo' shared by maps with
* different local BTF type
*/
.foo = (void *)foo
};
SEC(".struct_ops.link")
struct test_ops___v2 map_v2 = {
.foo = (void *)foo,
.bar = (void *)bar
};Assuming the following tweaks are done before loading:
/* to load v1 */
bpf_map__set_autocreate(skel->maps.map_v1, true);
bpf_map__set_autocreate(skel->maps.map_v2, false);
/* to load v2 */
bpf_map__set_autocreate(skel->maps.map_v1, false);
bpf_map__set_autocreate(skel->maps.map_v2, true);Patch #8 ties autocreate and autoload flags for struct_ops maps and programs.
Changelog:
- v3 [3] -> v4:
- changes for multiple styling suggestions from Andrii;
- patch #5: libbpf log capture now happens for LIBBPF_INFO and LIBBPF_WARN messages and does not depend on verbosity flags (Andrii);
- patch #6: fixed runtime crash caused by conflict with newly added
test case struct_ops_multi_pages;
- patch #7: fixed free of possibly uninitialized pointer (Daniel)
- patch #8: simpler algorithm to detect which programs to autoload
(Andrii);
- patch #9: added assertions for autoload flag after object load
(Andrii);
- patch #12: DATASEC name rewrite in libbpf is now done inplace, no
new strings added to BTF (Andrii); - patch #14: allow any printable characters in DATASEC names when kernel validates BTF (Andrii)
- v2 [2] -> v3:
- moved patch #8 logic to be fully done on load
(requested by Andrii in offlist discussion);
- in patch #9 added test case for shadow vars and autocreate/autoload interaction.
- v1 [1] -> v2:
- fixed memory leak in patch #1 (Kui-Feng);
- improved error messages in patch #2 (Martin, Andrii);
- in bad_struct_ops selftest from patch #6 added .test_2
map member setup (David);
- added utility functions to capture libbpf log from selftests (David)
- in selftests replaced usage of ...__open_and_load by separate
calls to ..._open() and ..._load() (Andrii);
- removed serial_... in selftest definitions (Andrii);
- improved comments in selftest struct_ops_autocreate
from patch #7 (David);
- removed autoload toggling logic incompatible with shadow variables
from bpf_map__set_autocreate(), instead struct_ops programs
autoload property is computed at struct_ops maps load phase,
see patch #8 (Kui-Feng, Martin, Andrii);
- added support for SEC("?.struct_ops") and SEC("?.struct_ops.link")
(Andrii).[1] https://lore.kernel.org/bpf/[email protected]/
[2] https://lore.kernel.org/bpf/[email protected]/
[3] https://lore.kernel.org/bpf/[email protected]/
Eduard Zingerman (15):
libbpf: allow version suffixes (___smth) for struct_ops types
libbpf: tie struct_ops programs to kernel BTF ids, not to local ids
libbpf: honor autocreate flag for struct_ops maps selftests/bpf: test struct_ops map definition with type suffix selftests/bpf: utility functions to capture libbpf log in test_progs selftests/bpf: bad_struct_ops test
selftests/bpf: test autocreate behavior for struct_ops maps
libbpf: sync progs autoload with maps autocreate for struct_ops maps selftests/bpf: verify struct_ops autoload/autocreate sync
libbpf: replace elf_state->st_ops_* fields with SEC_ST_OPS sec_type
libbpf: struct_ops in SEC("?.struct_ops") / SEC("?.struct_ops.link")
libbpf: rewrite btf datasec names starting from '?' selftests/bpf: test case for SEC("?.struct_ops")
bpf: allow all printable characters in BTF DATASEC names selftests/bpf: test cases for '?' in BTF names
kernel/bpf/btf.c | 16 +- tools/lib/bpf/features.c | 22 ++ tools/lib/bpf/libbpf.c | 209 +++++++++++++----- tools/lib/bpf/libbpf_internal.h | 2 + .../selftests/bpf/bpf_testmod/bpf_testmod.c | 26 +++ .../selftests/bpf/bpf_testmod/bpf_testmod.h | 4 + .../selftests/bpf/prog_tests/bad_struct_ops.c | 67 ++++++ tools/testing/selftests/bpf/prog_tests/btf.c | 29 +++ .../bpf/prog_tests/struct_ops_autocreate.c | 159 +++++++++++++ .../bpf/prog_tests/test_struct_ops_module.c | 33 ++- .../selftests/bpf/progs/bad_struct_ops.c | 25 +++ .../selftests/bpf/progs/bad_struct_ops2.c | 14 ++ .../bpf/progs/struct_ops_autocreate.c | 52 +++++ .../bpf/progs/struct_ops_autocreate2.c | 32 +++ .../selftests/bpf/progs/struct_ops_module.c | 21 +- tools/testing/selftests/bpf/test_progs.c | 59 +++++ tools/testing/selftests/bpf/test_progs.h | 3 + 17 files changed, 704 insertions(+), 69 deletions(-) create mode 100644 tools/testing/selftests/bpf/prog_tests/bad_struct_ops.c create mode 100644 tools/testing/selftests/bpf/prog_tests/struct_ops_autocreate.c create mode 100644 tools/testing/selftests/bpf/progs/bad_struct_ops.c create mode 100644 tools/testing/selftests/bpf/progs/bad_struct_ops2.c create mode 100644 tools/testing/selftests/bpf/progs/struct_ops_autocreate.c create mode 100644 tools/testing/selftests/bpf/progs/struct_ops_autocreate2.c
Signature · 1 line
2.43.0
Eduard Zingerman
maintainerPATCH v4 1/15 libbpf: allow version suffixes (___smth) for struct_ops types
E.g. allow the following struct_ops definitions:
struct bpf_testmod_ops___v1 { int (*test)(void); };
struct bpf_testmod_ops___v2 { int (*test)(void); };
SEC(".struct_ops.link")
struct bpf_testmod_ops___v1 a = { .test = ... }
SEC(".struct_ops.link")
struct bpf_testmod_ops___v2 b = { .test = ... }Where both bpf_testmod_ops__v1 and bpf_testmod_ops__v2 would be resolved as 'struct bpf_testmod_ops' from kernel BTF.
--- tools/lib/bpf/libbpf.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
tools/lib/bpf/libbpf.c +5 -1
@@ -948,7 +948,7 @@ static int find_btf_by_prefix_kind(const struct btf *btf, const char *prefix, const char *name, __u32 kind); static int -find_struct_ops_kern_types(struct bpf_object *obj, const char *tname, +find_struct_ops_kern_types(struct bpf_object *obj, const char *tname_raw, struct module_btf **mod_btf, const struct btf_type **type, __u32 *type_id, const struct btf_type **vtype, __u32 *vtype_id,
@@ -958,8 +958,12 @@ find_struct_ops_kern_types(struct bpf_object *obj, const char *tname,const struct btf_member *kern_data_member;struct btf *btf;__s32 kern_vtype_id, kern_type_id; +char tname[256 ];__u32 i; +snprintf (tname,sizeof (tname),"%.*s" , + (int )bpf_core_essential_name_len (tname_raw), tname_raw); + kern_type_id =find_ksym_btf_id (obj, tname,BTF_KIND_STRUCT , &btf, mod_btf);if (kern_type_id <0 ) {
Signature · 1 line
2.43.0
Eduard Zingerman
maintainerPATCH v4 2/15 libbpf: tie struct_ops programs to kernel BTF ids, not to local ids
In reply to Eduard ZingermanEnforce the following existing limitation on struct_ops programs based on kernel BTF id instead of program-local BTF id:
struct_ops BPF prog can be re-used between multiple .struct_ops & .struct_ops.link as long as it's the same struct_ops struct definition and the same function pointer field
This allows reusing same BPF program for versioned struct_ops map definitions, e.g.:
SEC("struct_ops/test")
int BPF_PROG(foo) { ... }
struct some_ops___v1 { int (*test)(void); };
struct some_ops___v2 { int (*test)(void); };
SEC(".struct_ops.link") struct some_ops___v1 a = { .test = foo }
SEC(".struct_ops.link") struct some_ops___v2 b = { .test = foo }--- tools/lib/bpf/libbpf.c | 49 ++++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 23 deletions(-)
tools/lib/bpf/libbpf.c +26 -23
@@ -1146,8 +1146,32 @@ static int bpf_map__init_kern_struct_ops(struct bpf_map *map)if (mod_btf) prog->attach_btf_obj_fd = mod_btf->fd; - prog->attach_btf_id = kern_type_id; - prog->expected_attach_type = kern_member_idx; + +/* if we haven't yet processed this BPF program, record proper +* attach_btf_id and member_idx +*/ +if (!prog->attach_btf_id) { + prog->attach_btf_id = kern_type_id; + prog->expected_attach_type = kern_member_idx; + } + +/* struct_ops BPF prog can be re-used between multiple +* .struct_ops & .struct_ops.link as long as it's the +* same struct_ops struct definition and the same +* function pointer field +*/ +if (prog->attach_btf_id != kern_type_id) { +pr_warn ("struct_ops init_kern %s func ptr %s: invalid reuse of prog %s in sec %s with type %u: attach_btf_id %u != kern_type_id %u\n" , + map->name, mname, prog->name, prog->sec_name, prog->type, + prog->attach_btf_id, kern_type_id); +return -EINVAL ; + } +if (prog->expected_attach_type != kern_member_idx) { +pr_warn ("struct_ops init_kern %s func ptr %s: invalid reuse of prog %s in sec %s with type %u: expected_attach_type %u != kern_member_idx %u\n" , + map->name, mname, prog->name, prog->sec_name, prog->type, + prog->expected_attach_type, kern_member_idx); +return -EINVAL ; + } st_ops->kern_func_off[i] = kern_data_off + kern_moff;
@@ -9428,27 +9452,6 @@ static int bpf_object__collect_st_ops_relos(struct bpf_object *obj,return -EINVAL ; } -/* if we haven't yet processed this BPF program, record proper -* attach_btf_id and member_idx -*/ -if (!prog->attach_btf_id) { - prog->attach_btf_id = st_ops->type_id; - prog->expected_attach_type = member_idx; - } - -/* struct_ops BPF prog can be re-used between multiple -* .struct_ops & .struct_ops.link as long as it's the -* same struct_ops struct definition and the same -* function pointer field -*/ -if (prog->attach_btf_id != st_ops->type_id || - prog->expected_attach_type != member_idx) { -pr_warn ("struct_ops reloc %s: cannot use prog %s in sec %s with type %u attach_btf_id %u expected_attach_type %u for func ptr %s\n" , - map->name, prog->name, prog->sec_name, prog->type, - prog->attach_btf_id, prog->expected_attach_type, name); -return -EINVAL ; - } - st_ops->progs[member_idx] = prog;/* st_ops->data will be exposed to users, being returned by
Signature · 1 line
2.43.0
Eduard Zingerman
maintainerPATCH v4 3/15 libbpf: honor autocreate flag for struct_ops maps
In reply to Eduard ZingermanSkip load steps for struct_ops maps not marked for automatic creation. This should allow to load bpf object in situations like below:
SEC("struct_ops/foo") int BPF_PROG(foo) { ... }
SEC("struct_ops/bar") int BPF_PROG(bar) { ... }
struct test_ops___v1 {
int (*foo)(void);
};
struct test_ops___v2 {
int (*foo)(void);
int (*does_not_exist)(void);
};
SEC(".struct_ops.link")
struct test_ops___v1 map_for_old = {
.test_1 = (void *)foo
};
SEC(".struct_ops.link")
struct test_ops___v2 map_for_new = {
.test_1 = (void *)foo,
.does_not_exist = (void *)bar
};Suppose program is loaded on old kernel that does not have definition for 'does_not_exist' struct_ops member. After this commit it would be possible to load such object file after the following tweaks:
bpf_program__set_autoload(skel->progs.bar, false);
bpf_map__set_autocreate(skel->maps.map_for_new, false);--- tools/lib/bpf/libbpf.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-)
tools/lib/bpf/libbpf.c +15 -3
@@ -1212,6 +1212,9 @@ static int bpf_object__init_kern_struct_ops_maps(struct bpf_object *obj)if (!bpf_map__is_struct_ops (map))continue ; +if (!map->autocreate) +continue ; + err =bpf_map__init_kern_struct_ops (map);if (err)return err;
@@ -8133,11 +8136,20 @@ static void bpf_map_prepare_vdata(const struct bpf_map *map)static int bpf_object_prepare_struct_ops (struct bpf_object *obj) { +struct bpf_map *map;int i; -for (i =0 ; i < obj->nr_maps; i++) -if (bpf_map__is_struct_ops (&obj->maps[i])) -bpf_map_prepare_vdata (&obj->maps[i]); +for (i =0 ; i < obj->nr_maps; i++) { + map = &obj->maps[i]; + +if (!bpf_map__is_struct_ops (map)) +continue ; + +if (!map->autocreate) +continue ; + +bpf_map_prepare_vdata (map); + }return 0 ; }
Signature · 1 line
2.43.0
Eduard Zingerman
maintainerPATCH v4 4/15 selftests/bpf: test struct_ops map definition with type suffix
In reply to Eduard ZingermanExtend struct_ops_module test case to check if it is possible to use '___' suffixes for struct_ops type specification.
--- .../selftests/bpf/bpf_testmod/bpf_testmod.c | 2 ++ .../bpf/prog_tests/test_struct_ops_module.c | 33 ++++++++++++++----- .../selftests/bpf/progs/struct_ops_module.c | 21 +++++++++++- 3 files changed, 46 insertions(+), 10 deletions(-)
tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c +2 -0
@@ -564,6 +564,8 @@ static int bpf_dummy_reg(void *kdata) {struct bpf_testmod_ops *ops = kdata; +if (ops->test_1) + ops->test_1 ();/* Some test cases (ex. struct_ops_maybe_null) may not have test_2 * initialized, so we need to check for NULL. */
tools/testing/selftests/bpf/prog_tests/test_struct_ops_module.c +24 -9
@@ -30,11 +30,29 @@ static void check_map_info(struct bpf_map_info *info)close (fd); } +static int attach_ops_and_check (struct struct_ops_module *skel, +struct bpf_map *map, +int expected_test_2_result) +{ +struct bpf_link *link; + + link =bpf_map__attach_struct_ops (map); +ASSERT_OK_PTR (link,"attach_test_mod_1" ); +if (!link) +return -1 ; + +/* test_{1,2}() would be called from bpf_dummy_reg() in bpf_testmod.c */ +ASSERT_EQ (skel->bss->test_1_result,0xdeadbeef ,"test_1_result" ); +ASSERT_EQ (skel->bss->test_2_result, expected_test_2_result,"test_2_result" ); + +bpf_link__destroy (link); +return 0 ; +} +static void test_struct_ops_load (void ) {struct struct_ops_module *skel;struct bpf_map_info info = {}; -struct bpf_link *link;int err;u32 len;
@@ -59,20 +77,17 @@ static void test_struct_ops_load(void)if (!ASSERT_OK (err,"bpf_map_get_info_by_fd" )) goto cleanup; - link =bpf_map__attach_struct_ops (skel->maps.testmod_1); -ASSERT_OK_PTR (link,"attach_test_mod_1" ); - +check_map_info (&info);/* 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); - -check_map_info (&info); +if (!attach_ops_and_check (skel, skel->maps.testmod_1,20 )) + goto cleanup; +if (!attach_ops_and_check (skel, skel->maps.testmod_2,12 )) + goto cleanup; cleanup:struct_ops_module__destroy (skel);
tools/testing/selftests/bpf/progs/struct_ops_module.c +20 -1
@@ -7,12 +7,14 @@ char _license[] SEC("license") = "GPL"; +int test_1_result =0 ;int test_2_result =0 ; SEC("struct_ops/test_1")int BPF_PROG (test_1 ) { -return 0xdeadbeef ; + test_1_result =0xdeadbeef ; +return 0 ; }SEC ("struct_ops/test_2" )
@@ -35,3 +37,20 @@ struct bpf_testmod_ops testmod_1 = { .data = 0x1, }; +SEC("struct_ops/test_2") +void BPF_PROG (test_2_v2 ,int a,int b) +{ + test_2_result = a * b; +} + +struct bpf_testmod_ops___v2 { +int (*test_1)(void ); +void (*test_2)(int a,int b); +int (*test_maybe_null)(int dummy,struct task_struct *task); +}; + +SEC(".struct_ops.link") +struct bpf_testmod_ops___v2 testmod_2 = { + .test_1 = (void *)test_1, + .test_2 = (void *)test_2_v2, +};
Signature · 1 line
2.43.0
Eduard Zingerman
maintainerPATCH v4 5/15 selftests/bpf: utility functions to capture libbpf log in test_progs
In reply to Eduard ZingermanSeveral test_progs tests already capture libbpf log in order to check for some expected output, e.g bpf_tcp_ca.c, kfunc_dynptr_param.c, log_buf.c and a few others.
This commit provides a, hopefully, simple API to capture libbpf log w/o necessity to define new print callback in each test:
/* Creates a global memstream capturing INFO and WARN level output
* passed to libbpf_print_fn.
* Returns 0 on success, negative value on failure.
* On failure the description is printed using PRINT_FAIL and
* current test case is marked as fail.
*/
int start_libbpf_log_capture(void)
/* Destroys global memstream created by start_libbpf_log_capture().
* Returns a pointer to captured data which has to be freed.
* Returned buffer is null terminated.
*/
char *stop_libbpf_log_capture(void)
The intended usage is as follows:
if (start_libbpf_log_capture())
return;
use_libbpf();
char *log = stop_libbpf_log_capture();
ASSERT_HAS_SUBSTR(log, "... expected ...", "expected some message");
free(log);As a safety measure, free(start_libbpf_log_capture()) is invoked in the epilogue of the test_progs.c:run_one_test().
--- tools/testing/selftests/bpf/test_progs.c | 59 ++++++++++++++++++++++++ tools/testing/selftests/bpf/test_progs.h | 3 ++ 2 files changed, 62 insertions(+)
tools/testing/selftests/bpf/test_progs.c +59 -0
@@ -683,11 +683,69 @@ static const struct argp_option opts[] = { {}, }; +static FILE *libbpf_capture_stream; + +static struct { +char *buf; +size_t buf_sz; +} libbpf_output_capture; + +/* Creates a global memstream capturing INFO and WARN level output +* passed to libbpf_print_fn. +* Returns 0 on success, negative value on failure. +* On failure the description is printed using PRINT_FAIL and +* current test case is marked as fail. +*/ +int start_libbpf_log_capture (void ) +{ +if (libbpf_capture_stream) { +PRINT_FAIL ("%s: libbpf_capture_stream != NULL\n" , __func__); +return -EINVAL ; + } + + libbpf_capture_stream =open_memstream (&libbpf_output_capture.buf, + &libbpf_output_capture.buf_sz); +if (!libbpf_capture_stream) { +PRINT_FAIL ("%s: open_memstream failed errno=%d\n" , __func__, errno); +return -EINVAL ; + } + +return 0 ; +} + +/* Destroys global memstream created by start_libbpf_log_capture(). +* Returns a pointer to captured data which has to be freed. +* Returned buffer is null terminated. +*/ +char *stop_libbpf_log_capture (void ) +{ +char *buf; + +if (!libbpf_capture_stream) +return NULL ; + +fputc (0 , libbpf_capture_stream); +fclose (libbpf_capture_stream); + libbpf_capture_stream =NULL ; +/* get 'buf' after fclose(), see open_memstream() documentation */ + buf = libbpf_output_capture.buf; +memset (&libbpf_output_capture,0 ,sizeof (libbpf_output_capture)); +return buf; +} +static int libbpf_print_fn (enum libbpf_print_level level,const char *format,va_list args) { +if (libbpf_capture_stream && level !=LIBBPF_DEBUG ) { +va_list args2; + +va_copy (args2, args); +vfprintf (libbpf_capture_stream, format, args2); + } +if (env.verbosity <VERBOSE_VERY && level ==LIBBPF_DEBUG )return 0 ; +vfprintf (stdout, format, args);return 0 ; }
@@ -1081,6 +1139,7 @@ static void run_one_test(int test_num)cleanup_cgroup_environment ();stdio_restore (); +free (stop_libbpf_log_capture ());dump_test_log (test, state, false, false,NULL ); }
tools/testing/selftests/bpf/test_progs.h +3 -0
@@ -397,6 +397,9 @@ int test__join_cgroup(const char *path);system (cmd); \ }) +int start_libbpf_log_capture (void ); +char *stop_libbpf_log_capture (void ); +static inline __u64 ptr_to_u64 (const void *ptr) {return (__u64 ) (unsigned long ) ptr;
Signature · 1 line
2.43.0
Eduard Zingerman
maintainerPATCH v4 6/15 selftests/bpf: bad_struct_ops test
In reply to Eduard ZingermanWhen loading struct_ops programs kernel requires BTF id of the struct_ops type and member index for attachment point inside that type. This makes impossible to use same BPF program in several struct_ops maps that have different struct_ops type. Check if libbpf rejects such BPF objects files.
--- .../selftests/bpf/bpf_testmod/bpf_testmod.c | 24 +++++++++++++ .../selftests/bpf/bpf_testmod/bpf_testmod.h | 4 +++ .../selftests/bpf/prog_tests/bad_struct_ops.c | 35 +++++++++++++++++++ .../selftests/bpf/progs/bad_struct_ops.c | 25 +++++++++++++ 4 files changed, 88 insertions(+) create mode 100644 tools/testing/selftests/bpf/prog_tests/bad_struct_ops.c create mode 100644 tools/testing/selftests/bpf/progs/bad_struct_ops.c
tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c +24 -0
@@ -611,6 +611,29 @@ struct bpf_struct_ops bpf_bpf_testmod_ops = { .owner = THIS_MODULE, }; +static int bpf_dummy_reg2 (void *kdata) +{ +struct bpf_testmod_ops2 *ops = kdata; + + ops->test_1 (); +return 0 ; +} + +static struct bpf_testmod_ops2 __bpf_testmod_ops2 = { + .test_1 = bpf_testmod_test_1, +}; + +struct bpf_struct_ops bpf_testmod_ops2 = { + .verifier_ops = &bpf_testmod_verifier_ops, + .init = bpf_testmod_ops_init, + .init_member = bpf_testmod_ops_init_member, + .reg = bpf_dummy_reg2, + .unreg = bpf_dummy_unreg, + .cfi_stubs = &__bpf_testmod_ops2, + .name ="bpf_testmod_ops2" , + .owner =THIS_MODULE , +}; +extern int bpf_fentry_test1 (int a);static int bpf_testmod_init (void )
@@ -622,6 +645,7 @@ static int bpf_testmod_init(void) 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); 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);if (ret <0 )return ret;if (bpf_fentry_test1 (0 ) <0 )
tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.h +4 -0
@@ -89,4 +89,8 @@ struct bpf_testmod_ops { int (*tramp_40)(int value); }; +struct bpf_testmod_ops2 { +int (*test_1)(void ); +}; +#endif /* _BPF_TESTMOD_H */
tools/testing/selftests/bpf/prog_tests/bad_struct_ops.c new file +35 -0
@@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <test_progs.h> +#include "bad_struct_ops.skel.h" + +static void invalid_prog_reuse (void ) +{ +struct bad_struct_ops *skel; +char *log =NULL ; +int err; + + skel =bad_struct_ops__open (); +if (!ASSERT_OK_PTR (skel,"bad_struct_ops__open" )) +return ; + +if (start_libbpf_log_capture ()) + goto cleanup; + + err =bad_struct_ops__load (skel); + log =stop_libbpf_log_capture (); +ASSERT_ERR (err,"bad_struct_ops__load should fail" ); +ASSERT_HAS_SUBSTR (log, +"struct_ops init_kern testmod_2 func ptr test_1: invalid reuse of prog test_1" , +"expected init_kern message" ); + +cleanup: +free (log); +bad_struct_ops__destroy (skel); +} + +void test_bad_struct_ops (void ) +{ +if (test__start_subtest ("invalid_prog_reuse" )) +invalid_prog_reuse (); +}
tools/testing/selftests/bpf/progs/bad_struct_ops.c new file +25 -0
@@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <vmlinux.h> +#include <bpf/bpf_helpers.h> +#include <bpf/bpf_tracing.h> +#include "../bpf_testmod/bpf_testmod.h" + +char _license[] SEC("license") = "GPL"; + +SEC("struct_ops/test_1") +int BPF_PROG (test_1 ) {return 0 ; } + +SEC("struct_ops/test_2") +int BPF_PROG (test_2 ) {return 0 ; } + +SEC(".struct_ops.link") +struct bpf_testmod_ops testmod_1 = { + .test_1 = (void *)test_1, + .test_2 = (void *)test_2 +}; + +SEC(".struct_ops.link") +struct bpf_testmod_ops2 testmod_2 = { + .test_1 = (void *)test_1 +};
Signature · 1 line
2.43.0
Eduard Zingerman
maintainerPATCH v4 7/15 selftests/bpf: test autocreate behavior for struct_ops maps
In reply to Eduard ZingermanCheck that bpf_map__set_autocreate() can be used to disable automatic creation for struct_ops maps.
--- .../bpf/prog_tests/struct_ops_autocreate.c | 76 +++++++++++++++++++ .../bpf/progs/struct_ops_autocreate.c | 42 ++++++++++ 2 files changed, 118 insertions(+) create mode 100644 tools/testing/selftests/bpf/prog_tests/struct_ops_autocreate.c create mode 100644 tools/testing/selftests/bpf/progs/struct_ops_autocreate.c
tools/testing/selftests/bpf/prog_tests/struct_ops_autocreate.c new file +76 -0
@@ -0,0 +1,76 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <test_progs.h> +#include "struct_ops_autocreate.skel.h" + +static void cant_load_full_object (void ) +{ +struct struct_ops_autocreate *skel; +char *log =NULL ; +int err; + + skel =struct_ops_autocreate__open (); +if (!ASSERT_OK_PTR (skel,"struct_ops_autocreate__open" )) +return ; + +if (start_libbpf_log_capture ()) + goto cleanup; +/* The testmod_2 map BTF type (struct bpf_testmod_ops___v2) doesn't +* match the BTF of the actual struct bpf_testmod_ops defined in the +* kernel, so we should fail to load it if we don't disable autocreate +* for that map. +*/ + err =struct_ops_autocreate__load (skel); + log =stop_libbpf_log_capture (); +if (!ASSERT_ERR (err,"struct_ops_autocreate__load" )) + goto cleanup; + +ASSERT_HAS_SUBSTR (log,"libbpf: struct_ops init_kern" ,"init_kern message" ); +ASSERT_EQ (err, -ENOTSUP ,"errno should be ENOTSUP" ); + +cleanup: +free (log); +struct_ops_autocreate__destroy (skel); +} + +static void can_load_partial_object (void ) +{ +struct struct_ops_autocreate *skel; +struct bpf_link *link =NULL ; +int err; + + skel =struct_ops_autocreate__open (); +if (!ASSERT_OK_PTR (skel,"struct_ops_autocreate__open_opts" )) +return ; + + err =bpf_program__set_autoload (skel->progs.test_2, false); +if (!ASSERT_OK (err,"bpf_program__set_autoload" )) + goto cleanup; + + err =bpf_map__set_autocreate (skel->maps.testmod_2, false); +if (!ASSERT_OK (err,"bpf_map__set_autocreate" )) + goto cleanup; + + err =struct_ops_autocreate__load (skel); +if (ASSERT_OK (err,"struct_ops_autocreate__load" )) + goto cleanup; + + link =bpf_map__attach_struct_ops (skel->maps.testmod_1); +if (!ASSERT_OK_PTR (link,"bpf_map__attach_struct_ops" )) + goto cleanup; + +/* test_1() would be called from bpf_dummy_reg2() in bpf_testmod.c */ +ASSERT_EQ (skel->bss->test_1_result,42 ,"test_1_result" ); + +cleanup: +bpf_link__destroy (link); +struct_ops_autocreate__destroy (skel); +} + +void test_struct_ops_autocreate (void ) +{ +if (test__start_subtest ("cant_load_full_object" )) +cant_load_full_object (); +if (test__start_subtest ("can_load_partial_object" )) +can_load_partial_object (); +}
tools/testing/selftests/bpf/progs/struct_ops_autocreate.c new file +42 -0
42 lines not on this page · raw message
Signature · 1 line
2.43.0
Eduard Zingerman
maintainerPATCH v4 8/15 libbpf: sync progs autoload with maps autocreate for struct_ops maps
In reply to Eduard ZingermanAutomatically select which struct_ops programs to load depending on which struct_ops maps are selected for automatic creation. E.g. for the BPF code below:
SEC("struct_ops/test_1") int BPF_PROG(foo) { ... }
SEC("struct_ops/test_2") int BPF_PROG(bar) { ... }
SEC(".struct_ops.link")
struct test_ops___v1 A = {
.foo = (void *)foo
};
SEC(".struct_ops.link")
struct test_ops___v2 B = {
.foo = (void *)foo,
.bar = (void *)bar,
};And the following libbpf API calls:
bpf_map__set_autocreate(skel->maps.A, true);
bpf_map__set_autocreate(skel->maps.B, false);The autoload would be enabled for program 'foo' and disabled for program 'bar'.
During load, for each struct_ops program P, referenced from some struct_ops map M:
- set P.autoload = true if M.autocreate is true for some M;
- set P.autoload = false if M.autocreate is false for all M;
- don't change P.autoload, if P is not referenced from any map.
Do this after bpf_object__init_kern_struct_ops_maps() to make sure that shadow vars assignment is done.
--- tools/lib/bpf/libbpf.c | 43 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+)
tools/lib/bpf/libbpf.c +43 -0
@@ -1031,6 +1031,48 @@ static bool is_valid_st_ops_program(struct bpf_object *obj,return false; } +/* For each struct_ops program P, referenced from some struct_ops map M, +* enable P.autoload if there are Ms for which M.autocreate is true, +* disable P.autoload if for all Ms M.autocreate is false. +* Don't change P.autoload for programs that are not referenced from any maps. +*/ +static int bpf_object_adjust_struct_ops_autoload (struct bpf_object *obj) +{ +struct bpf_program *prog, *slot_prog; +struct bpf_map *map; +int i, j, k, vlen; + +for (i =0 ; i < obj->nr_programs; ++i) { +int should_load = false; +int use_cnt =0 ; + + prog = &obj->programs[i]; +if (prog->type !=BPF_PROG_TYPE_STRUCT_OPS ) +continue ; + +for (j =0 ; j < obj->nr_maps; ++j) { + map = &obj->maps[j]; +if (!bpf_map__is_struct_ops (map)) +continue ; + + vlen =btf_vlen (map->st_ops->type); +for (k =0 ; k < vlen; ++k) { + slot_prog = map->st_ops->progs[k]; +if (prog != slot_prog) +continue ; + + use_cnt++; +if (map->autocreate) + should_load = true; + } + } +if (use_cnt) + prog->autoload = should_load; + } + +return 0 ; +} +/* Init the map's fields that depend on kern_btf */ static int bpf_map__init_kern_struct_ops (struct bpf_map *map) {
@@ -8175,6 +8217,7 @@ static int bpf_object_load(struct bpf_object *obj, int extra_log_level, const ch err = err ? :bpf_object__resolve_externs (obj, obj->kconfig); err = err ? :bpf_object__sanitize_maps (obj); err = err ? :bpf_object__init_kern_struct_ops_maps (obj); + err = err ? :bpf_object_adjust_struct_ops_autoload (obj); err = err ? :bpf_object__relocate (obj, obj->btf_custom_path ? : target_btf_path); err = err ? :bpf_object__sanitize_and_load_btf (obj); err = err ? :bpf_object__create_maps (obj);
Signature · 1 line
2.43.0
Eduard Zingerman
maintainerPATCH v4 9/15 selftests/bpf: verify struct_ops autoload/autocreate sync
In reply to Eduard ZingermanCheck that autocreate flags of struct_ops map cause autoload of struct_ops corresponding programs:
- when struct_ops program is referenced only from a map for which autocreate is set to false, that program should not be loaded;
- when struct_ops program with autoload == false is set to be used from a map with autocreate == true using shadow var, that program should be loaded;
- when struct_ops program is not referenced from any map object load should fail.
--- .../selftests/bpf/prog_tests/bad_struct_ops.c | 32 ++++++++++++ .../bpf/prog_tests/struct_ops_autocreate.c | 51 +++++++++++++++++-- .../selftests/bpf/progs/bad_struct_ops2.c | 14 +++++ .../bpf/progs/struct_ops_autocreate2.c | 32 ++++++++++++ 4 files changed, 125 insertions(+), 4 deletions(-) create mode 100644 tools/testing/selftests/bpf/progs/bad_struct_ops2.c create mode 100644 tools/testing/selftests/bpf/progs/struct_ops_autocreate2.c
tools/testing/selftests/bpf/prog_tests/bad_struct_ops.c +32 -0
@@ -2,6 +2,7 @@#include <test_progs.h> #include "bad_struct_ops.skel.h" +#include "bad_struct_ops2.skel.h" static void invalid_prog_reuse (void ) {
@@ -28,8 +29,39 @@ static void invalid_prog_reuse(void)bad_struct_ops__destroy (skel); } +static void unused_program (void ) +{ +struct bad_struct_ops2 *skel; +char *log =NULL ; +int err; + + skel =bad_struct_ops2__open (); +if (!ASSERT_OK_PTR (skel,"bad_struct_ops2__open" )) +return ; + +/* struct_ops programs not referenced from any maps are open +* with autoload set to true. +*/ +ASSERT_TRUE (bpf_program__autoload (skel->progs.foo),"foo autoload == true" ); + +if (start_libbpf_log_capture ()) + goto cleanup; + + err =bad_struct_ops2__load (skel); +ASSERT_ERR (err,"bad_struct_ops2__load should fail" ); + log =stop_libbpf_log_capture (); +ASSERT_HAS_SUBSTR (log,"prog 'foo': failed to load" , +"message about 'foo' failing to load" ); + +cleanup: +free (log); +bad_struct_ops2__destroy (skel); +} +void test_bad_struct_ops (void ) {if (test__start_subtest ("invalid_prog_reuse" ))invalid_prog_reuse (); +if (test__start_subtest ("unused_program" )) +unused_program (); }
tools/testing/selftests/bpf/prog_tests/struct_ops_autocreate.c +47 -4
81 lines not on this page · raw message
tools/testing/selftests/bpf/progs/bad_struct_ops2.c new file +14 -0
@@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/bpf.h> +#include <bpf/bpf_helpers.h> + +char _license[] SEC("license") = "GPL"; + +/* This is an unused struct_ops program, it lacks corresponding +* struct_ops map, which provides attachment information. +* W/o additional configuration attempt to load such +* BPF object file would fail. +*/ +SEC("struct_ops/foo") +void foo (void ) {}
tools/testing/selftests/bpf/progs/struct_ops_autocreate2.c new file +32 -0
@@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/bpf.h> +#include <bpf/bpf_helpers.h> +#include <bpf/bpf_tracing.h> + +char _license[] SEC("license") = "GPL"; + +int test_1_result =0 ; + +SEC("?struct_ops/test_1") +int BPF_PROG (foo ) +{ + test_1_result =42 ; +return 0 ; +} + +SEC("?struct_ops/test_1") +int BPF_PROG (bar ) +{ + test_1_result =24 ; +return 0 ; +} + +struct bpf_testmod_ops { +int (*test_1)(void ); +}; + +SEC(".struct_ops.link") +struct bpf_testmod_ops testmod_1 = { + .test_1 = (void *)bar +};
Signature · 1 line
2.43.0
Eduard Zingerman
maintainerPATCH v4 10/15 libbpf: replace elf_state->st_ops_* fields with SEC_ST_OPS sec_type
In reply to Eduard ZingermanThe next patch would add two new section names for struct_ops maps. To make working with multiple struct_ops sections more convenient:
- remove fields like elf_state->st_ops_{shndx,link_shndx};
- mark section descriptions hosting struct_ops as elf_sec_desc->sec_type == SEC_ST_OPS;
After these changes struct_ops sections could be processed uniformly by iterating bpf_object->efile.secs entries.
--- tools/lib/bpf/libbpf.c | 61 ++++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 29 deletions(-)
tools/lib/bpf/libbpf.c +32 -29
@@ -612,6 +612,7 @@ enum sec_type {SEC_BSS ,SEC_DATA ,SEC_RODATA , + SEC_ST_OPS, }; struct elf_sec_desc {
@@ -627,8 +628,6 @@ struct elf_state {Elf *elf;Elf64_Ehdr *ehdr;Elf_Data *symbols; -Elf_Data *st_ops_data; -Elf_Data *st_ops_link_data;size_t shstrndx;/* section index for section name strings */ size_t strtabidx;struct elf_sec_desc *secs;
@@ -637,8 +636,7 @@ struct elf_state {__u32 btf_maps_sec_btf_id;int text_shndx;int symbols_shndx; -int st_ops_shndx; -int st_ops_link_shndx; +bool has_st_ops; };struct usdt_manager ;
@@ -1266,7 +1264,7 @@ static int bpf_object__init_kern_struct_ops_maps(struct bpf_object *obj) }static int init_struct_ops_maps (struct bpf_object *obj,const char *sec_name, -int shndx,Elf_Data *data,__u32 map_flags) +int shndx,Elf_Data *data) {const struct btf_type *type, *datasec;const struct btf_var_secinfo *vsi;
@@ -1328,7 +1326,7 @@ static int init_struct_ops_maps(struct bpf_object *obj, const char *sec_name, map->def.key_size =sizeof (int ); map->def.value_size = type->size; map->def.max_entries =1 ; - map->def.map_flags = map_flags; + map->def.map_flags =strcmp (sec_name,STRUCT_OPS_LINK_SEC ) ==0 ?BPF_F_LINK :0 ; map->st_ops =calloc (1 ,sizeof (*map->st_ops));if (!map->st_ops)
@@ -1363,15 +1361,25 @@ static int init_struct_ops_maps(struct bpf_object *obj, const char *sec_name,static int bpf_object_init_struct_ops (struct bpf_object *obj) { -int err; +const char *sec_name; +int sec_idx, err; - err =init_struct_ops_maps (obj,STRUCT_OPS_SEC , obj->efile.st_ops_shndx, - obj->efile.st_ops_data,0 ); - err = err ?:init_struct_ops_maps (obj,STRUCT_OPS_LINK_SEC , - obj->efile.st_ops_link_shndx, - obj->efile.st_ops_link_data, -BPF_F_LINK ); -return err; +for (sec_idx =0 ; sec_idx < obj->efile.sec_cnt; ++sec_idx) { +struct elf_sec_desc *desc = &obj->efile.secs[sec_idx]; + +if (desc->sec_type !=SEC_ST_OPS ) +continue ; + + sec_name =elf_sec_name (obj,elf_sec_by_idx (obj, sec_idx)); +if (!sec_name) +return -LIBBPF_ERRNO__FORMAT ; + + err =init_struct_ops_maps (obj, sec_name, sec_idx, desc->data); +if (err) +return err; + } + +return 0 ; } static struct bpf_object *bpf_object__new(const char *path,
@@ -1409,8 +1417,6 @@ static struct bpf_object *bpf_object__new(const char *path, obj->efile.obj_buf = obj_buf; obj->efile.obj_buf_sz = obj_buf_sz; obj->efile.btf_maps_shndx =-1 ; - obj->efile.st_ops_shndx =-1 ; - obj->efile.st_ops_link_shndx =-1 ; obj->kconfig_map_idx =-1 ; obj->kern_version =get_kernel_version ();
@@ -1427,8 +1433,6 @@ static void bpf_object__elf_finish(struct bpf_object *obj)elf_end (obj->efile.elf); obj->efile.elf =NULL ; obj->efile.symbols =NULL ; - obj->efile.st_ops_data =NULL ; - obj->efile.st_ops_link_data =NULL ;zfree (&obj->efile.secs); obj->efile.sec_cnt =0 ;
@@ -2973,14 +2977,13 @@ static int bpf_object__sanitize_btf(struct bpf_object *obj, struct btf *btf)static bool libbpf_needs_btf (const struct bpf_object *obj) {return obj->efile.btf_maps_shndx >=0 || - obj->efile.st_ops_shndx >=0 || - obj->efile.st_ops_link_shndx >=0 || + obj->efile.has_st_ops || obj->nr_extern >0 ; }static bool kernel_needs_btf (const struct bpf_object *obj) { -return obj->efile.st_ops_shndx >=0 || obj->efile.st_ops_link_shndx >=0 ; +return obj->efile.has_st_ops; } static int bpf_object__init_btf(struct bpf_object *obj,
@@ -3681,12 +3684,12 @@ static int bpf_object__elf_collect(struct bpf_object *obj) sec_desc->sec_type =SEC_RODATA ; sec_desc->shdr = sh; sec_desc->data = data; - }else if (strcmp (name,STRUCT_OPS_SEC ) ==0 ) { - obj->efile.st_ops_data = data; - obj->efile.st_ops_shndx = idx; - }else if (strcmp (name,STRUCT_OPS_LINK_SEC ) ==0 ) { - obj->efile.st_ops_link_data = data; - obj->efile.st_ops_link_shndx = idx; + }else if (strcmp (name,STRUCT_OPS_SEC ) ==0 || +strcmp (name,STRUCT_OPS_LINK_SEC ) ==0 ) { + sec_desc->sec_type =SEC_ST_OPS ; + sec_desc->shdr = sh; + sec_desc->data = data; + obj->efile.has_st_ops = true; }else {pr_info ("elf: skipping unrecognized data section(%d) %s\n" , idx, name);
@@ -6999,12 +7002,12 @@ static int bpf_object__collect_relos(struct bpf_object *obj) data = sec_desc->data; idx = shdr->sh_info; -if (shdr->sh_type !=SHT_REL ) { +if (shdr->sh_type !=SHT_REL || idx <0 || idx >= obj->efile.sec_cnt) {pr_warn ("internal error at %d\n" , __LINE__);return -LIBBPF_ERRNO__INTERNAL ; } -if (idx == obj->efile.st_ops_shndx || idx == obj->efile.st_ops_link_shndx) +if (obj->efile.secs[idx].sec_type ==SEC_ST_OPS ) err =bpf_object__collect_st_ops_relos (obj, shdr, data);else if (idx == obj->efile.btf_maps_shndx) err =bpf_object__collect_map_relos (obj, shdr, data);
Signature · 1 line
2.43.0
Eduard Zingerman
maintainerPATCH v4 11/15 libbpf: struct_ops in SEC("?.struct_ops") / SEC("?.struct_ops.link")
In reply to Eduard ZingermanAllow using two new section names for struct_ops maps:
- SEC("?.struct_ops")
- SEC("?.struct_ops.link")
To specify maps that have bpf_map->autocreate == false after open.
--- tools/lib/bpf/libbpf.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-)
tools/lib/bpf/libbpf.c +14 -1
@@ -1322,6 +1322,15 @@ static int init_struct_ops_maps(struct bpf_object *obj, const char *sec_name,return -ENOMEM ; map->btf_value_type_id = type_id; +/* Follow same convention as for programs autoload: +* SEC("?.struct_ops") means map is not created by default. +*/ +if (sec_name[0 ] =='?' ) { + map->autocreate = false; +/* from now on forget there was ? in section name */ + sec_name++; + } + map->def.type =BPF_MAP_TYPE_STRUCT_OPS ; map->def.key_size =sizeof (int ); map->def.value_size = type->size;
@@ -3685,7 +3694,9 @@ static int bpf_object__elf_collect(struct bpf_object *obj) sec_desc->shdr = sh; sec_desc->data = data; }else if (strcmp (name,STRUCT_OPS_SEC ) ==0 || -strcmp (name,STRUCT_OPS_LINK_SEC ) ==0 ) { +strcmp (name,STRUCT_OPS_LINK_SEC ) ==0 || +strcmp (name,"?" STRUCT_OPS_SEC ) ==0 || +strcmp (name,"?" STRUCT_OPS_LINK_SEC ) ==0 ) { sec_desc->sec_type =SEC_ST_OPS ; sec_desc->shdr = sh; sec_desc->data = data;
@@ -3705,6 +3716,8 @@ static int bpf_object__elf_collect(struct bpf_object *obj)if (!section_have_execinstr (obj, targ_sec_idx) &&strcmp (name,".rel" STRUCT_OPS_SEC ) &&strcmp (name,".rel" STRUCT_OPS_LINK_SEC ) && +strcmp (name,".rel?" STRUCT_OPS_SEC ) && +strcmp (name,".rel?" STRUCT_OPS_LINK_SEC ) &&strcmp (name,".rel" MAPS_ELF_SEC )) { pr_info("elf: skipping relo section(%d) %s for section(%d) %s\n", idx, name, targ_sec_idx,
Signature · 1 line
2.43.0
Eduard Zingerman
maintainerPATCH v4 12/15 libbpf: rewrite btf datasec names starting from '?'
In reply to Eduard ZingermanOptional struct_ops maps are defined using question mark at the start of the section name, e.g.:
SEC("?.struct_ops")
struct test_ops optional_map = { ... };This commit teaches libbpf to detect if kernel allows '?' prefix in datasec names, and if it doesn't then to rewrite such names by replacing '?' with '_', e.g.:
DATASEC ?.struct_ops -> DATASEC _.struct_ops
--- tools/lib/bpf/features.c | 22 ++++++++++++++++++++++ tools/lib/bpf/libbpf.c | 19 +++++++++++++++++-- tools/lib/bpf/libbpf_internal.h | 2 ++ 3 files changed, 41 insertions(+), 2 deletions(-)
tools/lib/bpf/features.c +22 -0
@@ -147,6 +147,25 @@ static int probe_kern_btf_datasec(int token_fd) strs, sizeof(strs), token_fd)); } +static int probe_kern_btf_qmark_datasec (int token_fd) +{ +static const char strs[] ="\0x\0?.data" ; +/* static int a; */ +__u32 types[] = { +/* int */ +BTF_TYPE_INT_ENC (0 ,BTF_INT_SIGNED ,0 ,32 ,4 ),/* [1] */ +/* VAR x */ /* [2] */ +BTF_TYPE_ENC (1 ,BTF_INFO_ENC (BTF_KIND_VAR ,0 ,0 ),1 ), +BTF_VAR_STATIC , +/* DATASEC ?.data */ /* [3] */ +BTF_TYPE_ENC (3 ,BTF_INFO_ENC (BTF_KIND_DATASEC ,0 ,1 ),4 ), +BTF_VAR_SECINFO_ENC (2 ,0 ,4 ), + }; + +return probe_fd (libbpf__load_raw_btf ((char *)types,sizeof (types), + strs,sizeof (strs), token_fd)); +} +static int probe_kern_btf_float (int token_fd) {static const char strs[] ="\0float" ;
@@ -534,6 +553,9 @@ static struct kern_feature_desc { [FEAT_ARG_CTX_TAG ] = {"kernel-side __arg_ctx tag" , probe_kern_arg_ctx_tag, }, + [FEAT_BTF_QMARK_DATASEC ] = { +"BTF DATASEC names starting from '?'" , probe_kern_btf_qmark_datasec, + }, };bool feat_supported (struct kern_feature_cache *cache,enum kern_feature_id feat_id)
tools/lib/bpf/libbpf.c +17 -2
@@ -2869,6 +2869,11 @@ static bool section_have_execinstr(struct bpf_object *obj, int idx)return sh->sh_flags &SHF_EXECINSTR ; } +static bool starts_with_qmark (const char *s) +{ +return s && s[0 ] =='?' ; +} +static bool btf_needs_sanitization (struct bpf_object *obj) {bool has_func_global =kernel_supports (obj,FEAT_BTF_GLOBAL_FUNC );
@@ -2878,9 +2883,10 @@ static bool btf_needs_sanitization(struct bpf_object *obj)bool has_decl_tag =kernel_supports (obj,FEAT_BTF_DECL_TAG );bool has_type_tag =kernel_supports (obj,FEAT_BTF_TYPE_TAG );bool has_enum64 =kernel_supports (obj,FEAT_BTF_ENUM64 ); +bool has_qmark_datasec =kernel_supports (obj,FEAT_BTF_QMARK_DATASEC );return !has_func || !has_datasec || !has_func_global || !has_float || - !has_decl_tag || !has_type_tag || !has_enum64; + !has_decl_tag || !has_type_tag || !has_enum64 || !has_qmark_datasec; }static int bpf_object__sanitize_btf (struct bpf_object *obj,struct btf *btf)
@@ -2892,6 +2898,7 @@ static int bpf_object__sanitize_btf(struct bpf_object *obj, struct btf *btf)bool has_decl_tag =kernel_supports (obj,FEAT_BTF_DECL_TAG );bool has_type_tag =kernel_supports (obj,FEAT_BTF_TYPE_TAG );bool has_enum64 =kernel_supports (obj,FEAT_BTF_ENUM64 ); +bool has_qmark_datasec =kernel_supports (obj,FEAT_BTF_QMARK_DATASEC );int enum64_placeholder_id =0 ;struct btf_type *t;int i, j, vlen;
@@ -2918,7 +2925,7 @@ static int bpf_object__sanitize_btf(struct bpf_object *obj, struct btf *btf) name = (char *)btf__name_by_offset (btf, t->name_off);while (*name) { -if (*name =='.' ) +if (*name =='.' || *name =='?' ) *name ='_' ; name++; }
@@ -2933,6 +2940,14 @@ static int bpf_object__sanitize_btf(struct bpf_object *obj, struct btf *btf) vt = (void *)btf__type_by_id (btf, v->type); m->name_off = vt->name_off; } + }else if (!has_qmark_datasec &&btf_is_datasec (t) && +starts_with_qmark (btf__name_by_offset (btf, t->name_off))) { +/* replace '?' prefix with '_' for DATASEC names */ +char *name; + + name = (char *)btf__name_by_offset (btf, t->name_off); +if (name[0 ] =='?' ) + name[0 ] ='_' ; }else if (!has_func &&btf_is_func_proto (t)) {/* replace FUNC_PROTO with ENUM */ vlen =btf_vlen (t);
tools/lib/bpf/libbpf_internal.h +2 -0
@@ -374,6 +374,8 @@ enum kern_feature_id {FEAT_UPROBE_MULTI_LINK ,/* Kernel supports arg:ctx tag (__arg_ctx) for global subprogs natively */ FEAT_ARG_CTX_TAG , +/* Kernel supports '?' at the front of datasec names */ +FEAT_BTF_QMARK_DATASEC , __FEAT_CNT, };
Signature · 1 line
2.43.0
Eduard Zingerman
maintainerPATCH v4 13/15 selftests/bpf: test case for SEC("?.struct_ops")
In reply to Eduard ZingermanCheck that "?.struct_ops" and "?.struct_ops.link" section names define struct_ops maps with autocreate == false after open.
--- .../bpf/prog_tests/struct_ops_autocreate.c | 52 ++++++++++++++++--- .../bpf/progs/struct_ops_autocreate.c | 10 ++++ 2 files changed, 56 insertions(+), 6 deletions(-)
tools/testing/selftests/bpf/prog_tests/struct_ops_autocreate.c +46 -6
@@ -34,10 +34,24 @@ static void cant_load_full_object(void)struct_ops_autocreate__destroy (skel); } +static int check_test_1_link (struct struct_ops_autocreate *skel,struct bpf_map *map) +{ +struct bpf_link *link; +int err; + + link =bpf_map__attach_struct_ops (skel->maps.testmod_1); +if (!ASSERT_OK_PTR (link,"bpf_map__attach_struct_ops" )) +return -1 ; + +/* test_1() would be called from bpf_dummy_reg2() in bpf_testmod.c */ + err =ASSERT_EQ (skel->bss->test_1_result,42 ,"test_1_result" ); +bpf_link__destroy (link); +return err; +} +static void can_load_partial_object (void ) {struct struct_ops_autocreate *skel; -struct bpf_link *link =NULL ;int err; skel =struct_ops_autocreate__open ();
@@ -58,15 +72,39 @@ static void can_load_partial_object(void)ASSERT_TRUE (bpf_program__autoload (skel->progs.test_1),"test_1 actual autoload" );ASSERT_FALSE (bpf_program__autoload (skel->progs.test_2),"test_2 actual autoload" ); - link =bpf_map__attach_struct_ops (skel->maps.testmod_1); -if (!ASSERT_OK_PTR (link,"bpf_map__attach_struct_ops" )) +check_test_1_link (skel, skel->maps.testmod_1); + +cleanup: +struct_ops_autocreate__destroy (skel); +} + +static void optional_maps (void ) +{ +struct struct_ops_autocreate *skel; +int err; + + skel =struct_ops_autocreate__open (); +if (!ASSERT_OK_PTR (skel,"struct_ops_autocreate__open" )) +return ; + +ASSERT_TRUE (bpf_map__autocreate (skel->maps.testmod_1),"testmod_1 autocreate" ); +ASSERT_TRUE (bpf_map__autocreate (skel->maps.testmod_2),"testmod_2 autocreate" ); +ASSERT_FALSE (bpf_map__autocreate (skel->maps.optional_map),"optional_map autocreate" ); +ASSERT_FALSE (bpf_map__autocreate (skel->maps.optional_map2),"optional_map2 autocreate" ); + + err =bpf_map__set_autocreate (skel->maps.testmod_1, false); + err |=bpf_map__set_autocreate (skel->maps.testmod_2, false); + err |=bpf_map__set_autocreate (skel->maps.optional_map2, true); +if (!ASSERT_OK (err,"bpf_map__set_autocreate" )) goto cleanup; -/* test_1() would be called from bpf_dummy_reg2() in bpf_testmod.c */ -ASSERT_EQ (skel->bss->test_1_result,42 ,"test_1_result" ); + err =struct_ops_autocreate__load (skel); +if (ASSERT_OK (err,"struct_ops_autocreate__load" )) + goto cleanup; + +check_test_1_link (skel, skel->maps.optional_map2); cleanup: -bpf_link__destroy (link);struct_ops_autocreate__destroy (skel); }
@@ -116,4 +154,6 @@ void test_struct_ops_autocreate(void)can_load_partial_object ();if (test__start_subtest ("autoload_and_shadow_vars" ))autoload_and_shadow_vars (); +if (test__start_subtest ("optional_maps" )) +optional_maps (); }
tools/testing/selftests/bpf/progs/struct_ops_autocreate.c +10 -0
@@ -40,3 +40,13 @@ struct bpf_testmod_ops___v2 testmod_2 = { .test_1 = (void *)test_1, .does_not_exist = (void *)test_2 }; + +SEC("?.struct_ops") +struct bpf_testmod_ops___v1 optional_map = { + .test_1 = (void *)test_1, +}; + +SEC("?.struct_ops.link") +struct bpf_testmod_ops___v1 optional_map2 = { + .test_1 = (void *)test_1, +};
Signature · 1 line
2.43.0
Eduard Zingerman
maintainerPATCH v4 14/15 bpf: allow all printable characters in BTF DATASEC names
In reply to Eduard ZingermanThe intent is to allow libbpf to use SEC("?.struct_ops") to identify struct_ops maps that are optional, e.g. like in the following BPF code:
SEC("?.struct_ops")
struct test_ops optional_map = { ... };Which yields the following BTF:
...
[13] DATASEC '?.struct_ops' size=0 vlen=...
...To load such BTF libbpf rewrites DATASEC name before load. After this patch the rewrite won't be necessary.
--- kernel/bpf/btf.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-)
kernel/bpf/btf.c +15 -1
@@ -809,9 +809,23 @@ static bool btf_name_valid_identifier(const struct btf *btf, u32 offset)return __btf_name_valid (btf, offset); } +/* Allow any printable character in DATASEC names */ static bool btf_name_valid_section (const struct btf *btf,u32 offset) { -return __btf_name_valid (btf, offset); +/* offset must be valid */ +const char *src =btf_str_by_offset (btf, offset); +const char *src_limit; + +/* set a limit on identifier length */ + src_limit = src +KSYM_NAME_LEN ; + src++; +while (*src && src < src_limit) { +if (!isprint (*src)) +return false; + src++; + } + +return !*src; }static const char *__btf_name_by_offset (const struct btf *btf,u32 offset)
Signature · 1 line
2.43.0
Re: [PATCH bpf-next v4 14/15] bpf: allow all printable characters in BTF DATASEC names
Quoted 26 lines · Eduard Zingerman
The intent is to allow libbpf to use
SEC("?.struct_ops")to identifystruct_opsmaps that are optional, e.g. like in the following BPF code:SEC("?.struct_ops") struct test_ops optional_map = { ... };Which yields the following BTF:
... [13] DATASEC '?.struct_ops' size=0 vlen=... ...To load such BTF libbpf rewrites DATASEC name before load. After this patch the rewrite won't be necessary.
Signed-off-by Eduard Zingerman <[email protected]>--- kernel/bpf/btf.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-)kernel/bpf/btf.c +0
-0
kernel/bpf/btf.c static bool btf_name_valid_identifier(const struct btf *btf, u32 offset) · line 809
Quoted 1 line
kernel/bpf/btf.c +0
-0@@ -809,9 +809,23 @@ static bool btf_name_valid_identifier(const struct btf *btf, u32 offset)return __btf_name_valid (btf, offset);
We should just rename __btf_name_valid() into btf_name_valid_indentifier() and use that everywhere. Please consider a clean up patch as a follow up, thanks!
Quoted 26 lines
kernel/bpf/btf.c +15
-2} +/* Allow any printable character in DATASEC names */ static bool btf_name_valid_section (const struct btf *btf,u32 offset) { -return __btf_name_valid (btf, offset); +/* offset must be valid */ +const char *src =btf_str_by_offset (btf, offset); +const char *src_limit; + +/* set a limit on identifier length */ + src_limit = src +KSYM_NAME_LEN ; + src++; +while (*src && src < src_limit) { +if (!isprint (*src)) +return false; + src++; + } + +return !*src; }static const char *__btf_name_by_offset (const struct btf *btf,u32 offset) --2.43.0
Eduard Zingerman
maintainerPATCH v4 15/15 selftests/bpf: test cases for '?' in BTF names
In reply to Eduard ZingermanTwo test cases to verify that '?' and other printable characters are allowed in BTF DATASEC names:
- DATASEC with name "?.foo bar:buz" should be accepted;
- type with name "?foo" should be rejected.
--- tools/testing/selftests/bpf/prog_tests/btf.c | 29 ++++++++++++++++++++ 1 file changed, 29 insertions(+)
tools/testing/selftests/bpf/prog_tests/btf.c +29 -0
@@ -3535,6 +3535,32 @@ static struct btf_raw_test raw_tests[] = { .value_type_id = 1, .max_entries = 1, }, +{ + .descr = "datasec: name '?.foo bar:buz' is ok", + .raw_types = { +/* int */ +BTF_TYPE_INT_ENC (0 ,BTF_INT_SIGNED ,0 ,32 ,4 ),/* [1] */ +/* VAR x */ /* [2] */ +BTF_TYPE_ENC (1 ,BTF_INFO_ENC (BTF_KIND_VAR ,0 ,0 ),1 ), +BTF_VAR_STATIC , +/* DATASEC ?.data */ /* [3] */ +BTF_TYPE_ENC (3 ,BTF_INFO_ENC (BTF_KIND_DATASEC ,0 ,1 ),4 ), +BTF_VAR_SECINFO_ENC (2 ,0 ,4 ), +BTF_END_RAW , + }, + BTF_STR_SEC("\0x\0?.foo bar:buz"), +}, +{ + .descr ="type name '?foo' is not ok" , + .raw_types = { +/* union ?foo; */ +BTF_TYPE_ENC (1 ,BTF_INFO_ENC (BTF_KIND_FWD ,1 ,0 ),0 ),/* [1] */ +BTF_END_RAW , + }, +BTF_STR_SEC ("\0?foo" ), + .err_str = "Invalid name", + .btf_load_err = true, +}, { .descr = "float test #1, well-formed",
@@ -4363,6 +4389,9 @@ static void do_test_raw(unsigned int test_num)if (err || btf_fd <0 ) goto done; +if (!test->map_type) + goto done; + opts.btf_fd = btf_fd; opts.btf_key_type_id = test->key_type_id; opts.btf_value_type_id = test->value_type_id;
Signature · 1 line
2.43.0
This series was applied to bpf/bpf-next.git (master) by Andrii Nakryiko <[email protected]>:
Hello:
This series was applied to bpf/bpf-next.git (master) by Andrii Nakryiko <[email protected]>:
Quoted 12 lines · On Wed, 6 Mar 2024 12:45:14 +0200 you wrote:
Tweak
struct_opsrelated APIs to allow the following features:
- specify version suffixes forstuct_opsmap types;
- share same BPF program between several map definitions with different local BTF types, assuming only maps with same kernel BTF type would be selected for load;
- toggle autocreate flag forstruct_opsmaps;
- automatically toggle autoload forstruct_opsprograms referenced fromstruct_opsmaps, depending on autocreate status of the corresponding map;
- useSEC("?.struct_ops")andSEC("?.struct_ops.link")
to definestruct_opsmaps with autocreate == false after object open.
Quoted 1 line
[...]
Here is the summary with links:
- [bpf-next,v4,01/15] libbpf: allow version suffixes (___smth) for struct_ops types
https://git.kernel.org/bpf/bpf-next/c/88972acee1f9 - [bpf-next,v4,02/15] libbpf: tie struct_ops programs to kernel BTF ids, not to local ids https://git.kernel.org/bpf/bpf-next/c/1fcdcca90bf2 - [bpf-next,v4,03/15] libbpf: honor autocreate flag for struct_ops maps https://git.kernel.org/bpf/bpf-next/c/08c5dde48dd0 - [bpf-next,v4,04/15] selftests/bpf: test struct_ops map definition with type suffix https://git.kernel.org/bpf/bpf-next/c/901a9c536f26 - [bpf-next,v4,05/15] selftests/bpf: utility functions to capture libbpf log in test_progs https://git.kernel.org/bpf/bpf-next/c/4597f853d5bc - [bpf-next,v4,06/15] selftests/bpf: bad_struct_ops test https://git.kernel.org/bpf/bpf-next/c/9c82929ce1bc - [bpf-next,v4,07/15] selftests/bpf: test autocreate behavior for struct_ops maps https://git.kernel.org/bpf/bpf-next/c/08dfc1369554 - [bpf-next,v4,08/15] libbpf: sync progs autoload with maps autocreate for struct_ops maps https://git.kernel.org/bpf/bpf-next/c/607a50ff2802 - [bpf-next,v4,09/15] selftests/bpf: verify struct_ops autoload/autocreate sync https://git.kernel.org/bpf/bpf-next/c/404855a8c3be - [bpf-next,v4,10/15] libbpf: replace elf_state->st_ops_* fields with SEC_ST_OPS sec_type https://git.kernel.org/bpf/bpf-next/c/24d9fe302af5 - [bpf-next,v4,11/15] libbpf: struct_ops in SEC("?.struct_ops") / SEC("?.struct_ops.link") https://git.kernel.org/bpf/bpf-next/c/cea716b7850b - [bpf-next,v4,12/15] libbpf: rewrite btf datasec names starting from '?' https://git.kernel.org/bpf/bpf-next/c/43525abbdb0d - [bpf-next,v4,13/15] selftests/bpf: test case for SEC("?.struct_ops") https://git.kernel.org/bpf/bpf-next/c/df32d591bf63 - [bpf-next,v4,14/15] bpf: allow all printable characters in BTF DATASEC names https://git.kernel.org/bpf/bpf-next/c/503012aad7f3 - [bpf-next,v4,15/15] selftests/bpf: test cases for '?' in BTF names https://git.kernel.org/bpf/bpf-next/c/bc22f9a13840
You are awesome, thank you!
Signature · 2 lines
Deet-doot-dot, I am a bot. https://korg.docs.kernel.org/patchwork/pwbot.html