[PATCH bpf-next 14/16] selftests/bpf: Add test for arena percpu-only object
From: Xu Kuohai
Date: Tue Oct 20 2026 - 10:02:58 EST
From: Xu Kuohai <xukuohai@xxxxxxxxxx>
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 <xukuohai@xxxxxxxxxx>
---
.../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 <test_progs.h>
+#include <unistd.h>
+#include <sched.h>
+#include <sys/mman.h>
+#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 <vmlinux.h>
+#include <bpf/bpf_helpers.h>
+#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