Merge tag 'driver-core-3.16-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git...
[pandora-kernel.git] / kernel / exit.c
index 6480d1c..6ed6a1d 100644 (file)
@@ -570,7 +570,7 @@ static void reparent_leader(struct task_struct *father, struct task_struct *p,
        if (same_thread_group(p->real_parent, father))
                return;
 
-       /* We don't want people slaying init.  */
+       /* We don't want people slaying init. */
        p->exit_signal = SIGCHLD;
 
        /* If it has exited notify the new parent about this child's death. */
@@ -784,9 +784,10 @@ void do_exit(long code)
        exit_shm(tsk);
        exit_files(tsk);
        exit_fs(tsk);
+       if (group_dead)
+               disassociate_ctty(1);
        exit_task_namespaces(tsk);
        exit_task_work(tsk);
-       check_stack_usage();
        exit_thread();
 
        /*
@@ -799,19 +800,15 @@ void do_exit(long code)
 
        cgroup_exit(tsk);
 
-       if (group_dead)
-               disassociate_ctty(1);
-
        module_put(task_thread_info(tsk)->exec_domain->module);
 
-       proc_exit_connector(tsk);
-
        /*
         * FIXME: do that only when needed, using sched_exit tracepoint
         */
        flush_ptrace_hw_breakpoint(tsk);
 
        exit_notify(tsk, group_dead);
+       proc_exit_connector(tsk);
 #ifdef CONFIG_NUMA
        task_lock(tsk);
        mpol_put(tsk->mempolicy);
@@ -844,6 +841,7 @@ void do_exit(long code)
 
        validate_creds_for_do_exit(tsk);
 
+       check_stack_usage();
        preempt_disable();
        if (tsk->nr_dirtied)
                __this_cpu_add(dirty_throttle_leaks, tsk->nr_dirtied);
@@ -1038,17 +1036,13 @@ static int wait_task_zombie(struct wait_opts *wo, struct task_struct *p)
                return wait_noreap_copyout(wo, p, pid, uid, why, status);
        }
 
+       traced = ptrace_reparented(p);
        /*
-        * Try to move the task's state to DEAD
-        * only one thread is allowed to do this:
+        * Move the task's state to DEAD/TRACE, only one thread can do this.
         */
-       state = xchg(&p->exit_state, EXIT_DEAD);
-       if (state != EXIT_ZOMBIE) {
-               BUG_ON(state != EXIT_DEAD);
+       state = traced && thread_group_leader(p) ? EXIT_TRACE : EXIT_DEAD;
+       if (cmpxchg(&p->exit_state, EXIT_ZOMBIE, state) != EXIT_ZOMBIE)
                return 0;
-       }
-
-       traced = ptrace_reparented(p);
        /*
         * It can be ptraced but not reparented, check
         * thread_group_leader() to filter out sub-threads.
@@ -1109,7 +1103,7 @@ static int wait_task_zombie(struct wait_opts *wo, struct task_struct *p)
 
        /*
         * Now we are sure this task is interesting, and no other
-        * thread can reap it because we set its state to EXIT_DEAD.
+        * thread can reap it because we its state == DEAD/TRACE.
         */
        read_unlock(&tasklist_lock);
 
@@ -1146,22 +1140,19 @@ static int wait_task_zombie(struct wait_opts *wo, struct task_struct *p)
        if (!retval)
                retval = pid;
 
