]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
selftests/bpf: add selftests for bpf_arena_reserve_pages
authorEmil Tsalapatis <emil@etsalapatis.com>
Wed, 9 Jul 2025 19:13:12 +0000 (15:13 -0400)
committerAlexei Starovoitov <ast@kernel.org>
Fri, 11 Jul 2025 17:43:54 +0000 (10:43 -0700)
Add selftests for the new bpf_arena_reserve_pages kfunc.

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-3-emil@etsalapatis.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
tools/testing/selftests/bpf/bpf_arena_common.h
tools/testing/selftests/bpf/progs/verifier_arena.c
tools/testing/selftests/bpf/progs/verifier_arena_large.c

index 68a51dcc0669240d78539417faf630485b94a6bb..16f8ce8320047808e420211e6e9b8a97a5e44797 100644 (file)
 
 void __arena* bpf_arena_alloc_pages(void *map, void __arena *addr, __u32 page_cnt,
                                    int node_id, __u64 flags) __ksym __weak;
+int bpf_arena_reserve_pages(void *map, void __arena *addr, __u32 page_cnt) __ksym __weak;
 void bpf_arena_free_pages(void *map, void __arena *ptr, __u32 page_cnt) __ksym __weak;
 
+#define arena_base(map) ((void __arena *)((struct bpf_arena *)(map))->user_vm_start)
+
 #else /* when compiled as user space code */
 
 #define __arena
index 67509c5d3982a1699c9b3f1bc0536425335b2e4d..7f4827eede3c513e766eb581f0a1d942887c5583 100644 (file)
@@ -3,6 +3,7 @@
 
 #define BPF_NO_KFUNC_PROTOTYPES
 #include <vmlinux.h>
+#include <errno.h>
 #include <bpf/bpf_helpers.h>
 #include <bpf/bpf_tracing.h>
 #include "bpf_misc.h"
@@ -114,6 +115,111 @@ int basic_alloc3(void *ctx)
        return 0;
 }
 
+SEC("syscall")
+__success __retval(0)
+int basic_reserve1(void *ctx)
+{
+#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
+       char __arena *page;
+       int ret;
+
+       page = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0);
+       if (!page)
+               return 1;
+
+       page += __PAGE_SIZE;
+
+       /* Reserve the second page */
+       ret = bpf_arena_reserve_pages(&arena, page, 1);
+       if (ret)
+               return 2;
+
+       /* Try to explicitly allocate the reserved page. */
+       page = bpf_arena_alloc_pages(&arena, page, 1, NUMA_NO_NODE, 0);
+       if (page)
+               return 3;
+
+       /* Try to implicitly allocate the page (since there's only 2 of them). */
+       page = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0);
+       if (page)
+               return 4;
+#endif
+       return 0;
+}
+
+SEC("syscall")
+__success __retval(0)
+int basic_reserve2(void *ctx)
+{
+#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
+       char __arena *page;
+       int ret;
+
+       page = arena_base(&arena);
+       ret = bpf_arena_reserve_pages(&arena, page, 1);
+       if (ret)
+               return 1;
+
+       page = bpf_arena_alloc_pages(&arena, page, 1, NUMA_NO_NODE, 0);
+       if ((u64)page)
+               return 2;
+#endif
+       return 0;
+}
+
+/* Reserve the same page twice, should return -EBUSY. */
+SEC("syscall")
+__success __retval(0)
+int reserve_twice(void *ctx)
+{
+#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
+       char __arena *page;
+       int ret;
+
+       page = arena_base(&arena);
+
+       ret = bpf_arena_reserve_pages(&arena, page, 1);
+       if (ret)
+               return 1;
+
+       ret = bpf_arena_reserve_pages(&arena, page, 1);
+       if (ret != -EBUSY)
+               return 2;
+#endif
+       return 0;
+}
+
+/* Try to reserve past the end of the arena. */
+SEC("syscall")
+__success __retval(0)
+int reserve_invalid_region(void *ctx)
+{
+#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
+       char __arena *page;
+       int ret;
+
+       /* Try a NULL pointer. */
+       ret = bpf_arena_reserve_pages(&arena, NULL, 3);
+       if (ret != -EINVAL)
+               return 1;
+
+       page = arena_base(&arena);
+
+       ret = bpf_arena_reserve_pages(&arena, page, 3);
+       if (ret != -EINVAL)
+               return 2;
+
+       ret = bpf_arena_reserve_pages(&arena, page, 4096);
+       if (ret != -EINVAL)
+               return 3;
+
+       ret = bpf_arena_reserve_pages(&arena, page, (1ULL << 32) - 1);
+       if (ret != -EINVAL)
+               return 4;
+#endif
+       return 0;
+}
+
 SEC("iter.s/bpf_map")
 __success __log_level(2)
 int iter_maps1(struct bpf_iter__bpf_map *ctx)
