From: Greg Kroah-Hartman Date: Tue, 15 Jul 2014 00:44:40 +0000 (-0700) Subject: 3.4-stable patches X-Git-Tag: v3.4.99~19 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=9e6166bb296f880fef104304645f9ab01dd3d316;p=thirdparty%2Fkernel%2Fstable-queue.git 3.4-stable patches added patches: rtmutex-detect-changes-in-the-pi-lock-chain.patch rtmutex-fix-deadlock-detector-for-real.patch --- diff --git a/queue-3.4/rtmutex-detect-changes-in-the-pi-lock-chain.patch b/queue-3.4/rtmutex-detect-changes-in-the-pi-lock-chain.patch new file mode 100644 index 00000000000..d98d2c732c9 --- /dev/null +++ b/queue-3.4/rtmutex-detect-changes-in-the-pi-lock-chain.patch @@ -0,0 +1,274 @@ +From 82084984383babe728e6e3c9a8e5c46278091315 Mon Sep 17 00:00:00 2001 +From: Thomas Gleixner +Date: Thu, 5 Jun 2014 11:16:12 +0200 +Subject: rtmutex: Detect changes in the pi lock chain + +From: Thomas Gleixner + +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 +Reported-by: Brad Mouring +Cc: Steven Rostedt +Cc: Peter Zijlstra +Link: http://lkml.kernel.org/r/20140605152801.930031935@linutronix.de +Signed-off-by: Mike Galbraith +Signed-off-by: Greg Kroah-Hartman + +--- + kernel/rtmutex.c | 74 +++++++++++++++++++++++++++++++++++++++++++------------ + 1 file changed, 59 insertions(+), 15 deletions(-) + +--- a/kernel/rtmutex.c ++++ b/kernel/rtmutex.c +@@ -141,6 +141,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. +@@ -149,6 +154,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) + { +@@ -207,6 +213,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! +@@ -292,11 +310,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; + +@@ -407,8 +440,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 +@@ -441,20 +475,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; + + /* +@@ -466,8 +508,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); + +@@ -516,8 +558,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(¤t->pi_lock, flags); + plist_del(&waiter->list_entry, &lock->wait_list); +@@ -541,15 +583,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()! */ +@@ -557,7 +599,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); + } +@@ -570,6 +612,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); +@@ -579,12 +622,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.4/rtmutex-fix-deadlock-detector-for-real.patch b/queue-3.4/rtmutex-fix-deadlock-detector-for-real.patch new file mode 100644 index 00000000000..caa358439c0 --- /dev/null +++ b/queue-3.4/rtmutex-fix-deadlock-detector-for-real.patch @@ -0,0 +1,106 @@ +From 397335f004f41e5fcf7a795e94eb3ab83411a17c Mon Sep 17 00:00:00 2001 +From: Thomas Gleixner +Date: Thu, 22 May 2014 03:25:39 +0000 +Subject: rtmutex: Fix deadlock detector for real + +From: Thomas Gleixner + +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 +Cc: Peter Zijlstra +Reviewed-by: Steven Rostedt +Cc: Lai Jiangshan +Link: http://lkml.kernel.org/r/20140522031949.725272460@linutronix.de +Signed-off-by: Thomas Gleixner +Signed-off-by: Mike Galbraith +Signed-off-by: Greg Kroah-Hartman + +--- + kernel/rtmutex.c | 32 ++++++++++++++++++++++++++++---- + 1 file changed, 28 insertions(+), 4 deletions(-) + +--- a/kernel/rtmutex.c ++++ b/kernel/rtmutex.c +@@ -211,9 +211,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 +@@ -229,7 +236,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); +@@ -398,6 +410,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; diff --git a/queue-3.4/series b/queue-3.4/series index 42afa94a71f..80e782580fa 100644 --- a/queue-3.4/series +++ b/queue-3.4/series @@ -9,3 +9,5 @@ ext4-clarify-error-count-warning-messages.patch drm-radeon-stop-poisoning-the-gart-tlb.patch tracing-remove-ftrace_stop-start-from-reading-the-trace-file.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