forked from grate-driver/linux
-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
selftests/bpf: Mix legacy (maps) and modern (vars) BPF in one test
Add selftest that combines two BPF programs within single BPF object file such that one of the programs is using global variables, but can be skipped at runtime on old kernels that don't support global data. Another BPF program is written with the goal to be runnable on very old kernels and only relies on explicitly accessed BPF maps. Such test, run against old kernels (e.g., libbpf CI will run it against 4.9 kernel that doesn't support global data), allows to test the approach and ensure that libbpf doesn't make unnecessary assumption about necessary kernel features. Signed-off-by: Andrii Nakryiko <[email protected]> Signed-off-by: Daniel Borkmann <[email protected]> Acked-by: Song Liu <[email protected]> Link: https://lore.kernel.org/bpf/[email protected]
- Loading branch information
Showing
2 changed files
with
138 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
// SPDX-License-Identifier: GPL-2.0 | ||
/* Copyright (c) 2021 Facebook */ | ||
#include <test_progs.h> | ||
#include "test_legacy_printk.skel.h" | ||
|
||
static int execute_one_variant(bool legacy) | ||
{ | ||
struct test_legacy_printk *skel; | ||
int err, zero = 0, my_pid = getpid(), res, map_fd; | ||
|
||
skel = test_legacy_printk__open(); | ||
if (!ASSERT_OK_PTR(skel, "skel_open")) | ||
return -errno; | ||
|
||
bpf_program__set_autoload(skel->progs.handle_legacy, legacy); | ||
bpf_program__set_autoload(skel->progs.handle_modern, !legacy); | ||
|
||
err = test_legacy_printk__load(skel); | ||
/* no ASSERT_OK, we expect one of two variants can fail here */ | ||
if (err) | ||
goto err_out; | ||
|
||
if (legacy) { | ||
map_fd = bpf_map__fd(skel->maps.my_pid_map); | ||
err = bpf_map_update_elem(map_fd, &zero, &my_pid, BPF_ANY); | ||
if (!ASSERT_OK(err, "my_pid_map_update")) | ||
goto err_out; | ||
err = bpf_map_lookup_elem(map_fd, &zero, &res); | ||
} else { | ||
skel->bss->my_pid_var = my_pid; | ||
} | ||
|
||
err = test_legacy_printk__attach(skel); | ||
if (!ASSERT_OK(err, "skel_attach")) | ||
goto err_out; | ||
|
||
usleep(1); /* trigger */ | ||
|
||
if (legacy) { | ||
map_fd = bpf_map__fd(skel->maps.res_map); | ||
err = bpf_map_lookup_elem(map_fd, &zero, &res); | ||
if (!ASSERT_OK(err, "res_map_lookup")) | ||
goto err_out; | ||
} else { | ||
res = skel->bss->res_var; | ||
} | ||
|
||
if (!ASSERT_GT(res, 0, "res")) { | ||
err = -EINVAL; | ||
goto err_out; | ||
} | ||
|
||
err_out: | ||
test_legacy_printk__destroy(skel); | ||
return err; | ||
} | ||
|
||
void test_legacy_printk(void) | ||
{ | ||
/* legacy variant should work everywhere */ | ||
ASSERT_OK(execute_one_variant(true /* legacy */), "legacy_case"); | ||
|
||
/* execute modern variant, can fail the load on old kernels */ | ||
execute_one_variant(false); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
// SPDX-License-Identifier: GPL-2.0 | ||
/* Copyright (c) 2021 Facebook */ | ||
|
||
#include <linux/bpf.h> | ||
#define BPF_NO_GLOBAL_DATA | ||
#include <bpf/bpf_helpers.h> | ||
|
||
char LICENSE[] SEC("license") = "GPL"; | ||
|
||
struct { | ||
__uint(type, BPF_MAP_TYPE_ARRAY); | ||
__type(key, int); | ||
__type(value, int); | ||
__uint(max_entries, 1); | ||
} my_pid_map SEC(".maps"); | ||
|
||
struct { | ||
__uint(type, BPF_MAP_TYPE_ARRAY); | ||
__type(key, int); | ||
__type(value, int); | ||
__uint(max_entries, 1); | ||
} res_map SEC(".maps"); | ||
|
||
volatile int my_pid_var = 0; | ||
volatile int res_var = 0; | ||
|
||
SEC("tp/raw_syscalls/sys_enter") | ||
int handle_legacy(void *ctx) | ||
{ | ||
int zero = 0, *my_pid, cur_pid, *my_res; | ||
|
||
my_pid = bpf_map_lookup_elem(&my_pid_map, &zero); | ||
if (!my_pid) | ||
return 1; | ||
|
||
cur_pid = bpf_get_current_pid_tgid() >> 32; | ||
if (cur_pid != *my_pid) | ||
return 1; | ||
|
||
my_res = bpf_map_lookup_elem(&res_map, &zero); | ||
if (!my_res) | ||
return 1; | ||
|
||
if (*my_res == 0) | ||
/* use bpf_printk() in combination with BPF_NO_GLOBAL_DATA to | ||
* force .rodata.str1.1 section that previously caused | ||
* problems on old kernels due to libbpf always tried to | ||
* create a global data map for it | ||
*/ | ||
bpf_printk("Legacy-case bpf_printk test, pid %d\n", cur_pid); | ||
*my_res = 1; | ||
|
||
return *my_res; | ||
} | ||
|
||
SEC("tp/raw_syscalls/sys_enter") | ||
int handle_modern(void *ctx) | ||
{ | ||
int zero = 0, cur_pid; | ||
|
||
cur_pid = bpf_get_current_pid_tgid() >> 32; | ||
if (cur_pid != my_pid_var) | ||
return 1; | ||
|
||
if (res_var == 0) | ||
/* we need bpf_printk() to validate libbpf logic around unused | ||
* global maps and legacy kernels; see comment in handle_legacy() | ||
*/ | ||
bpf_printk("Modern-case bpf_printk test, pid %d\n", cur_pid); | ||
res_var = 1; | ||
|
||
return res_var; | ||
} |