summaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
authorAndrew Morton <akpm@digeo.com>2003-02-19 21:19:00 -0800
committerPaul Mackerras <paulus@samba.org>2003-02-19 21:19:00 -0800
commit776969f04b4a372e9eea8f7ddf683907613765bf (patch)
tree45fae7a66a495d6a9e051b6535aaa1a38c2afa6b /kernel
parent3b6244e1374384311d6b262aa522a0f2ae7bd041 (diff)
[PATCH] Keep interrupts enabled in exit path
We are leaving local interrupts disabled coming out of exit_notify(). But we are about to call wait_task_inactive() which spins, waiting for another CPU to end a task. If that CPU has issued smp_call_function() to this CPU, deadlock. So the patch enables interrupts again before returning from exit_notify(). Also, exit_notify() returns with preemption disabled, so there is no need to perform another preempt_disable() in do_exit().
Diffstat (limited to 'kernel')
-rw-r--r--kernel/exit.c17
1 files changed, 11 insertions, 6 deletions
diff --git a/kernel/exit.c b/kernel/exit.c
index c45159219e83..224cb7adc73f 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -674,13 +674,19 @@ static void exit_notify(struct task_struct *tsk)
tsk->state = TASK_ZOMBIE;
/*
- * No need to unlock IRQs, we'll schedule() immediately
- * anyway. In the preemption case this also makes it
- * impossible for the task to get runnable again (thus
- * the "_raw_" unlock - to make sure we don't try to
- * preempt here).
+ * In the preemption case it must be impossible for the task
+ * to get runnable again, so use "_raw_" unlock to keep
+ * preempt_count elevated until we schedule().
+ *
+ * To avoid deadlock on SMP, interrupts must be unmasked. If we
+ * don't, subsequently called functions (e.g, wait_task_inactive()
+ * via release_task()) will spin, with interrupt flags
+ * unwittingly blocked, until the other task sleeps. That task
+ * may itself be waiting for smp_call_function() to answer and
+ * complete, and with interrupts blocked that will never happen.
*/
_raw_write_unlock(&tasklist_lock);
+ local_irq_enable();
}
NORET_TYPE void do_exit(long code)
@@ -727,7 +733,6 @@ NORET_TYPE void do_exit(long code)
tsk->exit_code = code;
exit_notify(tsk);
- preempt_disable();
if (tsk->exit_signal == -1)
release_task(tsk);