xref: /linux/tools/testing/selftests/bpf/progs/linked_list.c (revision 08b7174fb8d126e607e385e34b9e1da4f3be274f)
1 // SPDX-License-Identifier: GPL-2.0
2 #include <vmlinux.h>
3 #include <bpf/bpf_tracing.h>
4 #include <bpf/bpf_helpers.h>
5 #include <bpf/bpf_core_read.h>
6 #include "bpf_experimental.h"
7 
8 #ifndef ARRAY_SIZE
9 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
10 #endif
11 
12 #include "linked_list.h"
13 
14 static __always_inline
15 int list_push_pop(struct bpf_spin_lock *lock, struct bpf_list_head *head, bool leave_in_map)
16 {
17 	struct bpf_list_node *n;
18 	struct foo *f;
19 
20 	f = bpf_obj_new(typeof(*f));
21 	if (!f)
22 		return 2;
23 
24 	bpf_spin_lock(lock);
25 	n = bpf_list_pop_front(head);
26 	bpf_spin_unlock(lock);
27 	if (n) {
28 		bpf_obj_drop(container_of(n, struct foo, node2));
29 		bpf_obj_drop(f);
30 		return 3;
31 	}
32 
33 	bpf_spin_lock(lock);
34 	n = bpf_list_pop_back(head);
35 	bpf_spin_unlock(lock);
36 	if (n) {
37 		bpf_obj_drop(container_of(n, struct foo, node2));
38 		bpf_obj_drop(f);
39 		return 4;
40 	}
41 
42 
43 	bpf_spin_lock(lock);
44 	f->data = 42;
45 	bpf_list_push_front(head, &f->node2);
46 	bpf_spin_unlock(lock);
47 	if (leave_in_map)
48 		return 0;
49 	bpf_spin_lock(lock);
50 	n = bpf_list_pop_back(head);
51 	bpf_spin_unlock(lock);
52 	if (!n)
53 		return 5;
54 	f = container_of(n, struct foo, node2);
55 	if (f->data != 42) {
56 		bpf_obj_drop(f);
57 		return 6;
58 	}
59 
60 	bpf_spin_lock(lock);
61 	f->data = 13;
62 	bpf_list_push_front(head, &f->node2);
63 	bpf_spin_unlock(lock);
64 	bpf_spin_lock(lock);
65 	n = bpf_list_pop_front(head);
66 	bpf_spin_unlock(lock);
67 	if (!n)
68 		return 7;
69 	f = container_of(n, struct foo, node2);
70 	if (f->data != 13) {
71 		bpf_obj_drop(f);
72 		return 8;
73 	}
74 	bpf_obj_drop(f);
75 
76 	bpf_spin_lock(lock);
77 	n = bpf_list_pop_front(head);
78 	bpf_spin_unlock(lock);
79 	if (n) {
80 		bpf_obj_drop(container_of(n, struct foo, node2));
81 		return 9;
82 	}
83 
84 	bpf_spin_lock(lock);
85 	n = bpf_list_pop_back(head);
86 	bpf_spin_unlock(lock);
87 	if (n) {
88 		bpf_obj_drop(container_of(n, struct foo, node2));
89 		return 10;
90 	}
91 	return 0;
92 }
93 
94 
95 static __always_inline
96 int list_push_pop_multiple(struct bpf_spin_lock *lock, struct bpf_list_head *head, bool leave_in_map)
97 {
98 	struct bpf_list_node *n;
99 	struct foo *f[200], *pf;
100 	int i;
101 
102 	/* Loop following this check adds nodes 2-at-a-time in order to
103 	 * validate multiple release_on_unlock release logic
104 	 */
105 	if (ARRAY_SIZE(f) % 2)
106 		return 10;
107 
108 	for (i = 0; i < ARRAY_SIZE(f); i += 2) {
109 		f[i] = bpf_obj_new(typeof(**f));
110 		if (!f[i])
111 			return 2;
112 		f[i]->data = i;
113 
114 		f[i + 1] = bpf_obj_new(typeof(**f));
115 		if (!f[i + 1]) {
116 			bpf_obj_drop(f[i]);
117 			return 9;
118 		}
119 		f[i + 1]->data = i + 1;
120 
121 		bpf_spin_lock(lock);
122 		bpf_list_push_front(head, &f[i]->node2);
123 		bpf_list_push_front(head, &f[i + 1]->node2);
124 		bpf_spin_unlock(lock);
125 	}
126 
127 	for (i = 0; i < ARRAY_SIZE(f); i++) {
128 		bpf_spin_lock(lock);
129 		n = bpf_list_pop_front(head);
130 		bpf_spin_unlock(lock);
131 		if (!n)
132 			return 3;
133 		pf = container_of(n, struct foo, node2);
134 		if (pf->data != (ARRAY_SIZE(f) - i - 1)) {
135 			bpf_obj_drop(pf);
136 			return 4;
137 		}
138 		bpf_spin_lock(lock);
139 		bpf_list_push_back(head, &pf->node2);
140 		bpf_spin_unlock(lock);
141 	}
142 
143 	if (leave_in_map)
144 		return 0;
145 
146 	for (i = 0; i < ARRAY_SIZE(f); i++) {
147 		bpf_spin_lock(lock);
148 		n = bpf_list_pop_back(head);
149 		bpf_spin_unlock(lock);
150 		if (!n)
151 			return 5;
152 		pf = container_of(n, struct foo, node2);
153 		if (pf->data != i) {
154 			bpf_obj_drop(pf);
155 			return 6;
156 		}
157 		bpf_obj_drop(pf);
158 	}
159 	bpf_spin_lock(lock);
160 	n = bpf_list_pop_back(head);
161 	bpf_spin_unlock(lock);
162 	if (n) {
163 		bpf_obj_drop(container_of(n, struct foo, node2));
164 		return 7;
165 	}
166 
167 	bpf_spin_lock(lock);
168 	n = bpf_list_pop_front(head);
169 	bpf_spin_unlock(lock);
170 	if (n) {
171 		bpf_obj_drop(container_of(n, struct foo, node2));
172 		return 8;
173 	}
174 	return 0;
175 }
176 
177 static __always_inline
178 int list_in_list(struct bpf_spin_lock *lock, struct bpf_list_head *head, bool leave_in_map)
179 {
180 	struct bpf_list_node *n;
181 	struct bar *ba[8], *b;
182 	struct foo *f;
183 	int i;
184 
185 	f = bpf_obj_new(typeof(*f));
186 	if (!f)
187 		return 2;
188 	for (i = 0; i < ARRAY_SIZE(ba); i++) {
189 		b = bpf_obj_new(typeof(*b));
190 		if (!b) {
191 			bpf_obj_drop(f);
192 			return 3;
193 		}
194 		b->data = i;
195 		bpf_spin_lock(&f->lock);
196 		bpf_list_push_back(&f->head, &b->node);
197 		bpf_spin_unlock(&f->lock);
198 	}
199 
200 	bpf_spin_lock(lock);
201 	f->data = 42;
202 	bpf_list_push_front(head, &f->node2);
203 	bpf_spin_unlock(lock);
204 
205 	if (leave_in_map)
206 		return 0;
207 
208 	bpf_spin_lock(lock);
209 	n = bpf_list_pop_front(head);
210 	bpf_spin_unlock(lock);
211 	if (!n)
212 		return 4;
213 	f = container_of(n, struct foo, node2);
214 	if (f->data != 42) {
215 		bpf_obj_drop(f);
216 		return 5;
217 	}
218 
219 	for (i = 0; i < ARRAY_SIZE(ba); i++) {
220 		bpf_spin_lock(&f->lock);
221 		n = bpf_list_pop_front(&f->head);
222 		bpf_spin_unlock(&f->lock);
223 		if (!n) {
224 			bpf_obj_drop(f);
225 			return 6;
226 		}
227 		b = container_of(n, struct bar, node);
228 		if (b->data != i) {
229 			bpf_obj_drop(f);
230 			bpf_obj_drop(b);
231 			return 7;
232 		}
233 		bpf_obj_drop(b);
234 	}
235 	bpf_spin_lock(&f->lock);
236 	n = bpf_list_pop_front(&f->head);
237 	bpf_spin_unlock(&f->lock);
238 	if (n) {
239 		bpf_obj_drop(f);
240 		bpf_obj_drop(container_of(n, struct bar, node));
241 		return 8;
242 	}
243 	bpf_obj_drop(f);
244 	return 0;
245 }
246 
247 static __always_inline
248 int test_list_push_pop(struct bpf_spin_lock *lock, struct bpf_list_head *head)
249 {
250 	int ret;
251 
252 	ret = list_push_pop(lock, head, false);
253 	if (ret)
254 		return ret;
255 	return list_push_pop(lock, head, true);
256 }
257 
258 static __always_inline
259 int test_list_push_pop_multiple(struct bpf_spin_lock *lock, struct bpf_list_head *head)
260 {
261 	int ret;
262 
263 	ret = list_push_pop_multiple(lock, head, false);
264 	if (ret)
265 		return ret;
266 	return list_push_pop_multiple(lock, head, true);
267 }
268 
269 static __always_inline
270 int test_list_in_list(struct bpf_spin_lock *lock, struct bpf_list_head *head)
271 {
272 	int ret;
273 
274 	ret = list_in_list(lock, head, false);
275 	if (ret)
276 		return ret;
277 	return list_in_list(lock, head, true);
278 }
279 
280 SEC("tc")
281 int map_list_push_pop(void *ctx)
282 {
283 	struct map_value *v;
284 
285 	v = bpf_map_lookup_elem(&array_map, &(int){0});
286 	if (!v)
287 		return 1;
288 	return test_list_push_pop(&v->lock, &v->head);
289 }
290 
291 SEC("tc")
292 int inner_map_list_push_pop(void *ctx)
293 {
294 	struct map_value *v;
295 	void *map;
296 
297 	map = bpf_map_lookup_elem(&map_of_maps, &(int){0});
298 	if (!map)
299 		return 1;
300 	v = bpf_map_lookup_elem(map, &(int){0});
301 	if (!v)
302 		return 1;
303 	return test_list_push_pop(&v->lock, &v->head);
304 }
305 
306 SEC("tc")
307 int global_list_push_pop(void *ctx)
308 {
309 	return test_list_push_pop(&glock, &ghead);
310 }
311 
312 SEC("tc")
313 int map_list_push_pop_multiple(void *ctx)
314 {
315 	struct map_value *v;
316 
317 	v = bpf_map_lookup_elem(&array_map, &(int){0});
318 	if (!v)
319 		return 1;
320 	return test_list_push_pop_multiple(&v->lock, &v->head);
321 }
322 
323 SEC("tc")
324 int inner_map_list_push_pop_multiple(void *ctx)
325 {
326 	struct map_value *v;
327 	void *map;
328 
329 	map = bpf_map_lookup_elem(&map_of_maps, &(int){0});
330 	if (!map)
331 		return 1;
332 	v = bpf_map_lookup_elem(map, &(int){0});
333 	if (!v)
334 		return 1;
335 	return test_list_push_pop_multiple(&v->lock, &v->head);
336 }
337 
338 SEC("tc")
339 int global_list_push_pop_multiple(void *ctx)
340 {
341 	int ret;
342 
343 	ret = list_push_pop_multiple(&glock, &ghead, false);
344 	if (ret)
345 		return ret;
346 	return list_push_pop_multiple(&glock, &ghead, true);
347 }
348 
349 SEC("tc")
350 int map_list_in_list(void *ctx)
351 {
352 	struct map_value *v;
353 
354 	v = bpf_map_lookup_elem(&array_map, &(int){0});
355 	if (!v)
356 		return 1;
357 	return test_list_in_list(&v->lock, &v->head);
358 }
359 
360 SEC("tc")
361 int inner_map_list_in_list(void *ctx)
362 {
363 	struct map_value *v;
364 	void *map;
365 
366 	map = bpf_map_lookup_elem(&map_of_maps, &(int){0});
367 	if (!map)
368 		return 1;
369 	v = bpf_map_lookup_elem(map, &(int){0});
370 	if (!v)
371 		return 1;
372 	return test_list_in_list(&v->lock, &v->head);
373 }
374 
375 SEC("tc")
376 int global_list_in_list(void *ctx)
377 {
378 	return test_list_in_list(&glock, &ghead);
379 }
380 
381 char _license[] SEC("license") = "GPL";
382