Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests/bpf: add selftests for bpf_arena_reserve_pages

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>

authored by

Emil Tsalapatis and committed by
Alexei Starovoitov
9f9559f0 8fc3d2d8

+207
+3
tools/testing/selftests/bpf/bpf_arena_common.h
··· 46 46 47 47 void __arena* bpf_arena_alloc_pages(void *map, void __arena *addr, __u32 page_cnt, 48 48 int node_id, __u64 flags) __ksym __weak; 49 + int bpf_arena_reserve_pages(void *map, void __arena *addr, __u32 page_cnt) __ksym __weak; 49 50 void bpf_arena_free_pages(void *map, void __arena *ptr, __u32 page_cnt) __ksym __weak; 51 + 52 + #define arena_base(map) ((void __arena *)((struct bpf_arena *)(map))->user_vm_start) 50 53 51 54 #else /* when compiled as user space code */ 52 55
+106
tools/testing/selftests/bpf/progs/verifier_arena.c
··· 3 3 4 4 #define BPF_NO_KFUNC_PROTOTYPES 5 5 #include <vmlinux.h> 6 + #include <errno.h> 6 7 #include <bpf/bpf_helpers.h> 7 8 #include <bpf/bpf_tracing.h> 8 9 #include "bpf_misc.h" ··· 112 111 pages = bpf_arena_alloc_pages(&ar->map, NULL, ar->map.max_entries, NUMA_NO_NODE, 0); 113 112 if (!pages) 114 113 return 1; 114 + return 0; 115 + } 116 + 117 + SEC("syscall") 118 + __success __retval(0) 119 + int basic_reserve1(void *ctx) 120 + { 121 + #if defined(__BPF_FEATURE_ADDR_SPACE_CAST) 122 + char __arena *page; 123 + int ret; 124 + 125 + page = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0); 126 + if (!page) 127 + return 1; 128 + 129 + page += __PAGE_SIZE; 130 + 131 + /* Reserve the second page */ 132 + ret = bpf_arena_reserve_pages(&arena, page, 1); 133 + if (ret) 134 + return 2; 135 + 136 + /* Try to explicitly allocate the reserved page. */ 137 + page = bpf_arena_alloc_pages(&arena, page, 1, NUMA_NO_NODE, 0); 138 + if (page) 139 + return 3; 140 + 141 + /* Try to implicitly allocate the page (since there's only 2 of them). */ 142 + page = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0); 143 + if (page) 144 + return 4; 145 + #endif 146 + return 0; 147 + } 148 + 149 + SEC("syscall") 150 + __success __retval(0) 151 + int basic_reserve2(void *ctx) 152 + { 153 + #if defined(__BPF_FEATURE_ADDR_SPACE_CAST) 154 + char __arena *page; 155 + int ret; 156 + 157 + page = arena_base(&arena); 158 + ret = bpf_arena_reserve_pages(&arena, page, 1); 159 + if (ret) 160 + return 1; 161 + 162 + page = bpf_arena_alloc_pages(&arena, page, 1, NUMA_NO_NODE, 0); 163 + if ((u64)page) 164 + return 2; 165 + #endif 166 + return 0; 167 + } 168 + 169 + /* Reserve the same page twice, should return -EBUSY. */ 170 + SEC("syscall") 171 + __success __retval(0) 172 + int reserve_twice(void *ctx) 173 + { 174 + #if defined(__BPF_FEATURE_ADDR_SPACE_CAST) 175 + char __arena *page; 176 + int ret; 177 + 178 + page = arena_base(&arena); 179 + 180 + ret = bpf_arena_reserve_pages(&arena, page, 1); 181 + if (ret) 182 + return 1; 183 + 184 + ret = bpf_arena_reserve_pages(&arena, page, 1); 185 + if (ret != -EBUSY) 186 + return 2; 187 + #endif 188 + return 0; 189 + } 190 + 191 + /* Try to reserve past the end of the arena. */ 192 + SEC("syscall") 193 + __success __retval(0) 194 + int reserve_invalid_region(void *ctx) 195 + { 196 + #if defined(__BPF_FEATURE_ADDR_SPACE_CAST) 197 + char __arena *page; 198 + int ret; 199 + 200 + /* Try a NULL pointer. */ 201 + ret = bpf_arena_reserve_pages(&arena, NULL, 3); 202 + if (ret != -EINVAL) 203 + return 1; 204 + 205 + page = arena_base(&arena); 206 + 207 + ret = bpf_arena_reserve_pages(&arena, page, 3); 208 + if (ret != -EINVAL) 209 + return 2; 210 + 211 + ret = bpf_arena_reserve_pages(&arena, page, 4096); 212 + if (ret != -EINVAL) 213 + return 3; 214 + 215 + ret = bpf_arena_reserve_pages(&arena, page, (1ULL << 32) - 1); 216 + if (ret != -EINVAL) 217 + return 4; 218 + #endif 115 219 return 0; 116 220 } 117 221
+98
tools/testing/selftests/bpf/progs/verifier_arena_large.c
··· 67 67 return 0; 68 68 } 69 69 70 + /* Try to access a reserved page. Behavior should be identical with accessing unallocated pages. */ 71 + SEC("syscall") 72 + __success __retval(0) 73 + int access_reserved(void *ctx) 74 + { 75 + #if defined(__BPF_FEATURE_ADDR_SPACE_CAST) 76 + volatile char __arena *page; 77 + char __arena *base; 78 + const size_t len = 4; 79 + int ret, i; 80 + 81 + /* Get a separate region of the arena. */ 82 + page = base = arena_base(&arena) + 16384 * PAGE_SIZE; 83 + 84 + ret = bpf_arena_reserve_pages(&arena, base, len); 85 + if (ret) 86 + return 1; 87 + 88 + /* Try to dirty reserved memory. */ 89 + for (i = 0; i < len && can_loop; i++) 90 + *page = 0x5a; 91 + 92 + for (i = 0; i < len && can_loop; i++) { 93 + page = (volatile char __arena *)(base + i * PAGE_SIZE); 94 + 95 + /* 96 + * Error out in case either the write went through, 97 + * or the address has random garbage. 98 + */ 99 + if (*page == 0x5a) 100 + return 2 + 2 * i; 101 + 102 + if (*page) 103 + return 2 + 2 * i + 1; 104 + } 105 + #endif 106 + return 0; 107 + } 108 + 109 + /* Try to allocate a region overlapping with a reservation. */ 110 + SEC("syscall") 111 + __success __retval(0) 112 + int request_partially_reserved(void *ctx) 113 + { 114 + #if defined(__BPF_FEATURE_ADDR_SPACE_CAST) 115 + volatile char __arena *page; 116 + char __arena *base; 117 + int ret; 118 + 119 + /* Add an arbitrary page offset. */ 120 + page = base = arena_base(&arena) + 4096 * __PAGE_SIZE; 121 + 122 + ret = bpf_arena_reserve_pages(&arena, base + 3 * __PAGE_SIZE, 4); 123 + if (ret) 124 + return 1; 125 + 126 + page = bpf_arena_alloc_pages(&arena, base, 5, NUMA_NO_NODE, 0); 127 + if ((u64)page != 0ULL) 128 + return 2; 129 + #endif 130 + return 0; 131 + } 132 + 133 + SEC("syscall") 134 + __success __retval(0) 135 + int free_reserved(void *ctx) 136 + { 137 + #if defined(__BPF_FEATURE_ADDR_SPACE_CAST) 138 + char __arena *addr; 139 + char __arena *page; 140 + int ret; 141 + 142 + /* Add an arbitrary page offset. */ 143 + addr = arena_base(&arena) + 32768 * __PAGE_SIZE; 144 + 145 + page = bpf_arena_alloc_pages(&arena, addr, 2, NUMA_NO_NODE, 0); 146 + if (!page) 147 + return 1; 148 + 149 + ret = bpf_arena_reserve_pages(&arena, addr + 2 * __PAGE_SIZE, 2); 150 + if (ret) 151 + return 2; 152 + 153 + /* 154 + * Reserved and allocated pages should be interchangeable for 155 + * bpf_arena_free_pages(). Free a reserved and an allocated 156 + * page with a single call. 157 + */ 158 + bpf_arena_free_pages(&arena, addr + __PAGE_SIZE , 2); 159 + 160 + /* The free call above should have succeeded, so this allocation should too. */ 161 + page = bpf_arena_alloc_pages(&arena, addr + __PAGE_SIZE, 2, NUMA_NO_NODE, 0); 162 + if (!page) 163 + return 3; 164 + #endif 165 + return 0; 166 + } 167 + 70 168 #if defined(__BPF_FEATURE_ADDR_SPACE_CAST) 71 169 #define PAGE_CNT 100 72 170 __u8 __arena * __arena page[PAGE_CNT]; /* occupies the first page */