Merge commit '900cfa46191a7d87cf1891924cb90499287fd235'; branches 'timers/nohz',...
[safe/jmp/linux-2.6] / kernel / exit.c
index 1f2c152..8f6185e 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/acct.h>
 #include <linux/tsacct_kern.h>
 #include <linux/file.h>
+#include <linux/fdtable.h>
 #include <linux/binfmts.h>
 #include <linux/nsproxy.h>
 #include <linux/pid_namespace.h>
 
 static void exit_mm(struct task_struct * tsk);
 
+static inline int task_detached(struct task_struct *p)
+{
+       return p->exit_signal == -1;
+}
+
 static void __unhash_process(struct task_struct *p)
 {
        nr_threads--;
@@ -120,6 +126,12 @@ static void __exit_signal(struct task_struct *tsk)
 
        __unhash_process(tsk);
 
+       /*
+        * Do this under ->siglock, we can race with another thread
+        * doing sigqueue_free() if we have SIGQUEUE_PREALLOC signals.
+        */
+       flush_sigqueue(&tsk->pending);
+
        tsk->signal = NULL;
        tsk->sighand = NULL;
        spin_unlock(&sighand->siglock);
@@ -127,7 +139,6 @@ static void __exit_signal(struct task_struct *tsk)
 
        __cleanup_sighand(sighand);
        clear_tsk_thread_flag(tsk,TIF_SIGPENDING);
-       flush_sigqueue(&tsk->pending);
        if (sig) {
                flush_sigqueue(&sig->shared_pending);
                taskstats_tgid_free(sig);
@@ -160,7 +171,7 @@ repeat:
        zap_leader = 0;
        leader = p->group_leader;
        if (leader != p && thread_group_empty(leader) && leader->exit_state == EXIT_ZOMBIE) {
-               BUG_ON(leader->exit_signal == -1);
+               BUG_ON(task_detached(leader));
                do_notify_parent(leader, leader->exit_signal);
                /*
                 * If we were the last child thread and the leader has
@@ -170,7 +181,7 @@ repeat:
                 * do_notify_parent() will have marked it self-reaping in
                 * that case.
                 */
-               zap_leader = (leader->exit_signal == -1);
+               zap_leader = task_detached(leader);
        }
 
        write_unlock_irq(&tasklist_lock);
@@ -214,20 +225,19 @@ struct pid *session_of_pgrp(struct pid *pgrp)
 static int will_become_orphaned_pgrp(struct pid *pgrp, struct task_struct *ignored_task)
 {
        struct task_struct *p;
-       int ret = 1;
 
        do_each_pid_task(pgrp, PIDTYPE_PGID, p) {
-               if (p == ignored_task
-                               || p->exit_state
-                               || is_global_init(p->real_parent))
+               if ((p == ignored_task) ||
+                   (p->exit_state && thread_group_empty(p)) ||
+                   is_global_init(p->real_parent))
                        continue;
+
                if (task_pgrp(p->real_parent) != pgrp &&
-                   task_session(p->real_parent) == task_session(p)) {
-                       ret = 0;
-                       break;
-               }
+                   task_session(p->real_parent) == task_session(p))
+                       return 0;
        } while_each_pid_task(pgrp, PIDTYPE_PGID, p);
-       return ret;     /* (sighing) "Often!" */
+
+       return 1;
 }
 
 int is_current_pgrp_orphaned(void)
@@ -255,6 +265,37 @@ static int has_stopped_jobs(struct pid *pgrp)
        return retval;
 }
 
+/*
+ * Check to see if any process groups have become orphaned as
+ * a result of our exiting, and if they have any stopped jobs,
+ * send them a SIGHUP and then a SIGCONT. (POSIX 3.2.2.2)
+ */
+static void
+kill_orphaned_pgrp(struct task_struct *tsk, struct task_struct *parent)
+{
+       struct pid *pgrp = task_pgrp(tsk);
+       struct task_struct *ignored_task = tsk;
+
+       if (!parent)
+                /* exit: our father is in a different pgrp than
+                 * we are and we were the only connection outside.
+                 */
+               parent = tsk->real_parent;
+       else
+               /* reparent: our child is in a different pgrp than
+                * we are, and it was the only connection outside.
+                */
+               ignored_task = NULL;
+
+       if (task_pgrp(parent) != pgrp &&
+           task_session(parent) == task_session(tsk) &&
+           will_become_orphaned_pgrp(pgrp, ignored_task) &&
+           has_stopped_jobs(pgrp)) {
+               __kill_pgrp_info(SIGHUP, SEND_SIG_PRIV, pgrp);
+               __kill_pgrp_info(SIGCONT, SEND_SIG_PRIV, pgrp);
+       }
+}
+
 /**
  * reparent_to_kthreadd - Reparent the calling kernel thread to kthreadd
  *
@@ -293,26 +334,25 @@ static void reparent_to_kthreadd(void)
        switch_uid(INIT_USER);
 }
 
-void __set_special_pids(pid_t session, pid_t pgrp)
+void __set_special_pids(struct pid *pid)
 {
        struct task_struct *curr = current->group_leader;
+       pid_t nr = pid_nr(pid);
 
-       if (task_session_nr(curr) != session) {
-               detach_pid(curr, PIDTYPE_SID);
-               set_task_session(curr, session);
-               attach_pid(curr, PIDTYPE_SID, find_pid(session));
+       if (task_session(curr) != pid) {
+               change_pid(curr, PIDTYPE_SID, pid);
+               set_task_session(curr, nr);
        }
-       if (task_pgrp_nr(curr) != pgrp) {
-               detach_pid(curr, PIDTYPE_PGID);
-               set_task_pgrp(curr, pgrp);
-               attach_pid(curr, PIDTYPE_PGID, find_pid(pgrp));
+       if (task_pgrp(curr) != pid) {
+               change_pid(curr, PIDTYPE_PGID, pid);
+               set_task_pgrp(curr, nr);
        }
 }
 
-static void set_special_pids(pid_t session, pid_t pgrp)
+static void set_special_pids(struct pid *pid)
 {
        write_lock_irq(&tasklist_lock);
-       __set_special_pids(session, pgrp);
+       __set_special_pids(pid);
        write_unlock_irq(&tasklist_lock);
 }
 
@@ -383,7 +423,11 @@ void daemonize(const char *name, ...)
         */
        current->flags |= PF_NOFREEZE;
 
