aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2016-07-01 02:53:51 -0400
committerDavid Howells <dhowells@redhat.com>2016-07-06 05:51:14 -0400
commitc1adf20052d80f776849fa2c1acb472cdeb7786c (patch)
treee73407fdc52ab77e9666ae7798c8b38b6da1ed09
parent1291e9d1084506c5cba6313ce809d7516bb5868a (diff)
Introduce rb_replace_node_rcu()
Implement an RCU-safe variant of rb_replace_node() and rearrange rb_replace_node() to do things in the same order. Signed-off-by: David Howells <dhowells@redhat.com> Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
-rw-r--r--include/linux/rbtree.h2
-rw-r--r--include/linux/rbtree_augmented.h13
-rw-r--r--lib/rbtree.c26
3 files changed, 39 insertions, 2 deletions
diff --git a/include/linux/rbtree.h b/include/linux/rbtree.h
index b6900099ea81..e585018498d5 100644
--- a/include/linux/rbtree.h
+++ b/include/linux/rbtree.h
@@ -76,6 +76,8 @@ extern struct rb_node *rb_next_postorder(const struct rb_node *);
76/* Fast replacement of a single node without remove/rebalance/add/rebalance */ 76/* Fast replacement of a single node without remove/rebalance/add/rebalance */
77extern void rb_replace_node(struct rb_node *victim, struct rb_node *new, 77extern void rb_replace_node(struct rb_node *victim, struct rb_node *new,
78 struct rb_root *root); 78 struct rb_root *root);
79extern void rb_replace_node_rcu(struct rb_node *victim, struct rb_node *new,
80 struct rb_root *root);
79 81
80static inline void rb_link_node(struct rb_node *node, struct rb_node *parent, 82static inline void rb_link_node(struct rb_node *node, struct rb_node *parent,
81 struct rb_node **rb_link) 83 struct rb_node **rb_link)
diff --git a/include/linux/rbtree_augmented.h b/include/linux/rbtree_augmented.h
index 14d7b831b63a..d076183e49be 100644
--- a/include/linux/rbtree_augmented.h
+++ b/include/linux/rbtree_augmented.h
@@ -130,6 +130,19 @@ __rb_change_child(struct rb_node *old, struct rb_node *new,
130 WRITE_ONCE(root->rb_node, new); 130 WRITE_ONCE(root->rb_node, new);
131} 131}
132 132
133static inline void
134__rb_change_child_rcu(struct rb_node *old, struct rb_node *new,
135 struct rb_node *parent, struct rb_root *root)
136{
137 if (parent) {
138 if (parent->rb_left == old)
139 rcu_assign_pointer(parent->rb_left, new);
140 else
141 rcu_assign_pointer(parent->rb_right, new);
142 } else
143 rcu_assign_pointer(root->rb_node, new);
144}
145
133extern void __rb_erase_color(struct rb_node *parent, struct rb_root *root, 146extern void __rb_erase_color(struct rb_node *parent, struct rb_root *root,
134 void (*augment_rotate)(struct rb_node *old, struct rb_node *new)); 147 void (*augment_rotate)(struct rb_node *old, struct rb_node *new));
135 148
diff --git a/lib/rbtree.c b/lib/rbtree.c
index 1356454e36de..eb8a19fee110 100644
--- a/lib/rbtree.c
+++ b/lib/rbtree.c
@@ -539,17 +539,39 @@ void rb_replace_node(struct rb_node *victim, struct rb_node *new,
539{ 539{
540 struct rb_node *parent = rb_parent(victim); 540 struct rb_node *parent = rb_parent(victim);
541 541
542 /* Copy the pointers/colour from the victim to the replacement */
543 *new = *victim;
544
542 /* Set the surrounding nodes to point to the replacement */ 545 /* Set the surrounding nodes to point to the replacement */
543 __rb_change_child(victim, new, parent, root);
544 if (victim->rb_left) 546 if (victim->rb_left)
545 rb_set_parent(victim->rb_left, new); 547 rb_set_parent(victim->rb_left, new);
546 if (victim->rb_right) 548 if (victim->rb_right)
547 rb_set_parent(victim->rb_right, new); 549 rb_set_parent(victim->rb_right, new);
550 __rb_change_child(victim, new, parent, root);
551}
552EXPORT_SYMBOL(rb_replace_node);
553
554void rb_replace_node_rcu(struct rb_node *victim, struct rb_node *new,
555 struct rb_root *root)
556{
557 struct rb_node *parent = rb_parent(victim);
548 558
549 /* Copy the pointers/colour from the victim to the replacement */ 559 /* Copy the pointers/colour from the victim to the replacement */
550 *new = *victim; 560 *new = *victim;
561
562 /* Set the surrounding nodes to point to the replacement */
563 if (victim->rb_left)
564 rb_set_parent(victim->rb_left, new);
565 if (victim->rb_right)
566 rb_set_parent(victim->rb_right, new);
567
568 /* Set the parent's pointer to the new node last after an RCU barrier
569 * so that the pointers onwards are seen to be set correctly when doing
570 * an RCU walk over the tree.
571 */
572 __rb_change_child_rcu(victim, new, parent, root);
551} 573}
552EXPORT_SYMBOL(rb_replace_node); 574EXPORT_SYMBOL(rb_replace_node_rcu);
553 575
554static struct rb_node *rb_left_deepest_node(const struct rb_node *node) 576static struct rb_node *rb_left_deepest_node(const struct rb_node *node)
555{ 577{