Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v6.4-rc2 406 lines 9.6 kB view raw
1// SPDX-License-Identifier: GPL-2.0 2/* Copyright (c) 2023 Meta Platforms, Inc. and affiliates. */ 3 4#include <vmlinux.h> 5#include <bpf/bpf_tracing.h> 6#include <bpf/bpf_helpers.h> 7#include <bpf/bpf_core_read.h> 8#include "bpf_misc.h" 9#include "bpf_experimental.h" 10 11struct node_data { 12 long key; 13 long list_data; 14 struct bpf_rb_node r; 15 struct bpf_list_node l; 16 struct bpf_refcount ref; 17}; 18 19struct map_value { 20 struct node_data __kptr *node; 21}; 22 23struct { 24 __uint(type, BPF_MAP_TYPE_ARRAY); 25 __type(key, int); 26 __type(value, struct map_value); 27 __uint(max_entries, 1); 28} stashed_nodes SEC(".maps"); 29 30struct node_acquire { 31 long key; 32 long data; 33 struct bpf_rb_node node; 34 struct bpf_refcount refcount; 35}; 36 37#define private(name) SEC(".bss." #name) __hidden __attribute__((aligned(8))) 38private(A) struct bpf_spin_lock lock; 39private(A) struct bpf_rb_root root __contains(node_data, r); 40private(A) struct bpf_list_head head __contains(node_data, l); 41 42private(B) struct bpf_spin_lock alock; 43private(B) struct bpf_rb_root aroot __contains(node_acquire, node); 44 45static bool less(struct bpf_rb_node *node_a, const struct bpf_rb_node *node_b) 46{ 47 struct node_data *a; 48 struct node_data *b; 49 50 a = container_of(node_a, struct node_data, r); 51 b = container_of(node_b, struct node_data, r); 52 53 return a->key < b->key; 54} 55 56static bool less_a(struct bpf_rb_node *a, const struct bpf_rb_node *b) 57{ 58 struct node_acquire *node_a; 59 struct node_acquire *node_b; 60 61 node_a = container_of(a, struct node_acquire, node); 62 node_b = container_of(b, struct node_acquire, node); 63 64 return node_a->key < node_b->key; 65} 66 67static long __insert_in_tree_and_list(struct bpf_list_head *head, 68 struct bpf_rb_root *root, 69 struct bpf_spin_lock *lock) 70{ 71 struct node_data *n, *m; 72 73 n = bpf_obj_new(typeof(*n)); 74 if (!n) 75 return -1; 76 77 m = bpf_refcount_acquire(n); 78 m->key = 123; 79 m->list_data = 456; 80 81 bpf_spin_lock(lock); 82 if (bpf_rbtree_add(root, &n->r, less)) { 83 /* Failure to insert - unexpected */ 84 bpf_spin_unlock(lock); 85 bpf_obj_drop(m); 86 return -2; 87 } 88 bpf_spin_unlock(lock); 89 90 bpf_spin_lock(lock); 91 if (bpf_list_push_front(head, &m->l)) { 92 /* Failure to insert - unexpected */ 93 bpf_spin_unlock(lock); 94 return -3; 95 } 96 bpf_spin_unlock(lock); 97 return 0; 98} 99 100static long __stash_map_insert_tree(int idx, int val, struct bpf_rb_root *root, 101 struct bpf_spin_lock *lock) 102{ 103 struct map_value *mapval; 104 struct node_data *n, *m; 105 106 mapval = bpf_map_lookup_elem(&stashed_nodes, &idx); 107 if (!mapval) 108 return -1; 109 110 n = bpf_obj_new(typeof(*n)); 111 if (!n) 112 return -2; 113 114 n->key = val; 115 m = bpf_refcount_acquire(n); 116 117 n = bpf_kptr_xchg(&mapval->node, n); 118 if (n) { 119 bpf_obj_drop(n); 120 bpf_obj_drop(m); 121 return -3; 122 } 123 124 bpf_spin_lock(lock); 125 if (bpf_rbtree_add(root, &m->r, less)) { 126 /* Failure to insert - unexpected */ 127 bpf_spin_unlock(lock); 128 return -4; 129 } 130 bpf_spin_unlock(lock); 131 return 0; 132} 133 134static long __read_from_tree(struct bpf_rb_root *root, 135 struct bpf_spin_lock *lock, 136 bool remove_from_tree) 137{ 138 struct bpf_rb_node *rb; 139 struct node_data *n; 140 long res = -99; 141 142 bpf_spin_lock(lock); 143 144 rb = bpf_rbtree_first(root); 145 if (!rb) { 146 bpf_spin_unlock(lock); 147 return -1; 148 } 149 150 n = container_of(rb, struct node_data, r); 151 res = n->key; 152 153 if (!remove_from_tree) { 154 bpf_spin_unlock(lock); 155 return res; 156 } 157 158 rb = bpf_rbtree_remove(root, rb); 159 bpf_spin_unlock(lock); 160 if (!rb) 161 return -2; 162 n = container_of(rb, struct node_data, r); 163 bpf_obj_drop(n); 164 return res; 165} 166 167static long __read_from_list(struct bpf_list_head *head, 168 struct bpf_spin_lock *lock, 169 bool remove_from_list) 170{ 171 struct bpf_list_node *l; 172 struct node_data *n; 173 long res = -99; 174 175 bpf_spin_lock(lock); 176 177 l = bpf_list_pop_front(head); 178 if (!l) { 179 bpf_spin_unlock(lock); 180 return -1; 181 } 182 183 n = container_of(l, struct node_data, l); 184 res = n->list_data; 185 186 if (!remove_from_list) { 187 if (bpf_list_push_back(head, &n->l)) { 188 bpf_spin_unlock(lock); 189 return -2; 190 } 191 } 192 193 bpf_spin_unlock(lock); 194 195 if (remove_from_list) 196 bpf_obj_drop(n); 197 return res; 198} 199 200static long __read_from_unstash(int idx) 201{ 202 struct node_data *n = NULL; 203 struct map_value *mapval; 204 long val = -99; 205 206 mapval = bpf_map_lookup_elem(&stashed_nodes, &idx); 207 if (!mapval) 208 return -1; 209 210 n = bpf_kptr_xchg(&mapval->node, n); 211 if (!n) 212 return -2; 213 214 val = n->key; 215 bpf_obj_drop(n); 216 return val; 217} 218 219#define INSERT_READ_BOTH(rem_tree, rem_list, desc) \ 220SEC("tc") \ 221__description(desc) \ 222__success __retval(579) \ 223long insert_and_remove_tree_##rem_tree##_list_##rem_list(void *ctx) \ 224{ \ 225 long err, tree_data, list_data; \ 226 \ 227 err = __insert_in_tree_and_list(&head, &root, &lock); \ 228 if (err) \ 229 return err; \ 230 \ 231 err = __read_from_tree(&root, &lock, rem_tree); \ 232 if (err < 0) \ 233 return err; \ 234 else \ 235 tree_data = err; \ 236 \ 237 err = __read_from_list(&head, &lock, rem_list); \ 238 if (err < 0) \ 239 return err; \ 240 else \ 241 list_data = err; \ 242 \ 243 return tree_data + list_data; \ 244} 245 246/* After successful insert of struct node_data into both collections: 247 * - it should have refcount = 2 248 * - removing / not removing the node_data from a collection after 249 * reading should have no effect on ability to read / remove from 250 * the other collection 251 */ 252INSERT_READ_BOTH(true, true, "insert_read_both: remove from tree + list"); 253INSERT_READ_BOTH(false, false, "insert_read_both: remove from neither"); 254INSERT_READ_BOTH(true, false, "insert_read_both: remove from tree"); 255INSERT_READ_BOTH(false, true, "insert_read_both: remove from list"); 256 257#undef INSERT_READ_BOTH 258#define INSERT_READ_BOTH(rem_tree, rem_list, desc) \ 259SEC("tc") \ 260__description(desc) \ 261__success __retval(579) \ 262long insert_and_remove_lf_tree_##rem_tree##_list_##rem_list(void *ctx) \ 263{ \ 264 long err, tree_data, list_data; \ 265 \ 266 err = __insert_in_tree_and_list(&head, &root, &lock); \ 267 if (err) \ 268 return err; \ 269 \ 270 err = __read_from_list(&head, &lock, rem_list); \ 271 if (err < 0) \ 272 return err; \ 273 else \ 274 list_data = err; \ 275 \ 276 err = __read_from_tree(&root, &lock, rem_tree); \ 277 if (err < 0) \ 278 return err; \ 279 else \ 280 tree_data = err; \ 281 \ 282 return tree_data + list_data; \ 283} 284 285/* Similar to insert_read_both, but list data is read and possibly removed 286 * first 287 * 288 * Results should be no different than reading and possibly removing rbtree 289 * node first 290 */ 291INSERT_READ_BOTH(true, true, "insert_read_both_list_first: remove from tree + list"); 292INSERT_READ_BOTH(false, false, "insert_read_both_list_first: remove from neither"); 293INSERT_READ_BOTH(true, false, "insert_read_both_list_first: remove from tree"); 294INSERT_READ_BOTH(false, true, "insert_read_both_list_first: remove from list"); 295 296#define INSERT_DOUBLE_READ_AND_DEL(read_fn, read_root, desc) \ 297SEC("tc") \ 298__description(desc) \ 299__success __retval(-1) \ 300long insert_double_##read_fn##_and_del_##read_root(void *ctx) \ 301{ \ 302 long err, list_data; \ 303 \ 304 err = __insert_in_tree_and_list(&head, &root, &lock); \ 305 if (err) \ 306 return err; \ 307 \ 308 err = read_fn(&read_root, &lock, true); \ 309 if (err < 0) \ 310 return err; \ 311 else \ 312 list_data = err; \ 313 \ 314 err = read_fn(&read_root, &lock, true); \ 315 if (err < 0) \ 316 return err; \ 317 \ 318 return err + list_data; \ 319} 320 321/* Insert into both tree and list, then try reading-and-removing from either twice 322 * 323 * The second read-and-remove should fail on read step since the node has 324 * already been removed 325 */ 326INSERT_DOUBLE_READ_AND_DEL(__read_from_tree, root, "insert_double_del: 2x read-and-del from tree"); 327INSERT_DOUBLE_READ_AND_DEL(__read_from_list, head, "insert_double_del: 2x read-and-del from list"); 328 329#define INSERT_STASH_READ(rem_tree, desc) \ 330SEC("tc") \ 331__description(desc) \ 332__success __retval(84) \ 333long insert_rbtree_and_stash__del_tree_##rem_tree(void *ctx) \ 334{ \ 335 long err, tree_data, map_data; \ 336 \ 337 err = __stash_map_insert_tree(0, 42, &root, &lock); \ 338 if (err) \ 339 return err; \ 340 \ 341 err = __read_from_tree(&root, &lock, rem_tree); \ 342 if (err < 0) \ 343 return err; \ 344 else \ 345 tree_data = err; \ 346 \ 347 err = __read_from_unstash(0); \ 348 if (err < 0) \ 349 return err; \ 350 else \ 351 map_data = err; \ 352 \ 353 return tree_data + map_data; \ 354} 355 356/* Stash a refcounted node in map_val, insert same node into tree, then try 357 * reading data from tree then unstashed map_val, possibly removing from tree 358 * 359 * Removing from tree should have no effect on map_val kptr validity 360 */ 361INSERT_STASH_READ(true, "insert_stash_read: remove from tree"); 362INSERT_STASH_READ(false, "insert_stash_read: don't remove from tree"); 363 364SEC("tc") 365__success 366long rbtree_refcounted_node_ref_escapes(void *ctx) 367{ 368 struct node_acquire *n, *m; 369 370 n = bpf_obj_new(typeof(*n)); 371 if (!n) 372 return 1; 373 374 bpf_spin_lock(&alock); 375 bpf_rbtree_add(&aroot, &n->node, less_a); 376 m = bpf_refcount_acquire(n); 377 bpf_spin_unlock(&alock); 378 379 m->key = 2; 380 bpf_obj_drop(m); 381 return 0; 382} 383 384SEC("tc") 385__success 386long rbtree_refcounted_node_ref_escapes_owning_input(void *ctx) 387{ 388 struct node_acquire *n, *m; 389 390 n = bpf_obj_new(typeof(*n)); 391 if (!n) 392 return 1; 393 394 m = bpf_refcount_acquire(n); 395 m->key = 2; 396 397 bpf_spin_lock(&alock); 398 bpf_rbtree_add(&aroot, &n->node, less_a); 399 bpf_spin_unlock(&alock); 400 401 bpf_obj_drop(m); 402 403 return 0; 404} 405 406char _license[] SEC("license") = "GPL";