mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-09 04:10:18 +09:00
UPSTREAM: sched: Consider task_struct::saved_state in wait_task_inactive()
With the introduction of task_struct::saved_state in commit5f220be214("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 commit1c06918788) Bug: 292064955 Change-Id: I2cc02dfdf3c04be146078f80d09c3a87979d79a6 Signed-off-by: Elliot Berman <quic_eberman@quicinc.com>
This commit is contained in:
committed by
Carlos Llamas
parent
b52b33e912
commit
ac9005946a
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
Reference in New Issue
Block a user