Commit 372b0ffc authored by Frederic Weisbecker's avatar Frederic Weisbecker Committed by Wei Li
Browse files

entry/rcu: Check TIF_RESCHED _after_ delayed RCU wake-up

mainline inclusion
from mainline-v6.3-rc4
commit b4165140
category: bugfix
bugzilla: https://gitee.com/openeuler/kernel/issues/I9NZ3E

Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit?id=b416514054810cf2d2cc348ae477cea619b64da7



--------------------------------

RCU sometimes needs to perform a delayed wake up for specific kthreads
handling offloaded callbacks (RCU_NOCB).  These wakeups are performed
by timers and upon entry to idle (also to guest and to user on nohz_full).

However the delayed wake-up on kernel exit is actually performed after
the thread flags are fetched towards the fast path check for work to
do on exit to user. As a result, and if there is no other pending work
to do upon that kernel exit, the current task will resume to userspace
with TIF_RESCHED set and the pending wake up ignored.

Fix this with fetching the thread flags _after_ the delayed RCU-nocb
kthread wake-up.

Fixes: 47b8ff19 ("entry: Explicitly flush pending rcuog wakeup before last rescheduling point")
Signed-off-by: default avatarFrederic Weisbecker <frederic@kernel.org>
Signed-off-by: default avatarPaul E. McKenney <paulmck@kernel.org>
Signed-off-by: default avatarJoel Fernandes (Google) <joel@joelfernandes.org>
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
Link: https://lore.kernel.org/r/20230315194349.10798-3-joel@joelfernandes.org


Signed-off-by: default avatarWei Li <liwei391@huawei.com>
parent 12bd646d
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -195,13 +195,14 @@ static unsigned long exit_to_user_mode_loop(struct pt_regs *regs,

static void exit_to_user_mode_prepare(struct pt_regs *regs)
{
	unsigned long ti_work = read_thread_flags();
	unsigned long ti_work;

	lockdep_assert_irqs_disabled();

	/* Flush pending rcuog wakeup before the last need_resched() check */
	rcu_nocb_flush_deferred_wakeup();

	ti_work = read_thread_flags();
	if (unlikely(ti_work & EXIT_TO_USER_MODE_WORK))
		ti_work = exit_to_user_mode_loop(regs, ti_work);