mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
To: Peter Zijlstra <peterz@infradead.org>
Cc: tglx@kernel.org, mingo@redhat.com, linux-kernel@vger.kernel.org,
	will@kernel.org, boqun@kernel.org, longman@redhat.com,
	rostedt@goodmis.org, bigeasy@linutronix.de, clrkwllms@kernel.org,
	dvhart@infradead.org, dave@stgolabs.net, andrealmeid@igalia.com,
	shuah@kernel.org, juri.lelli@redhat.com,
	linux-rt-devel@lists.linux.dev, linux-kselftest@vger.kernel.org,
	stable@vger.kernel.org
Subject: Re: [PATCH 3/5] locking/rtmutex: Clone a waiter's sort keys as it enters pi_waiters
Date: Sat, 7 Nov 2026 09:19:27 -0500	[thread overview]
Message-ID: <7bac6a0c-2645-43b4-9b63-b9cd025d9e1c@efficios.com> (raw)
In-Reply-To: <20261107105704.GB2668564@noisy.programming.kicks-ass.net>

On 2026-11-07 05:57, Peter Zijlstra wrote:
> On Fri, Nov 06, 2026 at 06:59:29PM -0500, Mathieu Desnoyers wrote:
[...]
>> @@ -1243,7 +1249,6 @@ static int __sched task_blocks_on_rt_mutex(struct rt_mutex_base *lock,
>>  	waiter->task = task;
>>  	waiter->lock = lock;
>>  	waiter_update_prio(waiter, task);
>> -	waiter_clone_prio(waiter, task);
>
> Reverse engineering the problem leads me to think this is the problem
> spot. Because this is the one site that is not immediately followed by
> rt_mutex_enqueue_pi().
>
> I'm thinking there's race due to dropping pi_lock between this copy and
> the subsequent enqueue?

I don't think there is a race there. task_blocks_on_rt_mutex() holds
the wait_lock from this copy to the enqueue. waiter->tree only changes
under the wait_lock. So at the enqueue, waiter->pi_tree still equals
waiter->tree.

The problem spots are the two enqueues with no copy before them, in
remove_waiter() and try_to_take_rt_mutex(). Both enqueue the new top
waiter of the lock. When a waiter's priority changes, the chain walk
updates its waiter->tree. It copies waiter->tree to waiter->pi_tree for
the top waiter only. So a waiter that was not the top waiter can enter
pi_waiters with old keys.

This copy goes away only because rt_mutex_enqueue_pi() now does it.

I rewrote the changelog and the comment. The new version of the patch
is below. Do they read better now? I would like your feedback on both
before I send v2.

Thanks,

Mathieu

-- >8 --
From: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Subject: locking/rtmutex: Clone the sort keys in rt_mutex_enqueue_pi()

An rt_mutex waiter has two copies of its sort keys. waiter->tree sorts
the waiters of its lock. waiter->pi_tree sorts the pi_waiters of the
lock owner. Only the top waiter of each lock is in pi_waiters.

When the priority of a waiter changes, the chain walk updates
waiter->tree. It copies waiter->tree to waiter->pi_tree for the top
waiter only. A waiter that is not the top waiter keeps its old pi_tree
keys.

Such a waiter can become the top waiter later. remove_waiter() does this
when the top waiter leaves. try_to_take_rt_mutex() does it when the lock
changes owner. Both put the new top waiter in pi_waiters without a new
copy. So pi_waiters sorts it by a priority it no longer has. The owner
then inherits from the wrong waiter, at that waiter's current priority.

In this example, O is the owner, a SCHED_OTHER task. L1 and L2 are two
rt_mutexes. W1, W2 and W3 are waiters. Their SCHED_FIFO priority is
under their name. T is any other task. It changes the priority of W2:

   T                         O                W1                            W2           W3
                                              FIFO 80                       FIFO 70      FIFO 50

                             locks L1, L2
                                                                                         blocks on L2
                                              blocks on L1 (timed)
                                                                            blocks on L1
   sched_setscheduler(W2)
     W2 now at FIFO 10
     rt_mutex_adjust_pi(W2)
       W2->tree: FIFO 10
       W2 not top: no copy
       W2->pi_tree: FIFO 70
                                              <timeout>
                                              remove_waiter()
                                                rt_mutex_enqueue_pi(O, W2)
                                                  W2 sorted as FIFO 70,
                                                  ahead of W3
                                                rt_mutex_adjust_prio(O)
                                                  O inherits W2's FIFO 10
                             runs at FIFO 10                                             still waits

The owner runs at SCHED_FIFO 10 while a SCHED_FIFO 50 task waits for
it. This needs no race. Each step can come long after the one before.

Fix this by copying the keys in rt_mutex_enqueue_pi(). All callers hold
the wait_lock and the owner's pi_lock, as waiter_clone_prio() requires.
Remove the three copies the callers made.

With this patch, the owner runs at SCHED_FIFO 50. The
futex_lock_pi_inherit selftest added later in this series tests this
case (promoted_waiter).

Fixes: f7853c342418 ("locking/rtmutex: Fix task->pi_waiters integrity")
Cc: stable@vger.kernel.org
Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
---
  kernel/locking/rtmutex.c | 5 ++---
  1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/kernel/locking/rtmutex.c b/kernel/locking/rtmutex.c
index 323973be2074..975efa1d864c 100644
--- a/kernel/locking/rtmutex.c
+++ b/kernel/locking/rtmutex.c
@@ -517,6 +517,8 @@ rt_mutex_enqueue_pi(struct task_struct *task, struct rt_mutex_waiter *waiter)
  {
  	lockdep_assert_held(&task->pi_lock);
  
+	/* The tree keys can change while @waiter is not in pi_waiters. */
+	waiter_clone_prio(waiter, task);
  	rb_add_cached(&waiter->pi_tree.entry, &task->pi_waiters, __pi_waiter_less);
  }
  
@@ -1005,7 +1007,6 @@ static int __sched rt_mutex_adjust_prio_chain(struct task_struct *task,
  		 * and adjust the priority of the owner.
  		 */
  		rt_mutex_dequeue_pi(task, prerequeue_top_waiter);
-		waiter_clone_prio(waiter, task);
  		rt_mutex_enqueue_pi(task, waiter);
  		rt_mutex_adjust_prio(lock, task);
  
@@ -1022,7 +1023,6 @@ static int __sched rt_mutex_adjust_prio_chain(struct task_struct *task,
  		 */
  		rt_mutex_dequeue_pi(task, waiter);
  		waiter = rt_mutex_top_waiter(lock);
-		waiter_clone_prio(waiter, task);
  		rt_mutex_enqueue_pi(task, waiter);
  		rt_mutex_adjust_prio(lock, task);
  	} else {
@@ -1243,7 +1243,6 @@ static int __sched task_blocks_on_rt_mutex(struct rt_mutex_base *lock,
  	waiter->task = task;
  	waiter->lock = lock;
  	waiter_update_prio(waiter, task);
-	waiter_clone_prio(waiter, task);
  
  	/* Get the top priority waiter on the lock */
  	if (rt_mutex_has_waiters(lock))

  reply	other threads:[~2026-11-07 14:19 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-06 23:59 [PATCH 0/5] locking/rtmutex: priority inheritance fixes and tests Mathieu Desnoyers
2026-11-06 23:59 ` [PATCH 1/5] locking/rtmutex: Stop spinning on the owner when the waiter is woken Mathieu Desnoyers
2026-11-07  5:29   ` Steven Rostedt
2026-11-07 10:43     ` Peter Zijlstra
2026-11-07 12:29       ` Mathieu Desnoyers
2026-11-07 17:36         ` Steven Rostedt
2026-11-06 23:59 ` [PATCH 2/5] locking/rtmutex: Initialize the ww_mutex context of a waiter Mathieu Desnoyers
2026-11-06 23:59 ` [PATCH 3/5] locking/rtmutex: Clone a waiter's sort keys as it enters pi_waiters Mathieu Desnoyers
2026-11-07 10:57   ` Peter Zijlstra
2026-11-07 14:19     ` Mathieu Desnoyers [this message]
2026-11-06 23:59 ` [PATCH 4/5] selftests/futex: Test priority inheritance on PI futexes Mathieu Desnoyers
2026-11-06 23:59 ` [PATCH 5/5] locking/ww_mutex/test: Time an ABBA with both owners running Mathieu Desnoyers
2026-11-07  0:11   ` sashiko-bot
2026-11-07  0:27     ` Mathieu Desnoyers

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=7bac6a0c-2645-43b4-9b63-b9cd025d9e1c@efficios.com \
    --to=mathieu.desnoyers@efficios.com \
    --cc=andrealmeid@igalia.com \
    --cc=bigeasy@linutronix.de \
    --cc=boqun@kernel.org \
    --cc=clrkwllms@kernel.org \
    --cc=dave@stgolabs.net \
    --cc=dvhart@infradead.org \
    --cc=juri.lelli@redhat.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-kselftest@vger.kernel.org \
    --cc=linux-rt-devel@lists.linux.dev \
    --cc=longman@redhat.com \
    --cc=mingo@redhat.com \
    --cc=peterz@infradead.org \
    --cc=rostedt@goodmis.org \
    --cc=shuah@kernel.org \
    --cc=stable@vger.kernel.org \
    --cc=tglx@kernel.org \
    --cc=will@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®