Merge tag 'perf-tools-for-v5.15-2021-09-11' of git://git.kernel.org/pub/scm/linux...
[linux-2.6-microblaze.git] / kernel / locking / rtmutex.c
index b3c0961..6bb116c 100644 (file)
@@ -656,6 +656,31 @@ static int __sched rt_mutex_adjust_prio_chain(struct task_struct *task,
        if (next_lock != waiter->lock)
                goto out_unlock_pi;
 
+       /*
+        * There could be 'spurious' loops in the lock graph due to ww_mutex,
+        * consider:
+        *
+        *   P1: A, ww_A, ww_B
+        *   P2: ww_B, ww_A
+        *   P3: A
+        *
+        * P3 should not return -EDEADLK because it gets trapped in the cycle
+        * created by P1 and P2 (which will resolve -- and runs into
+        * max_lock_depth above). Therefore disable detect_deadlock such that
+        * the below termination condition can trigger once all relevant tasks
+        * are boosted.
+        *
+        * Even when we start with ww_mutex we can disable deadlock detection,
+        * since we would supress a ww_mutex induced deadlock at [6] anyway.
+        * Supressing it here however is not sufficient since we might still
+        * hit [6] due to adjustment driven iteration.
+        *
+        * NOTE: if someone were to create a deadlock between 2 ww_classes we'd
+        * utterly fail to report it; lockdep should.
+        */
+       if (IS_ENABLED(CONFIG_PREEMPT_RT) && waiter->ww_ctx && detect_deadlock)
+               detect_deadlock = false;
+
        /*
         * Drop out, when the task has no waiters. Note,
         * top_waiter can be NULL, when we are in the deboosting
@@ -717,8 +742,21 @@ static int __sched rt_mutex_adjust_prio_chain(struct task_struct *task,
         * walk, we detected a deadlock.
         */
        if (lock == orig_lock || rt_mutex_owner(lock) == top_task) {
-               raw_spin_unlock(&lock->wait_lock);
                ret = -EDEADLK;
+
+               /*
+                * When the deadlock is due to ww_mutex; also see above. Don't
+                * report the deadlock and instead let the ww_mutex wound/die
+                * logic pick which of the contending threads gets -EDEADLK.
+                *
+                * NOTE: assumes the cycle only contains a single ww_class; any
+                * other configuration and we fail to report; also, see
+                * lockdep.
+                */
+               if (IS_ENABLED(CONFIG_PREEMPT_RT) && orig_waiter && orig_waiter->ww_ctx)
+                       ret = 0;
+
+               raw_spin_unlock(&lock->wait_lock);
                goto out_unlock_pi;
        }
 
@@ -1082,8 +1120,13 @@ static int __sched task_blocks_on_rt_mutex(struct rt_mutex_base *lock,
                /* Check whether the waiter should back out immediately */
                rtm = container_of(lock, struct rt_mutex, rtmutex);
                res = __ww_mutex_add_waiter(waiter, rtm, ww_ctx);
-               if (res)
+               if (res) {
+                       raw_spin_lock(&task->pi_lock);
+                       rt_mutex_dequeue(lock, waiter);
+                       task->pi_blocked_on = NULL;
+                       raw_spin_unlock(&task->pi_lock);
                        return res;
+               }
        }
 
        if (!owner)