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.

Signed-off-by: Thomas Gleixner <[email protected]>
Cc: [email protected]
---
 kernel/locking/rtmutex.c |   22 +++++++++++++++++-----
 1 file changed, 17 insertions(+), 5 deletions(-)

Index: linux-2.6/kernel/locking/rtmutex.c
===================================================================
--- linux-2.6.orig/kernel/locking/rtmutex.c
+++ linux-2.6/kernel/locking/rtmutex.c
@@ -343,16 +343,24 @@ 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 (top_waiter != task_top_pi_waiter(task)) {
+                       if (!detect_deadlock)
+                               goto out_unlock_pi;
+               }
+       }
 
        /*
         * When deadlock detection is off then we check, if further
         * priority adjustment is necessary.
         */
-       if (!detect_deadlock && waiter->prio == task->prio)
-               goto out_unlock_pi;
+       if (waiter->prio == task->prio) {
+               if (!detect_deadlock)
+                       goto out_unlock_pi;
+       }
 
        lock = waiter->lock;
        if (!raw_spin_trylock(&lock->wait_lock)) {
@@ -527,6 +535,10 @@ static int task_blocks_on_rt_mutex(struc
        unsigned long flags;
        int chain_walk = 0, res;
 
+       /* Early deadlock detection */
+       if (detect_deadlock && owner == task)
+               return -EDEADLK;
+
        raw_spin_lock_irqsave(&task->pi_lock, flags);
        __rt_mutex_adjust_prio(task);
        waiter->task = task;


--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to