[PATCH 1/5] bpf: Add an arena flag to retain allocated pages

From: Andrea Righi

Date: Sat Oct 10 2026 - 18:49:46 EST


Add BPF_F_ARENA_NO_FREE to retain arena pages until the map is
destroyed.

Make BPF page-free operations no-ops in both sleepable and non-sleepable
programs when the flag is set. Programs can manage reusable objects
within the retained pages without releasing their backing memory.

This provides a page-retention policy independently of how userspace
accesses the arena. It also supplies stable backing for external
consumers that must not lose pages while they are still using them.

Signed-off-by: Andrea Righi <arighi@xxxxxxxxxx>
---
include/uapi/linux/bpf.h | 3 +++
kernel/bpf/arena.c | 9 ++++++---
tools/include/uapi/linux/bpf.h | 3 +++
3 files changed, 12 insertions(+), 3 deletions(-)

diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index e0ed44b1bbcb7..75101d602bd32 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -1500,6 +1500,9 @@ enum {

/* Enable BPF ringbuf overwrite mode */
BPF_F_RB_OVERWRITE = (1U << 19),
+
+ /* Keep arena pages allocated until the map is destroyed. */
+ BPF_F_ARENA_NO_FREE = (1U << 20),
};

/* Flags for BPF_PROG_QUERY. */
diff --git a/kernel/bpf/arena.c b/kernel/bpf/arena.c
index 0ff707707da04..343e84139ec29 100644
--- a/kernel/bpf/arena.c
+++ b/kernel/bpf/arena.c
@@ -283,7 +283,8 @@ static struct bpf_map *arena_map_alloc(union bpf_attr *attr)
/* BPF_F_MMAPABLE must be set */
!(attr->map_flags & BPF_F_MMAPABLE) ||
/* No unsupported flags present */
- (attr->map_flags & ~(BPF_F_SEGV_ON_FAULT | BPF_F_MMAPABLE | BPF_F_NO_USER_CONV)))
+ (attr->map_flags & ~(BPF_F_SEGV_ON_FAULT | BPF_F_MMAPABLE |
+ BPF_F_NO_USER_CONV | BPF_F_ARENA_NO_FREE)))
return ERR_PTR(-EINVAL);

if (attr->map_extra & ~PAGE_MASK)
@@ -1134,7 +1135,8 @@ __bpf_kfunc void bpf_arena_free_pages(void *p__map, void *ptr__ign, u32 page_cnt
struct bpf_map *map = p__map;
struct bpf_arena *arena = container_of(map, struct bpf_arena, map);

- if (map->map_type != BPF_MAP_TYPE_ARENA || !page_cnt || !ptr__ign)
+ if (map->map_type != BPF_MAP_TYPE_ARENA || !page_cnt || !ptr__ign ||
+ (map->map_flags & BPF_F_ARENA_NO_FREE))
return;
arena_free_pages(arena, (long)ptr__ign, page_cnt, true);
}
@@ -1144,7 +1146,8 @@ void bpf_arena_free_pages_non_sleepable(void *p__map, void *ptr__ign, u32 page_c
struct bpf_map *map = p__map;
struct bpf_arena *arena = container_of(map, struct bpf_arena, map);

- if (map->map_type != BPF_MAP_TYPE_ARENA || !page_cnt || !ptr__ign)
+ if (map->map_type != BPF_MAP_TYPE_ARENA || !page_cnt || !ptr__ign ||
+ (map->map_flags & BPF_F_ARENA_NO_FREE))
return;
arena_free_pages(arena, (long)ptr__ign, page_cnt, false);
}
diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
index e0ed44b1bbcb7..75101d602bd32 100644
--- a/tools/include/uapi/linux/bpf.h
+++ b/tools/include/uapi/linux/bpf.h
@@ -1500,6 +1500,9 @@ enum {

/* Enable BPF ringbuf overwrite mode */
BPF_F_RB_OVERWRITE = (1U << 19),
+
+ /* Keep arena pages allocated until the map is destroyed. */
+ BPF_F_ARENA_NO_FREE = (1U << 20),
};

/* Flags for BPF_PROG_QUERY. */
--
2.56.0