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 #include "bpf_misc.h" 8 9 #include "linked_list.h" 10 11 struct head_nested_inner { 12 struct bpf_spin_lock lock; 13 struct bpf_list_head head __contains(foo, node2); 14 }; 15 16 struct head_nested { 17 int dummy; 18 struct head_nested_inner inner; 19 }; 20 21 private(C) struct bpf_spin_lock glock_c; 22 private(C) struct bpf_list_head ghead_array[2] __contains(foo, node2); 23 private(C) struct bpf_list_head ghead_array_one[1] __contains(foo, node2); 24 25 private(D) struct head_nested ghead_nested; 26 27 static __always_inline 28 int list_push_pop(struct bpf_spin_lock *lock, struct bpf_list_head *head, bool leave_in_map) 29 { 30 struct bpf_list_node *n; 31 struct foo *f; 32 33 f = bpf_obj_new(typeof(*f)); 34 if (!f) 35 return 2; 36 37 bpf_spin_lock(lock); 38 n = bpf_list_pop_front(head); 39 bpf_spin_unlock(lock); 40 if (n) { 41 bpf_obj_drop(container_of(n, struct foo, node2)); 42 bpf_obj_drop(f); 43 return 3; 44 } 45 46 bpf_spin_lock(lock); 47 n = bpf_list_pop_back(head); 48 bpf_spin_unlock(lock); 49 if (n) { 50 bpf_obj_drop(container_of(n, struct foo, node2)); 51 bpf_obj_drop(f); 52 return 4; 53 } 54 55 56 bpf_spin_lock(lock); 57 f->data = 42; 58 bpf_list_push_front(head, &f->node2); 59 bpf_spin_unlock(lock); 60 if (leave_in_map) 61 return 0; 62 bpf_spin_lock(lock); 63 n = bpf_list_pop_back(head); 64 bpf_spin_unlock(lock); 65 if (!n) 66 return 5; 67 f = container_of(n, struct foo, node2); 68 if (f->data != 42) { 69 bpf_obj_drop(f); 70 return 6; 71 } 72 73 bpf_spin_lock(lock); 74 f->data = 13; 75 bpf_list_push_front(head, &f->node2); 76 bpf_spin_unlock(lock); 77 bpf_spin_lock(lock); 78 n = bpf_list_pop_front(head); 79 bpf_spin_unlock(lock); 80 if (!n) 81 return 7; 82 f = container_of(n, struct foo, node2); 83 if (f->data != 13) { 84 bpf_obj_drop(f); 85 return 8; 86 } 87 bpf_obj_drop(f); 88 89 bpf_spin_lock(lock); 90 n = bpf_list_pop_front(head); 91 bpf_spin_unlock(lock); 92 if (n) { 93 bpf_obj_drop(container_of(n, struct foo, node2)); 94 return 9; 95 } 96 97 bpf_spin_lock(lock); 98 n = bpf_list_pop_back(head); 99 bpf_spin_unlock(lock); 100 if (n) { 101 bpf_obj_drop(container_of(n, struct foo, node2)); 102 return 10; 103 } 104 return 0; 105 } 106 107 108 static __always_inline 109 int list_push_pop_multiple(struct bpf_spin_lock *lock, struct bpf_list_head *head, bool leave_in_map) 110 { 111 struct bpf_list_node *n; 112 struct foo *f[200], *pf; 113 int i; 114 115 /* Loop following this check adds nodes 2-at-a-time in order to 116 * validate multiple release_on_unlock release logic 117 */ 118 if (ARRAY_SIZE(f) % 2) 119 return 10; 120 121 for (i = 0; i < ARRAY_SIZE(f); i += 2) { 122 f[i] = bpf_obj_new(typeof(**f)); 123 if (!f[i]) 124 return 2; 125 f[i]->data = i; 126 127 f[i + 1] = bpf_obj_new(typeof(**f)); 128 if (!f[i + 1]) { 129 bpf_obj_drop(f[i]); 130 return 9; 131 } 132 f[i + 1]->data = i + 1; 133 134 bpf_spin_lock(lock); 135 bpf_list_push_front(head, &f[i]->node2); 136 bpf_list_push_front(head, &f[i + 1]->node2); 137 bpf_spin_unlock(lock); 138 } 139 140 for (i = 0; i < ARRAY_SIZE(f); i++) { 141 bpf_spin_lock(lock); 142 n = bpf_list_pop_front(head); 143 bpf_spin_unlock(lock); 144 if (!n) 145 return 3; 146 pf = container_of(n, struct foo, node2); 147 if (pf->data != (ARRAY_SIZE(f) - i - 1)) { 148 bpf_obj_drop(pf); 149 return 4; 150 } 151 bpf_spin_lock(lock); 152 bpf_list_push_back(head, &pf->node2); 153 bpf_spin_unlock(lock); 154 } 155 156 if (leave_in_map) 157 return 0; 158 159 for (i = 0; i < ARRAY_SIZE(f); i++) { 160 bpf_spin_lock(lock); 161 n = bpf_list_pop_back(head); 162 bpf_spin_unlock(lock); 163 if (!n) 164 return 5; 165 pf = container_of(n, struct foo, node2); 166 if (pf->data != i) { 167 bpf_obj_drop(pf); 168 return 6; 169 } 170 bpf_obj_drop(pf); 171 } 172 bpf_spin_lock(lock); 173 n = bpf_list_pop_back(head); 174 bpf_spin_unlock(lock); 175 if (n) { 176 bpf_obj_drop(container_of(n, struct foo, node2)); 177 return 7; 178 } 179 180 bpf_spin_lock(lock); 181 n = bpf_list_pop_front(head); 182 bpf_spin_unlock(lock); 183 if (n) { 184 bpf_obj_drop(container_of(n, struct foo, node2)); 185 return 8; 186 } 187 return 0; 188 } 189 190 static __always_inline 191 int list_in_list(struct bpf_spin_lock *lock, struct bpf_list_head *head, bool leave_in_map) 192 { 193 struct bpf_list_node *n; 194 struct bar *ba[8], *b; 195 struct foo *f; 196 int i; 197 198 f = bpf_obj_new(typeof(*f)); 199 if (!f) 200 return 2; 201 for (i = 0; i < ARRAY_SIZE(ba); i++) { 202 b = bpf_obj_new(typeof(*b)); 203 if (!b) { 204 bpf_obj_drop(f); 205 return 3; 206 } 207 b->data = i; 208 bpf_spin_lock(&f->lock); 209 bpf_list_push_back(&f->head, &b->node); 210 bpf_spin_unlock(&f->lock); 211 } 212 213 bpf_spin_lock(lock); 214 f->data = 42; 215 bpf_list_push_front(head, &f->node2); 216 bpf_spin_unlock(lock); 217 218 if (leave_in_map) 219 return 0; 220 221 bpf_spin_lock(lock); 222 n = bpf_list_pop_front(head); 223 bpf_spin_unlock(lock); 224 if (!n) 225 return 4; 226 f = container_of(n, struct foo, node2); 227 if (f->data != 42) { 228 bpf_obj_drop(f); 229 return 5; 230 } 231 232 for (i = 0; i < ARRAY_SIZE(ba); i++) { 233 bpf_spin_lock(&f->lock); 234 n = bpf_list_pop_front(&f->head); 235 bpf_spin_unlock(&f->lock); 236 if (!n) { 237 bpf_obj_drop(f); 238 return 6; 239 } 240 b = container_of(n, struct bar, node); 241 if (b->data != i) { 242 bpf_obj_drop(f); 243 bpf_obj_drop(b); 244 return 7; 245 } 246 bpf_obj_drop(b); 247 } 248 bpf_spin_lock(&f->lock); 249 n = bpf_list_pop_front(&f->head); 250 bpf_spin_unlock(&f->lock); 251 if (n) { 252 bpf_obj_drop(f); 253 bpf_obj_drop(container_of(n, struct bar, node)); 254 return 8; 255 } 256 bpf_obj_drop(f); 257 return 0; 258 } 259 260 static __always_inline 261 int test_list_push_pop(struct bpf_spin_lock *lock, struct bpf_list_head *head) 262 { 263 int ret; 264 265 ret = list_push_pop(lock, head, false); 266 if (ret) 267 return ret; 268 return list_push_pop(lock, head, true); 269 } 270 271 static __always_inline 272 int test_list_push_pop_multiple(struct bpf_spin_lock *lock, struct bpf_list_head *head) 273 { 274 int ret; 275 276 ret = list_push_pop_multiple(lock, head, false); 277 if (ret) 278 return ret; 279 return list_push_pop_multiple(lock, head, true); 280 } 281 282 static __always_inline 283 int test_list_in_list(struct bpf_spin_lock *lock, struct bpf_list_head *head) 284 { 285 int ret; 286 287 ret = list_in_list(lock, head, false); 288 if (ret) 289 return ret; 290 return list_in_list(lock, head, true); 291 } 292 293 #define MAX_LIST_CLEAR_NODES 256 294 295 static __always_inline 296 int clear_list(struct bpf_spin_lock *lock, struct bpf_list_head *head) 297 { 298 struct bpf_list_node *n; 299 int i; 300 301 for (i = 0; i < MAX_LIST_CLEAR_NODES; i++) { 302 bpf_spin_lock(lock); 303 n = bpf_list_pop_front(head); 304 bpf_spin_unlock(lock); 305 if (!n) 306 return 0; 307 bpf_obj_drop(container_of(n, struct foo, node2)); 308 } 309 return 1; 310 } 311 312 SEC("syscall") 313 int clear_map_list(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 clear_list(&v->lock, &v->head); 321 } 322 323 SEC("syscall") 324 int clear_inner_map_list(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 clear_list(&v->lock, &v->head); 336 } 337 338 SEC("syscall") 339 int clear_global_list(void *ctx) 340 { 341 return clear_list(&glock, &ghead); 342 } 343 344 SEC("syscall") 345 int clear_global_nested_list(void *ctx) 346 { 347 return clear_list(&ghead_nested.inner.lock, &ghead_nested.inner.head); 348 } 349 350 SEC("syscall") 351 int clear_global_array_list(void *ctx) 352 { 353 int ret; 354 355 ret = clear_list(&glock_c, &ghead_array[0]); 356 if (ret) 357 return ret; 358 ret = clear_list(&glock_c, &ghead_array[1]); 359 if (ret) 360 return ret; 361 return clear_list(&glock_c, &ghead_array_one[0]); 362 } 363 364 SEC("tc") 365 int map_list_push_pop(void *ctx) 366 { 367 struct map_value *v; 368 369 v = bpf_map_lookup_elem(&array_map, &(int){0}); 370 if (!v) 371 return 1; 372 return test_list_push_pop(&v->lock, &v->head); 373 } 374 375 SEC("tc") 376 int inner_map_list_push_pop(void *ctx) 377 { 378 struct map_value *v; 379 void *map; 380 381 map = bpf_map_lookup_elem(&map_of_maps, &(int){0}); 382 if (!map) 383 return 1; 384 v = bpf_map_lookup_elem(map, &(int){0}); 385 if (!v) 386 return 1; 387 return test_list_push_pop(&v->lock, &v->head); 388 } 389 390 SEC("tc") 391 int global_list_push_pop(void *ctx) 392 { 393 return test_list_push_pop(&glock, &ghead); 394 } 395 396 SEC("tc") 397 int global_list_push_pop_nested(void *ctx) 398 { 399 return test_list_push_pop(&ghead_nested.inner.lock, &ghead_nested.inner.head); 400 } 401 402 SEC("tc") 403 int global_list_array_push_pop(void *ctx) 404 { 405 int r; 406 407 r = test_list_push_pop(&glock_c, &ghead_array[0]); 408 if (r) 409 return r; 410 411 r = test_list_push_pop(&glock_c, &ghead_array[1]); 412 if (r) 413 return r; 414 415 /* Arrays with only one element is a special case, being treated 416 * just like a bpf_list_head variable by the verifier, not an 417 * array. 418 */ 419 return test_list_push_pop(&glock_c, &ghead_array_one[0]); 420 } 421 422 SEC("tc") 423 int map_list_push_pop_multiple(void *ctx) 424 { 425 struct map_value *v; 426 427 v = bpf_map_lookup_elem(&array_map, &(int){0}); 428 if (!v) 429 return 1; 430 return test_list_push_pop_multiple(&v->lock, &v->head); 431 } 432 433 SEC("tc") 434 int inner_map_list_push_pop_multiple(void *ctx) 435 { 436 struct map_value *v; 437 void *map; 438 439 map = bpf_map_lookup_elem(&map_of_maps, &(int){0}); 440 if (!map) 441 return 1; 442 v = bpf_map_lookup_elem(map, &(int){0}); 443 if (!v) 444 return 1; 445 return test_list_push_pop_multiple(&v->lock, &v->head); 446 } 447 448 SEC("tc") 449 int global_list_push_pop_multiple(void *ctx) 450 { 451 int ret; 452 453 ret = list_push_pop_multiple(&glock, &ghead, false); 454 if (ret) 455 return ret; 456 return list_push_pop_multiple(&glock, &ghead, true); 457 } 458 459 SEC("tc") 460 int map_list_in_list(void *ctx) 461 { 462 struct map_value *v; 463 464 v = bpf_map_lookup_elem(&array_map, &(int){0}); 465 if (!v) 466 return 1; 467 return test_list_in_list(&v->lock, &v->head); 468 } 469 470 SEC("tc") 471 int inner_map_list_in_list(void *ctx) 472 { 473 struct map_value *v; 474 void *map; 475 476 map = bpf_map_lookup_elem(&map_of_maps, &(int){0}); 477 if (!map) 478 return 1; 479 v = bpf_map_lookup_elem(map, &(int){0}); 480 if (!v) 481 return 1; 482 return test_list_in_list(&v->lock, &v->head); 483 } 484 485 SEC("tc") 486 int global_list_in_list(void *ctx) 487 { 488 return test_list_in_list(&glock, &ghead); 489 } 490 491 char _license[] SEC("license") = "GPL"; 492