UPSTREAM: sched: Consider task_struct::saved_state in wait_task_inactive()

With the introduction of task_struct::saved_state in commit
5f220be214 ("sched/wakeup: Prepare for RT sleeping spin/rwlocks")
matching the task state has gotten more complicated. That same commit
changed try_to_wake_up() to consider both states, but
wait_task_inactive() has been neglected.

Sebastian noted that the wait_task_inactive() usage in
ptrace_check_attach() can misbehave when ptrace_stop() is blocked on
the tasklist_lock after it sets TASK_TRACED.

Therefore extract a common helper from ttwu_state_match() and use that
to teach wait_task_inactive() about the PREEMPT_RT locks.

Originally-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Tested-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Link: https://lkml.kernel.org/r/20230601091234.GW83892@hirez.programming.kicks-ass.net

(cherry picked from commit 1c06918788)
Bug: 292064955
Change-Id: I2cc02dfdf3c04be146078f80d09c3a87979d79a6
Signed-off-by: Elliot Berman <quic_eberman@quicinc.com>
This commit is contained in:
Peter Zijlstra
2023-05-31 16:39:07 +02:00
committed by Carlos Llamas
parent b52b33e912
commit ac9005946a

View File

@@ -2249,6 +2249,39 @@ void check_preempt_curr(struct rq *rq, struct task_struct *p, int flags)
} }
EXPORT_SYMBOL_GPL(check_preempt_curr); EXPORT_SYMBOL_GPL(check_preempt_curr);
static __always_inline
int __task_state_match(struct task_struct *p, unsigned int state)
{
if (READ_ONCE(p->__state) & state)
return 1;
#ifdef CONFIG_PREEMPT_RT
if (READ_ONCE(p->saved_state) & state)
return -1;
#endif
return 0;
}
static __always_inline
int task_state_match(struct task_struct *p, unsigned int state)
{
#ifdef CONFIG_PREEMPT_RT
int match;
/*
* Serialize against current_save_and_set_rtlock_wait_state() and
* current_restore_rtlock_saved_state().
*/
raw_spin_lock_irq(&p->pi_lock);
match = __task_state_match(p, state);
raw_spin_unlock_irq(&p->pi_lock);
return match;
#else
return __task_state_match(p, state);
#endif
}
/* /*
* wait_task_inactive - wait for a thread to unschedule. * wait_task_inactive - wait for a thread to unschedule.
* *
@@ -2267,7 +2300,7 @@ EXPORT_SYMBOL_GPL(check_preempt_curr);
*/ */
unsigned long wait_task_inactive(struct task_struct *p, unsigned int match_state) unsigned long wait_task_inactive(struct task_struct *p, unsigned int match_state)
{ {
int running, queued; int running, queued, match;
struct rq_flags rf; struct rq_flags rf;
unsigned long ncsw; unsigned long ncsw;
struct rq *rq; struct rq *rq;
@@ -2293,7 +2326,7 @@ unsigned long wait_task_inactive(struct task_struct *p, unsigned int match_state
* is actually now running somewhere else! * is actually now running somewhere else!
*/ */
while (task_on_cpu(rq, p)) { while (task_on_cpu(rq, p)) {
if (!(READ_ONCE(p->__state) & match_state)) if (!task_state_match(p, match_state))
return 0; return 0;
cpu_relax(); cpu_relax();
} }
@@ -2308,8 +2341,15 @@ unsigned long wait_task_inactive(struct task_struct *p, unsigned int match_state
running = task_on_cpu(rq, p); running = task_on_cpu(rq, p);
queued = task_on_rq_queued(p); queued = task_on_rq_queued(p);
ncsw = 0; ncsw = 0;
if (READ_ONCE(p->__state) & match_state) if ((match = __task_state_match(p, match_state))) {
/*
* When matching on p->saved_state, consider this task
* still queued so it will wait.
*/
if (match < 0)
queued = 1;
ncsw = p->nvcsw | LONG_MIN; /* sets MSB */ ncsw = p->nvcsw | LONG_MIN; /* sets MSB */
}
task_rq_unlock(rq, p, &rf); task_rq_unlock(rq, p, &rf);
/* /*
@@ -4019,15 +4059,14 @@ static void ttwu_queue(struct task_struct *p, int cpu, int wake_flags)
static __always_inline static __always_inline
bool ttwu_state_match(struct task_struct *p, unsigned int state, int *success) bool ttwu_state_match(struct task_struct *p, unsigned int state, int *success)
{ {
int match;
if (IS_ENABLED(CONFIG_DEBUG_PREEMPT)) { if (IS_ENABLED(CONFIG_DEBUG_PREEMPT)) {
WARN_ON_ONCE((state & TASK_RTLOCK_WAIT) && WARN_ON_ONCE((state & TASK_RTLOCK_WAIT) &&
state != TASK_RTLOCK_WAIT); state != TASK_RTLOCK_WAIT);
} }
if (READ_ONCE(p->__state) & state) { *success = !!(match = __task_state_match(p, state));
*success = 1;
return true;
}
#ifdef CONFIG_PREEMPT_RT #ifdef CONFIG_PREEMPT_RT
/* /*
@@ -4043,12 +4082,10 @@ bool ttwu_state_match(struct task_struct *p, unsigned int state, int *success)
* p::saved_state to TASK_RUNNING so any further tests will * p::saved_state to TASK_RUNNING so any further tests will
* not result in false positives vs. @success * not result in false positives vs. @success
*/ */
if (p->saved_state & state) { if (match < 0)
p->saved_state = TASK_RUNNING; p->saved_state = TASK_RUNNING;
*success = 1;
}
#endif #endif
return false; return match > 0;
} }
/* /*