]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
bpf/arena: add bpf_arena_reserve_pages kfunc
authorEmil Tsalapatis <emil@etsalapatis.com>
Wed, 9 Jul 2025 19:13:11 +0000 (15:13 -0400)
committerAlexei Starovoitov <ast@kernel.org>
Fri, 11 Jul 2025 17:43:54 +0000 (10:43 -0700)
Add a new BPF arena kfunc for reserving a range of arena virtual
addresses without backing them with pages. This prevents the range from
being populated using bpf_arena_alloc_pages().

Acked-by: Yonghong Song <yonghong.song@linux.dev>
Signed-off-by: Emil Tsalapatis <emil@etsalapatis.com>
Acked-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Link: https://lore.kernel.org/r/20250709191312.29840-2-emil@etsalapatis.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
kernel/bpf/arena.c

index 0d56cea7160223c23fba2de60033d6ef94e0805f..5b37753799d20113a91d16b35f35961f73f3c821 100644 (file)
@@ -550,6 +550,34 @@ static void arena_free_pages(struct bpf_arena *arena, long uaddr, long page_cnt)
        }
 }
 
+/*
+ * Reserve an arena virtual address range without populating it. This call stops
+ * bpf_arena_alloc_pages from adding pages to this range.
+ */
+static int arena_reserve_pages(struct bpf_arena *arena, long uaddr, u32 page_cnt)
+{
+       long page_cnt_max = (arena->user_vm_end - arena->user_vm_start) >> PAGE_SHIFT;
+       long pgoff;
+       int ret;
+
+       if (uaddr & ~PAGE_MASK)
+               return 0;
+
+       pgoff = compute_pgoff(arena, uaddr);
+       if (pgoff + page_cnt > page_cnt_max)
+               return -EINVAL;
+
+       guard(mutex)(&arena->lock);
+
+       /* Cannot guard already allocated pages. */
+       ret = is_range_tree_set(&arena->rt, pgoff, page_cnt);
+       if (ret)
+               return -EBUSY;
+
+       /* "Allocate" the region to prevent it from being allocated. */
+       return range_tree_clear(&arena->rt, pgoff, page_cnt);
+}
+
 __bpf_kfunc_start_defs();
 
 __bpf_kfunc void *bpf_arena_alloc_pages(void *p__map, void *addr__ign, u32 page_cnt,
@@ -573,11 +601,26 @@ __bpf_kfunc void bpf_arena_free_pages(void *p__map, void *ptr__ign, u32 page_cnt
                return;
        arena_free_pages(arena, (long)ptr__ign, page_cnt);
 }
+
+__bpf_kfunc int bpf_arena_reserve_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)
+               return -EINVAL;
+
+       if (!page_cnt)
+               return 0;
+
+       return arena_reserve_pages(arena, (long)ptr__ign, page_cnt);
+}
 __bpf_kfunc_end_defs();
 
 BTF_KFUNCS_START(arena_kfuncs)
 BTF_ID_FLAGS(func, bpf_arena_alloc_pages, KF_TRUSTED_ARGS | KF_SLEEPABLE | KF_ARENA_RET | KF_ARENA_ARG2)
 BTF_ID_FLAGS(func, bpf_arena_free_pages, KF_TRUSTED_ARGS | KF_SLEEPABLE | KF_ARENA_ARG2)
+BTF_ID_FLAGS(func, bpf_arena_reserve_pages, KF_TRUSTED_ARGS | KF_SLEEPABLE | KF_ARENA_ARG2)
 BTF_KFUNCS_END(arena_kfuncs)
 
 static const struct btf_kfunc_id_set common_kfunc_set = {