Skip to content

Commit c8bc3f6

Browse files
committed
selftests: bpf: test non-sleepable arena allocations
As arena kfuncs can now be called from non-sleepable contexts, test this by adding non-sleepable copies of tests in verifier_arena, this is done by using a socket program instead of syscall. Add a new test case in verifier_arena_large to check that the bpf_arena_alloc_pages() works for more than 1024 pages. 1024 * sizeof(struct page *) is the upper limit of kmalloc_nolock() but bpf_arena_alloc_pages() should still succeed because it re-uses this array in a loop. Augment the arena_list selftest to also run in non-sleepable context by taking rcu_read_lock. Signed-off-by: Puranjay Mohan <[email protected]>
1 parent fb2dfc5 commit c8bc3f6

File tree

4 files changed

+235
-5
lines changed

4 files changed

+235
-5
lines changed

tools/testing/selftests/bpf/prog_tests/arena_list.c

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,23 @@ static int list_sum(struct arena_list_head *head)
2727
return sum;
2828
}
2929

30-
static void test_arena_list_add_del(int cnt)
30+
static void test_arena_list_add_del(int cnt, bool nonsleepable)
3131
{
3232
LIBBPF_OPTS(bpf_test_run_opts, opts);
3333
struct arena_list *skel;
3434
int expected_sum = (u64)cnt * (cnt - 1) / 2;
3535
int ret, sum;
3636

37-
skel = arena_list__open_and_load();
38-
if (!ASSERT_OK_PTR(skel, "arena_list__open_and_load"))
37+
skel = arena_list__open();
38+
if (!ASSERT_OK_PTR(skel, "arena_list__open"))
3939
return;
4040

41+
skel->rodata->nonsleepable = nonsleepable;
42+
43+
ret = arena_list__load(skel);
44+
if (!ASSERT_OK(ret, "arena_list__load"))
45+
goto out;
46+
4147
skel->bss->cnt = cnt;
4248
ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.arena_list_add), &opts);
4349
ASSERT_OK(ret, "ret_add");
@@ -65,7 +71,11 @@ static void test_arena_list_add_del(int cnt)
6571
void test_arena_list(void)
6672
{
6773
if (test__start_subtest("arena_list_1"))
68-
test_arena_list_add_del(1);
74+
test_arena_list_add_del(1, false);
6975
if (test__start_subtest("arena_list_1000"))
70-
test_arena_list_add_del(1000);
76+
test_arena_list_add_del(1000, false);
77+
if (test__start_subtest("arena_list_1_nonsleepable"))
78+
test_arena_list_add_del(1, true);
79+
if (test__start_subtest("arena_list_1000_nonsleepable"))
80+
test_arena_list_add_del(1000, true);
7181
}

tools/testing/selftests/bpf/progs/arena_list.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ struct arena_list_head __arena *list_head;
3030
int list_sum;
3131
int cnt;
3232
bool skip = false;
33+
const volatile bool nonsleepable = false;
3334

3435
#ifdef __BPF_FEATURE_ADDR_SPACE_CAST
3536
long __arena arena_sum;
@@ -42,6 +43,9 @@ int test_val SEC(".addr_space.1");
4243

4344
int zero;
4445

