1 From c2e9a41dc8f0b2ce8253303456335990e09efff1 Mon Sep 17 00:00:00 2001
2 From: Steven Rostedt <rostedt@goodmis.org>
3 Date: Tue, 14 Jul 2015 14:26:34 +0200
4 Subject: [PATCH 116/366] futex: Fix bug on when a requeued RT task times out
6 Requeue with timeout causes a bug with PREEMPT_RT_FULL.
8 The bug comes from a timed out condition.
12 futex_wait_requeue_pi()
18 raw_spin_lock(pi_lock);
19 if (current->pi_blocked_on) {
21 current->pi_blocked_on = PI_WAKE_INPROGRESS;
22 run_spin_unlock(pi_lock);
23 spin_lock(hb->lock); <-- blocked!
25 plist_for_each_entry_safe(this) {
26 rt_mutex_start_proxy_lock();
27 task_blocks_on_rt_mutex();
28 BUG_ON(task->pi_blocked_on)!!!!
30 The BUG_ON() actually has a check for PI_WAKE_INPROGRESS, but the
31 problem is that, after TASK 1 sets PI_WAKE_INPROGRESS, it then tries to
32 grab the hb->lock, which it fails to do so. As the hb->lock is a mutex,
33 it will block and set the "pi_blocked_on" to the hb->lock.
35 When TASK 2 goes to requeue it, the check for PI_WAKE_INPROGESS fails
36 because the task1's pi_blocked_on is no longer set to that, but instead,
41 When calling rt_mutex_start_proxy_lock() a check is made to see
42 if the proxy tasks pi_blocked_on is set. If so, exit out early.
43 Otherwise set it to a new flag PI_REQUEUE_INPROGRESS, which notifies
44 the proxy task that it is being requeued, and will handle things
47 Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
48 Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
50 kernel/locking/rtmutex.c | 32 +++++++++++++++++++++++++++++++-
51 kernel/locking/rtmutex_common.h | 1 +
52 2 files changed, 32 insertions(+), 1 deletion(-)
54 diff --git a/kernel/locking/rtmutex.c b/kernel/locking/rtmutex.c
55 index 1a30a89..f032647 100644
56 --- a/kernel/locking/rtmutex.c
57 +++ b/kernel/locking/rtmutex.c
58 @@ -71,7 +71,8 @@ static void fixup_rt_mutex_waiters(struct rt_mutex *lock)
60 static int rt_mutex_real_waiter(struct rt_mutex_waiter *waiter)
62 - return waiter && waiter != PI_WAKEUP_INPROGRESS;
63 + return waiter && waiter != PI_WAKEUP_INPROGRESS &&
64 + waiter != PI_REQUEUE_INPROGRESS;
68 @@ -1631,6 +1632,35 @@ int rt_mutex_start_proxy_lock(struct rt_mutex *lock,
72 +#ifdef CONFIG_PREEMPT_RT_FULL
74 + * In PREEMPT_RT there's an added race.
75 + * If the task, that we are about to requeue, times out,
76 + * it can set the PI_WAKEUP_INPROGRESS. This tells the requeue
77 + * to skip this task. But right after the task sets
78 + * its pi_blocked_on to PI_WAKEUP_INPROGRESS it can then
79 + * block on the spin_lock(&hb->lock), which in RT is an rtmutex.
80 + * This will replace the PI_WAKEUP_INPROGRESS with the actual
81 + * lock that it blocks on. We *must not* place this task
82 + * on this proxy lock in that case.
84 + * To prevent this race, we first take the task's pi_lock
85 + * and check if it has updated its pi_blocked_on. If it has,
86 + * we assume that it woke up and we return -EAGAIN.
87 + * Otherwise, we set the task's pi_blocked_on to
88 + * PI_REQUEUE_INPROGRESS, so that if the task is waking up
89 + * it will know that we are in the process of requeuing it.
91 + raw_spin_lock_irq(&task->pi_lock);
92 + if (task->pi_blocked_on) {
93 + raw_spin_unlock_irq(&task->pi_lock);
94 + raw_spin_unlock(&lock->wait_lock);
97 + task->pi_blocked_on = PI_REQUEUE_INPROGRESS;
98 + raw_spin_unlock_irq(&task->pi_lock);
101 /* We enforce deadlock detection for futexes */
102 ret = task_blocks_on_rt_mutex(lock, waiter, task,
103 RT_MUTEX_FULL_CHAINWALK);
104 diff --git a/kernel/locking/rtmutex_common.h b/kernel/locking/rtmutex_common.h
105 index 1c1bcc5..11f2f51 100644
106 --- a/kernel/locking/rtmutex_common.h
107 +++ b/kernel/locking/rtmutex_common.h
108 @@ -98,6 +98,7 @@ enum rtmutex_chainwalk {
109 * PI-futex support (proxy locking functions, etc.):
111 #define PI_WAKEUP_INPROGRESS ((struct rt_mutex_waiter *) 1)
112 +#define PI_REQUEUE_INPROGRESS ((struct rt_mutex_waiter *) 2)
114 extern struct task_struct *rt_mutex_next_owner(struct rt_mutex *lock);
115 extern void rt_mutex_init_proxy_locked(struct rt_mutex *lock,