On Thu, Feb 13, 2025 at 8:20 AM Leon Hwang <leon.hwang@xxxxxxxxx> wrote: > > If the arch, like s390x, does not support percpu insn, this case won't > test global percpu data by checking -EOPNOTSUPP after loading prog. > > The following APIs have been tested for global percpu data: > 1. bpf_map__set_initial_value() > 2. bpf_map__initial_value() > 3. generated percpu struct pointer pointing to internal map's mmaped > 4. bpf_map__lookup_elem() for global percpu data map > 5. bpf_map__is_internal_percpu() > > At the same time, the case is also tested with 'bpftool gen skeleton -L'. > > 125 global_percpu_data_init:OK > 126 global_percpu_data_lskel:OK > Summary: 2/0 PASSED, 0 SKIPPED, 0 FAILED > > Signed-off-by: Leon Hwang <leon.hwang@xxxxxxxxx> > --- > tools/testing/selftests/bpf/Makefile | 2 +- > .../bpf/prog_tests/global_data_init.c | 217 +++++++++++++++++- > .../bpf/progs/test_global_percpu_data.c | 20 ++ > 3 files changed, 237 insertions(+), 2 deletions(-) > create mode 100644 tools/testing/selftests/bpf/progs/test_global_percpu_data.c > > diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile > index 0d552bfcfe7da..7991de79d55c5 100644 > --- a/tools/testing/selftests/bpf/Makefile > +++ b/tools/testing/selftests/bpf/Makefile > @@ -503,7 +503,7 @@ LSKELS := fentry_test.c fexit_test.c fexit_sleep.c atomics.c \ > > # Generate both light skeleton and libbpf skeleton for these > LSKELS_EXTRA := test_ksyms_module.c test_ksyms_weak.c kfunc_call_test.c \ > - kfunc_call_test_subprog.c > + kfunc_call_test_subprog.c test_global_percpu_data.c > SKEL_BLACKLIST += $$(LSKELS) > > test_static_linked.skel.h-deps := test_static_linked1.bpf.o test_static_linked2.bpf.o > diff --git a/tools/testing/selftests/bpf/prog_tests/global_data_init.c b/tools/testing/selftests/bpf/prog_tests/global_data_init.c > index 8466332d7406f..5ace86a0eace7 100644 > --- a/tools/testing/selftests/bpf/prog_tests/global_data_init.c > +++ b/tools/testing/selftests/bpf/prog_tests/global_data_init.c > @@ -1,5 +1,8 @@ > // SPDX-License-Identifier: GPL-2.0 > #include <test_progs.h> > +#include "bpf/libbpf_internal.h" > +#include "test_global_percpu_data.skel.h" > +#include "test_global_percpu_data.lskel.h" > > void test_global_data_init(void) > { > @@ -8,7 +11,7 @@ void test_global_data_init(void) > __u8 *buff = NULL, *newval = NULL; > struct bpf_object *obj; > struct bpf_map *map; > - __u32 duration = 0; > + __u32 duration = 0; > size_t sz; > > obj = bpf_object__open_file(file, NULL); > @@ -60,3 +63,215 @@ void test_global_data_init(void) > free(newval); > bpf_object__close(obj); > } > + > +void test_global_percpu_data_init(void) > +{ > + struct test_global_percpu_data__percpu init_value, *init_data, *data, *percpu_data; > + int key, prog_fd, err, num_cpus, num_online, comm_fd = -1, i; > + struct test_global_percpu_data *skel = NULL; > + __u64 args[2] = {0x1234ULL, 0x5678ULL}; > + size_t elem_sz, init_data_sz; > + char buf[] = "new_name"; > + struct bpf_map *map; > + bool *online; > + LIBBPF_OPTS(bpf_test_run_opts, topts, > + .ctx_in = args, > + .ctx_size_in = sizeof(args), > + .flags = BPF_F_TEST_RUN_ON_CPU, > + ); > + > + num_cpus = libbpf_num_possible_cpus(); > + if (!ASSERT_GT(num_cpus, 0, "libbpf_num_possible_cpus")) > + return; > + > + err = parse_cpu_mask_file("/sys/devices/system/cpu/online", > + &online, &num_online); > + if (!ASSERT_OK(err, "parse_cpu_mask_file")) > + return; > + > + elem_sz = sizeof(*percpu_data); > + percpu_data = calloc(num_cpus, elem_sz); > + if (!ASSERT_OK_PTR(percpu_data, "calloc percpu_data")) > + goto out; > + > + skel = test_global_percpu_data__open(); > + if (!ASSERT_OK_PTR(skel, "test_global_percpu_data__open")) > + goto out; > + if (!ASSERT_OK_PTR(skel->percpu, "skel->percpu")) > + goto out; > + > + ASSERT_EQ(skel->percpu->data, -1, "skel->percpu->data"); > + ASSERT_FALSE(skel->percpu->run, "skel->percpu->run"); > + ASSERT_EQ(skel->percpu->data2, 0, "skel->percpu->data2"); this will only check the value on cpu0, right? Let's check it on all ? > + map = skel->maps.percpu; > + if (!ASSERT_EQ(bpf_map__type(map), BPF_MAP_TYPE_PERCPU_ARRAY, "bpf_map__type")) > + goto out; > + if (!ASSERT_TRUE(bpf_map__is_internal_percpu(map), "bpf_map__is_internal_percpu")) > + goto out; > + > + init_value.data = 2; > + init_value.run = false; > + err = bpf_map__set_initial_value(map, &init_value, sizeof(init_value)); > + if (!ASSERT_OK(err, "bpf_map__set_initial_value")) > + goto out; > + > + init_data = bpf_map__initial_value(map, &init_data_sz); > + if (!ASSERT_OK_PTR(init_data, "bpf_map__initial_value")) > + goto out; > + > + ASSERT_EQ(init_data->data, init_value.data, "initial_value data"); > + ASSERT_EQ(init_data->run, init_value.run, "initial_value run"); > + ASSERT_EQ(init_data_sz, sizeof(init_value), "initial_value size"); > + ASSERT_EQ((void *) init_data, (void *) skel->percpu, "skel->percpu eq init_data"); > + ASSERT_EQ(skel->percpu->data, init_value.data, "skel->percpu->data"); > + ASSERT_EQ(skel->percpu->run, init_value.run, "skel->percpu->run"); > + > + err = test_global_percpu_data__load(skel); > + if (err == -EOPNOTSUPP) { > + test__skip(); > + goto out; > + } > + if (!ASSERT_OK(err, "test_global_percpu_data__load")) > + goto out; > + > + ASSERT_NULL(skel->percpu, "NULL skel->percpu"); > + > + err = test_global_percpu_data__attach(skel); > + if (!ASSERT_OK(err, "test_global_percpu_data__attach")) > + goto out; > + > + comm_fd = open("/proc/self/comm", O_WRONLY|O_TRUNC); > + if (!ASSERT_GE(comm_fd, 0, "open /proc/self/comm")) > + goto out; > + > + err = write(comm_fd, buf, sizeof(buf)); > + if (!ASSERT_GE(err, 0, "task rename")) > + goto out; > + > + prog_fd = bpf_program__fd(skel->progs.update_percpu_data); > + > + /* run on every CPU */ > + for (i = 0; i < num_online; i++) { > + if (!online[i]) > + continue; > + > + topts.cpu = i; > + topts.retval = 0; > + err = bpf_prog_test_run_opts(prog_fd, &topts); > + ASSERT_OK(err, "bpf_prog_test_run_opts"); > + ASSERT_EQ(topts.retval, 0, "bpf_prog_test_run_opts retval"); > + } > + > + key = 0; > + err = bpf_map__lookup_elem(map, &key, sizeof(key), percpu_data, > + elem_sz * num_cpus, 0); > + if (!ASSERT_OK(err, "bpf_map__lookup_elem")) > + goto out; > + > + for (i = 0; i < num_online; i++) { > + if (!online[i]) > + continue; > + > + data = percpu_data + i; > + ASSERT_EQ(data->data, 1, "percpu_data->data"); > + ASSERT_TRUE(data->run, "percpu_data->run"); > + ASSERT_EQ(data->data2, 0xc0de, "percpu_data->data2"); > + } > + > +out: > + close(comm_fd); > + test_global_percpu_data__destroy(skel); > + if (percpu_data) > + free(percpu_data); > + free(online); > +} > + > +void test_global_percpu_data_lskel(void) > +{ > + int key, prog_fd, map_fd, err, num_cpus, num_online, comm_fd = -1, i; > + struct test_global_percpu_data__percpu *data, *percpu_data; > + struct test_global_percpu_data_lskel *lskel = NULL; > + __u64 args[2] = {0x1234ULL, 0x5678ULL}; > + char buf[] = "new_name"; > + bool *online; > + LIBBPF_OPTS(bpf_test_run_opts, topts, > + .ctx_in = args, > + .ctx_size_in = sizeof(args), > + .flags = BPF_F_TEST_RUN_ON_CPU, > + ); > + > + num_cpus = libbpf_num_possible_cpus(); > + if (!ASSERT_GT(num_cpus, 0, "libbpf_num_possible_cpus")) > + return; > + > + err = parse_cpu_mask_file("/sys/devices/system/cpu/online", > + &online, &num_online); > + if (!ASSERT_OK(err, "parse_cpu_mask_file")) > + return; > + > + percpu_data = calloc(num_cpus, sizeof(*percpu_data)); > + if (!ASSERT_OK_PTR(percpu_data, "calloc percpu_data")) > + goto out; > + > + lskel = test_global_percpu_data_lskel__open(); > + if (!ASSERT_OK_PTR(lskel, "test_global_percpu_data_lskel__open")) > + goto out; > + > + err = test_global_percpu_data_lskel__load(lskel); > + if (err == -EOPNOTSUPP) { > + test__skip(); > + goto out; > + } > + if (!ASSERT_OK(err, "test_global_percpu_data_lskel__load")) > + goto out; > + > + err = test_global_percpu_data_lskel__attach(lskel); > + if (!ASSERT_OK(err, "test_global_percpu_data_lskel__attach")) > + goto out; > + > + comm_fd = open("/proc/self/comm", O_WRONLY|O_TRUNC); > + if (!ASSERT_GE(comm_fd, 0, "open /proc/self/comm")) > + goto out; > + > + err = write(comm_fd, buf, sizeof(buf)); > + if (!ASSERT_GE(err, 0, "task rename")) > + goto out; why this odd double run of bpf prog? First via task_rename and then directly? Only use bpf_prog_test_run_opts() and avoiding attaching to a tracepoint? > + > + prog_fd = lskel->progs.update_percpu_data.prog_fd; > + > + /* run on every CPU */ > + for (i = 0; i < num_online; i++) { > + if (!online[i]) > + continue; > + > + topts.cpu = i; > + topts.retval = 0; > + err = bpf_prog_test_run_opts(prog_fd, &topts); > + ASSERT_OK(err, "bpf_prog_test_run_opts"); > + ASSERT_EQ(topts.retval, 0, "bpf_prog_test_run_opts retval"); > + } > + > + key = 0; > + map_fd = lskel->maps.percpu.map_fd; > + err = bpf_map_lookup_elem(map_fd, &key, percpu_data); > + if (!ASSERT_OK(err, "bpf_map_lookup_elem")) > + goto out; > + > + for (i = 0; i < num_online; i++) { > + if (!online[i]) > + continue; > + > + data = percpu_data + i; > + ASSERT_EQ(data->data, 1, "percpu_data->data"); > + ASSERT_TRUE(data->run, "percpu_data->run"); > + ASSERT_EQ(data->data2, 0xc0de, "percpu_data->data2"); > + } > + > +out: > + close(comm_fd); > + test_global_percpu_data_lskel__destroy(lskel); > + if (percpu_data) > + free(percpu_data); > + free(online); > +} > diff --git a/tools/testing/selftests/bpf/progs/test_global_percpu_data.c b/tools/testing/selftests/bpf/progs/test_global_percpu_data.c > new file mode 100644 > index 0000000000000..ada292d3a164c > --- /dev/null > +++ b/tools/testing/selftests/bpf/progs/test_global_percpu_data.c > @@ -0,0 +1,20 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* Copyright Leon Hwang */ Are you sure you can do it in your country? Often enough copyright belongs to the company you work for. > +#include <linux/bpf.h> > +#include <bpf/bpf_helpers.h> > + > +int data SEC(".percpu") = -1; > +int run SEC(".percpu") = 0; > +int data2 SEC(".percpu"); Pls add u8, array of ints and struct { .. } vars for completeness. pw-bot: cr