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
next prev parent 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).