From: Ingo Molnar <mingo@redhat.com>



Signed-off-by: Andrew Morton <akpm@osdl.org>
---

 25-akpm/kernel/exit.c |   36 ++++++++++++++++++------------------
 1 files changed, 18 insertions(+), 18 deletions(-)

diff -puN kernel/exit.c~release_task-may-sleep kernel/exit.c
--- 25/kernel/exit.c~release_task-may-sleep	2004-07-26 23:24:19.872377536 -0700
+++ 25-akpm/kernel/exit.c	2004-07-26 23:24:19.876376928 -0700
@@ -57,8 +57,6 @@ void release_task(struct task_struct * p
 	struct dentry *proc_dentry;
 
 repeat: 
-	BUG_ON(p->state < TASK_ZOMBIE);
- 
 	atomic_dec(&p->user->processes);
 	spin_lock(&p->proc_lock);
 	proc_dentry = proc_pid_unhash(p);
@@ -757,9 +755,8 @@ static void exit_notify(struct task_stru
 	state = TASK_ZOMBIE;
 	if (tsk->exit_signal == -1 && tsk->ptrace == 0)
 		state = TASK_DEAD;
-	tsk->state = state;
-	tsk->flags |= PF_DEAD;
-
+	else
+		tsk->state = state;
 	/*
 	 * Clear these here so that update_process_times() won't try to deliver
 	 * itimer, profile or rlimit signals to this task while it is in late exit.
@@ -769,19 +766,14 @@ static void exit_notify(struct task_stru
 	tsk->rlim[RLIMIT_CPU].rlim_cur = RLIM_INFINITY;
 
 	/*
-	 * 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.
+	 * Get a reference to it so that we can set the state
+	 * as the last step. The state-setting only matters if the
+	 * current task is releasing itself, to trigger the final
+	 * put_task_struct() in finish_task_switch(). (thread self-reap)
 	 */
-	_raw_write_unlock(&tasklist_lock);
-	local_irq_enable();
+	get_task_struct(tsk);
+
+	write_unlock_irq(&tasklist_lock);
 
 	list_for_each_safe(_p, _n, &ptrace_dead) {
 		list_del_init(_p);
@@ -790,9 +782,17 @@ static void exit_notify(struct task_stru
 	}
 
 	/* If the process is dead, release it - nobody will wait for it */
-	if (state == TASK_DEAD)
+	if (state == TASK_DEAD) {
 		release_task(tsk);
+		write_lock_irq(&tasklist_lock);
+		tsk->state = state;
+		_raw_write_unlock(&tasklist_lock);
+		local_irq_enable();
+	} else
+		preempt_disable();
 
+	tsk->flags |= PF_DEAD;
+	put_task_struct(tsk);
 }
 
 asmlinkage NORET_TYPE void do_exit(long code)
_