(I am trying to cleanup this code and make it more understandable, but
 of course this is subjective).

utrace_do_stop() considers the dead task without _UTRACE_DEATH_EVENTS
as quiescent. This is correct, but

        - this is only needed for UTRACE_DETACH case

        - this _looks_ racy, ->exit_state is not protected

        - it is not immediately obvious this check also relies on
          utrace_control_dead() which was already called by our
          caller.

        - utrace_control_dead() if fact has already verified it is
          safe to detach. Even if _UTRACE_DEATH_EVENTS are set, we
          know that utrace_report_death/utrace_release_task can not
          be called until we drop utrace->lock (or utrace_report_death
          was already called). In any case it is safe to detach, we
          can't race with our quiesce/death/reap callbacks.

Also, I tried to document "if (exit_state)" check in utrace_control.

Signed-off-by: Oleg Nesterov <o...@redhat.com>

--- __UTRACE/kernel/utrace.c~7_DO_STOP_NO_EXIT_STATE    2009-08-26 
18:03:36.000000000 +0200
+++ __UTRACE/kernel/utrace.c    2009-08-26 19:22:02.000000000 +0200
@@ -660,15 +660,7 @@ static bool utrace_do_stop(struct task_s
 {
        bool stopped = false;
 
-       if (unlikely(target->exit_state)) {
-               /*
-                * On the exit path, it's only truly quiescent
-                * if it has already been through
-                * utrace_report_death(), or never will.
-                */
-               if (!(target->utrace_flags & _UTRACE_DEATH_EVENTS))
-                       stopped = true;
-       } else if (task_is_stopped(target)) {
+       if (task_is_stopped(target)) {
                /*
                 * Stopped is considered quiescent; when it wakes up, it will
                 * go through utrace_finish_jctl() before doing anything else.
@@ -1073,17 +1065,24 @@ int utrace_control(struct task_struct *t
        if (unlikely(IS_ERR(utrace)))
                return PTR_ERR(utrace);
 
-       if (target->exit_state) {
+       resume = utrace->stopped;
+       ret = 0;
+
+       /*
+        * ->exit_state can change under us, this doesn't matter.
+        * We do not care about ->exit_state in fact, but we do
+        * care about ->reap and ->death. If either flag is set,
+        * we must also see ->exit_state != 0.
+        */
+       if (unlikely(target->exit_state)) {
                ret = utrace_control_dead(target, utrace, action);
                if (ret) {
                        spin_unlock(&utrace->lock);
                        return ret;
                }
+               resume = true;
        }
 
-       resume = utrace->stopped;
-       ret = 0;
-
        clear_engine_wants_stop(engine);
        switch (action) {
        case UTRACE_STOP:

Reply via email to