From: Xu Kuohai Add the __arena_percpu macro to bpf_arena_common.h and a test object with only a arena percpu variable. Signed-off-by: Xu Kuohai --- .../bpf/libarena/include/bpf_arena_common.h | 2 + .../selftests/bpf/prog_tests/arena_percpu.c | 148 ++++++++++++++++++ .../selftests/bpf/progs/arena_percpu_only.c | 21 +++ 3 files changed, 171 insertions(+) create mode 100644 tools/testing/selftests/bpf/prog_tests/arena_percpu.c create mode 100644 tools/testing/selftests/bpf/progs/arena_percpu_only.c diff --git a/tools/testing/selftests/bpf/libarena/include/bpf_arena_common.h b/tools/testing/selftests/bpf/libarena/include/bpf_arena_common.h index 82aafe879fae..7cdee141764e 100644 --- a/tools/testing/selftests/bpf/libarena/include/bpf_arena_common.h +++ b/tools/testing/selftests/bpf/libarena/include/bpf_arena_common.h @@ -35,11 +35,13 @@ #if defined(__BPF_FEATURE_ADDR_SPACE_CAST) && !defined(BPF_ARENA_FORCE_ASM) #define __arena __attribute__((address_space(1))) __attribute__((btf_type_tag("arena"))) #define __arena_global __attribute__((address_space(1))) +#define __arena_percpu __attribute__((address_space(1))) __attribute__((section(".arena.percpu"))) #define cast_kern(ptr) /* nop for bpf prog. emitted by LLVM */ #define cast_user(ptr) /* nop for bpf prog. emitted by LLVM */ #else #define __arena __attribute__((btf_type_tag("arena"))) #define __arena_global SEC(".addr_space.1") +#define __arena_percpu SEC(".arena.percpu") #define cast_kern(ptr) bpf_addr_space_cast(ptr, 0, 1) #define cast_user(ptr) bpf_addr_space_cast(ptr, 1, 0) #endif diff --git a/tools/testing/selftests/bpf/prog_tests/arena_percpu.c b/tools/testing/selftests/bpf/prog_tests/arena_percpu.c new file mode 100644 index 000000000000..27d4721c7ae9 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/arena_percpu.c @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2026 Huawei Technologies Inc. */ +#include +#include +#include +#include +#include "bpf/libbpf_internal.h" +#include "arena_percpu_only.skel.h" + +static cpu_set_t affinity_save; + +static int pin_to(int cpu) +{ + cpu_set_t cpuset; + + CPU_ZERO(&cpuset); + CPU_SET(cpu, &cpuset); + return sched_setaffinity(0, sizeof(cpuset), &cpuset); +} + +static int run_on_cpu(int cpu, struct bpf_program *prog, const char *name) +{ + LIBBPF_OPTS(bpf_test_run_opts, opts); + int err; + + if (pin_to(cpu)) + return -1; + + err = bpf_prog_test_run_opts(bpf_program__fd(prog), &opts); + + if (!ASSERT_OK(err, name)) + return -1; + if (!ASSERT_OK(opts.retval, name)) + return -1; + return 0; +} + +static int save_affinity(void) +{ + if (!ASSERT_OK(sched_getaffinity(0, sizeof(affinity_save), &affinity_save), + "save_affinity")) + return -1; + return 0; +} + +static void restore_affinity(void) +{ + sched_setaffinity(0, sizeof(affinity_save), &affinity_save); +} + +static int parse_possible_cpus(bool **possible, int *nr_possible) +{ + if (!ASSERT_OK(parse_cpu_mask_file("/sys/devices/system/cpu/possible", + possible, nr_possible), + "parse_possible")) + return -1; + return 0; +} + +/* check one cpu's copy of a per-cpu global */ +static void check_percpu_value(struct bpf_map *arena, void *var, int cpu, + __u64 expected, const char *name) +{ + __u64 *p = bpf_map__arena_percpu_ptr(arena, var, cpu); + + if (!ASSERT_OK_PTR(p, name)) + return; + ASSERT_EQ(*p, expected, name); +} + +static void test_arena_percpu_only(void) +{ + struct arena_percpu_only *skel; + struct bpf_map *arena; + bool *possible = NULL; + int possible_cpus, cpu, picked_cpu = -1, i, err; + + if (save_affinity()) + return; + + skel = arena_percpu_only__open(); + if (!ASSERT_OK_PTR(skel, "open")) + return; + + if (parse_possible_cpus(&possible, &possible_cpus)) + goto out; + + /* size the arena for the per-cpu chunks and load the object */ + if (!ASSERT_OK(bpf_map__set_max_entries(skel->maps.arena, possible_cpus), + "set_max_entries")) + goto out; + if (!ASSERT_OK(arena_percpu_only__load(skel), "load")) + goto out; + arena = skel->maps.arena; + + if (!ASSERT_OK_PTR(skel->arena_percpu, "arena_percpu")) + goto out; + + /* every chunk starts as a copy of the initialized section */ + for (cpu = 0; cpu < possible_cpus; cpu++) { + if (!possible[cpu]) + continue; + check_percpu_value(arena, &skel->arena_percpu->counter, cpu, + 0x123456, "counter_init"); + } + + /* pick one cpu to pin */ + for (i = 0; i < CPU_SETSIZE; i++) { + if (CPU_ISSET(i, &affinity_save)) { + picked_cpu = i; + break; + } + } + if (!ASSERT_GE(picked_cpu, 0, "pick_cpu")) + goto out; + + err = run_on_cpu(picked_cpu, skel->progs.arena_percpu_only_write, "run on cpu"); + if (err < 0) + goto out; + + /* only the cpu the program ran on got written */ + for (cpu = 0; cpu < possible_cpus; cpu++) { + if (!possible[cpu]) + continue; + + if (cpu == picked_cpu) + check_percpu_value(arena, &skel->arena_percpu->counter, + cpu, picked_cpu, "counter_written"); + else + check_percpu_value(arena, &skel->arena_percpu->counter, + cpu, 0x123456, "counter_untouched"); + } + +out: + free(possible); + arena_percpu_only__destroy(skel); + restore_affinity(); +} + +void test_arena_percpu(void) +{ + if (!feat_supported(NULL, FEAT_ARENA_PERCPU_DATA)) { + test__skip(); + return; + } + if (test__start_subtest("percpu_only")) + test_arena_percpu_only(); +} diff --git a/tools/testing/selftests/bpf/progs/arena_percpu_only.c b/tools/testing/selftests/bpf/progs/arena_percpu_only.c new file mode 100644 index 000000000000..35e4ff983e65 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/arena_percpu_only.c @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2026 Huawei Technologies Inc. */ +#include +#include +#include "bpf_arena_common.h" + +struct { + __uint(type, BPF_MAP_TYPE_ARENA); + __uint(map_flags, BPF_F_MMAPABLE); +} arena SEC(".maps"); + +__u64 __arena_percpu counter = 0x123456; + +SEC("syscall") +int arena_percpu_only_write(void *ctx) +{ + counter = bpf_get_smp_processor_id(); + return 0; +} + +char LICENSE[] SEC("license") = "GPL"; -- 2.47.3