linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Yao Kai <yaokai34@huawei.com>
To: Sebastian Andrzej Siewior <bigeasy@linutronix.de>,
	Peter Zijlstra <peterz@infradead.org>
Cc: <linux-kernel@vger.kernel.org>, <tglx@kernel.org>,
	<mingo@redhat.com>, <dvhart@infradead.org>, <dave@stgolabs.net>,
	<andrealmeid@igalia.com>, <liuyongqiang13@huawei.com>,
	Crystal Wood <swood@redhat.com>
Subject: Re: [PATCH v2 1/2] futex/requeue: Fix rtmutex schedule preparation for requeue PI
Date: Tue, 25 Aug 2026 11:13:58 +0800	[thread overview]
Message-ID: <5df8a43e-11bb-4881-877d-40034c414984@huawei.com> (raw)
In-Reply-To: <20260824140809.eJxsIRHn@linutronix.de>



On 8/24/2026 10:08 PM, Sebastian Andrzej Siewior wrote:
> On 2026-08-04 14:21:02 [+0200], Peter Zijlstra wrote:
>>> @@ -865,7 +866,14 @@ int futex_wait_requeue_pi(u32 __user *uaddr, unsigned int flags,
>>>   	case Q_REQUEUE_PI_DONE:
>>>   		/* Requeue completed. Current is 'pi_blocked_on' the rtmutex */
>>>   		pi_mutex = &q.pi_state->pi_mutex;
>>> +		/*
>>> +		 * Requeue temporarily removes q from the hash bucket, so
>>> +		 * futex_do_wait() may skip schedule() even though the proxy
>>> +		 * waiter still has to block on the rtmutex.
>>> +		 */
>>> +		rt_mutex_pre_schedule();
>>>   		ret = rt_mutex_wait_proxy_lock(pi_mutex, to, &rt_waiter);
>>> +		rt_mutex_post_schedule();
>>
> …
>> So the purpose of rt_mutex_pre_schedule() was to avoid the double waiter
>> enqueue for rt_mutex on RT, where sched_submit_work() will hit a
>> spinlock-nee-rtlock.
>>
>> So rt_mutex_pre_schedule() must happen before the rt_mutex is added as a
>> waiter. However, AFAICT we're already a waiter at the above spot, no? So
>> this cannot be right.
> 
> Urgh. So I missed this part entirely while reading it.
> Isn't the usage of this in futex_lock_pi() just to keep the assert
> quiet?
> We do add a waiter there (futex_lock_pi()) and this
> (rt_mutex_pre_schedule()) must be done before a waiter is enqueued so we
> can acquire the lock (mutex) during the blk_flush_plug() which is in
> general part of schedule().
> While doing all this, we moved the flush outside for mutex_t locking
> (mutex_lock() and others like it) and use rt_mutex_schedule() instead of
> schedule().
> We do rt_mutex_pre_schedule() to flush the possible plug to avoid a
> deadlock in case we didn't flush it and someone waits for it. But need
> to do it before we add a waiter because we can be only have on one
> pi_waiter.
> spinlock_t doesn't flush it so we use schedule_rtlock() there instead
> because as per definition this kind of lock can't have any dependency so
> we don't flush the plug.
> 
> Since the futex's rt_mutex usage matches more the mutex we ended up
> with rt_mutex_pre_schedule() + rt_mutex_schedule() around the wait
> schedule.
> 
> blk_flush_plug() is not preserved across syscalls boundaries, there is
> usually blk_start_plug() followed by blk_finish_plug(). So the futex
> code shouldn't have the need to flush it at all because there shouldn't
> be any.
> 
> I suppose for futex's usage it should be enough to simple have a dummy
> to skip the assert and not flush the plug at all.
> 
> Otherwise we would have to flush the possible plug before
> futex_wait_setup() (since starting here the requeue code could act) and
> need to clear task_struct::sched_rt_mutex before futex_do_wait() again
> because it's schedule() usage will trigger another assert. And then we
> need something for rt_mutex_wait_proxy_lock().
> 
> So maybe, we have something to satisfy the assert without plug flush for
> futex/pi/requeue.
> 
>> The changelogs doesn't at all explain why this is correct. Please help?
> 
> I updated it for other reasons. Probably not what you have expected.
> Maybe the above is better reasoning but then we might want a different
> solution…
> 
> Sebastian

How about the patch below?

diff --git a/include/linux/sched/rt.h b/include/linux/sched/rt.h
index 4e3338103654..b7c1dbc193de 100644
--- a/include/linux/sched/rt.h
+++ b/include/linux/sched/rt.h
@@ -51,6 +51,8 @@ static inline bool rt_or_dl_task_policy(struct task_struct *tsk)
  }
  
  #ifdef CONFIG_RT_MUTEXES
+void rt_mutex_futex_pre_schedule(void);
+void rt_mutex_futex_post_schedule(void);
  extern void rt_mutex_pre_schedule(void);
  extern void rt_mutex_schedule(void);
  extern void rt_mutex_post_schedule(void);
