Module Name:    src
Committed By:   riastradh
Date:           Sun Feb 27 14:18:25 UTC 2022

Modified Files:
        src/sys/external/bsd/drm2/include/linux: interval_tree.h rbtree.h

Log Message:
linux: Actually do post-order tree traversal.

Requires breaking the rbtree(3) abstraction, but this is necessary
because the body of the loop often frees the element, so as is we had
a huge pile of use-after-free going on.

Requires changing struct interval_tree_node's rbnode member to match
the Linux name, since we now use container_of here, and radeon relies
on this.


To generate a diff of this commit:
cvs rdiff -u -r1.12 -r1.13 \
    src/sys/external/bsd/drm2/include/linux/interval_tree.h
cvs rdiff -u -r1.16 -r1.17 src/sys/external/bsd/drm2/include/linux/rbtree.h

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/external/bsd/drm2/include/linux/interval_tree.h
diff -u src/sys/external/bsd/drm2/include/linux/interval_tree.h:1.12 src/sys/external/bsd/drm2/include/linux/interval_tree.h:1.13
--- src/sys/external/bsd/drm2/include/linux/interval_tree.h:1.12	Sun Dec 19 11:00:18 2021
+++ src/sys/external/bsd/drm2/include/linux/interval_tree.h	Sun Feb 27 14:18:25 2022
@@ -1,4 +1,4 @@
-/*	$NetBSD: interval_tree.h,v 1.12 2021/12/19 11:00:18 riastradh Exp $	*/
+/*	$NetBSD: interval_tree.h,v 1.13 2022/02/27 14:18:25 riastradh Exp $	*/
 
 /*-
  * Copyright (c) 2018 The NetBSD Foundation, Inc.
@@ -43,7 +43,7 @@
 #include <linux/rbtree.h>
 
 struct interval_tree_node {
-	struct rb_node	itn_node;
+	struct rb_node	rb;
 	unsigned long	start;	/* inclusive */
 	unsigned long	last;	/* inclusive */
 };
@@ -81,7 +81,7 @@ interval_tree_compare_key(void *cookie, 
 static const rb_tree_ops_t interval_tree_ops = {
 	.rbto_compare_nodes = interval_tree_compare_nodes,
 	.rbto_compare_key = interval_tree_compare_key,
-	.rbto_node_offset = offsetof(struct interval_tree_node, itn_node),
+	.rbto_node_offset = offsetof(struct interval_tree_node, rb),
 };
 
 static inline void

Index: src/sys/external/bsd/drm2/include/linux/rbtree.h
diff -u src/sys/external/bsd/drm2/include/linux/rbtree.h:1.16 src/sys/external/bsd/drm2/include/linux/rbtree.h:1.17
--- src/sys/external/bsd/drm2/include/linux/rbtree.h:1.16	Mon Feb 14 19:13:04 2022
+++ src/sys/external/bsd/drm2/include/linux/rbtree.h	Sun Feb 27 14:18:25 2022
@@ -1,4 +1,4 @@
-/*	$NetBSD: rbtree.h,v 1.16 2022/02/14 19:13:04 riastradh Exp $	*/
+/*	$NetBSD: rbtree.h,v 1.17 2022/02/27 14:18:25 riastradh Exp $	*/
 
 /*-
  * Copyright (c) 2013 The NetBSD Foundation, Inc.
@@ -144,15 +144,72 @@ rb_replace_node_cached(struct rb_node *o
 }
 
 /*
- * XXX This is not actually postorder, but I can't fathom why you would
- * want postorder for an ordered tree; different insertion orders lead
- * to different traversal orders.
+ * This violates the abstraction of rbtree(3) for postorder traversal
+ * -- children first, then parents -- so it is safe for cleanup code
+ * that just frees all the nodes without removing them from the tree.
  */
-#define	rbtree_postorder_for_each_entry_safe(NODE, TMP, ROOT, FIELD)	      \
-	for ((NODE) = RB_TREE_MIN(&(ROOT)->rbr_tree);			      \
-		((NODE) != NULL &&					      \
-		    ((TMP) = rb_tree_iterate(&(ROOT)->rbr_tree, (NODE),	      \
-			RB_DIR_RIGHT), 1));				      \
-		(NODE) = (TMP))
+static inline struct rb_node *
+rb_first_postorder(const struct rb_root *root)
+{
+	struct rb_node *node, *child;
+
+	if ((node = root->rbr_tree.rbt_root) == NULL)
+		return NULL;
+	for (;; node = child) {
+		if ((child = node->rb_left) != NULL)
+			continue;
+		if ((child = node->rb_right) != NULL)
+			continue;
+		return node;
+	}
+}
+
+static inline struct rb_node *
+rb_next2_postorder(const struct rb_root *root, struct rb_node *node)
+{
+	struct rb_node *parent, *child;
+
+	if (node == NULL)
+		return NULL;
+
+	/*
+	 * If we're at the root, there are no more siblings and no
+	 * parent, so post-order iteration is done.
+	 */
+	if (RB_ROOT_P(&root->rbr_tree, node))
+		return NULL;
+	parent = RB_FATHER(node);	/* kinda sexist, innit */
+	KASSERT(parent != NULL);
+
+	/*
+	 * If we're the right child, we've already processed the left
+	 * child (which may be gone by now), so just return the parent.
+	 */
+	if (RB_RIGHT_P(node))
+		return parent;
+
+	/*
+	 * Otherwise, move down to the leftmost child of our right
+	 * sibling -- or return the parent if there is none.
+	 */
+	if ((node = parent->rb_right) == NULL)
+		return parent;
+	for (;; node = child) {
+		if ((child = node->rb_left) != NULL)
+			continue;
+		if ((child = node->rb_right) != NULL)
+			continue;
+		return node;
+	}
+}
+
+#define	rbtree_postorder_for_each_entry_safe(ENTRY, TMP, ROOT, FIELD)	      \
+	for ((ENTRY) = rb_entry_safe(rb_first_postorder(ROOT),		      \
+		    __typeof__(*(ENTRY)), FIELD);			      \
+		((ENTRY) != NULL &&					      \
+		    ((TMP) = rb_entry_safe(rb_next2_postorder((ROOT),	      \
+			    &(ENTRY)->FIELD), __typeof__(*(ENTRY)), FIELD),   \
+			1));						      \
+		(ENTRY) = (TMP))
 
 #endif  /* _LINUX_RBTREE_H_ */

Reply via email to