xref: /linux/tools/testing/selftests/bpf/progs/rhash.c (revision b2128290c29902315e632ea59e0504d6bc9e9b42)
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 {
23 	__uint(type, BPF_MAP_TYPE_RHASH);
24 	__uint(map_flags, BPF_F_NO_PREALLOC);
25 	__uint(max_entries, 128);
26 	__type(key, int);
27 	__type(value, struct elem);
28 } rhmap SEC(".maps");
29 
30 SEC("syscall")
31 int test_rhash_lookup_update(void *ctx)
32 {
33 	int key = 5;
34 	struct elem empty = {.val = 3, .arr = {0}};
35 	struct elem *e;
36 
37 	err = 1;
38 	e = bpf_map_lookup_elem(&rhmap, &key);
39 	if (e)
40 		return 1;
41 
42 	err = bpf_map_update_elem(&rhmap, &key, &empty, BPF_NOEXIST);
43 	if (err)
44 		return 1;
45 
46 	e = bpf_map_lookup_elem(&rhmap, &key);
47 	if (!e || e->val != empty.val) {
48 		err = 2;
49 		return 2;
50 	}
51 
52 	err = 0;
53 	return 0;
54 }
55 
56 SEC("syscall")
57 int test_rhash_update_delete(void *ctx)
58 {
59 	int key = 6;
60 	struct elem empty = {.val = 4, .arr = {0}};
61 	struct elem *e;
62 
63 	err = 1;
64 	e = bpf_map_lookup_elem(&rhmap, &key);
65 	if (e)
66 		return 1;
67 
68 	err = bpf_map_update_elem(&rhmap, &key, &empty, BPF_NOEXIST);
69 	if (err)
70 		return 2;
71 
72 	err = bpf_map_delete_elem(&rhmap, &key);
73 	if (err)
74 		return 3;
75 
76 	e = bpf_map_lookup_elem(&rhmap, &key);
77 	if (e) {
78 		err = 4;
79 		return 4;
80 	}
81 
82 	err = 0;
83 	return 0;
84 }
85 
86 SEC("syscall")
87 int test_rhash_update_elements(void *ctx)
88 {
89 	int key = 0;
90 	struct elem empty = {.val = 4, .arr = {0}};
91 	struct elem *e;
92 	int i;
93 
94 	err = 1;
95 
96 	for (i = 0; i < 128; ++i) {
97 		key = i;
98 		e = bpf_map_lookup_elem(&rhmap, &key);
99 		if (e)
100 			return 1;
101 
102 		empty.val = key;
103 		err = bpf_map_update_elem(&rhmap, &key, &empty, BPF_NOEXIST);
104 		if (err)
105 			return 2;
106 
107 		e = bpf_map_lookup_elem(&rhmap, &key);
108 		if (!e || e->val != key) {
109 			err = 4;
110 			return 4;
111 		}
112 	}
113 
114 	for (i = 0; i < 128; ++i) {
115 		key = i;
116 		err = bpf_map_delete_elem(&rhmap, &key);
117 		if (err)
118 			return 3;
119 
120 		e = bpf_map_lookup_elem(&rhmap, &key);
121 		if (e) {
122 			err = 5;
123 			return 5;
124 		}
125 	}
126 
127 	err = 0;
128 	return 0;
129 }
130 
131 SEC("syscall")
132 int test_rhash_update_exist(void *ctx)
133 {
134 	int key = 10;
135 	struct elem val1 = {.val = 100, .arr = {0}};
136 	struct elem val2 = {.val = 200, .arr = {0}};
137 	struct elem *e;
138 	int ret;
139 
140 	err = 1;
141 
142 	/* BPF_EXIST on non-existent key should fail with -ENOENT */
143 	ret = bpf_map_update_elem(&rhmap, &key, &val1, BPF_EXIST);
144 	if (ret != -ENOENT)
145 		return 1;
146 
147 	/* Insert element first */
148 	ret = bpf_map_update_elem(&rhmap, &key, &val1, BPF_NOEXIST);
149 	if (ret)
150 		return 2;
151 
152 	/* Verify initial value */
153 	e = bpf_map_lookup_elem(&rhmap, &key);
154 	if (!e || e->val != 100)
155 		return 3;
156 
157 	/* BPF_EXIST on existing key should succeed and update value */
158 	ret = bpf_map_update_elem(&rhmap, &key, &val2, BPF_EXIST);
159 	if (ret)
160 		return 4;
161 
162 	/* Verify value was updated */
163 	e = bpf_map_lookup_elem(&rhmap, &key);
164 	if (!e || e->val != 200)
165 		return 5;
166 
167 	/* Cleanup */
168 	bpf_map_delete_elem(&rhmap, &key);
169 	err = 0;
170 	return 0;
171 }
172 
173 SEC("syscall")
174 int test_rhash_update_any(void *ctx)
175 {
176 	int key = 11;
177 	struct elem val1 = {.val = 111, .arr = {0}};
178 	struct elem val2 = {.val = 222, .arr = {0}};
179 	struct elem *e;
180 	int ret;
181 
182 	err = 1;
183 
184 	/* BPF_ANY on non-existent key should insert */
185 	ret = bpf_map_update_elem(&rhmap, &key, &val1, BPF_ANY);
186 	if (ret)
187 		return 1;
188 
189 	e = bpf_map_lookup_elem(&rhmap, &key);
190 	if (!e || e->val != 111)
191 		return 2;
192 
193 	/* BPF_ANY on existing key should update */
194 	ret = bpf_map_update_elem(&rhmap, &key, &val2, BPF_ANY);
195 	if (ret)
196 		return 3;
197 
198 	e = bpf_map_lookup_elem(&rhmap, &key);
199 	if (!e || e->val != 222)
200 		return 4;
201 
202 	/* Cleanup */
203 	bpf_map_delete_elem(&rhmap, &key);
204 	err = 0;
205 	return 0;
206 }
207 
208 SEC("syscall")
209 int test_rhash_noexist_duplicate(void *ctx)
210 {
211 	int key = 12;
212 	struct elem val = {.val = 600, .arr = {0}};
213 	int ret;
214 
215 	err = 1;
216 
217 	/* Insert element */
218 	ret = bpf_map_update_elem(&rhmap, &key, &val, BPF_NOEXIST);
219 	if (ret)
220 		return 1;
221 
222 	/* Try to insert again with BPF_NOEXIST - should fail with -EEXIST */
223 	ret = bpf_map_update_elem(&rhmap, &key, &val, BPF_NOEXIST);
224 	if (ret != -EEXIST)
225 		return 2;
226 
227 	/* Cleanup */
228 	bpf_map_delete_elem(&rhmap, &key);
229 	err = 0;
230 	return 0;
231 }
232 
233 SEC("syscall")
234 int test_rhash_delete_nonexistent(void *ctx)
235 {
236 	int key = 99999;
237 	int ret;
238 
239 	err = 1;
240 
241 	/* Delete non-existent key should return -ENOENT */
242 	ret = bpf_map_delete_elem(&rhmap, &key);
243 	if (ret != -ENOENT)
244 		return 1;
245 
246 	err = 0;
247 	return 0;
248 }
249