[PATCH] sched: Fix race in rt_mutex_pre_schedule by removing non-atomic fetch_and_set

From: cuiguoqi
Date: Tue Aug 26 2025 - 07:08:57 EST


During Wound/Wait testing on PREEMPT_RT, a WARNING was hit:

WARNING: CPU: 0 PID: 0 at kernel/sched/core.c:7085 rt_mutex_pre_schedule+0xa8/0x108
Call trace:
rt_mutex_pre_schedule+0xa8/0x108
__ww_rt_mutex_lock+0x1d4/0x300
ww_mutex_lock+0x1c/0x30

The issue stems from the non-atomic `fetch_and_set` macro:
#define fetch_and_set(x, v) ({ int _x = (x); (x) = (v); _x; })

It lacks atomicity and memory ordering, leading to race conditions under
preemption or interrupts, where `current->sched_rt_mutex` may be corrupted.

Since this flag is only used for lockdep assertions and accessed per-task,
replace the unsafe macro with direct assignment and explicit state checks:

- In rt_mutex_pre_schedule(): assert is 0 before setting to 1.
- In rt_mutex_post_schedule(): assert is 1 before clearing to 0.

This fixes the false-positive warning without needing atomic operations.

Signed-off-by: cuiguoqi <cuiguoqi@xxxxxxxxxx>
---
kernel/sched/core.c | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 5e89a6eeadba..fb4c446e46f7 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -7078,11 +7078,11 @@ const struct sched_class *__setscheduler_class(struct task_struct *p, int prio)
* name such that if someone were to implement this function we get to compare
* notes.
*/
-#define fetch_and_set(x, v) ({ int _x = (x); (x) = (v); _x; })

void rt_mutex_pre_schedule(void)
{
- lockdep_assert(!fetch_and_set(current->sched_rt_mutex, 1));
+ lockdep_assert(!current->sched_rt_mutex);
+ current->sched_rt_mutex = 1;
sched_submit_work(current);
}

@@ -7095,7 +7095,9 @@ void rt_mutex_schedule(void)
void rt_mutex_post_schedule(void)
{
sched_update_worker(current);
- lockdep_assert(fetch_and_set(current->sched_rt_mutex, 0));
+ lockdep_assert(current->sched_rt_mutex);
+ current->sched_rt_mutex = 0;
+
}

/*
--
2.25.1