-       if (traced) {
+       if (state == EXIT_TRACE) {
                write_lock_irq(&tasklist_lock);
                /* We dropped tasklist, ptracer could die and untrace */
                ptrace_unlink(p);
-               /*
-                * If this is not a sub-thread, notify the parent.
-                * If parent wants a zombie, don't release it now.
-                */
-               if (thread_group_leader(p) &&
-                   !do_notify_parent(p, p->exit_signal)) {
-                       p->exit_state = EXIT_ZOMBIE;
-                       p = NULL;
-               }
+
+               /* If parent wants a zombie, don't release it now */
+               state = EXIT_ZOMBIE;
+               if (do_notify_parent(p, p->exit_signal))
+                       state = EXIT_DEAD;
+               p->exit_state = state;
                write_unlock_irq(&tasklist_lock);
        }
-       if (p != NULL)
+       if (state == EXIT_DEAD)
                release_task(p);
 
        return retval;
@@ -1338,7 +1329,12 @@ static int wait_task_continued(struct wait_opts *wo, struct task_struct *p)
 static int wait_consider_task(struct wait_opts *wo, int ptrace,
                                struct task_struct *p)
 {
-       int ret = eligible_child(wo, p);
+       int ret;
+
+       if (unlikely(p->exit_state == EXIT_DEAD))
+               return 0;
+
+       ret = eligible_child(wo, p);
        if (!ret)
                return ret;
 
@@ -1356,33 +1352,44 @@ static int wait_consider_task(struct wait_opts *wo, int ptrace,
                return 0;
        }
 
-       /* dead body doesn't have much to contribute */
-       if (unlikely(p->exit_state == EXIT_DEAD)) {
+       if (unlikely(p->exit_state == EXIT_TRACE)) {
                /*
-                * But do not ignore this task until the tracer does
-                * wait_task_zombie()->do_notify_parent().
+                * ptrace == 0 means we are the natural parent. In this case
+                * we should clear notask_error, debugger will notify us.
                 */
-               if (likely(!ptrace) && unlikely(ptrace_reparented(p)))
+               if (likely(!ptrace))
                        wo->notask_error = 0;
                return 0;
        }
 
-       /* slay zombie? */
-       if (p->exit_state == EXIT_ZOMBIE) {
+       if (likely(!ptrace) && unlikely(p->ptrace)) {
                /*
-                * A zombie ptracee is only visible to its ptracer.
-                * Notification and reaping will be cascaded to the real
-                * parent when the ptracer detaches.
+                * If it is traced by its real parent's group, just pretend
+                * the caller is ptrace_do_wait() and reap this child if it
+                * is zombie.
+                *
+                * This also hides group stop state from real parent; otherwise
+                * a single stop can be reported twice as group and ptrace stop.
+                * If a ptracer wants to distinguish these two events for its
+                * own children it should create a separate process which takes
+                * the role of real parent.
                 */
-               if (likely(!ptrace) && unlikely(p->ptrace)) {
-                       /* it will become visible, clear notask_error */
-                       wo->notask_error = 0;
-                       return 0;
-               }
+               if (!ptrace_reparented(p))
+                       ptrace = 1;
+       }
 
+       /* slay zombie? */
+       if (p->exit_state == EXIT_ZOMBIE) {
                /* we don't reap group leaders with subthreads */
-               if (!delay_group_leader(p))
-                       return wait_task_zombie(wo, p);
+               if (!delay_group_leader(p)) {
+                       /*
+                        * A zombie ptracee is only visible to its ptracer.
+                        * Notification and reaping will be cascaded to the
+                        * real parent when the ptracer detaches.
+                        */
+                       if (unlikely(ptrace) || likely(!p->ptrace))
+                               return wait_task_zombie(wo, p);
+               }
 
                /*
                 * Allow access to stopped/continued state via zombie by
@@ -1407,19 +1414,6 @@ static int wait_consider_task(struct wait_opts *wo, int ptrace,
                if (likely(!ptrace) || (wo->wo_flags & (WCONTINUED | WEXITED)))
                        wo->notask_error = 0;
        } else {
-               /*
-                * If @p is ptraced by a task in its real parent's group,
-                * hide group stop/continued state when looking at @p as
-                * the real parent; otherwise, a single stop can be
-                * reported twice as group and ptrace stops.
-                *
-                * If a ptracer wants to distinguish the two events for its
-                * own children, it should create a separate process which
-                * takes the role of real parent.
-                */
-               if (likely(!ptrace) && p->ptrace && !ptrace_reparented(p))
-                       return 0;
-
                /*
                 * @p is alive and it's gonna stop, continue or exit, so
                 * there always is something to wait for.