summaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2004-11-08 01:12:54 -0800
committerLinus Torvalds <torvalds@ppc970.osdl.org>2004-11-08 01:12:54 -0800
commit56b4c820f228c92c74335a2bfb8b1476fd203164 (patch)
tree1cfb251a5ce52e00d1b9c9482e45bfbd3a681fe2 /kernel
parent73d7a5fae092095759362231ea3f0e78abe1c47f (diff)
wait_task_stopped() must not just return 0 when it has
released the tasklist_lock. Since it released the lock, the process lists may not be valid any more, and we must repeat the loop rather than continue with the next parent. Use -EAGAIN to show this condition (separate from the normal -EFAULT that may happen if rusage information could not be copied to user space).
Diffstat (limited to 'kernel')
-rw-r--r--kernel/exit.c13
1 files changed, 11 insertions, 2 deletions
diff --git a/kernel/exit.c b/kernel/exit.c
index a2e154dba284..4f22175f601b 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -1201,8 +1201,15 @@ static int wait_task_stopped(task_t *p, int delayed_group_leader, int noreap,
write_unlock_irq(&tasklist_lock);
bail_ref:
put_task_struct(p);
- read_lock(&tasklist_lock);
- return 0;
+ /*
+ * We are returning to the wait loop without having successfully
+ * removed the process and having released the lock. We cannot
+ * continue, since the "p" task pointer is potentially stale.
+ *
+ * Return -EAGAIN, and do_wait() will restart the loop from the
+ * beginning. Do _not_ re-acquire the lock.
+ */
+ return -EAGAIN;
}
/* move to end of parent's list to avoid starvation */
@@ -1343,6 +1350,8 @@ repeat:
(options & WNOWAIT),
infop,
stat_addr, ru);
+ if (retval == -EAGAIN)
+ goto repeat;
if (retval != 0) /* He released the lock. */
goto end;
break;