Commit 3d5c9340 authored by Thomas Gleixner's avatar Thomas Gleixner

rtmutex: Handle deadlock detection smarter

Even in the case when deadlock detection is not requested by the
caller, we can detect deadlocks. Right now the code stops the lock
chain walk and keeps the waiter enqueued, even on itself. Silly not to
yell when such a scenario is detected and to keep the waiter enqueued.

Return -EDEADLK unconditionally and handle it at the call sites.

The futex calls return -EDEADLK. The non futex ones dequeue the
waiter, throw a warning and put the task into a schedule loop.

Tagged for stable as it makes the code more robust.
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Brad Mouring <bmouring@ni.com>
Link: http://lkml.kernel.org/r/20140605152801.836501969@linutronix.de
Cc: stable@vger.kernel.org
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
parent 951e2730
...@@ -31,3 +31,8 @@ static inline int debug_rt_mutex_detect_deadlock(struct rt_mutex_waiter *waiter, ...@@ -31,3 +31,8 @@ static inline int debug_rt_mutex_detect_deadlock(struct rt_mutex_waiter *waiter,
{ {
return (waiter != NULL); return (waiter != NULL);
} }
static inline void rt_mutex_print_deadlock(struct rt_mutex_waiter *w)
{
debug_rt_mutex_print_deadlock(w);
}
...@@ -314,7 +314,7 @@ static int rt_mutex_adjust_prio_chain(struct task_struct *task, ...@@ -314,7 +314,7 @@ static int rt_mutex_adjust_prio_chain(struct task_struct *task,
} }
put_task_struct(task); put_task_struct(task);
return deadlock_detect ? -EDEADLK : 0; return -EDEADLK;
} }
retry: retry:
/* /*
...@@ -377,7 +377,7 @@ static int rt_mutex_adjust_prio_chain(struct task_struct *task, ...@@ -377,7 +377,7 @@ static int rt_mutex_adjust_prio_chain(struct task_struct *task,
if (lock == orig_lock || rt_mutex_owner(lock) == top_task) { if (lock == orig_lock || rt_mutex_owner(lock) == top_task) {
debug_rt_mutex_deadlock(deadlock_detect, orig_waiter, lock); debug_rt_mutex_deadlock(deadlock_detect, orig_waiter, lock);
raw_spin_unlock(&lock->wait_lock); raw_spin_unlock(&lock->wait_lock);
ret = deadlock_detect ? -EDEADLK : 0; ret = -EDEADLK;
goto out_unlock_pi; goto out_unlock_pi;
} }
...@@ -548,7 +548,7 @@ static int task_blocks_on_rt_mutex(struct rt_mutex *lock, ...@@ -548,7 +548,7 @@ static int task_blocks_on_rt_mutex(struct rt_mutex *lock,
* which is wrong, as the other waiter is not in a deadlock * which is wrong, as the other waiter is not in a deadlock
* situation. * situation.
*/ */
if (detect_deadlock && owner == task) if (owner == task)
return -EDEADLK; return -EDEADLK;
raw_spin_lock_irqsave(&task->pi_lock, flags); raw_spin_lock_irqsave(&task->pi_lock, flags);
...@@ -763,6 +763,26 @@ __rt_mutex_slowlock(struct rt_mutex *lock, int state, ...@@ -763,6 +763,26 @@ __rt_mutex_slowlock(struct rt_mutex *lock, int state,
return ret; return ret;
} }
static void rt_mutex_handle_deadlock(int res, int detect_deadlock,
struct rt_mutex_waiter *w)
{
/*
* If the result is not -EDEADLOCK or the caller requested
* deadlock detection, nothing to do here.
*/
if (res != -EDEADLOCK || detect_deadlock)
return;
/*
* Yell lowdly and stop the task right here.
*/
rt_mutex_print_deadlock(w);
while (1) {
set_current_state(TASK_INTERRUPTIBLE);
schedule();
}
}
/* /*
* Slow path lock function: * Slow path lock function:
*/ */
...@@ -802,8 +822,10 @@ rt_mutex_slowlock(struct rt_mutex *lock, int state, ...@@ -802,8 +822,10 @@ rt_mutex_slowlock(struct rt_mutex *lock, int state,
set_current_state(TASK_RUNNING); set_current_state(TASK_RUNNING);
if (unlikely(ret)) if (unlikely(ret)) {
remove_waiter(lock, &waiter); remove_waiter(lock, &waiter);
rt_mutex_handle_deadlock(ret, detect_deadlock, &waiter);
}
/* /*
* try_to_take_rt_mutex() sets the waiter bit * try_to_take_rt_mutex() sets the waiter bit
...@@ -1112,7 +1134,8 @@ int rt_mutex_start_proxy_lock(struct rt_mutex *lock, ...@@ -1112,7 +1134,8 @@ int rt_mutex_start_proxy_lock(struct rt_mutex *lock,
return 1; return 1;
} }
ret = task_blocks_on_rt_mutex(lock, waiter, task, detect_deadlock); /* We enforce deadlock detection for futexes */
ret = task_blocks_on_rt_mutex(lock, waiter, task, 1);
if (ret && !rt_mutex_owner(lock)) { if (ret && !rt_mutex_owner(lock)) {
/* /*
......
...@@ -24,3 +24,8 @@ ...@@ -24,3 +24,8 @@
#define debug_rt_mutex_print_deadlock(w) do { } while (0) #define debug_rt_mutex_print_deadlock(w) do { } while (0)
#define debug_rt_mutex_detect_deadlock(w,d) (d) #define debug_rt_mutex_detect_deadlock(w,d) (d)
#define debug_rt_mutex_reset_waiter(w) do { } while (0) #define debug_rt_mutex_reset_waiter(w) do { } while (0)
static inline void rt_mutex_print_deadlock(struct rt_mutex_waiter *w)
{
WARN(1, "rtmutex deadlock detected\n");
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment