diff options
| -rw-r--r-- | include/linux/rbtree.h | 2 | ||||
| -rw-r--r-- | include/linux/rbtree_augmented.h | 13 | ||||
| -rw-r--r-- | lib/rbtree.c | 26 |
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 */ |
| 77 | extern void rb_replace_node(struct rb_node *victim, struct rb_node *new, | 77 | extern void rb_replace_node(struct rb_node *victim, struct rb_node *new, |
| 78 | struct rb_root *root); | 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 | static inline void rb_link_node(struct rb_node *node, struct rb_node *parent, | 82 | static 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 | ||
| 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 | extern void __rb_erase_color(struct rb_node *parent, struct rb_root *root, | 146 | extern 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 | } | ||
| 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); | ||
| 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 | } |
| 552 | EXPORT_SYMBOL(rb_replace_node); | 574 | EXPORT_SYMBOL(rb_replace_node_rcu); |
| 553 | 575 | ||
| 554 | static struct rb_node *rb_left_deepest_node(const struct rb_node *node) | 576 | static struct rb_node *rb_left_deepest_node(const struct rb_node *node) |
| 555 | { | 577 | { |
