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_ */