-       set_special_pids(1, 1);
+       if (current->nsproxy != &init_nsproxy) {
+               get_nsproxy(&init_nsproxy);
+               switch_task_namespaces(current, &init_nsproxy);
+       }
+       set_special_pids(&init_struct_pid);
        proc_clear_tty(current);
 
        /* Block and flush all signals */
@@ -398,11 +442,6 @@ void daemonize(const char *name, ...)
        current->fs = fs;
        atomic_inc(&fs->count);
 
-       if (current->nsproxy != init_task.nsproxy) {
-               get_nsproxy(init_task.nsproxy);
-               switch_task_namespaces(current, init_task.nsproxy);
-       }
-
        exit_files(current);
        current->files = init_task.files;
        atomic_inc(&current->files->count);
@@ -458,7 +497,7 @@ struct files_struct *get_files_struct(struct task_struct *task)
        return files;
 }
 
-void fastcall put_files_struct(struct files_struct *files)
+void put_files_struct(struct files_struct *files)
 {
        struct fdtable *fdt;
 
@@ -477,10 +516,9 @@ void fastcall put_files_struct(struct files_struct *files)
        }
 }
 
-EXPORT_SYMBOL(put_files_struct);
-
-void reset_files_struct(struct task_struct *tsk, struct files_struct *files)
+void reset_files_struct(struct files_struct *files)
 {
+       struct task_struct *tsk = current;
        struct files_struct *old;
 
        old = tsk->files;
@@ -489,9 +527,8 @@ void reset_files_struct(struct task_struct *tsk, struct files_struct *files)
        task_unlock(tsk);
        put_files_struct(old);
 }
-EXPORT_SYMBOL(reset_files_struct);
 
-static void __exit_files(struct task_struct *tsk)
+void exit_files(struct task_struct *tsk)
 {
        struct files_struct * files = tsk->files;
 
@@ -503,33 +540,19 @@ static void __exit_files(struct task_struct *tsk)
        }
 }
 
-void exit_files(struct task_struct *tsk)
-{
-       __exit_files(tsk);
-}
-
-static void __put_fs_struct(struct fs_struct *fs)
+void put_fs_struct(struct fs_struct *fs)
 {
        /* No need to hold fs->lock if we are killing it */
        if (atomic_dec_and_test(&fs->count)) {
-               dput(fs->root);
-               mntput(fs->rootmnt);
-               dput(fs->pwd);
-               mntput(fs->pwdmnt);
-               if (fs->altroot) {
-                       dput(fs->altroot);
-                       mntput(fs->altrootmnt);
-               }
+               path_put(&fs->root);
+               path_put(&fs->pwd);
+               if (fs->altroot.dentry)
+                       path_put(&fs->altroot);
                kmem_cache_free(fs_cachep, fs);
        }
 }
 
-void put_fs_struct(struct fs_struct *fs)
-{
-       __put_fs_struct(fs);
-}
-
-static void __exit_fs(struct task_struct *tsk)
+void exit_fs(struct task_struct *tsk)
 {
        struct fs_struct * fs = tsk->fs;
 
@@ -537,16 +560,93 @@ static void __exit_fs(struct task_struct *tsk)
                task_lock(tsk);
                tsk->fs = NULL;
                task_unlock(tsk);
-               __put_fs_struct(fs);
+               put_fs_struct(fs);
        }
 }
 
-void exit_fs(struct task_struct *tsk)
+EXPORT_SYMBOL_GPL(exit_fs);
+
+#ifdef CONFIG_MM_OWNER
+/*
+ * Task p is exiting and it owned mm, lets find a new owner for it
+ */
+static inline int
+mm_need_new_owner(struct mm_struct *mm, struct task_struct *p)
 {
-       __exit_fs(tsk);
+       /*
+        * If there are other users of the mm and the owner (us) is exiting
+        * we need to find a new owner to take on the responsibility.
+        */
+       if (!mm)
+               return 0;
+       if (atomic_read(&mm->mm_users) <= 1)
+               return 0;
+       if (mm->owner != p)
+               return 0;
+       return 1;
 }
 
