]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
3.10-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 15 Jul 2014 00:44:36 +0000 (17:44 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 15 Jul 2014 00:44:36 +0000 (17:44 -0700)
added patches:
rtmutex-detect-changes-in-the-pi-lock-chain.patch
rtmutex-fix-deadlock-detector-for-real.patch

queue-3.10/rtmutex-detect-changes-in-the-pi-lock-chain.patch [new file with mode: 0644]
queue-3.10/rtmutex-fix-deadlock-detector-for-real.patch [new file with mode: 0644]
queue-3.10/series

diff --git a/queue-3.10/rtmutex-detect-changes-in-the-pi-lock-chain.patch b/queue-3.10/rtmutex-detect-changes-in-the-pi-lock-chain.patch
new file mode 100644 (file)
index 0000000..96d445e
--- /dev/null
@@ -0,0 +1,274 @@
+From 82084984383babe728e6e3c9a8e5c46278091315 Mon Sep 17 00:00:00 2001
+From: Thomas Gleixner <tglx@linutronix.de>
+Date: Thu, 5 Jun 2014 11:16:12 +0200
+Subject: rtmutex: Detect changes in the pi lock chain
+
+From: Thomas Gleixner <tglx@linutronix.de>
+
+commit 82084984383babe728e6e3c9a8e5c46278091315 upstream.
+
+When we walk the lock chain, we drop all locks after each step. So the
+lock chain can change under us before we reacquire the locks. That's
+harmless in principle as we just follow the wrong lock path. But it
+can lead to a false positive in the dead lock detection logic:
+
+T0 holds L0
+T0 blocks on L1 held by T1
+T1 blocks on L2 held by T2
+T2 blocks on L3 held by T3
+T4 blocks on L4 held by T4
+
+Now we walk the chain
+
+lock T1 -> lock L2 -> adjust L2 -> unlock T1 ->
+     lock T2 ->  adjust T2 ->  drop locks
+
+T2 times out and blocks on L0
+
+Now we continue:
+
+lock T2 -> lock L0 -> deadlock detected, but it's not a deadlock at all.
+
+Brad tried to work around that in the deadlock detection logic itself,
+but the more I looked at it the less I liked it, because it's crystal
+ball magic after the fact.
+
+We actually can detect a chain change very simple:
+
+lock T1 -> lock L2 -> adjust L2 -> unlock T1 -> lock T2 -> adjust T2 ->
+
+     next_lock = T2->pi_blocked_on->lock;
+
+drop locks
+
+T2 times out and blocks on L0
+
+Now we continue:
+
+lock T2 ->
+
+     if (next_lock != T2->pi_blocked_on->lock)
+          return;
+
+So if we detect that T2 is now blocked on a different lock we stop the
+chain walk. That's also correct in the following scenario:
+
+lock T1 -> lock L2 -> adjust L2 -> unlock T1 -> lock T2 -> adjust T2 ->
+
+     next_lock = T2->pi_blocked_on->lock;
+
+drop locks
+
+T3 times out and drops L3
+T2 acquires L3 and blocks on L4 now
+
+Now we continue:
+
+lock T2 ->
+
+     if (next_lock != T2->pi_blocked_on->lock)
+          return;
+
+We don't have to follow up the chain at that point, because T2
+propagated our priority up to T4 already.
+
+[ Folded a cleanup patch from peterz ]
+
+Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
+Reported-by: Brad Mouring <bmouring@ni.com>
+Cc: Steven Rostedt <rostedt@goodmis.org>
+Cc: Peter Zijlstra <peterz@infradead.org>
+Link: http://lkml.kernel.org/r/20140605152801.930031935@linutronix.de
+Signed-off-by: Mike Galbraith <umgwanakikbuti@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ kernel/rtmutex.c |   74 +++++++++++++++++++++++++++++++++++++++++++------------
+ 1 file changed, 59 insertions(+), 15 deletions(-)
+
+--- a/kernel/rtmutex.c
++++ b/kernel/rtmutex.c
+@@ -142,6 +142,11 @@ static void rt_mutex_adjust_prio(struct
+  */
+ int max_lock_depth = 1024;
++static inline struct rt_mutex *task_blocked_on_lock(struct task_struct *p)
++{
++      return p->pi_blocked_on ? p->pi_blocked_on->lock : NULL;
++}
++
+ /*
+  * Adjust the priority chain. Also used for deadlock detection.
+  * Decreases task's usage by one - may thus free the task.
+@@ -150,6 +155,7 @@ int max_lock_depth = 1024;
+ static int rt_mutex_adjust_prio_chain(struct task_struct *task,
+                                     int deadlock_detect,
+                                     struct rt_mutex *orig_lock,
++                                    struct rt_mutex *next_lock,
+                                     struct rt_mutex_waiter *orig_waiter,
+                                     struct task_struct *top_task)
+ {
+@@ -208,6 +214,18 @@ static int rt_mutex_adjust_prio_chain(st
+               goto out_unlock_pi;
+       /*
++       * We dropped all locks after taking a refcount on @task, so
++       * the task might have moved on in the lock chain or even left
++       * the chain completely and blocks now on an unrelated lock or
++       * on @orig_lock.
++       *
++       * We stored the lock on which @task was blocked in @next_lock,
++       * so we can detect the chain change.
++       */
++      if (next_lock != waiter->lock)
++              goto out_unlock_pi;
++
++      /*
+        * Drop out, when the task has no waiters. Note,
+        * top_waiter can be NULL, when we are in the deboosting
+        * mode!
+@@ -293,11 +311,26 @@ static int rt_mutex_adjust_prio_chain(st
+               __rt_mutex_adjust_prio(task);
+       }
++      /*
++       * Check whether the task which owns the current lock is pi
++       * blocked itself. If yes we store a pointer to the lock for
++       * the lock chain change detection above. After we dropped
++       * task->pi_lock next_lock cannot be dereferenced anymore.
++       */
++      next_lock = task_blocked_on_lock(task);
++
+       raw_spin_unlock_irqrestore(&task->pi_lock, flags);
+       top_waiter = rt_mutex_top_waiter(lock);
+       raw_spin_unlock(&lock->wait_lock);
++      /*
++       * We reached the end of the lock chain. Stop right here. No
++       * point to go back just to figure that out.
++       */
++      if (!next_lock)
++              goto out_put_task;
++
+       if (!detect_deadlock && waiter != top_waiter)
+               goto out_put_task;
+@@ -408,8 +441,9 @@ static int task_blocks_on_rt_mutex(struc
+ {
+       struct task_struct *owner = rt_mutex_owner(lock);
+       struct rt_mutex_waiter *top_waiter = waiter;
+-      unsigned long flags;
++      struct rt_mutex *next_lock;
+       int chain_walk = 0, res;
++      unsigned long flags;
+       /*
+        * Early deadlock detection. We really don't want the task to
+@@ -442,20 +476,28 @@ static int task_blocks_on_rt_mutex(struc
+       if (!owner)
+               return 0;
++      raw_spin_lock_irqsave(&owner->pi_lock, flags);
+       if (waiter == rt_mutex_top_waiter(lock)) {
+-              raw_spin_lock_irqsave(&owner->pi_lock, flags);
+               plist_del(&top_waiter->pi_list_entry, &owner->pi_waiters);
+               plist_add(&waiter->pi_list_entry, &owner->pi_waiters);
+               __rt_mutex_adjust_prio(owner);
+               if (owner->pi_blocked_on)
+                       chain_walk = 1;
+-              raw_spin_unlock_irqrestore(&owner->pi_lock, flags);
+-      }
+-      else if (debug_rt_mutex_detect_deadlock(waiter, detect_deadlock))
++      } else if (debug_rt_mutex_detect_deadlock(waiter, detect_deadlock)) {
+               chain_walk = 1;
++      }
+-      if (!chain_walk)
++      /* Store the lock on which owner is blocked or NULL */
++      next_lock = task_blocked_on_lock(owner);
++
++      raw_spin_unlock_irqrestore(&owner->pi_lock, flags);
++      /*
++       * Even if full deadlock detection is on, if the owner is not
++       * blocked itself, we can avoid finding this out in the chain
++       * walk.
++       */
++      if (!chain_walk || !next_lock)
+               return 0;
+       /*
+@@ -467,8 +509,8 @@ static int task_blocks_on_rt_mutex(struc
+       raw_spin_unlock(&lock->wait_lock);
+-      res = rt_mutex_adjust_prio_chain(owner, detect_deadlock, lock, waiter,
+-                                       task);
++      res = rt_mutex_adjust_prio_chain(owner, detect_deadlock, lock,
++                                       next_lock, waiter, task);
+       raw_spin_lock(&lock->wait_lock);
+@@ -517,8 +559,8 @@ static void remove_waiter(struct rt_mute
+ {
+       int first = (waiter == rt_mutex_top_waiter(lock));
+       struct task_struct *owner = rt_mutex_owner(lock);
++      struct rt_mutex *next_lock = NULL;
+       unsigned long flags;
+-      int chain_walk = 0;
+       raw_spin_lock_irqsave(&current->pi_lock, flags);
+       plist_del(&waiter->list_entry, &lock->wait_list);
+@@ -542,15 +584,15 @@ static void remove_waiter(struct rt_mute
+               }
+               __rt_mutex_adjust_prio(owner);
+-              if (owner->pi_blocked_on)
+-                      chain_walk = 1;
++              /* Store the lock on which owner is blocked or NULL */
++              next_lock = task_blocked_on_lock(owner);
+               raw_spin_unlock_irqrestore(&owner->pi_lock, flags);
+       }
+       WARN_ON(!plist_node_empty(&waiter->pi_list_entry));
+-      if (!chain_walk)
++      if (!next_lock)
+               return;
+       /* gets dropped in rt_mutex_adjust_prio_chain()! */
+@@ -558,7 +600,7 @@ static void remove_waiter(struct rt_mute
+       raw_spin_unlock(&lock->wait_lock);
+-      rt_mutex_adjust_prio_chain(owner, 0, lock, NULL, current);
++      rt_mutex_adjust_prio_chain(owner, 0, lock, next_lock, NULL, current);
+       raw_spin_lock(&lock->wait_lock);
+ }
+@@ -571,6 +613,7 @@ static void remove_waiter(struct rt_mute
+ void rt_mutex_adjust_pi(struct task_struct *task)
+ {
+       struct rt_mutex_waiter *waiter;
++      struct rt_mutex *next_lock;
+       unsigned long flags;
+       raw_spin_lock_irqsave(&task->pi_lock, flags);
+@@ -580,12 +623,13 @@ void rt_mutex_adjust_pi(struct task_stru
+               raw_spin_unlock_irqrestore(&task->pi_lock, flags);
+               return;
+       }
+-
++      next_lock = waiter->lock;
+       raw_spin_unlock_irqrestore(&task->pi_lock, flags);
+       /* gets dropped in rt_mutex_adjust_prio_chain()! */
+       get_task_struct(task);
+-      rt_mutex_adjust_prio_chain(task, 0, NULL, NULL, task);
++
++      rt_mutex_adjust_prio_chain(task, 0, NULL, next_lock, NULL, task);
+ }
+ /**
diff --git a/queue-3.10/rtmutex-fix-deadlock-detector-for-real.patch b/queue-3.10/rtmutex-fix-deadlock-detector-for-real.patch
new file mode 100644 (file)
index 0000000..0c397d0
--- /dev/null
@@ -0,0 +1,106 @@
+From 397335f004f41e5fcf7a795e94eb3ab83411a17c Mon Sep 17 00:00:00 2001
+From: Thomas Gleixner <tglx@linutronix.de>
+Date: Thu, 22 May 2014 03:25:39 +0000
+Subject: rtmutex: Fix deadlock detector for real
+
+From: Thomas Gleixner <tglx@linutronix.de>
+
+commit 397335f004f41e5fcf7a795e94eb3ab83411a17c upstream.
+
+The current deadlock detection logic does not work reliably due to the
+following early exit path:
+
+       /*
+        * Drop out, when the task has no waiters. Note,
+        * top_waiter can be NULL, when we are in the deboosting
+        * mode!
+        */
+       if (top_waiter && (!task_has_pi_waiters(task) ||
+                          top_waiter != task_top_pi_waiter(task)))
+               goto out_unlock_pi;
+
+So this not only exits when the task has no waiters, it also exits
+unconditionally when the current waiter is not the top priority waiter
+of the task.
+
+So in a nested locking scenario, it might abort the lock chain walk
+and therefor miss a potential deadlock.
+
+Simple fix: Continue the chain walk, when deadlock detection is
+enabled.
+
+We also avoid the whole enqueue, if we detect the deadlock right away
+(A-A). It's an optimization, but also prevents that another waiter who
+comes in after the detection and before the task has undone the damage
+observes the situation and detects the deadlock and returns
+-EDEADLOCK, which is wrong as the other task is not in a deadlock
+situation.
+
+Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
+Cc: Peter Zijlstra <peterz@infradead.org>
+Reviewed-by: Steven Rostedt <rostedt@goodmis.org>
+Cc: Lai Jiangshan <laijs@cn.fujitsu.com>
+Link: http://lkml.kernel.org/r/20140522031949.725272460@linutronix.de
+Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
+Signed-off-by: Mike Galbraith <umgwanakikbuti@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ kernel/rtmutex.c |   32 ++++++++++++++++++++++++++++----
+ 1 file changed, 28 insertions(+), 4 deletions(-)
+
+--- a/kernel/rtmutex.c
++++ b/kernel/rtmutex.c
+@@ -212,9 +212,16 @@ static int rt_mutex_adjust_prio_chain(st
+        * top_waiter can be NULL, when we are in the deboosting
+        * mode!
+        */
+-      if (top_waiter && (!task_has_pi_waiters(task) ||
+-                         top_waiter != task_top_pi_waiter(task)))
+-              goto out_unlock_pi;
++      if (top_waiter) {
++              if (!task_has_pi_waiters(task))
++                      goto out_unlock_pi;
++              /*
++               * If deadlock detection is off, we stop here if we
++               * are not the top pi waiter of the task.
++               */
++              if (!detect_deadlock && top_waiter != task_top_pi_waiter(task))
++                      goto out_unlock_pi;
++      }
+       /*
+        * When deadlock detection is off then we check, if further
+@@ -230,7 +237,12 @@ static int rt_mutex_adjust_prio_chain(st
+               goto retry;
+       }
+-      /* Deadlock detection */
++      /*
++       * Deadlock detection. If the lock is the same as the original
++       * lock which caused us to walk the lock chain or if the
++       * current lock is owned by the task which initiated the chain
++       * walk, we detected a deadlock.
++       */
+       if (lock == orig_lock || rt_mutex_owner(lock) == top_task) {
+               debug_rt_mutex_deadlock(deadlock_detect, orig_waiter, lock);
+               raw_spin_unlock(&lock->wait_lock);
+@@ -399,6 +411,18 @@ static int task_blocks_on_rt_mutex(struc
+       unsigned long flags;
+       int chain_walk = 0, res;
++      /*
++       * Early deadlock detection. We really don't want the task to
++       * enqueue on itself just to untangle the mess later. It's not
++       * only an optimization. We drop the locks, so another waiter
++       * can come in before the chain walk detects the deadlock. So
++       * the other will detect the deadlock and return -EDEADLOCK,
++       * which is wrong, as the other waiter is not in a deadlock
++       * situation.
++       */
++      if (detect_deadlock && owner == task)
++              return -EDEADLK;
++
+       raw_spin_lock_irqsave(&task->pi_lock, flags);
+       __rt_mutex_adjust_prio(task);
+       waiter->task = task;
index 04fb23eb8fc2d4383efa869dae2c15df8fd922c4..7aae80f91121f666447a9c5245f1d18bde0c7cc7 100644 (file)
@@ -27,3 +27,5 @@ drm-radeon-fix-typo-in-golden-register-setup-on-evergreen.patch
 drm-radeon-stop-poisoning-the-gart-tlb.patch
 ring-buffer-check-if-buffer-exists-before-polling.patch
 x86-64-espfix-don-t-leak-bits-31-16-of-esp-returning-to-16-bit-stack.patch
+rtmutex-fix-deadlock-detector-for-real.patch
+rtmutex-detect-changes-in-the-pi-lock-chain.patch