diff --git a/kernel/futex/pi.c b/kernel/futex/pi.c
index 795011ea1202..169e6844cecb 100644
--- a/kernel/futex/pi.c
+++ b/kernel/futex/pi.c
@@ -1,7 +1,6 @@
  // SPDX-License-Identifier: GPL-2.0-or-later
  
  #include <linux/slab.h>
-#include <linux/sched/rt.h>
  #include <linux/sched/task.h>
  
  #include "futex.h"
@@ -1002,20 +1001,11 @@ int futex_lock_pi(u32 __user *uaddr, unsigned int flags, ktime_t *time, int tryl
  		}
  
  		/*
-		 * Caution; releasing @hb in-scope. The hb->lock is still locked
-		 * while the reference is dropped. The reference can not be dropped
-		 * after the unlock because if a user initiated resize is in progress
-		 * then we might need to wake him. This can not be done after the
-		 * rt_mutex_pre_schedule() invocation. The hb will remain valid because
-		 * the thread, performing resize, will block on hb->lock during
-		 * the requeue.
+		 * Drop the hash reference before releasing hb->lock below. If this
+		 * wakes a concurrent resize, holding hb->lock keeps the bucket valid
+		 * until the lock handoff is complete.
  		 */
  		futex_private_hash_put(no_free_ptr(hbr.fph));
-		/*
-		 * Must be done before we enqueue the waiter, here is unfortunately
-		 * under the hb lock, but that *should* work because it does nothing.
-		 */
-		rt_mutex_pre_schedule();
  
  		rt_mutex_init_waiter(&rt_waiter);
  
@@ -1081,10 +1071,6 @@ int futex_lock_pi(u32 __user *uaddr, unsigned int flags, ktime_t *time, int tryl
  		 * the
  		 */
  		futex_q_lockptr_lock(&q);
-		/*
-		 * Waiter is unqueued.
-		 */
-		rt_mutex_post_schedule();
  no_block:
  		/*
  		 * Fixup the pi_state owner and possibly acquire the lock if we
diff --git a/kernel/locking/rtmutex_api.c b/kernel/locking/rtmutex_api.c
index 5d48d64725b1..eb18b094473c 100644
--- a/kernel/locking/rtmutex_api.c
+++ b/kernel/locking/rtmutex_api.c
@@ -423,6 +423,7 @@ int __sched rt_mutex_wait_proxy_lock(struct rt_mutex_base *lock,
  {
  	int ret;
  
+	rt_mutex_futex_pre_schedule();
  	raw_spin_lock_irq(&lock->wait_lock);
  	/* sleep on the mutex */
  	set_current_state(TASK_INTERRUPTIBLE);
@@ -433,6 +434,7 @@ int __sched rt_mutex_wait_proxy_lock(struct rt_mutex_base *lock,
  	 */
  	fixup_rt_mutex_waiters(lock, true);
  	raw_spin_unlock_irq(&lock->wait_lock);
+	rt_mutex_futex_post_schedule();
  
  	return ret;
  }
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 96226707c2f6..eb6d7db5ad23 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -7595,6 +7595,23 @@ const struct sched_class *__setscheduler_class(int policy, int prio)
   */
  #define fetch_and_set(x, v) ({ int _x = (x); (x) = (v); _x; })
  
+/*
+ * PI futex waits run only in syscall context, so current cannot be a worker
+ * or carry plugged I/O across the userspace boundary. Only establish the
+ * state required by rt_mutex_schedule().
+ */
+void rt_mutex_futex_pre_schedule(void)
+{
+	lockdep_assert(!(current->flags & (PF_WQ_WORKER | PF_IO_WORKER)));
+	lockdep_assert(!current->plug);
+	lockdep_assert(!fetch_and_set(current->sched_rt_mutex, 1));
+}
+
+void rt_mutex_futex_post_schedule(void)
+{
+	lockdep_assert(fetch_and_set(current->sched_rt_mutex, 0));
+}
+
  void rt_mutex_pre_schedule(void)
  {
  	lockdep_assert(!fetch_and_set(current->sched_rt_mutex, 1));

Thanks,
Yao




  reply	other threads:[~2026-08-25  3:14 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-22  8:51 [PATCH v2 0/2] futex/requeue: Fix requeue PI races Yao Kai
2026-07-22  8:51 ` [PATCH v2 1/2] futex/requeue: Fix rtmutex schedule preparation for requeue PI Yao Kai
2026-08-04 12:21   ` Peter Zijlstra
2026-08-05  8:00     ` Yao Kai
2026-08-24  3:07       ` Yao Kai
2026-08-24 14:08     ` Sebastian Andrzej Siewior
2026-08-25  3:13       ` Yao Kai [this message]
2026-07-22  8:51 ` [PATCH v2 2/2] futex/requeue: Prevent rcuwait use-after-free during " Yao Kai

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=5df8a43e-11bb-4881-877d-40034c414984@huawei.com \
    --to=yaokai34@huawei.com \
    --cc=andrealmeid@igalia.com \
    --cc=bigeasy@linutronix.de \
    --cc=dave@stgolabs.net \
    --cc=dvhart@infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=liuyongqiang13@huawei.com \
    --cc=mingo@redhat.com \
    --cc=peterz@infradead.org \
    --cc=swood@redhat.com \
    --cc=tglx@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;
as well as URLs for NNTP newsgroup(s).