sched/fair: Remove duplicate code from can_migrate_task()
PATCH 1/7 sched/fair: Remove duplicate code from can_migrate_task()
From: Kirill Tkhai <[email protected]>
Two branches do the same, so we can combine them and get rid of duplicate code.
--- kernel/sched/fair.c | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-)
kernel/sched/fair.c +2 -14
@@ -5292,24 +5292,12 @@ int can_migrate_task(struct task_struct *p, struct lb_env *env)if (!tsk_cache_hot) tsk_cache_hot =migrate_degrades_locality (p, env); -if (migrate_improves_locality (p, env)) { -#ifdef CONFIG_SCHEDSTATS +if (migrate_improves_locality (p, env) || !tsk_cache_hot || + env->sd->nr_balance_failed > env->sd->cache_nice_tries) {if (tsk_cache_hot) {schedstat_inc (env->sd, lb_hot_gained[env->idle]);schedstat_inc (p, se.statistics.nr_forced_migrations); } -#endif -return 1 ; - } - -if (!tsk_cache_hot || - env->sd->nr_balance_failed > env->sd->cache_nice_tries) { - -if (tsk_cache_hot) { -schedstat_inc (env->sd, lb_hot_gained[env->idle]); -schedstat_inc (p, se.statistics.nr_forced_migrations); - } -return 1 ; }
PATCH 2/7 sched: Fix picking a task switching on other cpu (__ARCH_WANT_UNLOCKED_CTXSW)
From: Kirill Tkhai <[email protected]>
We may pick a task which is in context_switch() on other cpu at the moment. Parallel using of a single stack by two processes is not a good idea.
--- kernel/sched/core.c | 11 +---------- kernel/sched/deadline.c | 7 ++++++- kernel/sched/fair.c | 3 +++ kernel/sched/rt.c | 7 ++++++- kernel/sched/sched.h | 16 ++++++++++++++++ 5 files changed, 32 insertions(+), 12 deletions(-)
kernel/sched/core.c +1 -10
@@ -1699,16 +1699,7 @@ try_to_wake_up(struct task_struct *p, unsigned int state, int wake_flags) goto stat;#ifdef CONFIG_SMP -/* -* If the owning (remote) cpu is still in the middle of schedule() with -* this task as prev, wait until its done referencing the task. -*/ -while (p->on_cpu) -cpu_relax (); -/* -* Pairs with the smp_wmb() in finish_lock_switch(). -*/ -smp_rmb (); +cpu_relax__while_on_cpu (p); p->sched_contributes_to_load = !!task_contributes_to_load (p); p->state =TASK_WAKING ;
kernel/sched/deadline.c +6 -1
@@ -1364,7 +1364,9 @@ static int push_dl_task(struct rq *rq) next_task = task; goto retry; } - +#ifdef __ARCH_WANT_UNLOCKED_CTXSW +cpu_relax__while_on_cpu (next_task); +#endif deactivate_task (rq, next_task,0 );set_task_cpu (next_task, later_rq->cpu);activate_task (later_rq, next_task,0 );
@@ -1451,6 +1453,9 @@ static int pull_dl_task(struct rq *this_rq) ret =1 ; +#ifdef __ARCH_WANT_UNLOCKED_CTXSW +cpu_relax__while_on_cpu (p); +#endif deactivate_task (src_rq, p,0 );set_task_cpu (p, this_cpu);activate_task (this_rq, p,0 );
kernel/sched/fair.c +3 -0
@@ -5298,6 +5298,9 @@ int can_migrate_task(struct task_struct *p, struct lb_env *env)schedstat_inc (env->sd, lb_hot_gained[env->idle]);schedstat_inc (p, se.statistics.nr_forced_migrations); } +#ifdef __ARCH_WANT_UNLOCKED_CTXSW +cpu_relax__while_on_cpu (p); +#endif return 1 ; }
kernel/sched/rt.c +6 -1
@@ -1734,7 +1734,9 @@ static int push_rt_task(struct rq *rq) next_task = task; goto retry; } - +#ifdef __ARCH_WANT_UNLOCKED_CTXSW +cpu_relax__while_on_cpu (next_task); +#endif deactivate_task (rq, next_task,0 );set_task_cpu (next_task, lowest_rq->cpu);activate_task (lowest_rq, next_task,0 );
@@ -1823,6 +1825,9 @@ static int pull_rt_task(struct rq *this_rq) ret =1 ; +#ifdef __ARCH_WANT_UNLOCKED_CTXSW +cpu_relax__while_on_cpu (p); +#endif deactivate_task (src_rq, p,0 );set_task_cpu (p, this_cpu);activate_task (this_rq, p,0 );
kernel/sched/sched.h +16 -0
@@ -1034,6 +1034,22 @@ static inline void finish_lock_switch(struct rq *rq, struct task_struct *prev)#endif /* __ARCH_WANT_UNLOCKED_CTXSW */ /* +* If the owning (remote) cpu is still in the middle of schedule() with +* this task as prev, wait until its done referencing the task. +*/ +static inline void cpu_relax__while_on_cpu (struct task_struct *p) +{ +#ifdef CONFIG_SMP +while (p->on_cpu) +cpu_relax (); +/* +* Pairs with the smp_wmb() in finish_lock_switch(). +*/ +smp_rmb (); +#endif +} + +/* * wake flags */ #define WF_SYNC 0x01/* waker goes to sleep after wakeup */
Peter Zijlstra
maintainerRe: [PATCH 2/7] sched: Fix picking a task switching on other cpu (__ARCH_WANT_UNLOCKED_CTXSW)
Quoted 4 lines · Kirill Tkhai
From: Kirill Tkhai <[email protected]>
We may pick a task which is in
context_switch()on other cpu at the moment. Parallel using of a single stack by two processes is not a good idea.
Please elaborate on who exactly that might happen. Its best to have comprehensive changelogs for issues that fix races.
Peter Zijlstra
maintainerRe: [PATCH 2/7] sched: Fix picking a task switching on other cpu (__ARCH_WANT_UNLOCKED_CTXSW)
Quoted 8 lines · Peter Zijlstra
Quoted 4 lines · Kirill Tkhai
From: Kirill Tkhai <[email protected]>
We may pick a task which is in
context_switch()on other cpu at the moment. Parallel using of a single stack by two processes is not a good idea.Please elaborate on who exactly that might happen. Its best to have comprehensive changelogs for issues that fix races.
FWIW IIRC we can remove UNLOCKED_CTXSW from IA64 and I forgot if I audited MIPS, but I suspect we can (and should) remove it there too.
That would make this exception go away and clean up some of this ugly.
Re: [PATCH 2/7] sched: Fix picking a task switching on other cpu (__ARCH_WANT_UNLOCKED_CTXSW)
В Сб, 20/09/2014 в 20:54 +0200, Peter Zijlstra пишет:
Quoted 7 lines · Peter Zijlstra
Quoted 6 lines · Kirill Tkhai
Quoted 4 lines
From: Kirill Tkhai <[email protected]>
We may pick a task which is in
context_switch()on other cpu at the moment. Parallel using of a single stack by two processes is not a good idea.
Quoted 7 lines
Quoted 2 lines
Please elaborate on who exactly that might happen. Its best to have comprehensive changelogs for issues that fix races.
FWIW IIRC we can remove
UNLOCKED_CTXSWfrom IA64 and I forgot if I audited MIPS, but I suspect we can (and should) remove it there too.That would make this exception go away and clean up some of this ugly.
Yeah, you've said me about IA64:
http://www.spinics.net/lists/linux-ia64/msg10229.html
It's about 10 years since the logic, which was documented in ia64 header, has been removed. It looks like, ia64 maintainers are not interested much...
***
To do not to start a new message. I've found the above when I was analysing if the optimisation below is OK (assume, we have accessor cpu_relax__while_on_cpu()):
kernel/sched/core.c +3 -2
@@ -1699,8 +1699,6 @@ try_to_wake_up(struct task_struct *p, unsigned int state, int wake_flags) goto stat;#ifdef CONFIG_SMP -cpu_relax__while_on_cpu (p); - p->sched_contributes_to_load = !!task_contributes_to_load (p); p->state =TASK_WAKING ;
@@ -1708,6 +1706,9 @@ try_to_wake_up(struct task_struct *p, unsigned int state, int wake_flags) p->sched_class->task_waking (p); cpu =select_task_rq (p, p->wake_cpu,SD_BALANCE_WAKE , wake_flags); + +cpu_relax__while_on_cpu (p); +if (task_cpu (p) != cpu) { wake_flags |=WF_MIGRATED ;set_task_cpu (p, cpu);
Looks like, now problem here. Task p is dequeued, we can set sched_contributes_to_load and state here, also task_waking does not produce problems, only arithmetics is there. select_task_rq() is R/O function.
Now I'm testing this.
Re: [PATCH 2/7] sched: Fix picking a task switching on other cpu (__ARCH_WANT_UNLOCKED_CTXSW)
В Вс, 21/09/2014 в 00:09 +0400, Kirill Tkhai пишет:
Quoted 54 lines
В Сб, 20/09/2014 в 20:54 +0200, Peter Zijlstra пишет:
Quoted 7 lines · Peter Zijlstra
Quoted 6 lines · Kirill Tkhai
Quoted 4 lines
From: Kirill Tkhai <[email protected]>
We may pick a task which is in
context_switch()on other cpu at the moment. Parallel using of a single stack by two processes is not a good idea.Quoted 7 lines
Quoted 2 lines
Please elaborate on who exactly that might happen. Its best to have comprehensive changelogs for issues that fix races.
FWIW IIRC we can remove
UNLOCKED_CTXSWfrom IA64 and I forgot if I audited MIPS, but I suspect we can (and should) remove it there too.That would make this exception go away and clean up some of this ugly.
Yeah, you've said me about IA64:
http://www.spinics.net/lists/linux-ia64/msg10229.html
It's about 10 years since the logic, which was documented in ia64 header, has been removed. It looks like, ia64 maintainers are not interested much...
***
To do not to start a new message. I've found the above when I was analysing if the optimisation below is OK (assume, we have accessor
cpu_relax__while_on_cpu()):kernel/sched/core.c +3
-2@@ -1699,8 +1699,6 @@ try_to_wake_up(struct task_struct *p, unsigned int state, int wake_flags) goto stat;#ifdef CONFIG_SMP -cpu_relax__while_on_cpu (p); - p->sched_contributes_to_load = !!task_contributes_to_load (p); p->state =TASK_WAKING ;@@ -1708,6 +1706,9 @@ try_to_wake_up(struct task_struct *p, unsigned int state, int wake_flags) p->sched_class->task_waking (p); cpu =select_task_rq (p, p->wake_cpu,SD_BALANCE_WAKE , wake_flags); + +cpu_relax__while_on_cpu (p); +if (task_cpu (p) != cpu) { wake_flags |=WF_MIGRATED ;set_task_cpu (p, cpu);
Quoted 1 line
Looks like, now problem here. Task p is dequeued, we can set
sched_contributes_to_loadand state
s/now/no/
Quoted 4 lines
here, also
task_wakingdoes not produce problems, only arithmetics is there.select_task_rq()is R/O function.Now I'm testing this.
Peter Zijlstra
maintainerRe: [PATCH 2/7] sched: Fix picking a task switching on other cpu (__ARCH_WANT_UNLOCKED_CTXSW)
In reply to Peter ZijlstraQuoted 8 lines · Peter Zijlstra
Quoted 4 lines · Kirill Tkhai
From: Kirill Tkhai <[email protected]>
We may pick a task which is in
context_switch()on other cpu at the moment. Parallel using of a single stack by two processes is not a good idea.Please elaborate on who exactly that might happen. Its best to have comprehensive changelogs for issues that fix races.
typing hard, ugh. s/who/how/
PATCH 3/7 sched: Use dl_bw_of() under RCU read lock
In reply to Kirill TkhaiFrom: Kirill Tkhai <[email protected]>
dl_bw_of() dereferences rq->rd which has to have RCU read lock held. Probability of use-after-free and memory corruption aren't zero here.
--- kernel/sched/core.c | 6 ++++++ 1 file changed, 6 insertions(+)
kernel/sched/core.c +6 -0
@@ -7619,6 +7619,8 @@ static int sched_dl_global_constraints(void)int cpu, ret =0 ;unsigned long flags; +rcu_read_lock (); +/* * Here we want to check the bandwidth not being set to some * value smaller than the currently allocated bandwidth in
@@ -7640,6 +7642,8 @@ static int sched_dl_global_constraints(void)break ; } +rcu_read_unlock (); +return ret; }
@@ -7655,6 +7659,7 @@ static void sched_dl_do_global(void)if (global_rt_runtime () !=RUNTIME_INF ) new_bw =to_ratio (global_rt_period (),global_rt_runtime ()); +rcu_read_lock ();/* * FIXME: As above... */
@@ -7665,6 +7670,7 @@ static void sched_dl_do_global(void) dl_b->bw = new_bw;raw_spin_unlock_irqrestore (&dl_b->lock, flags); } +rcu_read_unlock (); }static int sched_rt_global_validate (void )
Peter Zijlstra
maintainerRe: [PATCH 3/7] sched: Use dl_bw_of() under RCU read lock
Quoted 5 lines · Kirill Tkhai
From: Kirill Tkhai <[email protected]>
dl_bw_of()dereferencesrq->rdwhich has to have RCU read lock held. Probability of use-after-free and memory corruption aren't zero here.
Additionally we might want to add something like:lockdep_assert_held_rcu() and put that in dl_bw_of() and other such places.
Re: [PATCH 3/7] sched: Use dl_bw_of() under RCU read lock
Quoted 10 lines · Peter Zijlstra
Quoted 5 lines · Kirill Tkhai
From: Kirill Tkhai <[email protected]>
dl_bw_of()dereferencesrq->rdwhich has to have RCU read lock held. Probability of use-after-free and memory corruption aren't zero here.Additionally we might want to add something like:
lockdep_assert_held_rcu()and put that indl_bw_of()and other such places.
Should we change (not now, in general) RCU-related pointers to use rcu_dereference() to have unlocked RCU warnings in dmesg? To catch a problems like that.
This may make code worse readable though.
Peter Zijlstra
maintainerRe: [PATCH 3/7] sched: Use dl_bw_of() under RCU read lock
Quoted 12 lines · Kirill Tkhai
Quoted 10 lines · Peter Zijlstra
Quoted 5 lines · Kirill Tkhai
From: Kirill Tkhai <[email protected]>
dl_bw_of()dereferencesrq->rdwhich has to have RCU read lock held. Probability of use-after-free and memory corruption aren't zero here.Additionally we might want to add something like:
lockdep_assert_held_rcu()and put that indl_bw_of()and other such places.
Quoted 5 lines
Should we change (not now, in general) RCU-related pointers to use
rcu_dereference()to have unlocked RCU warnings in dmesg? To catch a problems like that.This may make code worse readable though.
Possibly, we should probably use rcu_assign_pointer() and rcu_dereference() on rq->rd. Sometimes you can avoid that if you're playing games with static objects, but I don't think that is true here.
PATCH 4/7 sched: cleanup: Rename out_unlock to out_free_new_mask
In reply to Kirill TkhaiFrom: Kirill Tkhai <[email protected]>
Nothing is locked there, so label's name only confuses a reader.
--- kernel/sched/core.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
kernel/sched/core.c +4 -4
@@ -3995,14 +3995,14 @@ long sched_setaffinity(pid_t pid, const struct cpumask *in_mask)rcu_read_lock ();if (!ns_capable (__task_cred (p)->user_ns,CAP_SYS_NICE )) {rcu_read_unlock (); - goto out_unlock; + goto out_free_new_mask; }rcu_read_unlock (); } retval =security_task_setscheduler (p);if (retval) - goto out_unlock; + goto out_free_new_mask;cpuset_cpus_allowed (p, cpus_allowed);
@@ -4020,7 +4020,7 @@ long sched_setaffinity(pid_t pid, const struct cpumask *in_mask)if (dl_bandwidth_enabled () && !cpumask_subset (span, new_mask)) { retval = -EBUSY ; - goto out_unlock; + goto out_free_new_mask; } }#endif
@@ -4039,7 +4039,7 @@ long sched_setaffinity(pid_t pid, const struct cpumask *in_mask) goto again; } } -out_unlock: +out_free_new_mask:free_cpumask_var (new_mask); out_free_cpus_allowed:free_cpumask_var (cpus_allowed);
PATCH 5/7 sched: Use rq->rd in sched_setaffinity() under RCU read lock
In reply to Kirill TkhaiFrom: Kirill Tkhai <[email protected]>
task_rq(p)->rd and task_rq(p)->rd->span may be used-after-free here. Probability of NULL pointer derefference isn't zero in this place.
--- kernel/sched/core.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-)
kernel/sched/core.c +5 -4
@@ -4015,13 +4015,14 @@ long sched_setaffinity(pid_t pid, const struct cpumask *in_mask)* root_domain. */ #ifdef CONFIG_SMP -if (task_has_dl_policy (p)) { -const struct cpumask *span =task_rq (p)->rd->span; - -if (dl_bandwidth_enabled () && !cpumask_subset (span, new_mask)) { +if (task_has_dl_policy (p) &&dl_bandwidth_enabled ()) { +rcu_read_lock (); +if (!cpumask_subset (task_rq (p)->rd->span, new_mask)) { retval = -EBUSY ; +rcu_read_unlock (); goto out_free_new_mask; } +rcu_read_unlock (); }#endif again:
Peter Zijlstra
maintainerRe: [PATCH 5/7] sched: Use rq->rd in sched_setaffinity() under RCU read lock
Quoted 4 lines · Kirill Tkhai
From: Kirill Tkhai <[email protected]>
task_rq(p)->rd and task_rq(p)->rd->span may be used-after-free here. Probability of NULL pointer derefference isn't zero in this place.
I don't see NULL derefs, just use-after-free.
Re: [PATCH 5/7] sched: Use rq->rd in sched_setaffinity() under RCU read lock
Quoted 8 lines · Peter Zijlstra
Quoted 4 lines · Kirill Tkhai
From: Kirill Tkhai <[email protected]>
task_rq(p)->rd and task_rq(p)->rd->span may be used-after-free here. Probability of NULL pointer derefference isn't zero in this place.
I don't see NULL derefs, just use-after-free.
It's very paranod case :). Two pointers are here:
task_rq(p)->rd (somebody zeroed it "rd") ->span
Peter Zijlstra
maintainerRe: [PATCH 5/7] sched: Use rq->rd in sched_setaffinity() under RCU read lock
Quoted 7 lines · Kirill Tkhai
Quoted 6 lines · Peter Zijlstra
Quoted 4 lines · Kirill Tkhai
From: Kirill Tkhai <[email protected]>
task_rq(p)->rd and task_rq(p)->rd->span may be used-after-free here. Probability of NULL pointer derefference isn't zero in this place.
Quoted 6 lines
Quoted 2 lines
I don't see NULL derefs, just use-after-free.
It's very paranod case :). Two pointers are here:
task_rq(p)->rd (somebody zeroed it "rd") ->span
What you're saying is: due to the reuse someone might have put a NULL in there. Which is fair, but I'd still call it use-after-free because that is the first order problem. Dereferencing 'unknown' memory can of course cause all kinds of 'fun' problems :-)
Re: [PATCH 5/7] sched: Use rq->rd in sched_setaffinity() under RCU read lock
Quoted 11 lines · Peter Zijlstra
Quoted 10 lines · Kirill Tkhai
Quoted 8 lines · Peter Zijlstra
Quoted 4 lines · Kirill Tkhai
From: Kirill Tkhai <[email protected]>
task_rq(p)->rd and task_rq(p)->rd->span may be used-after-free here. Probability of NULL pointer derefference isn't zero in this place.
I don't see NULL derefs, just use-after-free.
Quoted 8 lines
Quoted 3 lines
It's very paranod case :). Two pointers are here:
task_rq(p)->rd (somebody zeroed it "rd") ->span
What you're saying is: due to the reuse someone might have put a NULL in there. Which is fair, but I'd still call it use-after-free because that is the first order problem. Dereferencing 'unknown' memory can of course cause all kinds of 'fun' problems :-)
Yeah, it's logical, I'll update the description.
PATCH 6/7 sched: Delete rq::skip_clock_update == -1
In reply to Kirill TkhaiFrom: Kirill Tkhai <[email protected]>
Idle class task is always queued, so we can safely remove "-1" case here.
--- kernel/sched/core.c | 2 +- kernel/sched/rt.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)
kernel/sched/core.c +1 -1
@@ -2812,7 +2812,7 @@ static void __sched __schedule(void) switch_count = &prev->nvcsw; } -if (task_on_rq_queued (prev) || rq->skip_clock_update <0 ) +if (task_on_rq_queued (prev))update_rq_clock (rq); next =pick_next_task (rq, prev);
kernel/sched/rt.c +1 -1
@@ -835,7 +835,7 @@ static int do_sched_rt_period_timer(struct rt_bandwidth *rt_b, int overrun)* lest wakeup -> unthrottle time accumulate. */ if (rt_rq->rt_nr_running && rq->curr == rq->idle) - rq->skip_clock_update =-1 ; + rq->skip_clock_update =0 ; }if (rt_rq->rt_time || rt_rq->rt_nr_running) idle =0 ;
Peter Zijlstra
maintainerRe: [PATCH 6/7] sched: Delete rq::skip_clock_update == -1
Quoted 3 lines · Kirill Tkhai
From: Kirill Tkhai <[email protected]>
Idle class task is always queued, so we can safely remove "-1" case here.
Tag you're it :-)
Re: [PATCH 6/7] sched: Delete rq::skip_clock_update == -1
Quoted 8 lines · Peter Zijlstra
Quoted 3 lines · Kirill Tkhai
From: Kirill Tkhai <[email protected]>
Idle class task is always queued, so we can safely remove "-1" case here.
Tag you're it :-)
Maybe it should be given the too annoying to live treatment. Saving fastpath cycles is great, corner cases less so.
-Mike
PATCH 7/7 sched/rt: Use resched_curr() in task_tick_rt()
In reply to Kirill TkhaiFrom: Kirill Tkhai <[email protected]>
Some time ago PREEMPT_NEED_RESCHED was implemented, so reschedule technics is a little more difficult now.
--- kernel/sched/rt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/sched/rt.c +1 -1
@@ -2077,7 +2077,7 @@ static void task_tick_rt(struct rq *rq, struct task_struct *p, int queued)for_each_sched_rt_entity (rt_se) {if (rt_se->run_list.prev != rt_se->run_list.next) {requeue_task_rt (rq, p,0 ); -set_tsk_need_resched (p); +resched_curr (rq);return ; } }