[PATCH bpf-next 15/16] selftests/bpf: Add test for mixed arena percpu object

From: Xu Kuohai

Date: Tue Oct 20 2026 - 10:06:20 EST


From: Xu Kuohai <xukuohai@xxxxxxxxxx>

Add a test object that mixes percpu variables and a regular global in
one arena.

Signed-off-by: Xu Kuohai <xukuohai@xxxxxxxxxx>
---
.../selftests/bpf/prog_tests/arena_percpu.c | 96 +++++++++++++++++++
.../selftests/bpf/progs/arena_percpu_mixed.c | 29 ++++++
2 files changed, 125 insertions(+)
create mode 100644 tools/testing/selftests/bpf/progs/arena_percpu_mixed.c

diff --git a/tools/testing/selftests/bpf/prog_tests/arena_percpu.c b/tools/testing/selftests/bpf/prog_tests/arena_percpu.c
index 27d4721c7ae9..b450be5f2b36 100644
--- a/tools/testing/selftests/bpf/prog_tests/arena_percpu.c
+++ b/tools/testing/selftests/bpf/prog_tests/arena_percpu.c
@@ -6,6 +6,7 @@
#include <sys/mman.h>
#include "bpf/libbpf_internal.h"
#include "arena_percpu_only.skel.h"
+#include "arena_percpu_mixed.skel.h"

static cpu_set_t affinity_save;

@@ -137,6 +138,99 @@ static void test_arena_percpu_only(void)
restore_affinity();
}

+static void test_arena_percpu_mixed(void)
+{
+ struct arena_percpu_mixed *skel;
+ struct bpf_map *arena, *bss_map;
+ bool *possible = NULL, *online = NULL, *written = NULL;
+ int possible_cpus, online_cpus, nr_written = 0, cpu, err;
+
+ if (save_affinity())
+ return;
+
+ skel = arena_percpu_mixed__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 + 16),
+ "set_max_entries"))
+ goto out;
+ if (!ASSERT_OK(arena_percpu_mixed__load(skel), "load"))
+ goto out;
+ arena = skel->maps.arena;
+
+ for (cpu = 0; cpu < possible_cpus; cpu++) {
+ if (!possible[cpu])
+ continue;
+
+ check_percpu_value(arena, &skel->arena_percpu->data1, cpu,
+ 0x123456789, "data1_init");
+ check_percpu_value(arena, &skel->arena_percpu->data2, cpu,
+ 0x987654321, "data2_init");
+ }
+
+ if (!ASSERT_OK(parse_cpu_mask_file("/sys/devices/system/cpu/online",
+ &online, &online_cpus),
+ "parse_online"))
+ goto out;
+
+ written = calloc(online_cpus, sizeof(*written));
+ if (!ASSERT_OK_PTR(written, "written_alloc"))
+ goto out;
+
+ for (cpu = 0; cpu < online_cpus; cpu++) {
+ if (!online[cpu] || cpu >= CPU_SETSIZE || !CPU_ISSET(cpu, &affinity_save))
+ continue;
+
+ err = run_on_cpu(cpu, skel->progs.arena_percpu_mixed_write, "write");
+ if (err < 0)
+ goto out;
+ if (err > 0)
+ continue;
+ written[cpu] = true;
+ nr_written++;
+ }
+ if (!ASSERT_GE(nr_written, 1, "cpus_exercised"))
+ goto out;
+
+ /* check every written chunk from user space */
+ for (cpu = 0; cpu < online_cpus; cpu++) {
+ if (!written[cpu])
+ continue;
+
+ check_percpu_value(arena, &skel->arena_percpu->data1, cpu,
+ cpu, "data1_written");
+ check_percpu_value(arena, &skel->arena_percpu->data2, cpu,
+ cpu << 2, "data2_written");
+ }
+
+ /* the __arena global should not be touched */
+ ASSERT_EQ(skel->arena->arena_global, 0x5a5ba5a5c0dec0de, "arena_global_intact");
+
+ /* api error paths: cpu out of range, pointer outside cpu 0's chunk */
+ ASSERT_ERR_PTR(bpf_map__arena_percpu_ptr(arena, &skel->arena_percpu->data1,
+ possible_cpus), "api_cpu_range");
+ ASSERT_ERR_PTR(bpf_map__arena_percpu_ptr(arena, possible, 0),
+ "api_not_in_chunk");
+
+ /* a non-ARENA map has no per-cpu chunks to index */
+ bss_map = bpf_object__find_map_by_name(skel->obj, ".bss");
+ if (ASSERT_OK_PTR(bss_map, "bss_map"))
+ ASSERT_EQ(libbpf_get_error(bpf_map__arena_percpu_ptr(bss_map, &cpu, 0)),
+ -EINVAL, "api_not_arena");
+
+out:
+ free(written);
+ free(online);
+ free(possible);
+ arena_percpu_mixed__destroy(skel);
+ restore_affinity();
+}
+
void test_arena_percpu(void)
{
if (!feat_supported(NULL, FEAT_ARENA_PERCPU_DATA)) {
@@ -145,4 +239,6 @@ void test_arena_percpu(void)
}
if (test__start_subtest("percpu_only"))
test_arena_percpu_only();
+ if (test__start_subtest("percpu_mixed"))
+ test_arena_percpu_mixed();
}
diff --git a/tools/testing/selftests/bpf/progs/arena_percpu_mixed.c b/tools/testing/selftests/bpf/progs/arena_percpu_mixed.c
new file mode 100644
index 000000000000..9bc199e86031
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/arena_percpu_mixed.c
@@ -0,0 +1,29 @@
+// 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 data1 = 0x123456789;
+__u64 __arena_percpu data2 = 0x987654321;
+
+__u64 __arena_global arena_global = 0x5a5ba5a5c0dec0de;
+
+int bss_var;
+
+SEC("syscall")
+int arena_percpu_mixed_write(void *ctx)
+{
+ u32 cpu = bpf_get_smp_processor_id();
+
+ data1 = cpu;
+ data2 = cpu << 2;
+ return 0;
+}
+
+char LICENSE[] SEC("license") = "GPL";
--
2.47.3