mirror of
https://github.com/torvalds/linux.git
synced 2026-05-30 01:53:29 +02:00
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 <puranjay@kernel.org> Link: https://lore.kernel.org/r/20251222195022.431211-5-puranjay@kernel.org Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
parent
b8467290ed
commit
efecc9e825
|
|
@ -27,17 +27,23 @@ static int list_sum(struct arena_list_head *head)
|
|||
return sum;
|
||||
}
|
||||
|
||||
static void test_arena_list_add_del(int cnt)
|
||||
static void test_arena_list_add_del(int cnt, bool nonsleepable)
|
||||
{
|
||||
LIBBPF_OPTS(bpf_test_run_opts, opts);
|
||||
struct arena_list *skel;
|
||||
int expected_sum = (u64)cnt * (cnt - 1) / 2;
|
||||
int ret, sum;
|
||||
|
||||
skel = arena_list__open_and_load();
|
||||
if (!ASSERT_OK_PTR(skel, "arena_list__open_and_load"))
|
||||
skel = arena_list__open();
|
||||
if (!ASSERT_OK_PTR(skel, "arena_list__open"))
|
||||
return;
|
||||
|
||||
skel->rodata->nonsleepable = nonsleepable;
|
||||
|
||||
ret = arena_list__load(skel);
|
||||
if (!ASSERT_OK(ret, "arena_list__load"))
|
||||
goto out;
|
||||
|
||||
skel->bss->cnt = cnt;
|
||||
ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.arena_list_add), &opts);
|
||||
ASSERT_OK(ret, "ret_add");
|
||||
|
|
@ -65,7 +71,11 @@ static void test_arena_list_add_del(int cnt)
|
|||
void test_arena_list(void)
|
||||
{
|
||||
if (test__start_subtest("arena_list_1"))
|
||||
test_arena_list_add_del(1);
|
||||
test_arena_list_add_del(1, false);
|
||||
if (test__start_subtest("arena_list_1000"))
|
||||
test_arena_list_add_del(1000);
|
||||
test_arena_list_add_del(1000, false);
|
||||
if (test__start_subtest("arena_list_1_nonsleepable"))
|
||||
test_arena_list_add_del(1, true);
|
||||
if (test__start_subtest("arena_list_1000_nonsleepable"))
|
||||
test_arena_list_add_del(1000, true);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ struct arena_list_head __arena *list_head;
|
|||
int list_sum;
|
||||
int cnt;
|
||||
bool skip = false;
|
||||
const volatile bool nonsleepable = false;
|
||||
|
||||
#ifdef __BPF_FEATURE_ADDR_SPACE_CAST
|
||||
long __arena arena_sum;
|
||||
|
|
@ -42,6 +43,9 @@ int test_val SEC(".addr_space.1");
|
|||
|
||||
int zero;
|
||||
|
||||
void bpf_rcu_read_lock(void) __ksym;
|
||||
void bpf_rcu_read_unlock(void) __ksym;
|
||||
|
||||
SEC("syscall")
|
||||
int arena_list_add(void *ctx)
|
||||
{
|
||||
|
|
@ -71,6 +75,10 @@ int arena_list_del(void *ctx)
|
|||
struct elem __arena *n;
|
||||
int sum = 0;
|
||||
|
||||
/* Take rcu_read_lock to test non-sleepable context */
|
||||
if (nonsleepable)
|
||||
bpf_rcu_read_lock();
|
||||
|
||||
arena_sum = 0;
|
||||
list_for_each_entry(n, list_head, node) {
|
||||
sum += n->value;
|
||||
|
|
@ -79,6 +87,9 @@ int arena_list_del(void *ctx)
|
|||
bpf_free(n);
|
||||
}
|
||||
list_sum = sum;
|
||||
|
||||
if (nonsleepable)
|
||||
bpf_rcu_read_unlock();
|
||||
#else
|
||||
skip = true;
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -21,6 +21,37 @@ struct {
|
|||
#endif
|
||||
} arena SEC(".maps");
|
||||
|
||||
SEC("socket")
|
||||
__success __retval(0)
|
||||
int basic_alloc1_nosleep(void *ctx)
|
||||
{
|
||||
#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
|
||||
volatile int __arena *page1, *page2, *no_page;
|
||||
|
||||
page1 = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0);
|
||||
if (!page1)
|
||||
return 1;
|
||||
*page1 = 1;
|
||||
page2 = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0);
|
||||
if (!page2)
|
||||
return 2;
|
||||
*page2 = 2;
|
||||
no_page = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0);
|
||||
if (no_page)
|
||||
return 3;
|
||||
if (*page1 != 1)
|
||||
return 4;
|
||||
if (*page2 != 2)
|
||||
return 5;
|
||||
bpf_arena_free_pages(&arena, (void __arena *)page2, 1);
|
||||
if (*page1 != 1)
|
||||
return 6;
|
||||
if (*page2 != 0 && *page2 != 2) /* use-after-free should return 0 or the stored value */
|
||||
return 7;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("syscall")
|
||||
__success __retval(0)
|
||||
int basic_alloc1(void *ctx)
|
||||
|
|
@ -60,6 +91,44 @@ int basic_alloc1(void *ctx)
|
|||
return 0;
|
||||
}
|
||||
|
||||
SEC("socket")
|
||||
__success __retval(0)
|
||||
int basic_alloc2_nosleep(void *ctx)
|
||||
{
|
||||
#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
|
||||
volatile char __arena *page1, *page2, *page3, *page4;
|
||||
|
||||
page1 = bpf_arena_alloc_pages(&arena, NULL, 2, NUMA_NO_NODE, 0);
|
||||
if (!page1)
|
||||
return 1;
|
||||
page2 = page1 + __PAGE_SIZE;
|
||||
page3 = page1 + __PAGE_SIZE * 2;
|
||||
page4 = page1 - __PAGE_SIZE;
|
||||
*page1 = 1;
|
||||
*page2 = 2;
|
||||
*page3 = 3;
|
||||
*page4 = 4;
|
||||
if (*page1 != 1)
|
||||
return 1;
|
||||
if (*page2 != 2)
|
||||
return 2;
|
||||
if (*page3 != 0)
|
||||
return 3;
|
||||
if (*page4 != 0)
|
||||
return 4;
|
||||
bpf_arena_free_pages(&arena, (void __arena *)page1, 2);
|
||||
if (*page1 != 0 && *page1 != 1)
|
||||
return 5;
|
||||
if (*page2 != 0 && *page2 != 2)
|
||||
return 6;
|
||||
if (*page3 != 0)
|
||||
return 7;
|
||||
if (*page4 != 0)
|
||||
return 8;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("syscall")
|
||||
__success __retval(0)
|
||||
int basic_alloc2(void *ctx)
|
||||
|
|
@ -102,6 +171,19 @@ struct bpf_arena___l {
|
|||
struct bpf_map map;
|
||||
} __attribute__((preserve_access_index));
|
||||
|
||||
SEC("socket")
|
||||
__success __retval(0) __log_level(2)
|
||||
int basic_alloc3_nosleep(void *ctx)
|
||||
{
|
||||
struct bpf_arena___l *ar = (struct bpf_arena___l *)&arena;
|
||||
volatile char __arena *pages;
|
||||
|
||||
pages = bpf_arena_alloc_pages(&ar->map, NULL, ar->map.max_entries, NUMA_NO_NODE, 0);
|
||||
if (!pages)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("syscall")
|
||||
__success __retval(0) __log_level(2)
|
||||
int basic_alloc3(void *ctx)
|
||||
|
|
@ -115,6 +197,38 @@ int basic_alloc3(void *ctx)
|
|||
return 0;
|
||||
}
|
||||
|
||||
SEC("socket")
|
||||
__success __retval(0)
|
||||
int basic_reserve1_nosleep(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_reserve1(void *ctx)
|
||||
|
|
@ -147,6 +261,26 @@ int basic_reserve1(void *ctx)
|
|||
return 0;
|
||||
}
|
||||
|
||||
SEC("socket")
|
||||
__success __retval(0)
|
||||
int basic_reserve2_nosleep(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;
|
||||
}
|
||||
|
||||
SEC("syscall")
|
||||
__success __retval(0)
|
||||
int basic_reserve2(void *ctx)
|
||||
|
|
@ -168,6 +302,27 @@ int basic_reserve2(void *ctx)
|
|||
}
|
||||
|
||||
/* Reserve the same page twice, should return -EBUSY. */
|
||||
SEC("socket")
|
||||
__success __retval(0)
|
||||
int reserve_twice_nosleep(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;
|
||||
}
|
||||
|
||||
SEC("syscall")
|
||||
__success __retval(0)
|
||||
int reserve_twice(void *ctx)
|
||||
|
|
@ -190,6 +345,36 @@ int reserve_twice(void *ctx)
|
|||
}
|
||||
|
||||
/* Try to reserve past the end of the arena. */
|
||||
SEC("socket")
|
||||
__success __retval(0)
|
||||
int reserve_invalid_region_nosleep(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("syscall")
|
||||
__success __retval(0)
|
||||
int reserve_invalid_region(void *ctx)
|
||||
|
|
|
|||
|
|
@ -283,5 +283,34 @@ int big_alloc2(void *ctx)
|
|||
return 9;
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("socket")
|
||||
__success __retval(0)
|
||||
int big_alloc3(void *ctx)
|
||||
{
|
||||
#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
|
||||
char __arena *pages;
|
||||
u64 i;
|
||||
|
||||
/*
|
||||
* Allocate 2051 pages in one go to check how kmalloc_nolock() handles large requests.
|
||||
* Since kmalloc_nolock() can allocate up to 1024 struct page * at a time, this call should
|
||||
* result in three batches: two batches of 1024 pages each, followed by a final batch of 3
|
||||
* pages.
|
||||
*/
|
||||
pages = bpf_arena_alloc_pages(&arena, NULL, 2051, NUMA_NO_NODE, 0);
|
||||
if (!pages)
|
||||
return -1;
|
||||
|
||||
bpf_for(i, 0, 2051)
|
||||
pages[i * PAGE_SIZE] = 123;
|
||||
bpf_for(i, 0, 2051)
|
||||
if (pages[i * PAGE_SIZE] != 123)
|
||||
return i;
|
||||
|
||||
bpf_arena_free_pages(&arena, pages, 2051);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user