linux-stable/tools/testing/selftests/bpf/prog_tests/skeleton.c

143 lines
4.8 KiB
C
Raw Normal View History

// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2019 Facebook */
#include <test_progs.h>
libbpf: add non-mmapable data section selftest Add non-mmapable data section to test_skeleton selftest and make sure it really isn't mmapable by trying to mmap() it anyways. Also make sure that libbpf doesn't report BPF_F_MMAPABLE flag to users. Additional, some more manual testing was performed that this feature works as intended. Looking at created map through bpftool shows that flags passed to kernel are indeed zero: $ bpftool map show ... 1782: array name .data.non_mmapa flags 0x0 key 4B value 16B max_entries 1 memlock 4096B btf_id 1169 pids test_progs(8311) ... Checking BTF uploaded to kernel for this map shows that zero_key and zero_value are indeed marked as static, even though zero_key is actually original global (but STV_HIDDEN) variable: $ bpftool btf dump id 1169 ... [51] VAR 'zero_key' type_id=2, linkage=static [52] VAR 'zero_value' type_id=7, linkage=static ... [62] DATASEC '.data.non_mmapable' size=16 vlen=2 type_id=51 offset=0 size=4 (VAR 'zero_key') type_id=52 offset=4 size=12 (VAR 'zero_value') ... And original BTF does have zero_key marked as linkage=global: $ bpftool btf dump file test_skeleton.bpf.linked3.o ... [51] VAR 'zero_key' type_id=2, linkage=global [52] VAR 'zero_value' type_id=7, linkage=static ... [62] DATASEC '.data.non_mmapable' size=16 vlen=2 type_id=51 offset=0 size=4 (VAR 'zero_key') type_id=52 offset=4 size=12 (VAR 'zero_value') Bpftool didn't require any changes at all because it checks whether internal map is mmapable already, but just to double-check generated skeleton, we see that .data.non_mmapable neither sets mmaped pointer nor has a corresponding field in the skeleton: $ grep non_mmapable test_skeleton.skel.h struct bpf_map *data_non_mmapable; s->maps[7].name = ".data.non_mmapable"; s->maps[7].map = &obj->maps.data_non_mmapable; But .data.read_mostly has all of those things: $ grep read_mostly test_skeleton.skel.h struct bpf_map *data_read_mostly; struct test_skeleton__data_read_mostly { int read_mostly_var; } *data_read_mostly; s->maps[6].name = ".data.read_mostly"; s->maps[6].map = &obj->maps.data_read_mostly; s->maps[6].mmaped = (void **)&obj->data_read_mostly; _Static_assert(sizeof(s->data_read_mostly->read_mostly_var) == 4, "unexpected size of 'read_mostly_var'"); Acked-by: Stanislav Fomichev <sdf@google.com> Acked-by: Dave Marchevsky <davemarchevsky@fb.com> Signed-off-by: Andrii Nakryiko <andrii@kernel.org> Link: https://lore.kernel.org/r/20221019002816.359650-4-andrii@kernel.org Signed-off-by: Alexei Starovoitov <ast@kernel.org>
2022-10-19 00:28:16 +00:00
#include <sys/mman.h>
struct s {
int a;
long long b;
} __attribute__((packed));
#include "test_skeleton.skel.h"
void test_skeleton(void)
{
int duration = 0, err;
struct test_skeleton* skel;
struct test_skeleton__bss *bss;
struct test_skeleton__data *data;
struct test_skeleton__data_dyn *data_dyn;
struct test_skeleton__rodata *rodata;
struct test_skeleton__rodata_dyn *rodata_dyn;
struct test_skeleton__kconfig *kcfg;
const void *elf_bytes;
size_t elf_bytes_sz = 0;
libbpf: add non-mmapable data section selftest Add non-mmapable data section to test_skeleton selftest and make sure it really isn't mmapable by trying to mmap() it anyways. Also make sure that libbpf doesn't report BPF_F_MMAPABLE flag to users. Additional, some more manual testing was performed that this feature works as intended. Looking at created map through bpftool shows that flags passed to kernel are indeed zero: $ bpftool map show ... 1782: array name .data.non_mmapa flags 0x0 key 4B value 16B max_entries 1 memlock 4096B btf_id 1169 pids test_progs(8311) ... Checking BTF uploaded to kernel for this map shows that zero_key and zero_value are indeed marked as static, even though zero_key is actually original global (but STV_HIDDEN) variable: $ bpftool btf dump id 1169 ... [51] VAR 'zero_key' type_id=2, linkage=static [52] VAR 'zero_value' type_id=7, linkage=static ... [62] DATASEC '.data.non_mmapable' size=16 vlen=2 type_id=51 offset=0 size=4 (VAR 'zero_key') type_id=52 offset=4 size=12 (VAR 'zero_value') ... And original BTF does have zero_key marked as linkage=global: $ bpftool btf dump file test_skeleton.bpf.linked3.o ... [51] VAR 'zero_key' type_id=2, linkage=global [52] VAR 'zero_value' type_id=7, linkage=static ... [62] DATASEC '.data.non_mmapable' size=16 vlen=2 type_id=51 offset=0 size=4 (VAR 'zero_key') type_id=52 offset=4 size=12 (VAR 'zero_value') Bpftool didn't require any changes at all because it checks whether internal map is mmapable already, but just to double-check generated skeleton, we see that .data.non_mmapable neither sets mmaped pointer nor has a corresponding field in the skeleton: $ grep non_mmapable test_skeleton.skel.h struct bpf_map *data_non_mmapable; s->maps[7].name = ".data.non_mmapable"; s->maps[7].map = &obj->maps.data_non_mmapable; But .data.read_mostly has all of those things: $ grep read_mostly test_skeleton.skel.h struct bpf_map *data_read_mostly; struct test_skeleton__data_read_mostly { int read_mostly_var; } *data_read_mostly; s->maps[6].name = ".data.read_mostly"; s->maps[6].map = &obj->maps.data_read_mostly; s->maps[6].mmaped = (void **)&obj->data_read_mostly; _Static_assert(sizeof(s->data_read_mostly->read_mostly_var) == 4, "unexpected size of 'read_mostly_var'"); Acked-by: Stanislav Fomichev <sdf@google.com> Acked-by: Dave Marchevsky <davemarchevsky@fb.com> Signed-off-by: Andrii Nakryiko <andrii@kernel.org> Link: https://lore.kernel.org/r/20221019002816.359650-4-andrii@kernel.org Signed-off-by: Alexei Starovoitov <ast@kernel.org>
2022-10-19 00:28:16 +00:00
void *m;
int i, fd;
skel = test_skeleton__open();
if (CHECK(!skel, "skel_open", "failed to open skeleton\n"))
return;
if (CHECK(skel->kconfig, "skel_kconfig", "kconfig is mmaped()!\n"))
goto cleanup;
bss = skel->bss;
data = skel->data;
data_dyn = skel->data_dyn;
rodata = skel->rodata;
rodata_dyn = skel->rodata_dyn;
ASSERT_STREQ(bpf_map__name(skel->maps.rodata_dyn), ".rodata.dyn", "rodata_dyn_name");
ASSERT_STREQ(bpf_map__name(skel->maps.data_dyn), ".data.dyn", "data_dyn_name");
/* validate values are pre-initialized correctly */
CHECK(data->in1 != -1, "in1", "got %d != exp %d\n", data->in1, -1);
CHECK(data->out1 != -1, "out1", "got %d != exp %d\n", data->out1, -1);
CHECK(data->in2 != -1, "in2", "got %lld != exp %lld\n", data->in2, -1LL);
CHECK(data->out2 != -1, "out2", "got %lld != exp %lld\n", data->out2, -1LL);
CHECK(bss->in3 != 0, "in3", "got %d != exp %d\n", bss->in3, 0);
CHECK(bss->out3 != 0, "out3", "got %d != exp %d\n", bss->out3, 0);
CHECK(bss->in4 != 0, "in4", "got %lld != exp %lld\n", bss->in4, 0LL);
CHECK(bss->out4 != 0, "out4", "got %lld != exp %lld\n", bss->out4, 0LL);
CHECK(rodata->in.in6 != 0, "in6", "got %d != exp %d\n", rodata->in.in6, 0);
CHECK(bss->out6 != 0, "out6", "got %d != exp %d\n", bss->out6, 0);
ASSERT_EQ(rodata_dyn->in_dynarr_sz, 0, "in_dynarr_sz");
for (i = 0; i < 4; i++)
ASSERT_EQ(rodata_dyn->in_dynarr[i], -(i + 1), "in_dynarr");
for (i = 0; i < 4; i++)
ASSERT_EQ(data_dyn->out_dynarr[i], i + 1, "out_dynarr");
/* validate we can pre-setup global variables, even in .bss */
data->in1 = 10;
data->in2 = 11;
bss->in3 = 12;
bss->in4 = 13;
rodata->in.in6 = 14;
rodata_dyn->in_dynarr_sz = 4;
for (i = 0; i < 4; i++)
rodata_dyn->in_dynarr[i] = i + 10;
err = test_skeleton__load(skel);
if (CHECK(err, "skel_load", "failed to load skeleton: %d\n", err))
goto cleanup;
/* validate pre-setup values are still there */
CHECK(data->in1 != 10, "in1", "got %d != exp %d\n", data->in1, 10);
CHECK(data->in2 != 11, "in2", "got %lld != exp %lld\n", data->in2, 11LL);
CHECK(bss->in3 != 12, "in3", "got %d != exp %d\n", bss->in3, 12);
CHECK(bss->in4 != 13, "in4", "got %lld != exp %lld\n", bss->in4, 13LL);
CHECK(rodata->in.in6 != 14, "in6", "got %d != exp %d\n", rodata->in.in6, 14);
ASSERT_EQ(rodata_dyn->in_dynarr_sz, 4, "in_dynarr_sz");
for (i = 0; i < 4; i++)
ASSERT_EQ(rodata_dyn->in_dynarr[i], i + 10, "in_dynarr");
/* now set new values and attach to get them into outX variables */
data->in1 = 1;
data->in2 = 2;
bss->in3 = 3;
bss->in4 = 4;
bss->in5.a = 5;
bss->in5.b = 6;
kcfg = skel->kconfig;
skel->data_read_mostly->read_mostly_var = 123;
err = test_skeleton__attach(skel);
if (CHECK(err, "skel_attach", "skeleton attach failed: %d\n", err))
goto cleanup;
/* trigger tracepoint */
usleep(1);
CHECK(data->out1 != 1, "res1", "got %d != exp %d\n", data->out1, 1);
CHECK(data->out2 != 2, "res2", "got %lld != exp %d\n", data->out2, 2);
CHECK(bss->out3 != 3, "res3", "got %d != exp %d\n", (int)bss->out3, 3);
CHECK(bss->out4 != 4, "res4", "got %lld != exp %d\n", bss->out4, 4);
CHECK(bss->out5.a != 5, "res5", "got %d != exp %d\n", bss->out5.a, 5);
CHECK(bss->out5.b != 6, "res6", "got %lld != exp %d\n", bss->out5.b, 6);
CHECK(bss->out6 != 14, "res7", "got %d != exp %d\n", bss->out6, 14);
CHECK(bss->bpf_syscall != kcfg->CONFIG_BPF_SYSCALL, "ext1",
"got %d != exp %d\n", bss->bpf_syscall, kcfg->CONFIG_BPF_SYSCALL);
CHECK(bss->kern_ver != kcfg->LINUX_KERNEL_VERSION, "ext2",
"got %d != exp %d\n", bss->kern_ver, kcfg->LINUX_KERNEL_VERSION);
for (i = 0; i < 4; i++)
ASSERT_EQ(data_dyn->out_dynarr[i], i + 10, "out_dynarr");
ASSERT_EQ(skel->bss->out_mostly_var, 123, "out_mostly_var");
ASSERT_EQ(bss->huge_arr[ARRAY_SIZE(bss->huge_arr) - 1], 123, "huge_arr");
libbpf: add non-mmapable data section selftest Add non-mmapable data section to test_skeleton selftest and make sure it really isn't mmapable by trying to mmap() it anyways. Also make sure that libbpf doesn't report BPF_F_MMAPABLE flag to users. Additional, some more manual testing was performed that this feature works as intended. Looking at created map through bpftool shows that flags passed to kernel are indeed zero: $ bpftool map show ... 1782: array name .data.non_mmapa flags 0x0 key 4B value 16B max_entries 1 memlock 4096B btf_id 1169 pids test_progs(8311) ... Checking BTF uploaded to kernel for this map shows that zero_key and zero_value are indeed marked as static, even though zero_key is actually original global (but STV_HIDDEN) variable: $ bpftool btf dump id 1169 ... [51] VAR 'zero_key' type_id=2, linkage=static [52] VAR 'zero_value' type_id=7, linkage=static ... [62] DATASEC '.data.non_mmapable' size=16 vlen=2 type_id=51 offset=0 size=4 (VAR 'zero_key') type_id=52 offset=4 size=12 (VAR 'zero_value') ... And original BTF does have zero_key marked as linkage=global: $ bpftool btf dump file test_skeleton.bpf.linked3.o ... [51] VAR 'zero_key' type_id=2, linkage=global [52] VAR 'zero_value' type_id=7, linkage=static ... [62] DATASEC '.data.non_mmapable' size=16 vlen=2 type_id=51 offset=0 size=4 (VAR 'zero_key') type_id=52 offset=4 size=12 (VAR 'zero_value') Bpftool didn't require any changes at all because it checks whether internal map is mmapable already, but just to double-check generated skeleton, we see that .data.non_mmapable neither sets mmaped pointer nor has a corresponding field in the skeleton: $ grep non_mmapable test_skeleton.skel.h struct bpf_map *data_non_mmapable; s->maps[7].name = ".data.non_mmapable"; s->maps[7].map = &obj->maps.data_non_mmapable; But .data.read_mostly has all of those things: $ grep read_mostly test_skeleton.skel.h struct bpf_map *data_read_mostly; struct test_skeleton__data_read_mostly { int read_mostly_var; } *data_read_mostly; s->maps[6].name = ".data.read_mostly"; s->maps[6].map = &obj->maps.data_read_mostly; s->maps[6].mmaped = (void **)&obj->data_read_mostly; _Static_assert(sizeof(s->data_read_mostly->read_mostly_var) == 4, "unexpected size of 'read_mostly_var'"); Acked-by: Stanislav Fomichev <sdf@google.com> Acked-by: Dave Marchevsky <davemarchevsky@fb.com> Signed-off-by: Andrii Nakryiko <andrii@kernel.org> Link: https://lore.kernel.org/r/20221019002816.359650-4-andrii@kernel.org Signed-off-by: Alexei Starovoitov <ast@kernel.org>
2022-10-19 00:28:16 +00:00
fd = bpf_map__fd(skel->maps.data_non_mmapable);
m = mmap(NULL, getpagesize(), PROT_READ, MAP_SHARED, fd, 0);
if (!ASSERT_EQ(m, MAP_FAILED, "unexpected_mmap_success"))
munmap(m, getpagesize());
ASSERT_EQ(bpf_map__map_flags(skel->maps.data_non_mmapable), 0, "non_mmap_flags");
elf_bytes = test_skeleton__elf_bytes(&elf_bytes_sz);
ASSERT_OK_PTR(elf_bytes, "elf_bytes");
ASSERT_GE(elf_bytes_sz, 0, "elf_bytes_sz");
cleanup:
test_skeleton__destroy(skel);
}