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