xref: /linux/tools/testing/selftests/bpf/libarena/src/rbtree.bpf.c (revision 6c3e8a4d476521bc33362e90b2569548f1adb7a4)
1 // SPDX-License-Identifier: LGPL-2.1 OR BSD-2-Clause
2 /*
3  * Copyright (c) 2025-2026 Meta Platforms, Inc. and affiliates.
4  * Copyright (c) 2025-2026 Emil Tsalapatis <emil@etsalapatis.com>
5  */
6 
7 #include <libarena/common.h>
8 
9 #include <libarena/asan.h>
10 #include <libarena/rbtree.h>
11 
12 int rb_integrity_check(struct rbtree __arena *rbtree);
13 void rbnode_print(size_t depth, struct rbnode __arena *rbn);
14 static int rbnode_replace(struct rbtree __arena *rbtree,
15 			  struct rbnode __arena *existing,
16 			  struct rbnode __arena *replacement);
17 
18 struct rbtree __arena *rb_create(enum rbtree_alloc alloc,
19 				 enum rbtree_insert_mode insert)
20 {
21 	struct rbtree __arena *rbtree;
22 
23 	rbtree = arena_malloc(sizeof(*rbtree));
24 	if (unlikely(!rbtree))
25 		return NULL;
26 
27 	/*
28 	 * RB_UPDATE overwrites existing values in the nodes, but RB_NOALLOC
29 	 * trees manage the tree nodes directly (including holding pointers
30 	 * to them). Disallow mixing the two modes to avoid dealing with
31 	 * unintuitive semantics.
32 	 */
33 	if (alloc == RB_NOALLOC && insert == RB_UPDATE) {
34 		arena_stderr("WARNING: Cannot combine RB_NOALLOC and RB_UPDATE");
35 		arena_free(rbtree);
36 		return NULL;
37 	}
38 
39 	rbtree->alloc = alloc;
40 	rbtree->insert = insert;
41 	rbtree->root = NULL;
42 
43 	return rbtree;
44 }
45 
46 __weak
47 int rb_destroy(struct rbtree __arena *rbtree)
48 {
49 	int ret = 0;
50 
51 	arena_subprog_init();
52 
53 	if (unlikely(!rbtree))
54 		return -EINVAL;
55 
56 	if (rbtree->alloc == RB_NOALLOC) {
57 		/*
58 		 * We cannot do anything about RB_NOALLOC nodes. The whole
59 		 * point of RB_NOALLOC is that the nodes are directly owned
60 		 * by the caller that allocates and inserts them. We could
61 		 * unilaterally grab all nodes and free them anyway, but that
62 		 * would almost certainly cause UAF as the callers keep accessing
63 		 * the now freed nodes. Throw an error instead.
64 		 */
65 		if (rbtree->root) {
66 			arena_stderr("WARNING: Destroying RB_NOALLOC tree with > 0 nodes");
67 			return -EBUSY;
68 		}
69 
70 		goto out;
71 	}
72 
73 	while (rbtree->root && can_loop) {
74 		ret = rb_remove(rbtree, rbtree->root->key);
75 		if (ret)
76 			break;
77 	}
78 
79 out:
80 	arena_free(rbtree);
81 	return ret;
82 }
83 
84 static inline int rbnode_dir(struct rbnode __arena *node)
85 {
86 	/* Arbitrarily choose a direction for the root. */
87 	if (unlikely(!node->parent))
88 		return 0;
89 
90 	return (node->parent->left == node) ? 0 : 1;
91 }
92 
93 /*
94  * The __noinline is to prevent inlining from bloating the add
95  * remove calls, in turn causing register splits and increasing
96  * stack usage above what is permitted.
97  */
98 __noinline
99 int rbnode_rotate(struct rbtree __arena *rbtree,
100 		  struct rbnode __arena *node, int dir)
101 {
102 	struct rbnode __arena *tmp, *parent;
103 	int parentdir;
104 
105 	parent = node->parent;
106 	if (parent)
107 		parentdir = rbnode_dir(node);
108 
109 	/* If we're doing a root change, are we the root? */
110 	if (unlikely(!parent && rbtree->root != node))
111 		return -EINVAL;
112 
113 	/*
114 	 * Does the node we're turning into the root into exist?
115 	 * Note that the new root is on the opposite side of the
116 	 * rotation's direction.
117 	 */
118 	tmp = node->child[1 - dir];
119 	if (unlikely(!tmp))
120 		return -EINVAL;
121 
122 	/* Steal the closest child of the new root. */
123 	node->child[1 - dir] = tmp->child[dir];
124 	if (node->child[1 - dir])
125 		node->child[1 - dir]->parent = node;
126 
127 	/* Put the node below the new root.*/
128 	tmp->child[dir] = node;
129 	node->parent = tmp;
130 
131 	tmp->parent = parent;
132 	if (parent)
133 		parent->child[parentdir] = tmp;
134 	else
135 		rbtree->root = tmp;
136 
137 	return 0;
138 }
139 
140 static
141 struct rbnode __arena *rbnode_find(struct rbnode __arena *subtree, u64 key)
142 {
143 	struct rbnode __arena *node = subtree;
144 	int dir;
145 
146 	if (!subtree)
147 		return NULL;
148 
149 	while (can_loop) {
150 		if (node->key == key)
151 			break;
152 
153 		dir = (key < node->key) ? 0 : 1;
154 
155 		if (!node->child[dir])
156 			break;
157 
158 		node = node->child[dir];
159 	}
160 
161 	return node;
162 }
163 
164 static
165 struct rbnode __arena *rbnode_least_upper_bound(struct rbnode __arena *subtree, uint64_t key)
166 {
167 	struct rbnode __arena *node = subtree;
168 	int dir;
169 
170 	if (!subtree)
171 		return NULL;
172 
173 	while (can_loop) {
174 		dir = (key <= node->key) ? 0 : 1;
175 
176 		if (!node->child[dir])
177 			break;
178 
179 		node = node->child[dir];
180 	}
181 
182 	return node;
183 }
184 
185 __weak
186 int rb_find(struct rbtree __arena *rbtree, u64 key, u64 *value)
187 {
188 	struct rbnode __arena *node;
189 
190 	if (unlikely(!rbtree))
191 		return -EINVAL;
192 
193 	if (unlikely(!value))
194 		return -EINVAL;
195 
196 	node = rbnode_find(rbtree->root, key);
197 	if (!node || node->key != key)
198 		return -ENOENT;
199 
200 	*value = node->value;
201 
202 	return 0;
203 }
204 
205 __weak
206 struct rbnode __arena *rb_node_alloc(u64 key, u64 value)
207 {
208 	struct rbnode __arena *rbnode = NULL;
209 
210 	rbnode = (struct rbnode __arena *)arena_malloc(sizeof(*rbnode));
211 	if (!rbnode)
212 		return NULL;
213 
214 	/*
215 	 * WARNING: The order of assignments is weird on purpose.
216 	 * See comment in rb_insert_node() for more context.
217 	 * TL;DR: Prevent consecutive 0 assignments from being
218 	 * promoted into an unverifiable memset by the compiler.
219 	 */
220 
221 	rbnode->key = key;
222 	rbnode->parent = NULL;
223 	rbnode->value = value;
224 	rbnode->left = NULL;
225 	rbnode->is_red = true;
226 	rbnode->right = NULL;
227 
228 	return rbnode;
229 }
230 
231 __weak
232 void rb_node_free(struct rbnode __arena *rbnode)
233 {
234 	arena_free(rbnode);
235 }
236 
237 static
238 int rb_node_insert(struct rbtree __arena *rbtree,
239 		   struct rbnode __arena *node)
240 {
241 	struct rbnode __arena *grandparent, *parent = rbtree->root;
242 	u64 key = node->key;
243 	struct rbnode __arena *uncle;
244 	int dir;
245 	int ret;
246 
247 	if (unlikely(!rbtree))
248 		return -EINVAL;
249 
250 	if (!parent) {
251 		rbtree->root = node;
252 		return 0;
253 	}
254 
255 	if (rbtree->insert != RB_DUPLICATE)
256 		parent = rbnode_find(parent, key);
257 	else
258 		parent = rbnode_least_upper_bound(parent, key);
259 
260 	if (key == parent->key && rbtree->insert != RB_DUPLICATE) {
261 		if (rbtree->insert == RB_UPDATE) {
262 			/*
263 			 * Replace the old node with the new one.
264 			 * Free up the old node.
265 			 */
266 			ret = rbnode_replace(rbtree, parent, node);
267 			if (ret)
268 				return ret;
269 
270 			if (rbtree->alloc == RB_ALLOC)
271 				rb_node_free(parent);
272 
273 			return 0;
274 		}
275 
276 		/* Otherwise it's RB_DEFAULT. */
277 		return -EALREADY;
278 	}
279 
280 	node->parent = parent;
281 	/* Also works if key == parent->key. */
282 	if (key <= parent->key)
283 		parent->left = node;
284 	else
285 		parent->right = node;
286 
287 	while (can_loop) {
288 		parent = node->parent;
289 		if (!parent)
290 			return 0;
291 
292 		if (!parent->is_red)
293 			return 0;
294 
295 		grandparent = parent->parent;
296 		if (!grandparent) {
297 			parent->is_red = false;
298 			return 0;
299 		}
300 
301 		dir = rbnode_dir(parent);
302 		uncle = grandparent->child[1 - dir];
303 
304 		if (!uncle || !uncle->is_red) {
305 			if (node == parent->child[1 - dir]) {
306 				rbnode_rotate(rbtree, parent, dir);
307 				node = parent;
308 				parent = grandparent->child[dir];
309 			}
310 
311 			rbnode_rotate(rbtree, grandparent, 1 - dir);
312 			parent->is_red = false;
313 			grandparent->is_red = true;
314 
315 			return 0;
316 		}
317 
318 		/* Uncle is red. */
319 
320 		parent->is_red = false;
321 		uncle->is_red = false;
322 		grandparent->is_red = true;
323 
324 		node = grandparent;
325 	}
326 
327 	return 0;
328 }
329 
330 int rb_insert_node(struct rbtree __arena *rbtree,
331 		   struct rbnode __arena *node)
332 {
333 	if (unlikely(!rbtree))
334 		return -EINVAL;
335 
336 	if (unlikely(rbtree->alloc == RB_ALLOC))
337 		return -EINVAL;
338 
339 	node->left = NULL;
340 
341 	/*
342 	 * Workaround to break an optimization that causes
343 	 * verification failures on some compilers. Assignments
344 	 * of the kind
345 	 *
346 	 * *(r0 + 0) = 0;
347 	 * *(r0 + 8) = 0;
348 	 * *(r0 + 16) = 0;
349 	 *
350 	 * get promoted into a memset, and that in turn is not
351 	 * handled properly for arena memory by LLVM 21 and GCC 15.
352 	 * Add a barrier for now to prevent the assignments from being fused.
353 	 */
354 	barrier();
355 
356 	node->parent = NULL;
357 	node->right = NULL;
358 
359 	node->is_red = true;
360 
361 	return rb_node_insert(rbtree, node);
362 }
363 
364 __weak
365 int rb_insert(struct rbtree __arena *rbtree, u64 key, u64 value)
366 {
367 	struct rbnode __arena *node;
368 	int ret;
369 
370 	if (unlikely(!rbtree))
371 		return -EINVAL;
372 
373 	if (unlikely(rbtree->alloc != RB_ALLOC))
374 		return -EINVAL;
375 
376 	node = rb_node_alloc(key, value);
377 	if (!node)
378 		return -ENOMEM;
379 
380 	ret = rb_node_insert(rbtree, node);
381 	if (ret) {
382 		rb_node_free(node);
383 		return ret;
384 	}
385 
386 	return 0;
387 }
388 
389 static inline struct rbnode __arena *rbnode_least(struct rbnode __arena *subtree)
390 {
391 	while (subtree->left && can_loop)
392 		subtree = subtree->left;
393 
394 	return subtree;
395 }
396 
397 __weak int rb_least(struct rbtree __arena *rbtree, u64 *key, u64 *value)
398 {
399 	struct rbnode __arena *least;
400 
401 	if (unlikely(!rbtree))
402 		return -EINVAL;
403 
404 	if (!rbtree->root)
405 		return -ENOENT;
406 
407 	least = rbnode_least(rbtree->root);
408 	if (key)
409 		*key = least->key;
410 	if (value)
411 		*value = least->value;
412 
413 	return 0;
414 }
415 
416 
417 /*
418  * If we are referencing ourselves, a and b have a parent-child relation,
419  * and we should be pointing at the other node instead.
420  */
421 static inline void rbnode_fixup_pointers(struct rbnode __arena *a,
422 					 struct rbnode __arena *b)
423 {
424 #define fixup(n1, n2, member) do { if (n1->member == n1) n1->member = n2; } while (0)
425 	fixup(a, b, left);
426 	fixup(a, b, right);
427 	fixup(a, b, parent);
428 #undef fixup
429 }
430 
431 static inline void rbnode_swap_values(struct rbnode __arena *a,
432 				      struct rbnode __arena *b)
433 {
434 #define swap(n1, n2, tmp) do { (tmp) = (n1); (n1) = (n2); (n2) = (tmp); } while (0)
435 	struct rbnode __arena *tmpnode;
436 	u64 tmp;
437 
438 	/* Swap the pointers. */
439 	swap(a->is_red, b->is_red, tmp);
440 
441 	swap(a->left, b->left, tmpnode);
442 	swap(a->right, b->right, tmpnode);
443 	swap(a->parent, b->parent, tmpnode);
444 #undef swap
445 
446 	/* Account for the nodes being parent and child. */
447 	rbnode_fixup_pointers(b, a);
448 	rbnode_fixup_pointers(a, b);
449 }
450 
451 static inline void rbnode_adjust_neighbors(struct rbtree __arena *rbtree,
452 					   struct rbnode __arena *node, int dir)
453 {
454 	if (node->left)
455 		node->left->parent = node;
456 	if (node->right)
457 		node->right->parent = node;
458 
459 	if (node->parent) {
460 		node->parent->child[dir] = node;
461 		return;
462 	}
463 
464 	rbtree->root = node;
465 }
466 
467 /*
468  * Directly replace an existing node with a replacement. The replacement node
469  * should not already be in the tree.
470  */
471 static int rbnode_replace(struct rbtree __arena *rbtree,
472 			  struct rbnode __arena *existing,
473 			  struct rbnode __arena *replacement)
474 {
475 	int dir = 0;
476 
477 	if (unlikely(replacement->parent || replacement->left || replacement->right))
478 		return -EINVAL;
479 
480 	if (existing->parent)
481 		dir = rbnode_dir(existing);
482 
483 	replacement->is_red = existing->is_red;
484 	replacement->left = existing->left;
485 	replacement->right = existing->right;
486 	replacement->parent = existing->parent;
487 
488 	/* Fix up the new node's neighbors. */
489 	rbnode_adjust_neighbors(rbtree, replacement, dir);
490 
491 	return 0;
492 }
493 
494 /*
495  * Switch two nodes in the tree in place. This is useful during node deletion.
496  * This is more involved than switching the values of the two nodes because we
497  * must update all tree pointers.
498  */
499 static void rbnode_switch(struct rbtree __arena *rbtree,
500 			  struct rbnode __arena *a,
501 			  struct rbnode __arena *b)
502 {
503 	int adir = 0, bdir = 0;
504 
505 	/*
506 	 * Store the direction in the parent because we will not
507 	 * be able to recompute it once we start swapping values.
508 	 */
509 	if (a->parent)
510 		adir = rbnode_dir(a);
511 
512 	if (b->parent)
513 		bdir = rbnode_dir(b);
514 
515 	rbnode_swap_values(a, b);
516 
517 	/*
518 	 * Fix up the pointers from the children/parent to the
519 	 * new nodes.
520 	 */
521 	rbnode_adjust_neighbors(rbtree, a, bdir);
522 	rbnode_adjust_neighbors(rbtree, b, adir);
523 }
524 
525 static inline int rbnode_remove_node_single_child(struct rbtree __arena *rbtree,
526 						  struct rbnode __arena *node,
527 						  bool free)
528 {
529 	struct rbnode __arena *child;
530 	int dir;
531 
532 	if (unlikely(node->is_red)) {
533 		arena_stderr("Node unexpectedly red\n");
534 		return -EINVAL;
535 	}
536 
537 	child = node->left ? node->left : node->right;
538 	if (unlikely(!child->is_red)) {
539 		arena_stderr("Only child is black\n");
540 		return -EINVAL;
541 	}
542 
543 	/*
544 	 * Since it's the immediate child, we can just
545 	 * remove the parent.
546 	 */
547 	child->parent = node->parent;
548 
549 	if (node->parent) {
550 		dir = rbnode_dir(node);
551 		node->parent->child[dir] = child;
552 	} else {
553 		rbtree->root = child;
554 	}
555 
556 	/* Color the child black. */
557 	child->is_red = false;
558 
559 	/* Only free if called from rb_remove. */
560 	if (free)
561 		rb_node_free(node);
562 
563 	return 0;
564 }
565 
566 static inline bool rbnode_has_red_children(struct rbnode __arena *node)
567 {
568 	if (node->left && node->left->is_red)
569 		return true;
570 
571 	return node->right && node->right->is_red;
572 }
573 
574 static
575 int rb_node_remove(struct rbtree __arena *rbtree,
576 		   struct rbnode __arena *node)
577 {
578 	struct rbnode __arena *parent, *sibling, *close_nephew, *distant_nephew;
579 	bool free = (rbtree->alloc == RB_ALLOC);
580 	struct rbnode __arena *replace, *initial;
581 	bool is_red;
582 	int dir;
583 
584 	/* Both children present, replace with next largest key. */
585 	if (node->left && node->right) {
586 		/*
587 		 * Swap the node itself instead of just the
588 		 * key/value pair to account for nodes embedded
589 		 * in other structs.
590 		 */
591 
592 		replace = rbnode_least(node->right);
593 		rbnode_switch(rbtree, replace, node);
594 
595 		/*
596 		 * FALLTHROUGH: We moved the node we are removing to
597 		 * the leftmost position of the subtree. We can now
598 		 * remove it as if it was always where we moved it to.
599 		 */
600 	}
601 
602 	initial = node;
603 
604 	/* Only one child present, replace with child and paint it black. */
605 	if (!node->left != !node->right)
606 		return rbnode_remove_node_single_child(rbtree, node, free);
607 
608 	/* (!node->left && !node->right) */
609 
610 	parent = node->parent;
611 	if (!parent) {
612 		/* Check that we're _actually_ the root. */
613 		if (rbtree->root == node)
614 			rbtree->root = NULL;
615 		else
616 			arena_stderr("WARNING: Attempting to remove detached node from rbtree\n");
617 
618 		if (free)
619 			rb_node_free(node);
620 		return 0;
621 	}
622 
623 	dir = rbnode_dir(node);
624 	parent->child[dir] = NULL;
625 	is_red = node->is_red;
626 
627 	if (free)
628 		rb_node_free(node);
629 
630 	/* If we removed a red node, we did not unbalance the tree.*/
631 	if (is_red)
632 		return 0;
633 
634 	sibling = parent->child[1 - dir];
635 	if (unlikely(!sibling)) {
636 		arena_stderr("rbtree: removed black node has no sibling\n");
637 		return -EINVAL;
638 	}
639 
640 	/*
641 	 * We removed a black node, causing a change in path
642 	 * weight. Start rebalancing. The invariant is that
643 	 * all paths going through the node are shortened
644 	 * by one, and the current node is black.
645 	 */
646 	while (can_loop) {
647 
648 		/* Balancing reached the root, there can be no imbalance. */
649 		if (!parent)
650 			return 0;
651 
652 		/*
653 		 * We already determined the dir, either above or
654 		 * at the end of the loop.
655 		 */
656 
657 		/*
658 		 * If we have no sibling, the tree was
659 		 * already unbalanced.
660 		 */
661 		sibling = parent->child[1 - dir];
662 		if (unlikely(!sibling)) {
663 			arena_stderr("rbtree: removed black node has no sibling\n");
664 			return -EINVAL;
665 		}
666 
667 		/* Sibling is red, turn it into the grandparent. */
668 		if (sibling->is_red) {
669 			/*
670 			 * Sibling is red. Transform the tree to turn
671 			 * the sibling into the parent's position, and
672 			 * repaint them. This does not balance the tree
673 			 * but makes it so we know the sibling is black
674 			 * and so can use the transformations to balance.
675 			 */
676 			rbnode_rotate(rbtree, parent, dir);
677 			parent->is_red = true;
678 			sibling->is_red = false;
679 
680 			/* Our new sibling is now the close nephew. */
681 			sibling = parent->child[1 - dir];
682 			/* If sibling has any red siblings, break out. */
683 			if (rbnode_has_red_children(sibling))
684 				break;
685 
686 			/* We can repaint the sibling and parent, we're done. */
687 			sibling->is_red = true;
688 			parent->is_red = false;
689 
690 			return 0;
691 		}
692 
693 		/* Sibling guaranteed to be black. If it has red children, break out. */
694 		if (rbnode_has_red_children(sibling))
695 			break;
696 
697 		/*
698 		 * Both sibling and children are black. If parent is red, swap
699 		 * colors with the sibling. Otherwise
700 		 */
701 		if (parent->is_red) {
702 			parent->is_red = false;
703 			sibling->is_red = true;
704 			return 0;
705 		}
706 
707 		/*
708 		 * Parent, sibling, and all its children are black. Repaint the sibling.
709 		 * This shortens the paths through it, so pop up a level in the
710 		 * tree and repeat the balancing.
711 		 */
712 		sibling->is_red = true;
713 		node = parent;
714 		parent = node->parent;
715 		dir = rbnode_dir(node);
716 	}
717 
718 	if (node != initial) {
719 		dir = rbnode_dir(node);
720 		parent = node->parent;
721 		sibling = parent->child[1-dir];
722 	}
723 	/*
724 	 * Almost there. We know between the parent, sibling,
725 	 * and nephews only one or two of the nephews are red. If
726 	 * it is the close one, rotate it to the sibling position,
727 	 * paint it black, and paint the previous sibling red.
728 	 */
729 
730 	close_nephew = sibling->child[dir];
731 	distant_nephew = sibling->child[1 - dir];
732 
733 	/*
734 	 * If the distant red nephew is not red, rotate
735 	 * and repaint. We need the distant nephew
736 	 * to be red. We know the close nephew is red
737 	 * because at least one of them are, so the
738 	 * distant one is black if it exists.
739 	 */
740 	if (!distant_nephew || !distant_nephew->is_red) {
741 		rbnode_rotate(rbtree, sibling, 1 - dir);
742 		sibling->is_red = true;
743 		close_nephew->is_red = false;
744 		distant_nephew = sibling;
745 		sibling = close_nephew;
746 	}
747 
748 	/*
749 	 * We now know it's the distant nephew that's red.
750 	 * Rotate the sibling into our parent's position
751 	 * and paint both black.
752 	 */
753 
754 	rbnode_rotate(rbtree, parent, dir);
755 	sibling->is_red = parent->is_red;
756 	parent->is_red = false;
757 	distant_nephew->is_red = false;
758 
759 	return 0;
760 }
761 
762 __weak
763 int rb_remove_node(struct rbtree __arena *rbtree,
764 		   struct rbnode __arena *node)
765 {
766 	if (unlikely(!rbtree))
767 		return -EINVAL;
768 
769 	if (unlikely(rbtree->alloc == RB_ALLOC))
770 		return -EINVAL;
771 
772 	return rb_node_remove(rbtree, node);
773 }
774 
775 __weak
776 int rb_remove(struct rbtree __arena *rbtree, u64 key)
777 {
778 	struct rbnode __arena *node;
779 
780 	if (unlikely(!rbtree))
781 		return -EINVAL;
782 
783 	if (unlikely(rbtree->alloc != RB_ALLOC))
784 		return -EINVAL;
785 
786 	if (!rbtree->root)
787 		return -ENOENT;
788 
789 	node = rbnode_find(rbtree->root, key);
790 	if (!node || node->key != key)
791 		return -ENOENT;
792 
793 	return rb_node_remove(rbtree, node);
794 }
795 
796 __weak
797 int rb_pop(struct rbtree __arena *rbtree, u64 *key, u64 *value)
798 {
799 	struct rbnode __arena *node;
800 
801 	if (unlikely(!rbtree))
802 		return -EINVAL;
803 
804 	if (!rbtree->root)
805 		return -ENOENT;
806 
807 	if (rbtree->alloc != RB_ALLOC)
808 		return -EINVAL;
809 
810 	node = rbnode_least(rbtree->root);
811 	if (unlikely(!node))
812 		return -ENOENT;
813 
814 	if (key)
815 		*key = node->key;
816 	if (value)
817 		*value = node->value;
818 
819 	return rb_node_remove(rbtree, node);
820 }
821 
822 inline void rbnode_print(size_t depth, struct rbnode __arena *rbn)
823 {
824 	arena_stderr("[DEPTH %d] %p (%s)\n PARENT %p", depth, rbn, rbn->is_red ? "red" : "black", rbn->parent);
825 	arena_stderr("\tKV (%ld, %ld)\n LEFT %p RIGHT %p]\n", rbn->key, rbn->value, rbn->left, rbn->right);
826 }
827 
828 enum rb_print_state {
829 	RB_NONE_VISITED,
830 	RB_LEFT_VISITED,
831 	RB_RIGHT_VISITED,
832 };
833 
834 __weak
835 enum rb_print_state rb_print_next_state(struct rbnode __arena *rbnode,
836 					enum rb_print_state state, u64 *next)
837 {
838 	if (unlikely(!next))
839 		return RB_NONE_VISITED;
840 
841 	switch (state) {
842 	case RB_NONE_VISITED:
843 		if (rbnode->left) {
844 			*next = (u64)rbnode->left;
845 			state = RB_LEFT_VISITED;
846 			break;
847 		}
848 
849 		/* FALLTHROUGH */
850 
851 	case RB_LEFT_VISITED:
852 		if (rbnode->right) {
853 			*next = (u64)rbnode->right;
854 			state = RB_RIGHT_VISITED;
855 			break;
856 		}
857 
858 		/* FALLTHROUGH */
859 
860 	default:
861 		*next = 0;
862 		state = RB_RIGHT_VISITED;
863 	}
864 
865 	return state;
866 }
867 
868 __weak
869 int rb_print_pop_up(struct rbnode __arena **rbnodep, u8 *depthp, enum rb_print_state (*stack)[RB_MAXLVL_PRINT], enum rb_print_state *state)
870 {
871 	struct rbnode __arena *rbnode;
872 	volatile u8 depth;
873 	int j;
874 
875 	if (unlikely(!rbnodep || !depthp || !stack || !state))
876 		return -EINVAL;
877 
878 	rbnode = *rbnodep;
879 	depth = *depthp;
880 
881 	for (j = 0; j < RB_MAXLVL_PRINT && can_loop; j++) {
882 		if (*state != RB_RIGHT_VISITED)
883 			break;
884 
885 		depth -= 1;
886 		if (depth < 0 || depth >= RB_MAXLVL_PRINT)
887 			break;
888 
889 		*state = (*stack)[depth % RB_MAXLVL_PRINT];
890 		rbnode = rbnode->parent;
891 	}
892 
893 	*rbnodep = rbnode;
894 	*depthp = depth;
895 
896 	return 0;
897 }
898 
899 __weak
900 int rb_print(struct rbtree __arena *rbtree)
901 {
902 	enum rb_print_state stack[RB_MAXLVL_PRINT];
903 	struct rbnode __arena *rbnode = rbtree->root;
904 	enum rb_print_state state;
905 	struct rbnode __arena *next;
906 	u64 next_addr;
907 	u8 depth;
908 	int ret;
909 
910 	if (unlikely(!rbtree))
911 		return -EINVAL;
912 
913 	depth = 0;
914 	state = RB_NONE_VISITED;
915 
916 	arena_stderr("=== RB TREE START ===\n");
917 
918 	if (!rbtree->root)
919 		goto out;
920 
921 	/* Even with can_loop, the verifier doesn't like infinite loops. */
922 	while (can_loop) {
923 		if (state == RB_NONE_VISITED)
924 			rbnode_print(depth, rbnode);
925 
926 		/* Find which child to traverse next. */
927 		state = rb_print_next_state(rbnode, state, &next_addr);
928 		next = (struct rbnode __arena *)next_addr;
929 
930 		/* Child found. Store the node state and go on. */
931 		if (next) {
932 			if (depth < 0 || depth >= RB_MAXLVL_PRINT)
933 				return 0;
934 
935 			stack[depth++] = state;
936 
937 			rbnode = next;
938 			state = RB_NONE_VISITED;
939 
940 			continue;
941 		}
942 
943 		/* Otherwise, go as far up as possible. */
944 		ret = rb_print_pop_up(&rbnode, &depth, &stack, &state);
945 		if (ret)
946 			return -EINVAL;
947 
948 		if (depth < 0 || depth >= RB_MAXLVL_PRINT) {
949 			arena_stderr("=== RB TREE END (depth %d\n)===", depth);
950 			return 0;
951 		}
952 
953 	}
954 
955 out:
956 	arena_stderr("=== RB TREE END ===\n");
957 
958 	return 0;
959 }
960 
961 __weak
962 int rb_integrity_check(struct rbtree __arena *rbtree)
963 {
964 	enum rb_print_state stack[RB_MAXLVL_PRINT];
965 	struct rbnode __arena *rbnode = rbtree->root;
966 	enum rb_print_state state;
967 	struct rbnode __arena *next;
968 	u64 next_addr;
969 	u8 depth;
970 	int ret;
971 
972 	if (unlikely(!rbtree))
973 		return -EINVAL;
974 
975 	if (!rbtree->root)
976 		return 0;
977 
978 	depth = 0;
979 	state = RB_NONE_VISITED;
980 
981 	/* Even with can_loop, the verifier doesn't like infinite loops. */
982 	while (can_loop) {
983 		if (rbnode->parent && rbnode->parent->left != rbnode
984 			&& rbnode->parent->right != rbnode) {
985 			arena_stderr("WARNING: Inconsistent tree. Parent %p has no child %p\n", rbnode->parent, rbnode);
986 			return -EINVAL;
987 		}
988 
989 		if (rbnode->parent == rbnode) {
990 			arena_stderr("WARNING: Inconsistent tree, node %p is its own parent\n", rbnode);
991 			return -EINVAL;
992 		}
993 
994 		if (rbnode->left == rbnode) {
995 			arena_stderr("WARNING: Inconsistent tree, node %p is its own left child\n", rbnode);
996 			return -EINVAL;
997 		}
998 
999 		if (rbnode->right == rbnode) {
1000 			arena_stderr("WARNING: Inconsistent tree, node %p is its own right child\n", rbnode);
1001 			return -EINVAL;
1002 		}
1003 
1004 		if (rbnode->is_red) {
1005 			if (rbnode->left && rbnode->left->is_red) {
1006 				arena_stderr("WARNING: Inconsistent tree. Parent has %p has red child %p\n", rbnode, rbnode->left);
1007 				return -EINVAL;
1008 			}
1009 			if (rbnode->right && rbnode->right->is_red) {
1010 				arena_stderr("WARNING: Inconsistent tree. Parent has %p has red child %p\n", rbnode, rbnode->right);
1011 				return -EINVAL;
1012 			}
1013 		} else if (rbnode->parent && rbnode->parent->child[1 - rbnode_dir(rbnode)] == NULL) {
1014 			arena_stderr("WARNING: Inconsistent tree. Black node %p has no sibling\n", rbnode);
1015 			return -EINVAL;
1016 		}
1017 
1018 		/* Find which child to traverse next. */
1019 		state = rb_print_next_state(rbnode, state, &next_addr);
1020 		next = (struct rbnode __arena *)next_addr;
1021 
1022 		/* Child found. Store the node state and go on. */
1023 		if (next) {
1024 			if (depth < 0 || depth >= RB_MAXLVL_PRINT)
1025 				return 0;
1026 
1027 			stack[depth++] = state;
1028 
1029 			rbnode = next;
1030 			state = RB_NONE_VISITED;
1031 
1032 			continue;
1033 		}
1034 
1035 		/* Otherwise, go as far up as possible. */
1036 		ret = rb_print_pop_up(&rbnode, &depth, &stack, &state);
1037 		if (ret)
1038 			return -EINVAL;
1039 
1040 		if (depth < 0 || depth >= RB_MAXLVL_PRINT) {
1041 			return 0;
1042 		}
1043 
1044 	}
1045 
1046 	return 0;
1047 }
1048