| From 8c7c8225cf6bfcf8a6cdcc86bc8d1137e38dde56 Mon Sep 17 00:00:00 2001 |
| From: Steven Rostedt <rostedt@goodmis.org> |
| Date: Mon, 19 Aug 2013 11:35:32 -0400 |
| Subject: [PATCH] swait: Add memory barrier before checking list empty |
| |
| There's a race condition with swait wakeups and adding to the list. The |
| __swait_wake() does a check for swait_head_has_waiters(), and if it is |
| empty it will exit without doing any wake ups. The problem is that the |
| check does not include any memory barriers before it makes a decision |
| to wake up or not. |
| |
| CPU0 CPU1 |
| ---- ---- |
| |
| condition = 1 |
| |
| load h->list (is empty) |
| raw_spin_lock(hlist->lock) |
| hlist_add(); |
| __set_current_state(); |
| raw_spin_unlock(hlist->lock) |
| swait_wake() |
| swait_head_has_waiters() |
| (sees h->list as empty and returns) |
| |
| check_condition (sees condition = 0) |
| |
| store condition = 1 |
| |
| schedule() |
| |
| Now the task on CPU1 has just missed its wakeup. By adding a memory |
| barrier before the list empty check, we fix the problem of miss seeing |
| the list not empty as well as pushing out the condition for the other |
| task to see. |
| |
| Reviewed-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com> |
| Signed-off-by: Steven Rostedt <rostedt@goodmis.org> |
| Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> |
| --- |
| kernel/wait-simple.c | 2 ++ |
| 1 file changed, 2 insertions(+) |
| |
| --- a/kernel/wait-simple.c |
| +++ b/kernel/wait-simple.c |
| @@ -27,6 +27,8 @@ static inline void __swait_dequeue(struc |
| /* Check whether a head has waiters enqueued */ |
| static inline bool swait_head_has_waiters(struct swait_head *h) |
| { |
| + /* Make sure the condition is visible before checking list_empty() */ |
| + smp_mb(); |
| return !list_empty(&h->list); |
| } |
| |