Commit 857d315f authored by Peter Zijlstra's avatar Peter Zijlstra

sched: Simplify ttwu()

Use guards to reduce gotos and simplify control flow.
Signed-off-by: default avatarPeter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: default avatarValentin Schneider <vschneid@redhat.com>
Link: https://lore.kernel.org/r/20230801211812.101069260@infradead.org
parent 4eb054f9
...@@ -3733,14 +3733,14 @@ ttwu_stat(struct task_struct *p, int cpu, int wake_flags) ...@@ -3733,14 +3733,14 @@ ttwu_stat(struct task_struct *p, int cpu, int wake_flags)
struct sched_domain *sd; struct sched_domain *sd;
__schedstat_inc(p->stats.nr_wakeups_remote); __schedstat_inc(p->stats.nr_wakeups_remote);
rcu_read_lock();
guard(rcu)();
for_each_domain(rq->cpu, sd) { for_each_domain(rq->cpu, sd) {
if (cpumask_test_cpu(cpu, sched_domain_span(sd))) { if (cpumask_test_cpu(cpu, sched_domain_span(sd))) {
__schedstat_inc(sd->ttwu_wake_remote); __schedstat_inc(sd->ttwu_wake_remote);
break; break;
} }
} }
rcu_read_unlock();
} }
if (wake_flags & WF_MIGRATED) if (wake_flags & WF_MIGRATED)
...@@ -4199,10 +4199,9 @@ bool ttwu_state_match(struct task_struct *p, unsigned int state, int *success) ...@@ -4199,10 +4199,9 @@ bool ttwu_state_match(struct task_struct *p, unsigned int state, int *success)
static int static int
try_to_wake_up(struct task_struct *p, unsigned int state, int wake_flags) try_to_wake_up(struct task_struct *p, unsigned int state, int wake_flags)
{ {
unsigned long flags; guard(preempt)();
int cpu, success = 0; int cpu, success = 0;
preempt_disable();
if (p == current) { if (p == current) {
/* /*
* We're waking current, this means 'p->on_rq' and 'task_cpu(p) * We're waking current, this means 'p->on_rq' and 'task_cpu(p)
...@@ -4229,129 +4228,127 @@ try_to_wake_up(struct task_struct *p, unsigned int state, int wake_flags) ...@@ -4229,129 +4228,127 @@ try_to_wake_up(struct task_struct *p, unsigned int state, int wake_flags)
* reordered with p->state check below. This pairs with smp_store_mb() * reordered with p->state check below. This pairs with smp_store_mb()
* in set_current_state() that the waiting thread does. * in set_current_state() that the waiting thread does.
*/ */
raw_spin_lock_irqsave(&p->pi_lock, flags); scoped_guard (raw_spinlock_irqsave, &p->pi_lock) {
smp_mb__after_spinlock(); smp_mb__after_spinlock();
if (!ttwu_state_match(p, state, &success)) if (!ttwu_state_match(p, state, &success))
goto unlock; break;
trace_sched_waking(p); trace_sched_waking(p);
/* /*
* Ensure we load p->on_rq _after_ p->state, otherwise it would * Ensure we load p->on_rq _after_ p->state, otherwise it would
* be possible to, falsely, observe p->on_rq == 0 and get stuck * be possible to, falsely, observe p->on_rq == 0 and get stuck
* in smp_cond_load_acquire() below. * in smp_cond_load_acquire() below.
* *
* sched_ttwu_pending() try_to_wake_up() * sched_ttwu_pending() try_to_wake_up()
* STORE p->on_rq = 1 LOAD p->state * STORE p->on_rq = 1 LOAD p->state
* UNLOCK rq->lock * UNLOCK rq->lock
* *
* __schedule() (switch to task 'p') * __schedule() (switch to task 'p')
* LOCK rq->lock smp_rmb(); * LOCK rq->lock smp_rmb();
* smp_mb__after_spinlock(); * smp_mb__after_spinlock();
* UNLOCK rq->lock * UNLOCK rq->lock
* *
* [task p] * [task p]
* STORE p->state = UNINTERRUPTIBLE LOAD p->on_rq * STORE p->state = UNINTERRUPTIBLE LOAD p->on_rq
* *
* Pairs with the LOCK+smp_mb__after_spinlock() on rq->lock in * Pairs with the LOCK+smp_mb__after_spinlock() on rq->lock in
* __schedule(). See the comment for smp_mb__after_spinlock(). * __schedule(). See the comment for smp_mb__after_spinlock().
* *
* A similar smb_rmb() lives in try_invoke_on_locked_down_task(). * A similar smb_rmb() lives in try_invoke_on_locked_down_task().
*/ */
smp_rmb(); smp_rmb();
if (READ_ONCE(p->on_rq) && ttwu_runnable(p, wake_flags)) if (READ_ONCE(p->on_rq) && ttwu_runnable(p, wake_flags))
goto unlock; break;
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
/* /*
* Ensure we load p->on_cpu _after_ p->on_rq, otherwise it would be * Ensure we load p->on_cpu _after_ p->on_rq, otherwise it would be
* possible to, falsely, observe p->on_cpu == 0. * possible to, falsely, observe p->on_cpu == 0.
* *
* One must be running (->on_cpu == 1) in order to remove oneself * One must be running (->on_cpu == 1) in order to remove oneself
* from the runqueue. * from the runqueue.
* *
* __schedule() (switch to task 'p') try_to_wake_up() * __schedule() (switch to task 'p') try_to_wake_up()
* STORE p->on_cpu = 1 LOAD p->on_rq * STORE p->on_cpu = 1 LOAD p->on_rq
* UNLOCK rq->lock * UNLOCK rq->lock
* *
* __schedule() (put 'p' to sleep) * __schedule() (put 'p' to sleep)
* LOCK rq->lock smp_rmb(); * LOCK rq->lock smp_rmb();
* smp_mb__after_spinlock(); * smp_mb__after_spinlock();
* STORE p->on_rq = 0 LOAD p->on_cpu * STORE p->on_rq = 0 LOAD p->on_cpu
* *
* Pairs with the LOCK+smp_mb__after_spinlock() on rq->lock in * Pairs with the LOCK+smp_mb__after_spinlock() on rq->lock in
* __schedule(). See the comment for smp_mb__after_spinlock(). * __schedule(). See the comment for smp_mb__after_spinlock().
* *
* Form a control-dep-acquire with p->on_rq == 0 above, to ensure * Form a control-dep-acquire with p->on_rq == 0 above, to ensure
* schedule()'s deactivate_task() has 'happened' and p will no longer * schedule()'s deactivate_task() has 'happened' and p will no longer
* care about it's own p->state. See the comment in __schedule(). * care about it's own p->state. See the comment in __schedule().
*/ */
smp_acquire__after_ctrl_dep(); smp_acquire__after_ctrl_dep();
/* /*
* We're doing the wakeup (@success == 1), they did a dequeue (p->on_rq * We're doing the wakeup (@success == 1), they did a dequeue (p->on_rq
* == 0), which means we need to do an enqueue, change p->state to * == 0), which means we need to do an enqueue, change p->state to
* TASK_WAKING such that we can unlock p->pi_lock before doing the * TASK_WAKING such that we can unlock p->pi_lock before doing the
* enqueue, such as ttwu_queue_wakelist(). * enqueue, such as ttwu_queue_wakelist().
*/ */
WRITE_ONCE(p->__state, TASK_WAKING); WRITE_ONCE(p->__state, TASK_WAKING);
/* /*
* If the owning (remote) CPU is still in the middle of schedule() with * If the owning (remote) CPU is still in the middle of schedule() with
* this task as prev, considering queueing p on the remote CPUs wake_list * this task as prev, considering queueing p on the remote CPUs wake_list
* which potentially sends an IPI instead of spinning on p->on_cpu to * which potentially sends an IPI instead of spinning on p->on_cpu to
* let the waker make forward progress. This is safe because IRQs are * let the waker make forward progress. This is safe because IRQs are
* disabled and the IPI will deliver after on_cpu is cleared. * disabled and the IPI will deliver after on_cpu is cleared.
* *
* Ensure we load task_cpu(p) after p->on_cpu: * Ensure we load task_cpu(p) after p->on_cpu:
* *
* set_task_cpu(p, cpu); * set_task_cpu(p, cpu);
* STORE p->cpu = @cpu * STORE p->cpu = @cpu
* __schedule() (switch to task 'p') * __schedule() (switch to task 'p')
* LOCK rq->lock * LOCK rq->lock
* smp_mb__after_spin_lock() smp_cond_load_acquire(&p->on_cpu) * smp_mb__after_spin_lock() smp_cond_load_acquire(&p->on_cpu)
* STORE p->on_cpu = 1 LOAD p->cpu * STORE p->on_cpu = 1 LOAD p->cpu
* *
* to ensure we observe the correct CPU on which the task is currently * to ensure we observe the correct CPU on which the task is currently
* scheduling. * scheduling.
*/ */
if (smp_load_acquire(&p->on_cpu) && if (smp_load_acquire(&p->on_cpu) &&
ttwu_queue_wakelist(p, task_cpu(p), wake_flags)) ttwu_queue_wakelist(p, task_cpu(p), wake_flags))
goto unlock; break;
/* /*
* If the owning (remote) CPU is still in the middle of schedule() with * If the owning (remote) CPU is still in the middle of schedule() with
* this task as prev, wait until it's done referencing the task. * this task as prev, wait until it's done referencing the task.
* *
* Pairs with the smp_store_release() in finish_task(). * Pairs with the smp_store_release() in finish_task().
* *
* This ensures that tasks getting woken will be fully ordered against * This ensures that tasks getting woken will be fully ordered against
* their previous state and preserve Program Order. * their previous state and preserve Program Order.
*/ */
smp_cond_load_acquire(&p->on_cpu, !VAL); smp_cond_load_acquire(&p->on_cpu, !VAL);
cpu = select_task_rq(p, p->wake_cpu, wake_flags | WF_TTWU); cpu = select_task_rq(p, p->wake_cpu, wake_flags | WF_TTWU);
if (task_cpu(p) != cpu) { if (task_cpu(p) != cpu) {
if (p->in_iowait) { if (p->in_iowait) {
delayacct_blkio_end(p); delayacct_blkio_end(p);
atomic_dec(&task_rq(p)->nr_iowait); atomic_dec(&task_rq(p)->nr_iowait);
} }
wake_flags |= WF_MIGRATED; wake_flags |= WF_MIGRATED;
psi_ttwu_dequeue(p); psi_ttwu_dequeue(p);
set_task_cpu(p, cpu); set_task_cpu(p, cpu);
} }
#else #else
cpu = task_cpu(p); cpu = task_cpu(p);
#endif /* CONFIG_SMP */ #endif /* CONFIG_SMP */
ttwu_queue(p, cpu, wake_flags); ttwu_queue(p, cpu, wake_flags);
unlock: }
raw_spin_unlock_irqrestore(&p->pi_lock, flags);
out: out:
if (success) if (success)
ttwu_stat(p, task_cpu(p), wake_flags); ttwu_stat(p, task_cpu(p), wake_flags);
preempt_enable();
return success; return success;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment