Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/external/bsd/drm2/include/linux linux: Actually do post-...



details:   https://anonhg.NetBSD.org/src/rev/45c8547d45dc
branches:  trunk
changeset: 362430:45c8547d45dc
user:      riastradh <riastradh%NetBSD.org@localhost>
date:      Sun Feb 27 14:18:25 2022 +0000

description:
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.

diffstat:

 sys/external/bsd/drm2/include/linux/interval_tree.h |   6 +-
 sys/external/bsd/drm2/include/linux/rbtree.h        |  77 ++++++++++++++++++--
 2 files changed, 70 insertions(+), 13 deletions(-)

diffs (118 lines):

diff -r c604c42641f5 -r 45c8547d45dc sys/external/bsd/drm2/include/linux/interval_tree.h
--- a/sys/external/bsd/drm2/include/linux/interval_tree.h       Sun Feb 27 14:17:10 2022 +0000
+++ b/sys/external/bsd/drm2/include/linux/interval_tree.h       Sun Feb 27 14:18:25 2022 +0000
@@ -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 @@
 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
diff -r c604c42641f5 -r 45c8547d45dc sys/external/bsd/drm2/include/linux/rbtree.h
--- a/sys/external/bsd/drm2/include/linux/rbtree.h      Sun Feb 27 14:17:10 2022 +0000
+++ b/sys/external/bsd/drm2/include/linux/rbtree.h      Sun Feb 27 14:18:25 2022 +0000
@@ -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 @@
 }
 
 /*
- * 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_ */



Home | Main Index | Thread Index | Old Index