46+
void bpf_rcu_read_lock(void) __ksym;
47+
void bpf_rcu_read_unlock(void) __ksym;
48+
4549
SEC("syscall")
4650
int arena_list_add(void *ctx)
4751
{
@@ -71,6 +75,10 @@ int arena_list_del(void *ctx)
7175
struct elem __arena *n;
7276
int sum = 0;
7377

78+
/* Take rcu_read_lock to test non-sleepable context */
79+
if (nonsleepable)
80+
bpf_rcu_read_lock();
81+
7482
arena_sum = 0;
7583
list_for_each_entry(n, list_head, node) {
7684
sum += n->value;
@@ -79,6 +87,9 @@ int arena_list_del(void *ctx)
7987
bpf_free(n);
8088
}
8189
list_sum = sum;
90+
91+
if (nonsleepable)
92+
bpf_rcu_read_unlock();
8293
#else
8394
skip = true;
8495
#endif

tools/testing/selftests/bpf/progs/verifier_arena.c

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,37 @@ struct {
2121
#endif
2222
} arena SEC(".maps");
2323

24+
SEC("socket")
25+
__success __retval(0)
26+
int basic_alloc1_nosleep(void *ctx)
27+
{
28+
#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
29+
volatile int __arena *page1, *page2, *no_page;
30+
31+
page1 = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0);
32+
if (!page1)
33+
return 1;
34+
*page1 = 1;
35+
page2 = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0);
36+
if (!page2)
37+
return 2;
38+
*page2 = 2;
39+
no_page = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0);
40+
if (no_page)
41+
return 3;
42+
if (*page1 != 1)
43+
return 4;
44+
if (*page2 != 2)
45+
return 5;
46+
bpf_arena_free_pages(&arena, (void __arena *)page2, 1);
47+
if (*page1 != 1)
48+
return 6;
49+
if (*page2 != 0 && *page2 != 2) /* use-after-free should return 0 or the stored value */
50+
return 7;
51+
#endif
52+
return 0;
53+
}
54+
2455
SEC("syscall")
2556
__success __retval(0)
2657
int basic_alloc1(void *ctx)
@@ -60,6 +91,44 @@ int basic_alloc1(void *ctx)
6091
return 0;
6192
}
6293

94+
SEC("socket")
95+
__success __retval(0)
96+
int basic_alloc2_nosleep(void *ctx)
97+
{
98+
#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
99+
volatile char __arena *page1, *page2, *page3, *page4;
100+
101+
page1 = bpf_arena_alloc_pages(&arena, NULL, 2, NUMA_NO_NODE, 0);
102+
if (!page1)
103+
return 1;
104+
page2 = page1 + __PAGE_SIZE;
105+
page3 = page1 + __PAGE_SIZE * 2;
106+
page4 = page1 - __PAGE_SIZE;
107+
*page1 = 1;
108+
*page2 = 2;
109+
*page3 = 3;
110+
*page4 = 4;
111+
if (*page1 != 1)
112+
return 1;
113+
if (*page2 != 2)
114+
return 2;
115+
if (*page3 != 0)
116+
return 3;
117+
if (*page4 != 0)
118+
return 4;
119+
bpf_arena_free_pages(&arena, (void __arena *)page1, 2);
120+
if (*page1 != 0 && *page1 != 1)
121+
return 5;
122+
if (*page2 != 0 && *page2 != 2)
123+
return 6;
124+
if (*page3 != 0)
125+
return 7;
126+
if (*page4 != 0)
127+
return 8;
128+
#endif
129+
return 0;
130+
}
131+
63132
SEC("syscall")
64133
__success __retval(0)
65134
int basic_alloc2(void *ctx)
@@ -102,6 +171,19 @@ struct bpf_arena___l {
102171
struct bpf_map map;
103172
} __attribute__((preserve_access_index));
104173

174+
SEC("socket")
175+
__success __retval(0) __log_level(2)
176+
int basic_alloc3_nosleep(void *ctx)
177+
{
178+
struct bpf_arena___l *ar = (struct bpf_arena___l *)&arena;
179+
volatile char __arena *pages;
180+
181+
pages = bpf_arena_alloc_pages(&ar->map, NULL, ar->map.max_entries, NUMA_NO_NODE, 0);
182+
if (!pages)
183+
return 1;
184+
return 0;
185+
}
186+
105187
SEC("syscall")
106188
__success __retval(0) __log_level(2)
107189
int basic_alloc3(void *ctx)
@@ -115,6 +197,38 @@ int basic_alloc3(void *ctx)
115197
return 0;
116198
}
117199

200+
SEC("socket")
201+
__success __retval(0)
202+
int basic_reserve1_nosleep(void *ctx)
203+
{
204+
#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
205+
char __arena *page;
206+
int ret;
207+
208+
page = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0);
209+
if (!page)
210+
return 1;
211+
212+
page += __PAGE_SIZE;
213+
214+
/* Reserve the second page */
215+
ret = bpf_arena_reserve_pages(&arena, page, 1);
216+
if (ret)
217+
return 2;
218+
219+
/* Try to explicitly allocate the reserved page. */
220+
page = bpf_arena_alloc_pages(&arena, page, 1, NUMA_NO_NODE, 0);
221+
if (page)
222+
return 3;
223+
224+
/* Try to implicitly allocate the page (since there's only 2 of them). */
225+
page = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0);
226+
if (page)
227+
return 4;
228+
#endif
229+
return 0;
230+
}
231+
118232
SEC("syscall")
119233
__success __retval(0)
120234
int basic_reserve1(void *ctx)
@@ -147,6 +261,26 @@ int basic_reserve1(void *ctx)
147261
return 0;
148262
}
149263