-EXPORT_SYMBOL_GPL(exit_fs);
+void mm_update_next_owner(struct mm_struct *mm)
+{
+       struct task_struct *c, *g, *p = current;
+
+retry:
+       if (!mm_need_new_owner(mm, p))
+               return;
+
+       read_lock(&tasklist_lock);
+       /*
+        * Search in the children
+        */
+       list_for_each_entry(c, &p->children, sibling) {
+               if (c->mm == mm)
+                       goto assign_new_owner;
+       }
+
+       /*
+        * Search in the siblings
+        */
+       list_for_each_entry(c, &p->parent->children, sibling) {
+               if (c->mm == mm)
+                       goto assign_new_owner;
+       }
+
+       /*
+        * Search through everything else. We should not get
+        * here often
+        */
+       do_each_thread(g, c) {
+               if (c->mm == mm)
+                       goto assign_new_owner;
+       } while_each_thread(g, c);
+
+       read_unlock(&tasklist_lock);
+       return;
+
+assign_new_owner:
+       BUG_ON(c == p);
+       get_task_struct(c);
+       /*
+        * The task_lock protects c->mm from changing.
+        * We always want mm->owner->mm == mm
+        */
+       task_lock(c);
+       /*
+        * Delay read_unlock() till we have the task_lock()
+        * to ensure that c does not slip away underneath us
+        */
+       read_unlock(&tasklist_lock);
+       if (c->mm != mm) {
+               task_unlock(c);
+               put_task_struct(c);
+               goto retry;
+       }
+       cgroup_mm_owner_callbacks(mm->owner, c);
+       mm->owner = c;
+       task_unlock(c);
+       put_task_struct(c);
+}
+#endif /* CONFIG_MM_OWNER */
 
 /*
  * Turn us into a lazy TLB process if we
@@ -587,6 +687,7 @@ static void exit_mm(struct task_struct * tsk)
        /* We don't want this task to be frozen prematurely */
        clear_freeze_flag(tsk);
        task_unlock(tsk);
+       mm_update_next_owner(mm);
        mmput(mm);
 }
 
@@ -601,7 +702,7 @@ reparent_thread(struct task_struct *p, struct task_struct *father, int traced)
        if (unlikely(traced)) {
                /* Preserve ptrace links if someone else is tracing this child.  */
                list_del_init(&p->ptrace_list);
-               if (p->parent != p->real_parent)
+               if (ptrace_reparented(p))
                        list_add(&p->ptrace_list, &p->real_parent->ptrace_children);
        } else {
                /* If this child is being traced, then we're the one tracing it
@@ -625,36 +726,21 @@ reparent_thread(struct task_struct *p, struct task_struct *father, int traced)
        /* If this is a threaded reparent there is no need to
         * notify anyone anything has happened.
         */
-       if (p->real_parent->group_leader == father->group_leader)
+       if (same_thread_group(p->real_parent, father))
                return;
 
        /* We don't want people slaying init.  */
-       if (p->exit_signal != -1)
+       if (!task_detached(p))
                p->exit_signal = SIGCHLD;
 
        /* If we'd notified the old parent about this child's death,
         * also notify the new parent.
         */
        if (!traced && p->exit_state == EXIT_ZOMBIE &&
-           p->exit_signal != -1 && thread_group_empty(p))
+           !task_detached(p) && thread_group_empty(p))
                do_notify_parent(p, p->exit_signal);
 
-       /*
-        * process group orphan check
-        * Case ii: Our child is in a different pgrp
-        * than we are, and it was the only connection
-        * outside, so the child pgrp is now orphaned.
-        */
-       if ((task_pgrp(p) != task_pgrp(father)) &&
-           (task_session(p) == task_session(father))) {
-               struct pid *pgrp = task_pgrp(p);
-
-               if (will_become_orphaned_pgrp(pgrp, NULL) &&
-                   has_stopped_jobs(pgrp)) {
-                       __kill_pgrp_info(SIGHUP, SEND_SIG_PRIV, pgrp);
-                       __kill_pgrp_info(SIGCONT, SEND_SIG_PRIV, pgrp);
-               }
-       }
+       kill_orphaned_pgrp(p, father);
 }
 
 /*
@@ -704,18 +790,18 @@ static void forget_original_parent(struct task_struct *father)
                } else {
                        /* reparent ptraced task to its real parent */
                        __ptrace_unlink (p);
-                       if (p->exit_state == EXIT_ZOMBIE && p->exit_signal != -1 &&
+                       if (p->exit_state == EXIT_ZOMBIE && !task_detached(p) &&
                            thread_group_empty(p))
                                do_notify_parent(p, p->exit_signal);
                }
 
                /*
-                * if the ptraced child is a zombie with exit_signal == -1
-                * we must collect it before we exit, or it will remain
-                * zombie forever since we prevented it from self-reap itself
-                * while it was being traced by us, to be able to see it in wait4.
+                * if the ptraced child is a detached zombie we must collect
+                * it before we exit, or it will remain zombie forever since
+                * we prevented it from self-reap itself while it was being
+                * traced by us, to be able to see it in wait4.
                 */
