Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

+40 -3
+2
include/linux/rbtree.h
··· 76 76 /* Fast replacement of a single node without remove/rebalance/add/rebalance */ 77 77 extern void rb_replace_node(struct rb_node *victim, struct rb_node *new, 78 78 struct rb_root *root); 79 + extern void rb_replace_node_rcu(struct rb_node *victim, struct rb_node *new, 80 + struct rb_root *root); 79 81 80 82 static inline void rb_link_node(struct rb_node *node, struct rb_node *parent, 81 83 struct rb_node **rb_link)
+13
include/linux/rbtree_augmented.h
··· 130 130 WRITE_ONCE(root->rb_node, new); 131 131 } 132 132 133 + static 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 + 133 146 extern void __rb_erase_color(struct rb_node *parent, struct rb_root *root, 134 147 void (*augment_rotate)(struct rb_node *old, struct rb_node *new)); 135 148
+25 -3
lib/rbtree.c
··· 539 539 { 540 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 545 /* Set the surrounding nodes to point to the replacement */ 546 + if (victim->rb_left) 547 + rb_set_parent(victim->rb_left, new); 548 + if (victim->rb_right) 549 + rb_set_parent(victim->rb_right, new); 543 550 __rb_change_child(victim, new, parent, root); 551 + } 552 + EXPORT_SYMBOL(rb_replace_node); 553 + 554 + void 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); 558 + 559 + /* Copy the pointers/colour from the victim to the replacement */ 560 + *new = *victim; 561 + 562 + /* Set the surrounding nodes to point to the replacement */ 544 563 if (victim->rb_left) 545 564 rb_set_parent(victim->rb_left, new); 546 565 if (victim->rb_right) 547 566 rb_set_parent(victim->rb_right, new); 548 567 549 - /* Copy the pointers/colour from the victim to the replacement */ 550 - *new = *victim; 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 } 552 - EXPORT_SYMBOL(rb_replace_node); 574 + EXPORT_SYMBOL(rb_replace_node_rcu); 553 575 554 576 static struct rb_node *rb_left_deepest_node(const struct rb_node *node) 555 577 {