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 --- 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