[PATCH bpf-next 12/16] libbpf: Introduce arena percpu helpers for user space

From: Xu Kuohai

Date: Tue Oct 20 2026 - 10:07:52 EST


From: Xu Kuohai <xukuohai@xxxxxxxxxx>

Introduce two helpers for user space.
- bpf_map__arena_percpu_base(): return the base address of the arena
percpu. The generated skeleton will call it to set up its arena
percpu data.
- bpf_map__arena_percpu_ptr(): translate a variable address in cpu 0's
chunk to the address in the target cpu's chunk.

Signed-off-by: Xu Kuohai <xukuohai@xxxxxxxxxx>
---
tools/lib/bpf/libbpf.c | 49 ++++++++++++++++++++++++++++++++++++++++
tools/lib/bpf/libbpf.h | 26 +++++++++++++++++++++
tools/lib/bpf/libbpf.map | 2 ++
3 files changed, 77 insertions(+)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index be2ddaf96631..0edce476451c 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -12287,6 +12287,55 @@ void *bpf_map__initial_value(const struct bpf_map *map, size_t *psize)
return map->mmaped;
}

+void *bpf_map__arena_percpu_ptr(const struct bpf_map *map, void *ptr, unsigned int cpu)
+{
+ struct bpf_object *obj;
+ uint64_t base;
+
+ if (!map || !ptr)
+ return libbpf_err_ptr(-EINVAL);
+
+ obj = map->obj;
+ if (map->def.type != BPF_MAP_TYPE_ARENA || !obj->arena_percpu.present)
+ return libbpf_err_ptr(-EINVAL);
+
+ if (cpu >= obj->arena_percpu.nr_chunks)
+ return libbpf_err_ptr(-ERANGE);
+
+ /*
+ * obj->arena_percpu.chunk is freed by arena_percpu_populate(), and only
+ * then the map is mmaped
+ */
+ if (obj->arena_percpu.chunk)
+ return libbpf_err_ptr(-EINVAL);
+
+ base = (uint64_t)map->mmaped + obj->arena_percpu.offset;
+ if ((uint64_t)ptr < base || (uint64_t)ptr >= base + obj->arena_percpu.chunk_sz)
+ return libbpf_err_ptr(-EINVAL);
+
+ return (void *)((uint64_t)ptr + (uint64_t)cpu * obj->arena_percpu.chunk_sz);
+}
+
+void *bpf_map__arena_percpu_base(const struct bpf_map *map)
+{
+ struct bpf_object *obj;
+
+ if (!map)
+ return NULL;
+
+ obj = map->obj;
+ /*
+ * The per-cpu chunks exist once the map is created and populated;
+ * obj->arena_percpu.chunk is freed by arena_percpu_populate(), and
+ * only then the map is mmaped
+ */
+ if (map->def.type != BPF_MAP_TYPE_ARENA || !obj->arena_percpu.present ||
+ obj->arena_percpu.chunk)
+ return NULL;
+
+ return map->mmaped + obj->arena_percpu.offset;
+}
+
bool bpf_map__is_internal(const struct bpf_map *map)
{
return map->libbpf_type != LIBBPF_MAP_UNSPEC;
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index 3e0b7bc91bef..31d3982226cc 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -1230,6 +1230,32 @@ LIBBPF_API int bpf_map__set_initial_value(struct bpf_map *map,
*/
LIBBPF_API void *bpf_map__initial_value(const struct bpf_map *map, size_t *psize);

+/**
+ * @brief **bpf_map__arena_percpu_ptr()** translates an address within
+ * cpu 0's chunk of the arena's per-cpu area (e.g. the address of an
+ * `__arena_percpu` variable as seen in the skeleton's arena_percpu
+ * section) to the per-cpu chunk of the requested @cpu.
+ * @param map the ARENA map
+ * @param ptr address of an `__arena_percpu` variable (cpu 0's chunk)
+ * @param cpu cpu id to obtain the chunk for
+ * @return pointer to the variable in the chunk of @cpu; error pointer, if the
+ * map is not a loaded ARENA map with a per-cpu area, @cpu is out of range,
+ * or @ptr is not within cpu 0's chunk
+ */
+LIBBPF_API void *bpf_map__arena_percpu_ptr(const struct bpf_map *map, void *ptr,
+ unsigned int cpu);
+
+/**
+ * @brief **bpf_map__arena_percpu_base()** returns the base of cpu 0's
+ * chunk of the arena's per-cpu area in the user-space-visible mmap of
+ * the arena, i.e. where the skeleton's arena_percpu section points.
+ * @param map the ARENA map
+ * @return pointer to the start of cpu 0's chunk; NULL if @map is not a
+ * loaded ARENA map whose per-cpu chunks have been populated (e.g.
+ * before load, or for a map with `autocreate == false`)
+ */
+LIBBPF_API void *bpf_map__arena_percpu_base(const struct bpf_map *map);
+
/**
* @brief **bpf_map__is_internal()** tells the caller whether or not the
* passed map is a special map created by libbpf automatically for things like
diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
index eeba4072a5da..eda89ab60543 100644
--- a/tools/lib/bpf/libbpf.map
+++ b/tools/lib/bpf/libbpf.map
@@ -458,6 +458,8 @@ LIBBPF_1.7.0 {

LIBBPF_1.8.0 {
global:
+ bpf_map__arena_percpu_base;
+ bpf_map__arena_percpu_ptr;
bpf_map__attach_cgroup_opts;
bpf_prog_stream_open;
bpf_program__add_flags;
--
2.47.3