xref: /linux/tools/testing/selftests/bpf/progs/struct_ops_arena.c (revision 5a8cd539ac19f7a68e68e1d25ef9ca2ff55b8500)
1ba048419STejun Heo // SPDX-License-Identifier: GPL-2.0
2ba048419STejun Heo /* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
3ba048419STejun Heo 
4ba048419STejun Heo #define BPF_NO_KFUNC_PROTOTYPES
5ba048419STejun Heo #include <vmlinux.h>
6ba048419STejun Heo #include <bpf/bpf_helpers.h>
7ba048419STejun Heo #include "bpf_experimental.h"
8ba048419STejun Heo #include <bpf_arena_common.h>
9ba048419STejun Heo #include "../test_kmods/bpf_testmod.h"
10ba048419STejun Heo #include "../test_kmods/bpf_testmod_kfunc.h"
11ba048419STejun Heo 
12ba048419STejun Heo char _license[] SEC("license") = "GPL";
13ba048419STejun Heo 
14ba048419STejun Heo struct {
15ba048419STejun Heo 	__uint(type, BPF_MAP_TYPE_ARENA);
16ba048419STejun Heo 	__uint(map_flags, BPF_F_MMAPABLE);
17ba048419STejun Heo 	/* page 0 hosts the arena globals, page 1 is for allocations */
18ba048419STejun Heo 	__uint(max_entries, 2);
19ba048419STejun Heo } arena SEC(".maps");
20ba048419STejun Heo 
21ba048419STejun Heo /* also associates the callbacks with the arena */
22ba048419STejun Heo u64 __arena arena_touch;
23ba048419STejun Heo /* raw value of the last __arena ctx argument, captured by test_arena_cb */
24ba048419STejun Heo u64 __arena cb_ptr_val;
25ba048419STejun Heo 
26ba048419STejun Heo SEC("struct_ops/test_arena")
test_arena_cb(unsigned long long * ctx)27ba048419STejun Heo int test_arena_cb(unsigned long long *ctx)
28ba048419STejun Heo {
29ba048419STejun Heo 	u64 __arena *ptr = (u64 __arena *)ctx[0];
30ba048419STejun Heo 
31ba048419STejun Heo 	arena_touch++;
32ba048419STejun Heo 	cb_ptr_val = ctx[0];
33ba048419STejun Heo 	*ptr += 1;
34ba048419STejun Heo 	return 0;
35ba048419STejun Heo }
36ba048419STejun Heo 
37ba048419STejun Heo SEC("struct_ops/test_arena_nullable")
test_arena_nullable_cb(unsigned long long * ctx)38ba048419STejun Heo int test_arena_nullable_cb(unsigned long long *ctx)
39ba048419STejun Heo {
40ba048419STejun Heo 	u64 __arena *ptr = (u64 __arena *)ctx[0];
41ba048419STejun Heo 
42ba048419STejun Heo 	arena_touch++;
43ba048419STejun Heo 	if (!ptr)
44ba048419STejun Heo 		return 0xbee;
45ba048419STejun Heo 	*ptr += 1;
46ba048419STejun Heo 	return 0;
47ba048419STejun Heo }
48ba048419STejun Heo 
492d4de9a4STejun Heo SEC("struct_ops/test_arena_stack")
test_arena_stack_cb(unsigned long long * ctx)502d4de9a4STejun Heo int test_arena_stack_cb(unsigned long long *ctx)
512d4de9a4STejun Heo {
522d4de9a4STejun Heo 	u64 __arena *ptr = (u64 __arena *)ctx[8];
532d4de9a4STejun Heo 
542d4de9a4STejun Heo 	arena_touch++;
552d4de9a4STejun Heo 	/* pin the slot layout: the leading args fill ctx[0]..ctx[7] */
562d4de9a4STejun Heo 	if (ctx[0] != 1 || ctx[7] != 8)
572d4de9a4STejun Heo 		return 0xbad;
582d4de9a4STejun Heo 	*ptr += 1;
592d4de9a4STejun Heo 	return 0;
602d4de9a4STejun Heo }
612d4de9a4STejun Heo 
62*197d34b1SPuranjay Mohan SEC("struct_ops/test_arena_multislot")
test_arena_multislot_cb(unsigned long long * ctx)63*197d34b1SPuranjay Mohan int test_arena_multislot_cb(unsigned long long *ctx)
64*197d34b1SPuranjay Mohan {
65*197d34b1SPuranjay Mohan 	u64 __arena *ptr = (u64 __arena *)ctx[2];
66*197d34b1SPuranjay Mohan 
67*197d34b1SPuranjay Mohan 	arena_touch++;
68*197d34b1SPuranjay Mohan 	/*
69*197d34b1SPuranjay Mohan 	 * The 16-byte struct occupies ctx[0] and ctx[1], so @ptr is argument
70*197d34b1SPuranjay Mohan 	 * one but slot two. Getting that wrong hands the callback a scalar.
71*197d34b1SPuranjay Mohan 	 */
72*197d34b1SPuranjay Mohan 	if (ctx[0] != 11 || ctx[1] != 22)
73*197d34b1SPuranjay Mohan 		return 0xbad;
74*197d34b1SPuranjay Mohan 	*ptr += 1;
75*197d34b1SPuranjay Mohan 	return 0;
76*197d34b1SPuranjay Mohan }
77*197d34b1SPuranjay Mohan 
78ba048419STejun Heo SEC(".struct_ops.link")
79ba048419STejun Heo struct bpf_testmod_ops3 testmod_arena = {
80ba048419STejun Heo 	.test_arena = (void *)test_arena_cb,
81ba048419STejun Heo 	.test_arena_nullable = (void *)test_arena_nullable_cb,
822d4de9a4STejun Heo 	.test_arena_stack = (void *)test_arena_stack_cb,
83*197d34b1SPuranjay Mohan 	.test_arena_multislot = (void *)test_arena_multislot_cb,
84ba048419STejun Heo };
85ba048419STejun Heo 
86ba048419STejun Heo SEC("syscall")
trigger(void * ctx)87ba048419STejun Heo int trigger(void *ctx)
88ba048419STejun Heo {
89ba048419STejun Heo #if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
90ba048419STejun Heo 	u64 __arena *val;
91ba048419STejun Heo 	int ret;
92ba048419STejun Heo 
93ba048419STejun Heo 	val = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0);
94ba048419STejun Heo 	if (!val)
95ba048419STejun Heo 		return 1;
96ba048419STejun Heo 
97ba048419STejun Heo 	*val = 41;
98ba048419STejun Heo 	ret = bpf_testmod_ops3_call_test_arena((u64 *)val);
99ba048419STejun Heo 	if (ret)
100ba048419STejun Heo 		return 2;
101ba048419STejun Heo 	if (*val != 42)
102ba048419STejun Heo 		return 3;
103ba048419STejun Heo 
104ba048419STejun Heo 	/*
105ba048419STejun Heo 	 * The callback must have seen exactly (u32)(kaddr - kern_vm_start),
106ba048419STejun Heo 	 * which is the arena offset of val with the upper 32 bits clear.
107ba048419STejun Heo 	 */
108ba048419STejun Heo 	if (cb_ptr_val != (u32)(u64)val)
109ba048419STejun Heo 		return 4;
110ba048419STejun Heo 
111ba048419STejun Heo 	ret = bpf_testmod_ops3_call_test_arena_nullable((u64 *)val);
112ba048419STejun Heo 	if (ret)
113ba048419STejun Heo 		return 5;
114ba048419STejun Heo 	if (*val != 43)
115ba048419STejun Heo 		return 6;
116ba048419STejun Heo 
117ba048419STejun Heo 	/* NULL survives the nullable kfunc and the trampoline as NULL */
118ba048419STejun Heo 	ret = bpf_testmod_ops3_call_test_arena_nullable(NULL);
119ba048419STejun Heo 	if (ret != 0xbee)
120ba048419STejun Heo 		return 7;
121ba048419STejun Heo 
1222d4de9a4STejun Heo 	/* the arena pointer is stack-passed into the trampoline here */
1232d4de9a4STejun Heo 	ret = bpf_testmod_ops3_call_test_arena_stack((u64 *)val);
1242d4de9a4STejun Heo 	if (ret)
1252d4de9a4STejun Heo 		return 8;
1262d4de9a4STejun Heo 	if (*val != 44)
1272d4de9a4STejun Heo 		return 9;
1282d4de9a4STejun Heo 
129*197d34b1SPuranjay Mohan 	/* a multi-slot arg precedes the arena pointer here */
130*197d34b1SPuranjay Mohan 	ret = bpf_testmod_ops3_call_test_arena_multislot((u64 *)val);
131*197d34b1SPuranjay Mohan 	if (ret)
132*197d34b1SPuranjay Mohan 		return 10;
133*197d34b1SPuranjay Mohan 	if (*val != 45)
134*197d34b1SPuranjay Mohan 		return 11;
135*197d34b1SPuranjay Mohan 
136ba048419STejun Heo 	bpf_arena_free_pages(&arena, (void __arena *)val, 1);
137ba048419STejun Heo #endif
138ba048419STejun Heo 	return 0;
139ba048419STejun Heo }
140