From e03d13e985d48ac4885382c9e3b1510c78bd047f Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Wed, 19 Oct 2005 22:21:23 -0700 Subject: [PATCH] Fix cpu timers exit deadlock and races Oleg Nesterov reported an SMP deadlock. If there is a running timer tracking a different process's CPU time clock when the process owning the timer exits, we deadlock on tasklist_lock in posix_cpu_timer_del via exit_itimers. That code was using tasklist_lock to check for a race with __exit_signal being called on the timer-target task and clearing its ->signal. However, there is actually no such race. __exit_signal will have called posix_cpu_timers_exit and posix_cpu_timers_exit_group before it does that. Those will clear those k_itimer's association with the dying task, so posix_cpu_timer_del will return early and never reach the code in question. In addition, posix_cpu_timer_del called from exit_itimers during execve or directly from timer_delete in the process owning the timer can race with an exiting timer-target task to cause a double put on timer-target task struct. Make sure we always access cpu_timers lists with sighand lock held. Signed-off-by: Roland McGrath Signed-off-by: Chris Wright Signed-off-by: Linus Torvalds --- kernel/posix-cpu-timers.c | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) (limited to 'kernel') diff --git a/kernel/posix-cpu-timers.c b/kernel/posix-cpu-timers.c index 7a51a5597c33..b3f3edc475de 100644 --- a/kernel/posix-cpu-timers.c +++ b/kernel/posix-cpu-timers.c @@ -387,25 +387,19 @@ int posix_cpu_timer_del(struct k_itimer *timer) if (unlikely(p == NULL)) return 0; + spin_lock(&p->sighand->siglock); if (!list_empty(&timer->it.cpu.entry)) { - read_lock(&tasklist_lock); - if (unlikely(p->signal == NULL)) { - /* - * We raced with the reaping of the task. - * The deletion should have cleared us off the list. - */ - BUG_ON(!list_empty(&timer->it.cpu.entry)); - } else { - /* - * Take us off the task's timer list. - */ - spin_lock(&p->sighand->siglock); - list_del(&timer->it.cpu.entry); - spin_unlock(&p->sighand->siglock); - } - read_unlock(&tasklist_lock); + /* + * Take us off the task's timer list. We don't need to + * take tasklist_lock and check for the task being reaped. + * If it was reaped, it already called posix_cpu_timers_exit + * and posix_cpu_timers_exit_group to clear all the timers + * that pointed to it. + */ + list_del(&timer->it.cpu.entry); + put_task_struct(p); } - put_task_struct(p); + spin_unlock(&p->sighand->siglock); return 0; } -- cgit v1.2.3 From d1209d049bbc3df66650f8417637be4f7b57b604 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 19 Oct 2005 21:23:51 -0700 Subject: [PATCH] Threads shouldn't inherit PF_NOFREEZE The PF_NOFREEZE process flag should not be inherited when a thread is forked. This patch (as585) removes the flag from the child. This problem is starting to show up more and more as drivers turn to the kthread API instead of using kernel_thread(). As a result, their kernel threads are now children of the kthread worker instead of modprobe, and they inherit the PF_NOFREEZE flag. This can cause problems during system suspend; the kernel threads are not getting frozen as they ought to be. Signed-off-by: Alan Stern Acked-by: Pavel Machek Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- kernel/fork.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'kernel') diff --git a/kernel/fork.c b/kernel/fork.c index 533ce27f4b2c..280bd44ac441 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -848,7 +848,7 @@ static inline void copy_flags(unsigned long clone_flags, struct task_struct *p) { unsigned long new_flags = p->flags; - new_flags &= ~PF_SUPERPRIV; + new_flags &= ~(PF_SUPERPRIV | PF_NOFREEZE); new_flags |= PF_FORKNOEXEC; if (!(clone_flags & CLONE_PTRACE)) p->ptrace = 0; -- cgit v1.2.3