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) (int)(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(lock: &glock, head: &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(lock: &glock, head: &ghead); |
379 | } |
380 | |
381 | char _license[] SEC("license" ) = "GPL" ; |
382 | |