提交 98e55a21 编写于 作者: D Davidlohr Bueso 提交者: Cheng Jian

lib/timerqueue: Rely on rbtree semantics for next timer

mainline inclusion
from mainline-v5.4-rc1
commit 511885d7
category: bugfix
bugzilla: NA
CVE: CVE-2021-20317

---------------------------

Simplify the timerqueue code by using cached rbtrees and rely on the tree
leftmost node semantics to get the timer with earliest expiration time.
This is a drop in conversion, and therefore semantics remain untouched.

The runtime overhead of cached rbtrees is be pretty much the same as the
current head->next method, noting that when removing the leftmost node,
a common operation for the timerqueue, the rb_next(leftmost) is O(1) as
well, so the next timer will either be the right node or its parent.
Therefore no extra pointer chasing. Finally, the size of the struct
timerqueue_head remains the same.

Passes several hours of rcutorture.
Signed-off-by: NDavidlohr Bueso <dbueso@suse.de>
Signed-off-by: NThomas Gleixner <tglx@linutronix.de>
Link: https://lkml.kernel.org/r/20190724152323.bojciei3muvfxalm@linux-r8p5Signed-off-by: NXiongfeng Wang <wangxiongfeng2@huawei.com>
Reviewed-by: NXie XiuQi <xiexiuqi@huawei.com>
上级 2d4cf5a0
...@@ -12,8 +12,7 @@ struct timerqueue_node { ...@@ -12,8 +12,7 @@ struct timerqueue_node {
}; };
struct timerqueue_head { struct timerqueue_head {
struct rb_root head; struct rb_root_cached rb_root;
struct timerqueue_node *next;
}; };
...@@ -29,13 +28,14 @@ extern struct timerqueue_node *timerqueue_iterate_next( ...@@ -29,13 +28,14 @@ extern struct timerqueue_node *timerqueue_iterate_next(
* *
* @head: head of timerqueue * @head: head of timerqueue
* *
* Returns a pointer to the timer node that has the * Returns a pointer to the timer node that has the earliest expiration time.
* earliest expiration time.
*/ */
static inline static inline
struct timerqueue_node *timerqueue_getnext(struct timerqueue_head *head) struct timerqueue_node *timerqueue_getnext(struct timerqueue_head *head)
{ {
return head->next; struct rb_node *leftmost = rb_first_cached(&head->rb_root);
return rb_entry(leftmost, struct timerqueue_node, node);
} }
static inline void timerqueue_init(struct timerqueue_node *node) static inline void timerqueue_init(struct timerqueue_node *node)
...@@ -45,7 +45,6 @@ static inline void timerqueue_init(struct timerqueue_node *node) ...@@ -45,7 +45,6 @@ static inline void timerqueue_init(struct timerqueue_node *node)
static inline void timerqueue_init_head(struct timerqueue_head *head) static inline void timerqueue_init_head(struct timerqueue_head *head)
{ {
head->head = RB_ROOT; head->rb_root = RB_ROOT_CACHED;
head->next = NULL;
} }
#endif /* _LINUX_TIMERQUEUE_H */ #endif /* _LINUX_TIMERQUEUE_H */
...@@ -39,9 +39,10 @@ ...@@ -39,9 +39,10 @@
*/ */
bool timerqueue_add(struct timerqueue_head *head, struct timerqueue_node *node) bool timerqueue_add(struct timerqueue_head *head, struct timerqueue_node *node)
{ {
struct rb_node **p = &head->head.rb_node; struct rb_node **p = &head->rb_root.rb_root.rb_node;
struct rb_node *parent = NULL; struct rb_node *parent = NULL;
struct timerqueue_node *ptr; struct timerqueue_node *ptr;
bool leftmost = true;
/* Make sure we don't add nodes that are already added */ /* Make sure we don't add nodes that are already added */
WARN_ON_ONCE(!RB_EMPTY_NODE(&node->node)); WARN_ON_ONCE(!RB_EMPTY_NODE(&node->node));
...@@ -49,19 +50,17 @@ bool timerqueue_add(struct timerqueue_head *head, struct timerqueue_node *node) ...@@ -49,19 +50,17 @@ bool timerqueue_add(struct timerqueue_head *head, struct timerqueue_node *node)
while (*p) { while (*p) {
parent = *p; parent = *p;
ptr = rb_entry(parent, struct timerqueue_node, node); ptr = rb_entry(parent, struct timerqueue_node, node);
if (node->expires < ptr->expires) if (node->expires < ptr->expires) {
p = &(*p)->rb_left; p = &(*p)->rb_left;
else } else {
p = &(*p)->rb_right; p = &(*p)->rb_right;
leftmost = false;
}
} }
rb_link_node(&node->node, parent, p); rb_link_node(&node->node, parent, p);
rb_insert_color(&node->node, &head->head); rb_insert_color_cached(&node->node, &head->rb_root, leftmost);
if (!head->next || node->expires < head->next->expires) { return leftmost;
head->next = node;
return true;
}
return false;
} }
EXPORT_SYMBOL_GPL(timerqueue_add); EXPORT_SYMBOL_GPL(timerqueue_add);
...@@ -78,15 +77,10 @@ bool timerqueue_del(struct timerqueue_head *head, struct timerqueue_node *node) ...@@ -78,15 +77,10 @@ bool timerqueue_del(struct timerqueue_head *head, struct timerqueue_node *node)
{ {
WARN_ON_ONCE(RB_EMPTY_NODE(&node->node)); WARN_ON_ONCE(RB_EMPTY_NODE(&node->node));
/* update next pointer */ rb_erase_cached(&node->node, &head->rb_root);
if (head->next == node) {
struct rb_node *rbn = rb_next(&node->node);
head->next = rb_entry_safe(rbn, struct timerqueue_node, node);
}
rb_erase(&node->node, &head->head);
RB_CLEAR_NODE(&node->node); RB_CLEAR_NODE(&node->node);
return head->next != NULL;
return !RB_EMPTY_ROOT(&head->rb_root.rb_root);
} }
EXPORT_SYMBOL_GPL(timerqueue_del); EXPORT_SYMBOL_GPL(timerqueue_del);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册