xref: /linux/tools/testing/selftests/bpf/progs/rhash.c (revision 14c5eb685cdefbd32e73d2723071ecbd8effbce9)
1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
3 
4 #include <vmlinux.h>
5 #include <stdbool.h>
6 #include <bpf/bpf_helpers.h>
7 #include <bpf/bpf_tracing.h>
8 #include "bpf_misc.h"
9 
10 #define ENOENT 2
11 #define EEXIST 17
12 
13 char _license[] SEC("license") = "GPL";
14 
15 int err;
16 
17 struct elem {
18 	char arr[128];
19 	int val;
20 };
21 
22 struct special_elem {
23 	struct task_struct __kptr *task;
24 	int val;
25 };
26 
27 struct {
28 	__uint(type, BPF_MAP_TYPE_RHASH);
29 	__uint(map_flags, BPF_F_NO_PREALLOC);
30 	__uint(max_entries, 128);
31 	__type(key, int);
32 	__type(value, struct elem);
33 } rhmap SEC(".maps");
34 
35 struct {
36 	__uint(type, BPF_MAP_TYPE_RHASH);
37 	__uint(map_flags, BPF_F_NO_PREALLOC);
38 	__uint(max_entries, 1);
39 	__type(key, int);
40 	__type(value, struct special_elem);
41 } special_fields SEC(".maps");
42 
43 extern struct task_struct *bpf_task_acquire(struct task_struct *p) __ksym;
44 extern void bpf_task_release(struct task_struct *p) __ksym;
45 
46 SEC("syscall")
47 int test_rhash_lookup_update(void *ctx)
48 {
49 	int key = 5;
50 	struct elem empty = {.val = 3, .arr = {0}};
51 	struct elem *e;
52 
53 	err = 1;
54 	e = bpf_map_lookup_elem(&rhmap, &key);
55 	if (e)
56 		return 1;
57 
58 	err = bpf_map_update_elem(&rhmap, &key, &empty, BPF_NOEXIST);
59 	if (err)
60 		return 1;
61 
62 	e = bpf_map_lookup_elem(&rhmap, &key);
63 	if (!e || e->val != empty.val) {
64 		err = 2;
65 		return 2;
66 	}
67 
68 	err = 0;
69 	return 0;
70 }
71 
72 SEC("syscall")
73 int test_rhash_update_delete(void *ctx)
74 {
75 	int key = 6;
76 	struct elem empty = {.val = 4, .arr = {0}};
77 	struct elem *e;
78 
79 	err = 1;
80 	e = bpf_map_lookup_elem(&rhmap, &key);
81 	if (e)
82 		return 1;
83 
84 	err = bpf_map_update_elem(&rhmap, &key, &empty, BPF_NOEXIST);
85 	if (err)
86 		return 2;
87 
88 	err = bpf_map_delete_elem(&rhmap, &key);
89 	if (err)
90 		return 3;
91 
92 	e = bpf_map_lookup_elem(&rhmap, &key);
93 	if (e) {
94 		err = 4;
95 		return 4;
96 	}
97 
98 	err = 0;
99 	return 0;
100 }
101 
102 SEC("syscall")
103 int test_rhash_update_elements(void *ctx)
104 {
105 	int key = 0;
106 	struct elem empty = {.val = 4, .arr = {0}};
107 	struct elem *e;
108 	int i;
109 
110 	err = 1;
111 
112 	for (i = 0; i < 128; ++i) {
113 		key = i;
114 		e = bpf_map_lookup_elem(&rhmap, &key);
115 		if (e)
116 			return 1;
117 
118 		empty.val = key;
119 		err = bpf_map_update_elem(&rhmap, &key, &empty, BPF_NOEXIST);
120 		if (err)
121 			return 2;
122 
123 		e = bpf_map_lookup_elem(&rhmap, &key);
124 		if (!e || e->val != key) {
125 			err = 4;
126 			return 4;
127 		}
128 	}
129 
130 	for (i = 0; i < 128; ++i) {
131 		key = i;
132 		err = bpf_map_delete_elem(&rhmap, &key);
133 		if (err)
134 			return 3;
135 
136 		e = bpf_map_lookup_elem(&rhmap, &key);
137 		if (e) {
138 			err = 5;
139 			return 5;
140 		}
141 	}
142 
143 	err = 0;
144 	return 0;
145 }
146 
147 SEC("syscall")
148 int test_rhash_update_exist(void *ctx)
149 {
150 	int key = 10;
151 	struct elem val1 = {.val = 100, .arr = {0}};
152 	struct elem val2 = {.val = 200, .arr = {0}};
153 	struct elem *e;
154 	int ret;
155 
156 	err = 1;
157 
158 	/* BPF_EXIST on non-existent key should fail with -ENOENT */
159 	ret = bpf_map_update_elem(&rhmap, &key, &val1, BPF_EXIST);
160 	if (ret != -ENOENT)
161 		return 1;
162 
163 	/* Insert element first */
164 	ret = bpf_map_update_elem(&rhmap, &key, &val1, BPF_NOEXIST);
165 	if (ret)
166 		return 2;
167 
168 	/* Verify initial value */
169 	e = bpf_map_lookup_elem(&rhmap, &key);
170 	if (!e || e->val != 100)
171 		return 3;
172 
173 	/* BPF_EXIST on existing key should succeed and update value */
174 	ret = bpf_map_update_elem(&rhmap, &key, &val2, BPF_EXIST);
175 	if (ret)
176 		return 4;
177 
178 	/* Verify value was updated */
179 	e = bpf_map_lookup_elem(&rhmap, &key);
180 	if (!e || e->val != 200)
181 		return 5;
182 
183 	/* Cleanup */
184 	bpf_map_delete_elem(&rhmap, &key);
185 	err = 0;
186 	return 0;
187 }
188 
189 SEC("syscall")
190 int test_rhash_update_any(void *ctx)
191 {
192 	int key = 11;
193 	struct elem val1 = {.val = 111, .arr = {0}};
194 	struct elem val2 = {.val = 222, .arr = {0}};
195 	struct elem *e;
196 	int ret;
197 
198 	err = 1;
199 
200 	/* BPF_ANY on non-existent key should insert */
201 	ret = bpf_map_update_elem(&rhmap, &key, &val1, BPF_ANY);
202 	if (ret)
203 		return 1;
204 
205 	e = bpf_map_lookup_elem(&rhmap, &key);
206 	if (!e || e->val != 111)
207 		return 2;
208 
209 	/* BPF_ANY on existing key should update */
210 	ret = bpf_map_update_elem(&rhmap, &key, &val2, BPF_ANY);
211 	if (ret)
212 		return 3;
213 
214 	e = bpf_map_lookup_elem(&rhmap, &key);
215 	if (!e || e->val != 222)
216 		return 4;
217 
218 	/* Cleanup */
219 	bpf_map_delete_elem(&rhmap, &key);
220 	err = 0;
221 	return 0;
222 }
223 
224 SEC("syscall")
225 int test_rhash_noexist_duplicate(void *ctx)
226 {
227 	int key = 12;
228 	struct elem val = {.val = 600, .arr = {0}};
229 	int ret;
230 
231 	err = 1;
232 
233 	/* Insert element */
234 	ret = bpf_map_update_elem(&rhmap, &key, &val, BPF_NOEXIST);
235 	if (ret)
236 		return 1;
237 
238 	/* Try to insert again with BPF_NOEXIST - should fail with -EEXIST */
239 	ret = bpf_map_update_elem(&rhmap, &key, &val, BPF_NOEXIST);
240 	if (ret != -EEXIST)
241 		return 2;
242 
243 	/* Cleanup */
244 	bpf_map_delete_elem(&rhmap, &key);
245 	err = 0;
246 	return 0;
247 }
248 
249 SEC("syscall")
250 int test_rhash_delete_nonexistent(void *ctx)
251 {
252 	int key = 99999;
253 	int ret;
254 
255 	err = 1;
256 
257 	/* Delete non-existent key should return -ENOENT */
258 	ret = bpf_map_delete_elem(&rhmap, &key);
259 	if (ret != -ENOENT)
260 		return 1;
261 
262 	err = 0;
263 	return 0;
264 }
265 
266 SEC("syscall")
267 int test_rhash_kptr_update(void *ctx)
268 {
269 	struct special_elem val1 = { .val = 1 };
270 	struct special_elem val2 = { .val = 2 };
271 	struct task_struct *task, *old;
272 	struct special_elem *elem;
273 	int key = 0;
274 
275 	err = 1;
276 	if (bpf_map_update_elem(&special_fields, &key, &val1, BPF_NOEXIST))
277 		return 1;
278 
279 	err = 2;
280 	elem = bpf_map_lookup_elem(&special_fields, &key);
281 	if (!elem)
282 		return 2;
283 
284 	err = 3;
285 	task = bpf_task_acquire(bpf_get_current_task_btf());
286 	if (!task)
287 		return 3;
288 
289 	err = 4;
290 	old = bpf_kptr_xchg(&elem->task, task);
291 	if (old) {
292 		bpf_task_release(old);
293 		return 4;
294 	}
295 
296 	err = 5;
297 	if (bpf_map_update_elem(&special_fields, &key, &val2, BPF_EXIST))
298 		return 5;
299 
300 	err = 6;
301 	elem = bpf_map_lookup_elem(&special_fields, &key);
302 	if (!elem || elem->val != 2)
303 		return 6;
304 
305 	err = 7;
306 	old = bpf_kptr_xchg(&elem->task, NULL);
307 	if (!old)
308 		return 7;
309 	bpf_task_release(old);
310 
311 	err = 8;
312 	if (bpf_map_delete_elem(&special_fields, &key))
313 		return 8;
314 
315 	err = 0;
316 	return 0;
317 }
318 
319 SEC("syscall")
320 int test_rhash_kptr_delete(void *ctx)
321 {
322 	struct special_elem val = {};
323 	struct task_struct *task, *old;
324 	struct special_elem *elem;
325 	int key = 0;
326 
327 	err = 1;
328 	if (bpf_map_update_elem(&special_fields, &key, &val, BPF_NOEXIST))
329 		return 1;
330 
331 	err = 2;
332 	elem = bpf_map_lookup_elem(&special_fields, &key);
333 	if (!elem)
334 		return 2;
335 
336 	err = 3;
337 	task = bpf_task_acquire(bpf_get_current_task_btf());
338 	if (!task)
339 		return 3;
340 
341 	err = 4;
342 	old = bpf_kptr_xchg(&elem->task, task);
343 	if (old) {
344 		bpf_task_release(old);
345 		return 4;
346 	}
347 
348 	err = 5;
349 	if (bpf_map_delete_elem(&special_fields, &key))
350 		return 5;
351 
352 	err = 6;
353 	old = bpf_kptr_xchg(&elem->task, NULL);
354 	if (!old)
355 		return 6;
356 	bpf_task_release(old);
357 
358 	err = 0;
359 	return 0;
360 }
361