264+
SEC("socket")
265+
__success __retval(0)
266+
int basic_reserve2_nosleep(void *ctx)
267+
{
268+
#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
269+
char __arena *page;
270+
int ret;
271+
272+
page = arena_base(&arena);
273+
ret = bpf_arena_reserve_pages(&arena, page, 1);
274+
if (ret)
275+
return 1;
276+
277+
page = bpf_arena_alloc_pages(&arena, page, 1, NUMA_NO_NODE, 0);
278+
if ((u64)page)
279+
return 2;
280+
#endif
281+
return 0;
282+
}
283+
150284
SEC("syscall")
151285
__success __retval(0)
152286
int basic_reserve2(void *ctx)
@@ -168,6 +302,27 @@ int basic_reserve2(void *ctx)
168302
}
169303

170304
/* Reserve the same page twice, should return -EBUSY. */
305+
SEC("socket")
306+
__success __retval(0)
307+
int reserve_twice_nosleep(void *ctx)
308+
{
309+
#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
310+
char __arena *page;
311+
int ret;
312+
313+
page = arena_base(&arena);
314+
315+
ret = bpf_arena_reserve_pages(&arena, page, 1);
316+
if (ret)
317+
return 1;
318+
319+
ret = bpf_arena_reserve_pages(&arena, page, 1);
320+
if (ret != -EBUSY)
321+
return 2;
322+
#endif
323+
return 0;
324+
}
325+
171326
SEC("syscall")
172327
__success __retval(0)
173328
int reserve_twice(void *ctx)
@@ -190,6 +345,36 @@ int reserve_twice(void *ctx)
190345
}
191346

192347
/* Try to reserve past the end of the arena. */
348+
SEC("socket")
349+
__success __retval(0)
350+
int reserve_invalid_region_nosleep(void *ctx)
351+
{
352+
#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
353+
char __arena *page;
354+
int ret;
355+
356+
/* Try a NULL pointer. */
357+
ret = bpf_arena_reserve_pages(&arena, NULL, 3);
358+
if (ret != -EINVAL)
359+
return 1;
360+
361+
page = arena_base(&arena);
362+
363+
ret = bpf_arena_reserve_pages(&arena, page, 3);
364+
if (ret != -EINVAL)
365+
return 2;
366+
367+
ret = bpf_arena_reserve_pages(&arena, page, 4096);
368+
if (ret != -EINVAL)
369+
return 3;
370+
371+
ret = bpf_arena_reserve_pages(&arena, page, (1ULL << 32) - 1);
372+
if (ret != -EINVAL)
373+
return 4;
374+
#endif
375+
return 0;
376+
}
377+
193378
SEC("syscall")
194379
__success __retval(0)
195380
int reserve_invalid_region(void *ctx)

tools/testing/selftests/bpf/progs/verifier_arena_large.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,5 +270,29 @@ int big_alloc2(void *ctx)
270270
return 9;
271271
return 0;
272272
}
273+
274+
SEC("socket")
275+
__success __retval(0)
276+
int big_alloc3(void *ctx)
277+
{
278+
#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
279+
char __arena *pages;
280+
u64 i;
281+
282+
/* Allocate 2051 pages (more than 1024) at once to test the limit of kmalloc_nolock() */
283+
pages = bpf_arena_alloc_pages(&arena, NULL, 2051, NUMA_NO_NODE, 0);
284+
if (!pages)
285+
return -1;
286+
287+
bpf_for(i, 0, 2051)
288+
pages[i * PAGE_SIZE] = 123;
289+
bpf_for(i, 0, 2051)
290+
if (pages[i * PAGE_SIZE] != 123)
291+
return i;
292+
293+
bpf_arena_free_pages(&arena, pages, 1025);
294+
#endif
295+
return 0;
296+
}
273297
#endif
274298
char _license[] SEC("license") = "GPL";

0 commit comments

Comments
 (0)