index f94f30cf1bb80f64552318e074fe23cb04e679a0..9dbdf123542d3bc220372e8b404676dc9a51ebf4 100644 (file)
@@ -67,6 +67,104 @@ int big_alloc1(void *ctx)
        return 0;
 }
 
+/* Try to access a reserved page. Behavior should be identical with accessing unallocated pages. */
+SEC("syscall")
+__success __retval(0)
+int access_reserved(void *ctx)
+{
+#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
+       volatile char __arena *page;
+       char __arena *base;
+       const size_t len = 4;
+       int ret, i;
+
+       /* Get a separate region of the arena. */
+       page = base = arena_base(&arena) + 16384 * PAGE_SIZE;
+
+       ret = bpf_arena_reserve_pages(&arena, base, len);
+       if (ret)
+               return 1;
+
+       /* Try to dirty reserved memory. */
+       for (i = 0; i < len && can_loop; i++)
+               *page = 0x5a;
+
+       for (i = 0; i < len && can_loop; i++) {
+               page = (volatile char __arena *)(base + i * PAGE_SIZE);
+
+               /*
+                * Error out in case either the write went through,
+                * or the address has random garbage.
+                */
+               if (*page == 0x5a)
+                       return 2 + 2 * i;
+
+               if (*page)
+                       return 2 + 2 * i + 1;
+       }
+#endif
+       return 0;
+}
+
+/* Try to allocate a region overlapping with a reservation. */
+SEC("syscall")
+__success __retval(0)
+int request_partially_reserved(void *ctx)
+{
+#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
+       volatile char __arena *page;
+       char __arena *base;
+       int ret;
+
+       /* Add an arbitrary page offset. */
+       page = base = arena_base(&arena) + 4096 * __PAGE_SIZE;
+
+       ret = bpf_arena_reserve_pages(&arena, base + 3 * __PAGE_SIZE, 4);
+       if (ret)
+               return 1;
+
+       page = bpf_arena_alloc_pages(&arena, base, 5, NUMA_NO_NODE, 0);
+       if ((u64)page != 0ULL)
+               return 2;
+#endif
+       return 0;
+}
+
+SEC("syscall")
+__success __retval(0)
+int free_reserved(void *ctx)
+{
+#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
+       char __arena *addr;
+       char __arena *page;
+       int ret;
+
+       /* Add an arbitrary page offset. */
+       addr = arena_base(&arena) + 32768 * __PAGE_SIZE;
+
+       page = bpf_arena_alloc_pages(&arena, addr, 2, NUMA_NO_NODE, 0);
+       if (!page)
+               return 1;
+
+       ret = bpf_arena_reserve_pages(&arena, addr + 2 * __PAGE_SIZE, 2);
+       if (ret)
+               return 2;
+
+       /*
+        * Reserved and allocated pages should be interchangeable for
+        * bpf_arena_free_pages(). Free a reserved and an allocated
+        * page with a single call.
+        */
+       bpf_arena_free_pages(&arena, addr + __PAGE_SIZE , 2);
+
+       /* The free call above should have succeeded, so this allocation should too. */
+       page = bpf_arena_alloc_pages(&arena, addr + __PAGE_SIZE, 2, NUMA_NO_NODE, 0);
+       if (!page)
+               return 3;
+#endif
+       return 0;
+}
+
 #if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
 #define PAGE_CNT 100
 __u8 __arena * __arena page[PAGE_CNT]; /* occupies the first page */