account_group_exec_runtime: fix the racy usage of ->signal
PATCH 1/1 account_group_exec_runtime: fix the racy usage of ->signal
Compile tested.
Unlike other similar routines, account_group_exec_runtime() could be called "implicitly" after exit_notify(). This means we can race with the parent doing release_task(), we can't just check ->signal != NULL.
Take ->siglock to make sure ->signal can't go away.
This is the minimal fix, with this patch we don't need need get/put cpu, and I think we should uninline this function.
kernel/sched_stats.h renamed from kernel/sched_stats.h~A_G_E_R_FIX +6 -2
@@ -351,10 +351,12 @@ static inline void account_group_exec_ru unsigned long long ns) {struct signal_struct *sig; +unsigned long flags; - sig = tsk->signal; -if (unlikely (!sig)) +if (unlikely (!lock_task_sighand (tsk, &flags)))return ; + + sig = tsk->signal;if (sig->cputime.totals) {struct task_cputime *times;
@@ -362,4 +364,6 @@ static inline void account_group_exec_ru times->sum_exec_runtime += ns;put_cpu_no_resched (); } + +unlock_task_sighand (tsk, &flags); }
Quoted 17 lines · Oleg Nesterov
Compile tested.
Unlike other similar routines,
account_group_exec_runtime()could be called "implicitly" afterexit_notify(). This means we can race with the parent doingrelease_task(), we can't just check ->signal != NULL.Take ->siglock to make sure ->signal can't go away.
This is the minimal fix, with this patch we don't need need get/put cpu, and I think we should uninline this function.
Signed-off-by Oleg Nesterov <[email protected]>
kernel/sched_stats.h static inline void account_group_exec_ru · line 357
Quoted 9 lines
kernel/sched_stats.h renamed from kernel/sched_stats.h~A_G_E_R_FIX +2
-2@@ -351,10 +351,12 @@ static inline void account_group_exec_ru unsigned long long ns) {struct signal_struct *sig; +unsigned long flags; - sig = tsk->signal; -if (unlikely (!sig)) +if (unlikely (!lock_task_sighand (tsk, &flags)))return ;
i think this will lock up: the signal lock must not nest inside the rq lock, and these accounting functions are called from within the scheduler.
Ingo
Quoted 11 lines · Ingo Molnar
Quoted 8 lines · Oleg Nesterov
kernel/sched_stats.h renamed from kernel/sched_stats.h~A_G_E_R_FIX +1
-0@@ -351,10 +351,12 @@ static inline void account_group_exec_ru unsigned long long ns) {struct signal_struct *sig; +unsigned long flags;
Quoted 6 lines
kernel/sched_stats.h static inline void account_group_exec_ru · line 357
Quoted 4 lines
kernel/sched_stats.h renamed from kernel/sched_stats.h~A_G_E_R_FIX +1
-2- sig = tsk->signal; -if (unlikely (!sig)) +if (unlikely (!lock_task_sighand (tsk, &flags)))return ;i think this will lock up:
Ah. I worried about this, but convinced myself this is OK...
Quoted 3 lines
the signal lock must not nest inside the rq
lock, and these accounting functions are called from within the scheduler.
Why? we seem to never do task_rq_lock() under ->siglock ?
Oleg.
Quoted 19 lines · Oleg Nesterov
Quoted 11 lines · Ingo Molnar
Quoted 8 lines · Oleg Nesterov
kernel/sched_stats.h renamed from kernel/sched_stats.h~A_G_E_R_FIX +1
-0@@ -351,10 +351,12 @@ static inline void account_group_exec_ru unsigned long long ns) {struct signal_struct *sig; +unsigned long flags;Quoted 6 lines
kernel/sched_stats.h static inline void account_group_exec_ru · line 357
Quoted 4 lines
kernel/sched_stats.h renamed from kernel/sched_stats.h~A_G_E_R_FIX +1
-2- sig = tsk->signal; -if (unlikely (!sig)) +if (unlikely (!lock_task_sighand (tsk, &flags)))return ;i think this will lock up:
Quoted 7 lines
Ah. I worried about this, but convinced myself this is OK...
Quoted 3 lines
the signal lock must not nest inside the rq
lock, and these accounting functions are called from within the scheduler.Why? we seem to never do
task_rq_lock()under ->siglock ?
signal_wake_up() ?
Ingo
Quoted 5 lines · Ingo Molnar
Quoted 2 lines · Oleg Nesterov
Quoted 1 line · Ingo Molnar
Quoted 7 lines
Quoted 5 lines
Quoted 3 lines
the signal lock must not nest inside the rq
lock, and these accounting functions are called from within the scheduler.Why? we seem to never do
task_rq_lock()under ->siglock ?
signal_wake_up()?
I'd wish very much I could say I have already realized this, but I didn't. Thanks Ingo!
I don't see the good solution for this problem. I'll send the new patch in a minute, but it is ugly. Basically it is
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -141,6 +141,8 @@ static void __exit_signal(struct task_st
if (sig) {
flush_sigqueue(&sig->shared_pending);
taskstats_tgid_free(sig);
+ smp_mb();
+ spin_unlock_wait(&task_rq(tsk)->lock);
__cleanup_signal(sig);
}
}except this needs a helper in sched.c. You can nack it right now ;) Of course we can protect ->signal with rcu, but this is even worse imho.
Anybody sees a bettter fix?
Perhaps we can change sched.c to do update_curr() only when the task is not running (except ->task_tick), iow perhaps we can check sleep/wakeup == T before calling update_cur(). But this is not easy even if really possible.
Oleg.
Quoted 36 lines · Oleg Nesterov
Quoted 5 lines · Ingo Molnar
Quoted 2 lines · Oleg Nesterov
Quoted 1 line · Ingo Molnar
Quoted 7 lines
Quoted 5 lines
Quoted 3 lines
the signal lock must not nest inside the rq
lock, and these accounting functions are called from within the scheduler.Why? we seem to never do
task_rq_lock()under ->siglock ?
signal_wake_up()?I'd wish very much I could say I have already realized this, but I didn't. Thanks Ingo!
I don't see the good solution for this problem. I'll send the new patch in a minute, but it is ugly. Basically it is
--- a/kernel/exit.c +++ b/kernel/exit.c @@ -141,6 +141,8 @@ static void __exit_signal(struct task_st if (sig) { flush_sigqueue(&sig->shared_pending); taskstats_tgid_free(sig); + smp_mb(); + spin_unlock_wait(&task_rq(tsk)->lock); __cleanup_signal(sig); } }except this needs a helper in
sched.c. You can nack it right now ;) Of course we can protect ->signal with rcu, but this is even worse imho.
Quoted 7 lines
Anybody sees a bettter fix?
Perhaps we can change
sched.cto doupdate_curr()only when the task is not running (except ->task_tick), iow perhaps we can check sleep/wakeup == T before callingupdate_cur(). But this is not easy even if really possible.
and butt ugly to boot..
Quoted 33 lines · Ingo Molnar
Quoted 17 lines · Oleg Nesterov
Compile tested.
Unlike other similar routines,
account_group_exec_runtime()could be called "implicitly" afterexit_notify(). This means we can race with the parent doingrelease_task(), we can't just check ->signal != NULL.Take ->siglock to make sure ->signal can't go away.
This is the minimal fix, with this patch we don't need need get/put cpu, and I think we should uninline this function.
Signed-off-by Oleg Nesterov <[email protected]>kernel/sched_stats.h static inline void account_group_exec_ru · line 357
Quoted 9 lines
kernel/sched_stats.h renamed from kernel/sched_stats.h~A_G_E_R_FIX +2
-2@@ -351,10 +351,12 @@ static inline void account_group_exec_ru unsigned long long ns) {struct signal_struct *sig; +unsigned long flags; - sig = tsk->signal; -if (unlikely (!sig)) +if (unlikely (!lock_task_sighand (tsk, &flags)))return ;i think this will lock up: the signal lock must not nest inside the rq lock, and these accounting functions are called from within the scheduler.
Quoted 1 line
Ingo
I can confirm that this does hang on bootup.
- Doug
Quoted 19 lines · Doug Chapman
Quoted 8 lines · Ingo Molnar
Quoted 6 lines · Oleg Nesterov
(quoted diff) +1
-0@@ -351,10 +351,12 @@ static inline void account_group_exec_ru unsigned long long ns) { struct signal_struct *sig; + unsigned long flags;Quoted 8 lines
Review static inline void account_group_exec_ru · line 357
Quoted 4 lines
(quoted diff) +1
-2- sig = tsk->signal; - if (unlikely(!sig)) + if (unlikely(!lock_task_sighand(tsk, &flags))) return;i think this will lock up: the signal lock must not nest inside the rq lock, and these accounting functions are called from within the scheduler.
Quoted 1 line
I can confirm that this does hang on bootup.
Thanks a lot Doug.
If only I could understand what happens. I am running the 2.6.27 kernel with the patch below just fine.
Ingo, could you please explain?
OK, perhaps we can check ->exit_state... I'll return on Monday.
kernel/sched_fair.c renamed from kernel/sched_fair.c~DBG +11 -0
@@ -484,6 +484,16 @@ __update_curr(struct cfs_rq *cfs_rq, str curr->vruntime += delta_exec_weighted; } +static void ttt (struct task_struct *tsk) +{ +unsigned long flags; + +if (unlikely (!lock_task_sighand (tsk, &flags))) +return ; + +unlock_task_sighand (tsk, &flags); +} +static void update_curr (struct cfs_rq *cfs_rq) {struct sched_entity *curr = cfs_rq->curr;
@@ -507,6 +517,7 @@ static void update_curr(struct cfs_rq *cstruct task_struct *curtask =task_of (curr);cpuacct_charge (curtask, delta_exec); +ttt (curtask); } }