From: Ingo Molnar <mingo@elte.hu>

- remove the hotplug lock from around much of fork(), and re-copy the
  cpus_allowed mask to solve the hotplug race cleanly.

Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Srivatsa Vaddagiri <vatsa@in.ibm.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
---

 25-akpm/kernel/fork.c  |   22 +++++++++++-----------
 25-akpm/kernel/sched.c |   14 +-------------
 2 files changed, 12 insertions(+), 24 deletions(-)

diff -puN kernel/fork.c~sched-fork-hotplug-cleanuppatch kernel/fork.c
--- 25/kernel/fork.c~sched-fork-hotplug-cleanuppatch	2004-07-28 22:08:44.798220624 -0700
+++ 25-akpm/kernel/fork.c	2004-07-28 22:08:44.805219560 -0700
@@ -892,16 +892,6 @@ struct task_struct *copy_process(unsigne
 	if ((clone_flags & CLONE_SIGHAND) && !(clone_flags & CLONE_VM))
 		return ERR_PTR(-EINVAL);
 
-	/*
-	 * The newly dup'ed task shares the same cpus_allowed mask as its
-	 * parent (ie. current), and it is not attached to the tasklist.
-	 * The end result is that this CPU might go down and the parent
-	 * be migrated away, leaving the task on a dead CPU. So take the
-	 * hotplug lock here and release it after the child has been attached
-	 * to the tasklist.
-	 */
-	lock_cpu_hotplug();
-
 	retval = security_task_create(clone_flags);
 	if (retval)
 		goto fork_out;
@@ -1042,6 +1032,17 @@ struct task_struct *copy_process(unsigne
 
 	/* Need tasklist lock for parent etc handling! */
 	write_lock_irq(&tasklist_lock);
+
+	/*
+	 * The task hasn't been attached yet, so cpus_allowed mask cannot
+	 * have changed. The cpus_allowed mask of the parent may have
+	 * changed after it was copied first time, and it may then move to
+	 * another CPU - so we re-copy it here and set the child's CPU to
+	 * the parent's CPU. This avoids alot of nasty races.
+	 */
+	p->cpus_allowed = current->cpus_allowed;
+	set_task_cpu(p, smp_processor_id());
+
 	/*
 	 * Check for pending SIGKILL! The new thread should not be allowed
 	 * to slip out of an OOM kill. (or normal SIGKILL.)
@@ -1107,7 +1108,6 @@ struct task_struct *copy_process(unsigne
 	retval = 0;
 
 fork_out:
-	unlock_cpu_hotplug();
 	if (retval)
 		return ERR_PTR(retval);
 	return p;
diff -puN kernel/sched.c~sched-fork-hotplug-cleanuppatch kernel/sched.c
--- 25/kernel/sched.c~sched-fork-hotplug-cleanuppatch	2004-07-28 22:08:44.801220168 -0700
+++ 25-akpm/kernel/sched.c	2004-07-28 22:08:44.808219104 -0700
@@ -873,22 +873,10 @@ static int find_idlest_cpu(struct task_s
 
 /*
  * Perform scheduler related setup for a newly forked process p.
- * p is forked by current. The cpu hotplug lock is held.
+ * p is forked by current.
  */
 void fastcall sched_fork(task_t *p)
 {
-	int cpu = smp_processor_id();
-
-	/*
-	 * The task hasn't been attached yet, so cpus_allowed mask cannot
-	 * change. The cpus_allowed mask of the parent may have changed
-	 * after it is copied, and it may then move to a CPU that is not
-	 * allowed for the child.
-	 */
-	if (unlikely(!cpu_isset(cpu, p->cpus_allowed)))
-		cpu = any_online_cpu(p->cpus_allowed);
-	set_task_cpu(p, cpu);
-
 	/*
 	 * We mark the process as running here, but have not actually
 	 * inserted it onto the runqueue yet. This guarantees that
_