diff options
Diffstat (limited to 'kernels/linux-libre-pae/0008-futex-avoid-race-between-requeue-and-wake.patch')
-rw-r--r-- | kernels/linux-libre-pae/0008-futex-avoid-race-between-requeue-and-wake.patch | 94 |
1 files changed, 94 insertions, 0 deletions
diff --git a/kernels/linux-libre-pae/0008-futex-avoid-race-between-requeue-and-wake.patch b/kernels/linux-libre-pae/0008-futex-avoid-race-between-requeue-and-wake.patch new file mode 100644 index 000000000..8685e1168 --- /dev/null +++ b/kernels/linux-libre-pae/0008-futex-avoid-race-between-requeue-and-wake.patch @@ -0,0 +1,94 @@ +From aafcd8f8692fb9e389608c1efad2e57c0bbb9362 Mon Sep 17 00:00:00 2001 +From: Linus Torvalds <torvalds@linux-foundation.org> +Date: Tue, 8 Apr 2014 15:30:07 -0700 +Subject: [PATCH 08/10] futex: avoid race between requeue and wake + +commit 69cd9eba38867a493a043bb13eb9b33cad5f1a9a upstream. + +Jan Stancek reported: + "pthread_cond_broadcast/4-1.c testcase from openposix testsuite (LTP) + occasionally fails, because some threads fail to wake up. + + Testcase creates 5 threads, which are all waiting on same condition. + Main thread then calls pthread_cond_broadcast() without holding mutex, + which calls: + + futex(uaddr1, FUTEX_CMP_REQUEUE_PRIVATE, 1, 2147483647, uaddr2, ..) + + This immediately wakes up single thread A, which unlocks mutex and + tries to wake up another thread: + + futex(uaddr2, FUTEX_WAKE_PRIVATE, 1) + + If thread A manages to call futex_wake() before any waiters are + requeued for uaddr2, no other thread is woken up" + +The ordering constraints for the hash bucket waiter counting are that +the waiter counts have to be incremented _before_ getting the spinlock +(because the spinlock acts as part of the memory barrier), but the +"requeue" operation didn't honor those rules, and nobody had even +thought about that case. + +This fairly simple patch just increments the waiter count for the target +hash bucket (hb2) when requeing a futex before taking the locks. It +then decrements them again after releasing the lock - the code that +actually moves the futex(es) between hash buckets will do the additional +required waiter count housekeeping. + +Reported-and-tested-by: Jan Stancek <jstancek@redhat.com> +Acked-by: Davidlohr Bueso <davidlohr@hp.com> +Cc: Peter Zijlstra <peterz@infradead.org> +Cc: Thomas Gleixner <tglx@linutronix.de> +Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + kernel/futex.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/kernel/futex.c b/kernel/futex.c +index 08ec814..16b1f2c 100644 +--- a/kernel/futex.c ++++ b/kernel/futex.c +@@ -1450,6 +1450,7 @@ retry: + hb2 = hash_futex(&key2); + + retry_private: ++ hb_waiters_inc(hb2); + double_lock_hb(hb1, hb2); + + if (likely(cmpval != NULL)) { +@@ -1459,6 +1460,7 @@ retry_private: + + if (unlikely(ret)) { + double_unlock_hb(hb1, hb2); ++ hb_waiters_dec(hb2); + + ret = get_user(curval, uaddr1); + if (ret) +@@ -1508,6 +1510,7 @@ retry_private: + break; + case -EFAULT: + double_unlock_hb(hb1, hb2); ++ hb_waiters_dec(hb2); + put_futex_key(&key2); + put_futex_key(&key1); + ret = fault_in_user_writeable(uaddr2); +@@ -1517,6 +1520,7 @@ retry_private: + case -EAGAIN: + /* The owner was exiting, try again. */ + double_unlock_hb(hb1, hb2); ++ hb_waiters_dec(hb2); + put_futex_key(&key2); + put_futex_key(&key1); + cond_resched(); +@@ -1592,6 +1596,7 @@ retry_private: + + out_unlock: + double_unlock_hb(hb1, hb2); ++ hb_waiters_dec(hb2); + + /* + * drop_futex_key_refs() must be called outside the spinlocks. During +-- +1.9.2 + |