]> rtime.felk.cvut.cz Git - hercules2020/nv-tegra/linux-4.4.git/blob - rt-patches/0116-futex-Fix-bug-on-when-a-requeued-RT-task-times-out.patch
rt_patches: required rebase due to printk change
[hercules2020/nv-tegra/linux-4.4.git] / rt-patches / 0116-futex-Fix-bug-on-when-a-requeued-RT-task-times-out.patch
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
5
6 Requeue with timeout causes a bug with PREEMPT_RT_FULL.
7
8 The bug comes from a timed out condition.
9
10         TASK 1                          TASK 2
11         ------                          ------
12     futex_wait_requeue_pi()
13         futex_wait_queue_me()
14         <timed out>
15
16                                         double_lock_hb();
17
18         raw_spin_lock(pi_lock);
19         if (current->pi_blocked_on) {
20         } else {
21             current->pi_blocked_on = PI_WAKE_INPROGRESS;
22             run_spin_unlock(pi_lock);
23             spin_lock(hb->lock); <-- blocked!
24
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)!!!!
29
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.
34
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,
37 set to the hb->lock.
38
39 The fix:
40
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
45 appropriately.
46
47 Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
48 Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
49 ---
50  kernel/locking/rtmutex.c        | 32 +++++++++++++++++++++++++++++++-
51  kernel/locking/rtmutex_common.h |  1 +
52  2 files changed, 32 insertions(+), 1 deletion(-)
53
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)
59  
60  static int rt_mutex_real_waiter(struct rt_mutex_waiter *waiter)
61  {
62 -       return waiter && waiter != PI_WAKEUP_INPROGRESS;
63 +       return waiter && waiter != PI_WAKEUP_INPROGRESS &&
64 +               waiter != PI_REQUEUE_INPROGRESS;
65  }
66  
67  /*
68 @@ -1631,6 +1632,35 @@ int rt_mutex_start_proxy_lock(struct rt_mutex *lock,
69                 return 1;
70         }
71  
72 +#ifdef CONFIG_PREEMPT_RT_FULL
73 +       /*
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.
83 +        *
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.
90 +        */
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);
95 +               return -EAGAIN;
96 +       }
97 +       task->pi_blocked_on = PI_REQUEUE_INPROGRESS;
98 +       raw_spin_unlock_irq(&task->pi_lock);
99 +#endif
100 +
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.):
110   */
111  #define PI_WAKEUP_INPROGRESS   ((struct rt_mutex_waiter *) 1)
112 +#define PI_REQUEUE_INPROGRESS  ((struct rt_mutex_waiter *) 2)
113  
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,
116 -- 
117 1.9.1
118