rtmutex: Drop rt_mutex::wait_lock before scheduling
authorRoland Xu <mu001999@outlook.com>
Thu, 15 Aug 2024 02:58:13 +0000 (10:58 +0800)
committerThomas Gleixner <tglx@linutronix.de>
Thu, 15 Aug 2024 13:38:53 +0000 (15:38 +0200)
rt_mutex_handle_deadlock() is called with rt_mutex::wait_lock held.  In the
good case it returns with the lock held and in the deadlock case it emits a
warning and goes into an endless scheduling loop with the lock held, which
triggers the 'scheduling in atomic' warning.

Unlock rt_mutex::wait_lock in the dead lock case before issuing the warning
and dropping into the schedule for ever loop.

[ tglx: Moved unlock before the WARN(), removed the pointless comment,
   massaged changelog, added Fixes tag ]

Fixes: 3d5c9340d194 ("rtmutex: Handle deadlock detection smarter")
Signed-off-by: Roland Xu <mu001999@outlook.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Cc: stable@vger.kernel.org
Link: https://lore.kernel.org/all/ME0P300MB063599BEF0743B8FA339C2CECC802@ME0P300MB0635.AUSP300.PROD.OUTLOOK.COM
kernel/locking/rtmutex.c

index 88d08eeb8bc03df508146f30a8120d80654a1759..fba1229f1de669d55b2f195f2a73e1a684299c7a 100644 (file)
@@ -1644,6 +1644,7 @@ static int __sched rt_mutex_slowlock_block(struct rt_mutex_base *lock,
 }
 
 static void __sched rt_mutex_handle_deadlock(int res, int detect_deadlock,
+                                            struct rt_mutex_base *lock,
                                             struct rt_mutex_waiter *w)
 {
        /*
@@ -1656,10 +1657,10 @@ static void __sched rt_mutex_handle_deadlock(int res, int detect_deadlock,
        if (build_ww_mutex() && w->ww_ctx)
                return;
 
-       /*
-        * Yell loudly and stop the task right here.
-        */
+       raw_spin_unlock_irq(&lock->wait_lock);
+
        WARN(1, "rtmutex deadlock detected\n");
+
        while (1) {
                set_current_state(TASK_INTERRUPTIBLE);
                rt_mutex_schedule();
@@ -1713,7 +1714,7 @@ static int __sched __rt_mutex_slowlock(struct rt_mutex_base *lock,
        } else {
                __set_current_state(TASK_RUNNING);
                remove_waiter(lock, waiter);
-               rt_mutex_handle_deadlock(ret, chwalk, waiter);
+               rt_mutex_handle_deadlock(ret, chwalk, lock, waiter);
        }
 
        /*