-               if (unlikely(ptrace && p->exit_state == EXIT_ZOMBIE && p->exit_signal == -1))
+               if (unlikely(ptrace && p->exit_state == EXIT_ZOMBIE && task_detached(p)))
                        list_add(&p->ptrace_list, &ptrace_dead);
        }
 
@@ -739,29 +825,9 @@ static void forget_original_parent(struct task_struct *father)
  * Send signals to all our closest relatives so that they know
  * to properly mourn us..
  */
-static void exit_notify(struct task_struct *tsk)
+static void exit_notify(struct task_struct *tsk, int group_dead)
 {
        int state;
-       struct task_struct *t;
-       struct pid *pgrp;
-
-       if (signal_pending(tsk) && !(tsk->signal->flags & SIGNAL_GROUP_EXIT)
-           && !thread_group_empty(tsk)) {
-               /*
-                * This occurs when there was a race between our exit
-                * syscall and a group signal choosing us as the one to
-                * wake up.  It could be that we are the only thread
-                * alerted to check for pending signals, but another thread
-                * should be woken now to take the signal since we will not.
-                * Now we'll wake all the threads in the group just to make
-                * sure someone gets all the pending signals.
-                */
-               spin_lock_irq(&tsk->sighand->siglock);
-               for (t = next_thread(tsk); t != tsk; t = next_thread(t))
-                       if (!signal_pending(t) && !(t->flags & PF_EXITING))
-                               recalc_sigpending_and_wake(t);
-               spin_unlock_irq(&tsk->sighand->siglock);
-       }
 
        /*
         * This does two things:
@@ -775,25 +841,8 @@ static void exit_notify(struct task_struct *tsk)
        exit_task_namespaces(tsk);
 
        write_lock_irq(&tasklist_lock);
-       /*
-        * Check to see if any process groups have become orphaned
-        * as a result of our exiting, and if they have any stopped
-        * jobs, send them a SIGHUP and then a SIGCONT.  (POSIX 3.2.2.2)
-        *
-        * Case i: Our father is in a different pgrp than we are
-        * and we were the only connection outside, so our pgrp
-        * is about to become orphaned.
-        */
-       t = tsk->real_parent;
-
-       pgrp = task_pgrp(tsk);
-       if ((task_pgrp(t) != pgrp) &&
-           (task_session(t) == task_session(tsk)) &&
-           will_become_orphaned_pgrp(pgrp, tsk) &&
-           has_stopped_jobs(pgrp)) {
-               __kill_pgrp_info(SIGHUP, SEND_SIG_PRIV, pgrp);
-               __kill_pgrp_info(SIGCONT, SEND_SIG_PRIV, pgrp);
-       }
+       if (group_dead)
+               kill_orphaned_pgrp(tsk->group_leader, NULL);
 
        /* Let father know we died
         *
@@ -809,29 +858,30 @@ static void exit_notify(struct task_struct *tsk)
         * we have changed execution domain as these two values started
         * the same after a fork.
         */
-       if (tsk->exit_signal != SIGCHLD && tsk->exit_signal != -1 &&
-           ( tsk->parent_exec_id != t->self_exec_id  ||
-             tsk->self_exec_id != tsk->parent_exec_id)
-           && !capable(CAP_KILL))
+       if (tsk->exit_signal != SIGCHLD && !task_detached(tsk) &&
+           (tsk->parent_exec_id != tsk->real_parent->self_exec_id ||
+            tsk->self_exec_id != tsk->parent_exec_id) &&
+           !capable(CAP_KILL))
                tsk->exit_signal = SIGCHLD;
 
-
        /* If something other than our normal parent is ptracing us, then
         * send it a SIGCHLD instead of honoring exit_signal.  exit_signal
         * only has special meaning to our real parent.
         */
-       if (tsk->exit_signal != -1 && thread_group_empty(tsk)) {
-               int signal = tsk->parent == tsk->real_parent ? tsk->exit_signal : SIGCHLD;
+       if (!task_detached(tsk) && thread_group_empty(tsk)) {
+               int signal = ptrace_reparented(tsk) ?
+                               SIGCHLD : tsk->exit_signal;
                do_notify_parent(tsk, signal);
        } else if (tsk->ptrace) {
                do_notify_parent(tsk, SIGCHLD);
        }
 
        state = EXIT_ZOMBIE;
-       if (tsk->exit_signal == -1 && likely(!tsk->ptrace))
+       if (task_detached(tsk) && likely(!tsk->ptrace))
                state = EXIT_DEAD;
        tsk->exit_state = state;
 
+       /* mt-exec, de_thread() is waiting for us */
        if (thread_group_leader(tsk) &&
            tsk->signal->notify_count < 0 &&
            tsk->signal->group_exit_task)
@@ -905,7 +955,7 @@ static inline void exit_child_reaper(struct task_struct *tsk)
        zap_pid_ns_processes(tsk->nsproxy->pid_ns);
 }
 
-fastcall NORET_TYPE void do_exit(long code)
+NORET_TYPE void do_exit(long code)
 {
        struct task_struct *tsk = current;
        int group_dead;
@@ -947,7 +997,7 @@ fastcall NORET_TYPE void do_exit(long code)
                schedule();
        }
 
-       tsk->flags |= PF_EXITING;
+       exit_signals(tsk);  /* sets PF_EXITING */
        /*
         * tsk->flags are checked in the futex code to protect against
         * an exiting task cleaning up the robust pi futexes.
@@ -993,8 +1043,8 @@ fastcall NORET_TYPE void do_exit(long code)
        if (group_dead)
                acct_process();
        exit_sem(tsk);
-       __exit_files(tsk);
-       __exit_fs(tsk);
+       exit_files(tsk);
+       exit_fs(tsk);
        check_stack_usage();
        exit_thread();
        cgroup_exit(tsk, 1);
@@ -1008,9 +1058,9 @@ fastcall NORET_TYPE void do_exit(long code)
                module_put(tsk->binfmt->module);
 
        proc_exit_connector(tsk);
-       exit_notify(tsk);
+       exit_notify(tsk, group_dead);
 #ifdef CONFIG_NUMA
-       mpol_free(tsk->mempolicy);
+       mpol_put(tsk->mempolicy);
        tsk->mempolicy = NULL;
 #endif
 #ifdef CONFIG_FUTEX
@@ -1075,12 +1125,13 @@ asmlinkage long sys_exit(int error_code)
 NORET_TYPE void
 do_group_exit(int exit_code)
 {
+       struct signal_struct *sig = current->signal;
+
        BUG_ON(exit_code & 0x80); /* core dumps don't get here */
 
-       if (current->signal->flags & SIGNAL_GROUP_EXIT)
-               exit_code = current->signal->group_exit_code;
+       if (signal_group_exit(sig))
+               exit_code = sig->group_exit_code;
        else if (!thread_group_empty(current)) {
-               struct signal_struct *const sig = current->signal;
                struct sighand_struct *const sighand = current->sighand;
                spin_lock_irq(&sighand->siglock);
                if (signal_group_exit(sig))
@@ -1108,20 +1159,23 @@ asmlinkage void sys_exit_group(int error_code)
        do_group_exit((error_code & 0xff) << 8);
 }
 
-static int eligible_child(pid_t pid, int options, struct task_struct *p)
+static struct pid *task_pid_type(struct task_struct *task, enum pid_type type)
+{
+       struct pid *pid = NULL;
+       if (type == PIDTYPE_PID)
+               pid = task->pids[type].pid;
+       else if (type < PIDTYPE_MAX)
+               pid = task->group_leader->pids[type].pid;
+       return pid;
+}
+
+static int eligible_child(enum pid_type type, struct pid *pid, int options,
+                         struct task_struct *p)
 {
        int err;
-       struct pid_namespace *ns;
 
-       ns = current->nsproxy->pid_ns;
-       if (pid > 0) {
-               if (task_pid_nr_ns(p, ns) != pid)
-                       return 0;
-       } else if (!pid) {
-               if (task_pgrp_nr_ns(p, ns) != task_pgrp_vnr(current))
-                       return 0;
-       } else if (pid != -1) {
-               if (task_pgrp_nr_ns(p, ns) != -pid)
+       if (type < PIDTYPE_MAX) {
+               if (task_pid_type(p, type) != pid)
                        return 0;
        }
 
@@ -1129,7 +1183,7 @@ static int eligible_child(pid_t pid, int options, struct task_struct *p)
         * Do not consider detached threads that are
         * not ptraced:
         */
-       if (p->exit_signal == -1 && !p->ptrace)
+       if (task_detached(p) && !p->ptrace)
                return 0;
 
        /* Wait for all children (clone and not) if __WALL is set;
@@ -1140,18 +1194,16 @@ static int eligible_child(pid_t pid, int options, struct task_struct *p)
        if (((p->exit_signal != SIGCHLD) ^ ((options & __WCLONE) != 0))
            && !(options & __WALL))
                return 0;
-       /*
-        * Do not consider thread group leaders that are
-        * in a non-empty thread group:
-        */
-       if (delay_group_leader(p))
-               return 2;
 
        err = security_task_wait(p);
-       if (err)
-               return err;
+       if (likely(!err))
+               return 1;
 
-       return 1;
+       if (type != PIDTYPE_PID)
+               return 0;
+       /* This child was explicitly requested, abort */
+       read_unlock(&tasklist_lock);
+       return err;
 }
 
 static int wait_noreap_copyout(struct task_struct *p, pid_t pid, uid_t uid,
@@ -1191,20 +1243,13 @@ static int wait_task_zombie(struct task_struct *p, int noreap,
 {
        unsigned long state;
        int retval, status, traced;
-       struct pid_namespace *ns;
-
-       ns = current->nsproxy->pid_ns;
+       pid_t pid = task_pid_vnr(p);
 
        if (unlikely(noreap)) {
-               pid_t pid = task_pid_nr_ns(p, ns);
                uid_t uid = p->uid;
                int exit_code = p->exit_code;
                int why, status;
 
-               if (unlikely(p->exit_state != EXIT_ZOMBIE))
-                       return 0;
-               if (unlikely(p->exit_signal == -1 && p->ptrace == 0))
-                       return 0;
                get_task_struct(p);
                read_unlock(&tasklist_lock);
                if ((exit_code & 0x7f) == 0) {
@@ -1228,8 +1273,7 @@ static int wait_task_zombie(struct task_struct *p, int noreap,
                return 0;
        }
 
-       /* traced means p->ptrace, but not vice versa */
-       traced = (p->real_parent != p->parent);
+       traced = ptrace_reparented(p);
 
        if (likely(!traced)) {
                struct signal_struct *psig;
@@ -1315,11 +1359,11 @@ static int wait_task_zombie(struct task_struct *p, int noreap,
                        retval = put_user(status, &infop->si_status);
        }
        if (!retval && infop)
-               retval = put_user(task_pid_nr_ns(p, ns), &infop->si_pid);
+               retval = put_user(pid, &infop->si_pid);
        if (!retval && infop)
                retval = put_user(p->uid, &infop->si_uid);
        if (!retval)
-               retval = task_pid_nr_ns(p, ns);
+               retval = pid;
 
        if (traced) {
                write_lock_irq(&tasklist_lock);
@@ -1330,9 +1374,9 @@ static int wait_task_zombie(struct task_struct *p, int noreap,
                 * If it's still not detached after that, don't release
                 * it now.
                 */
-               if (p->exit_signal != -1) {
+               if (!task_detached(p)) {
                        do_notify_parent(p, p->exit_signal);
-                       if (p->exit_signal != -1) {
+                       if (!task_detached(p)) {
                                p->exit_state = EXIT_ZOMBIE;
                                p = NULL;
                        }
@@ -1351,21 +1395,38 @@ static int wait_task_zombie(struct task_struct *p, int noreap,
  * the lock and this task is uninteresting.  If we return nonzero, we have
  * released the lock and the system call should return.
  */
-static int wait_task_stopped(struct task_struct *p, int delayed_group_leader,
+static int wait_task_stopped(struct task_struct *p,
                             int noreap, struct siginfo __user *infop,
                             int __user *stat_addr, struct rusage __user *ru)
 {
-       int retval, exit_code;
+       int retval, exit_code, why;
+       uid_t uid = 0; /* unneeded, required by compiler */
        pid_t pid;
 
-       if (!p->exit_code)
-               return 0;
-       if (delayed_group_leader && !(p->ptrace & PT_PTRACED) &&
-           p->signal->group_stop_count > 0)
+       exit_code = 0;
+       spin_lock_irq(&p->sighand->siglock);
+
+       if (unlikely(!task_is_stopped_or_traced(p)))
+               goto unlock_sig;
+
+       if (!(p->ptrace & PT_PTRACED) && p->signal->group_stop_count > 0)
                /*
                 * A group stop is in progress and this is the group leader.
                 * We won't report until all threads have stopped.
                 */
+               goto unlock_sig;
+
+       exit_code = p->exit_code;
+       if (!exit_code)
+               goto unlock_sig;
+
+       if (!noreap)
+               p->exit_code = 0;
+
+       uid = p->uid;
+unlock_sig:
+       spin_unlock_irq(&p->sighand->siglock);
+       if (!exit_code)
                return 0;
 
        /*
@@ -1375,65 +1436,15 @@ static int wait_task_stopped(struct task_struct *p, int delayed_group_leader,
         * keep holding onto the tasklist_lock while we call getrusage and
         * possibly take page faults for user memory.
         */
-       pid = task_pid_nr_ns(p, current->nsproxy->pid_ns);
        get_task_struct(p);
+       pid = task_pid_vnr(p);
+       why = (p->ptrace & PT_PTRACED) ? CLD_TRAPPED : CLD_STOPPED;
        read_unlock(&tasklist_lock);
 
-       if (unlikely(noreap)) {
-               uid_t uid = p->uid;
-               int why = (p->ptrace & PT_PTRACED) ? CLD_TRAPPED : CLD_STOPPED;
-
-               exit_code = p->exit_code;
-               if (unlikely(!exit_code) || unlikely(p->exit_state))
-                       goto bail_ref;
+       if (unlikely(noreap))
                return wait_noreap_copyout(p, pid, uid,
                                           why, exit_code,
                                           infop, ru);
-       }
-
-       write_lock_irq(&tasklist_lock);
-
-       /*
-        * This uses xchg to be atomic with the thread resuming and setting
-        * it.  It must also be done with the write lock held to prevent a
-        * race with the EXIT_ZOMBIE case.
-        */
-       exit_code = xchg(&p->exit_code, 0);
-       if (unlikely(p->exit_state)) {
-               /*
-                * The task resumed and then died.  Let the next iteration
-                * catch it in EXIT_ZOMBIE.  Note that exit_code might
-                * already be zero here if it resumed and did _exit(0).
-                * The task itself is dead and won't touch exit_code again;
-                * other processors in this function are locked out.
-                */
-               p->exit_code = exit_code;
-               exit_code = 0;
-       }
-       if (unlikely(exit_code == 0)) {
-               /*
-                * Another thread in this function got to it first, or it
-                * resumed, or it resumed and then died.
-                */
-               write_unlock_irq(&tasklist_lock);
-bail_ref:
-               put_task_struct(p);
-               /*
-                * 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 */
-       remove_parent(p);
-       add_parent(p);
-
-       write_unlock_irq(&tasklist_lock);
 
        retval = ru ? getrusage(p, RUSAGE_BOTH, ru) : 0;
        if (!retval && stat_addr)
@@ -1443,15 +1454,13 @@ bail_ref:
        if (!retval && infop)
                retval = put_user(0, &infop->si_errno);
        if (!retval && infop)
-               retval = put_user((short)((p->ptrace & PT_PTRACED)
-                                         ? CLD_TRAPPED : CLD_STOPPED),
-                                 &infop->si_code);
+               retval = put_user((short)why, &infop->si_code);
        if (!retval && infop)
                retval = put_user(exit_code, &infop->si_status);
        if (!retval && infop)
                retval = put_user(pid, &infop->si_pid);
        if (!retval && infop)
-               retval = put_user(p->uid, &infop->si_uid);
+               retval = put_user(uid, &infop->si_uid);
        if (!retval)
                retval = pid;
        put_task_struct(p);
@@ -1473,7 +1482,6 @@ static int wait_task_continued(struct task_struct *p, int noreap,
        int retval;
        pid_t pid;
        uid_t uid;
-       struct pid_namespace *ns;
 
        if (!(p->signal->flags & SIGNAL_STOP_CONTINUED))
                return 0;
@@ -1488,8 +1496,7 @@ static int wait_task_continued(struct task_struct *p, int noreap,
                p->signal->flags &= ~SIGNAL_STOP_CONTINUED;
        spin_unlock_irq(&p->sighand->siglock);
 
-       ns = current->nsproxy->pid_ns;
-       pid = task_pid_nr_ns(p, ns);
+       pid = task_pid_vnr(p);
        uid = p->uid;
        get_task_struct(p);
        read_unlock(&tasklist_lock);
@@ -1500,7 +1507,7 @@ static int wait_task_continued(struct task_struct *p, int noreap,
                if (!retval && stat_addr)
                        retval = put_user(0xffff, stat_addr);
                if (!retval)
-                       retval = task_pid_nr_ns(p, ns);
+                       retval = pid;
        } else {
                retval = wait_noreap_copyout(p, pid, uid,
                                             CLD_CONTINUED, SIGCONT,
@@ -1511,90 +1518,63 @@ static int wait_task_continued(struct task_struct *p, int noreap,
        return retval;
 }
 
-
-static inline int my_ptrace_child(struct task_struct *p)
-{
-       return p->ptrace & PT_PTRACED;
-}
-
-static long do_wait(pid_t pid, int options, struct siginfo __user *infop,
-                   int __user *stat_addr, struct rusage __user *ru)
+static long do_wait(enum pid_type type, struct pid *pid, int options,
+                   struct siginfo __user *infop, int __user *stat_addr,
+                   struct rusage __user *ru)
 {
        DECLARE_WAITQUEUE(wait, current);
        struct task_struct *tsk;
        int flag, retval;
-       int allowed, denied;
 
        add_wait_queue(&current->signal->wait_chldexit,&wait);
 repeat:
+       /* If there is nothing that can match our critier just get out */
+       retval = -ECHILD;
+       if ((type < PIDTYPE_MAX) && (!pid || hlist_empty(&pid->tasks[type])))
+               goto end;
+
        /*
         * We will set this flag if we see any child that might later
         * match our criteria, even if we are not able to reap it yet.
         */
-       flag = 0;
-       allowed = denied = 0;
+       flag = retval = 0;
        current->state = TASK_INTERRUPTIBLE;
        read_lock(&tasklist_lock);
        tsk = current;
        do {
                struct task_struct *p;
-               int ret;
 
                list_for_each_entry(p, &tsk->children, sibling) {
-                       ret = eligible_child(pid, options, p);
+                       int ret = eligible_child(type, pid, options, p);
                        if (!ret)
                                continue;
 
                        if (unlikely(ret < 0)) {
-                               denied = ret;
-                               continue;
-                       }
-                       allowed = 1;
-
-                       if (task_is_stopped_or_traced(p)) {
+                               retval = ret;
+                       } else if (task_is_stopped_or_traced(p)) {
                                /*
                                 * It's stopped now, so it might later
                                 * continue, exit, or stop again.
-                                *
-                                * When we hit the race with PTRACE_ATTACH, we
-                                * will not report this child.  But the race
-                                * means it has not yet been moved to our
-                                * ptrace_children list, so we need to set the
-                                * flag here to avoid a spurious ECHILD when
-                                * the race happens with the only child.
                                 */
                                flag = 1;
+                               if (!(p->ptrace & PT_PTRACED) &&
+                                   !(options & WUNTRACED))
+                                       continue;
 
-                               if (!my_ptrace_child(p)) {
-                                       if (task_is_traced(p))
-                                               continue;
-                                       if (!(options & WUNTRACED))
-                                               continue;
-                               }
-
-                               retval = wait_task_stopped(p, ret == 2,
+                               retval = wait_task_stopped(p,
                                                (options & WNOWAIT), infop,
                                                stat_addr, ru);
-                               if (retval == -EAGAIN)
-                                       goto repeat;
-                               if (retval != 0) /* He released the lock.  */
-                                       goto end;
-                       } else if (p->exit_state == EXIT_ZOMBIE) {
+                       } else if (p->exit_state == EXIT_ZOMBIE &&
+                                       !delay_group_leader(p)) {
                                /*
-                                * Eligible but we cannot release it yet:
+                                * We don't reap group leaders with subthreads.
                                 */
-                               if (ret == 2)
-                                       goto check_continued;
                                if (!likely(options & WEXITED))
                                        continue;
                                retval = wait_task_zombie(p,
                                                (options & WNOWAIT), infop,
                                                stat_addr, ru);
-                               /* He released the lock.  */
-                               if (retval != 0)
-                                       goto end;
                        } else if (p->exit_state != EXIT_DEAD) {
-check_continued:
                                /*
                                 * It's running now, so it might later
                                 * exit, stop, or stop and then continue.
@@ -1605,17 +1585,20 @@ check_continued:
                                retval = wait_task_continued(p,
                                                (options & WNOWAIT), infop,
                                                stat_addr, ru);
-                               if (retval != 0) /* He released the lock.  */
-                                       goto end;
                        }
+                       if (retval != 0) /* tasklist_lock released */
+                               goto end;
                }
                if (!flag) {
                        list_for_each_entry(p, &tsk->ptrace_children,
-                                           ptrace_list) {
-                               if (!eligible_child(pid, options, p))
+                                                               ptrace_list) {
+                               flag = eligible_child(type, pid, options, p);
+                               if (!flag)
                                        continue;
-                               flag = 1;
-                               break;
+                               if (likely(flag > 0))
+                                       break;
+                               retval = flag;
+                               goto end;
                        }
                }
                if (options & __WNOTHREAD)
@@ -1623,10 +1606,9 @@ check_continued:
                tsk = next_thread(tsk);
                BUG_ON(tsk->signal != current->signal);
        } while (tsk != current);
-
        read_unlock(&tasklist_lock);
+
        if (flag) {
-               retval = 0;
                if (options & WNOHANG)
                        goto end;
                retval = -ERESTARTSYS;
@@ -1636,14 +1618,12 @@ check_continued:
                goto repeat;
        }
        retval = -ECHILD;
-       if (unlikely(denied) && !allowed)
-               retval = denied;
 end:
        current->state = TASK_RUNNING;
        remove_wait_queue(&current->signal->wait_chldexit,&wait);
        if (infop) {
                if (retval > 0)
-               retval = 0;
+                       retval = 0;
                else {
                        /*
                         * For a WNOHANG return, clear out all the fields
@@ -1667,10 +1647,12 @@ end:
        return retval;
 }
 
-asmlinkage long sys_waitid(int which, pid_t pid,
+asmlinkage long sys_waitid(int which, pid_t upid,
                           struct siginfo __user *infop, int options,
                           struct rusage __user *ru)
 {
+       struct pid *pid = NULL;
+       enum pid_type type;
        long ret;
 
        if (options & ~(WNOHANG|WNOWAIT|WEXITED|WSTOPPED|WCONTINUED))
@@ -1680,40 +1662,61 @@ asmlinkage long sys_waitid(int which, pid_t pid,
 
        switch (which) {
        case P_ALL:
-               pid = -1;
+               type = PIDTYPE_MAX;
                break;
        case P_PID:
-               if (pid <= 0)
+               type = PIDTYPE_PID;
+               if (upid <= 0)
                        return -EINVAL;
                break;
        case P_PGID:
-               if (pid <= 0)
+               type = PIDTYPE_PGID;
+               if (upid <= 0)
                        return -EINVAL;
-               pid = -pid;
                break;
        default:
                return -EINVAL;
        }
 
-       ret = do_wait(pid, options, infop, NULL, ru);
+       if (type < PIDTYPE_MAX)
+               pid = find_get_pid(upid);
+       ret = do_wait(type, pid, options, infop, NULL, ru);
+       put_pid(pid);
 
        /* avoid REGPARM breakage on x86: */
-       prevent_tail_call(ret);
+       asmlinkage_protect(5, ret, which, upid, infop, options, ru);
        return ret;
 }
 
-asmlinkage long sys_wait4(pid_t pid, int __user *stat_addr,
+asmlinkage long sys_wait4(pid_t upid, int __user *stat_addr,
                          int options, struct rusage __user *ru)
 {
+       struct pid *pid = NULL;
+       enum pid_type type;
        long ret;
 
        if (options & ~(WNOHANG|WUNTRACED|WCONTINUED|
                        __WNOTHREAD|__WCLONE|__WALL))
                return -EINVAL;
-       ret = do_wait(pid, options | WEXITED, NULL, stat_addr, ru);
+
+       if (upid == -1)
+               type = PIDTYPE_MAX;
+       else if (upid < 0) {
+               type = PIDTYPE_PGID;
+               pid = find_get_pid(-upid);
+       } else if (upid == 0) {
+               type = PIDTYPE_PGID;
+               pid = get_pid(task_pgrp(current));
+       } else /* upid > 0 */ {
+               type = PIDTYPE_PID;
+               pid = find_get_pid(upid);
+       }
+
+       ret = do_wait(type, pid, options | WEXITED, NULL, stat_addr, ru);
+       put_pid(pid);
 
        /* avoid REGPARM breakage on x86: */
-       prevent_tail_call(ret);
+       asmlinkage_protect(4, ret, upid, stat_addr, options, ru);
        return ret;
 }