Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

kill tracehook_notify_death()

Kill tracehook_notify_death(), reimplement the logic in its caller,
exit_notify().

Also, change the exec_id's check to use thread_group_leader() instead
of task_detached(), this is more clear. This logic only applies to
the exiting leader, a sub-thread must never change its exit_signal.

Note: when the traced group leader exits the exit_signal-or-SIGCHLD
logic looks really strange:

- we notify the tracer even if !thread_group_empty() but
do_wait(WEXITED) can't work until all threads exit

- if the tracer is real_parent, it is not clear why can't
we use ->exit_signal event if !thread_group_empty()

-v2: do not try to fix the 2nd oddity to avoid the subtle behavior
change mixed with reorganization, suggested by Tejun.

Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Reviewed-by: Tejun Heo <tj@kernel.org>

+13 -42
-34
include/linux/tracehook.h
··· 152 152 ptrace_notify(SIGTRAP); 153 153 } 154 154 155 - #define DEATH_REAP -1 156 - #define DEATH_DELAYED_GROUP_LEADER -2 157 - 158 - /** 159 - * tracehook_notify_death - task is dead, ready to notify parent 160 - * @task: @current task now exiting 161 - * @death_cookie: value to pass to tracehook_report_death() 162 - * @group_dead: nonzero if this was the last thread in the group to die 163 - * 164 - * A return value >= 0 means call do_notify_parent() with that signal 165 - * number. Negative return value can be %DEATH_REAP to self-reap right 166 - * now, or %DEATH_DELAYED_GROUP_LEADER to a zombie without notifying our 167 - * parent. Note that a return value of 0 means a do_notify_parent() call 168 - * that sends no signal, but still wakes up a parent blocked in wait*(). 169 - * 170 - * Called with write_lock_irq(&tasklist_lock) held. 171 - */ 172 - static inline int tracehook_notify_death(struct task_struct *task, 173 - void **death_cookie, int group_dead) 174 - { 175 - if (task_detached(task)) 176 - return task->ptrace ? SIGCHLD : DEATH_REAP; 177 - 178 - /* 179 - * If something other than our normal parent is ptracing us, then 180 - * send it a SIGCHLD instead of honoring exit_signal. exit_signal 181 - * only has special meaning to our real parent. 182 - */ 183 - if (thread_group_empty(task) && !ptrace_reparented(task)) 184 - return task->exit_signal; 185 - 186 - return task->ptrace ? SIGCHLD : DEATH_DELAYED_GROUP_LEADER; 187 - } 188 - 189 155 #ifdef TIF_NOTIFY_RESUME 190 156 /** 191 157 * set_notify_resume - cause tracehook_notify_resume() to be called
+13 -8
kernel/exit.c
··· 819 819 */ 820 820 static void exit_notify(struct task_struct *tsk, int group_dead) 821 821 { 822 - int signal; 823 822 bool autoreap; 824 - void *cookie; 825 823 826 824 /* 827 825 * This does two things: ··· 850 852 * we have changed execution domain as these two values started 851 853 * the same after a fork. 852 854 */ 853 - if (tsk->exit_signal != SIGCHLD && !task_detached(tsk) && 855 + if (thread_group_leader(tsk) && tsk->exit_signal != SIGCHLD && 854 856 (tsk->parent_exec_id != tsk->real_parent->self_exec_id || 855 857 tsk->self_exec_id != tsk->parent_exec_id)) 856 858 tsk->exit_signal = SIGCHLD; 857 859 858 - signal = tracehook_notify_death(tsk, &cookie, group_dead); 859 - if (signal >= 0) 860 - autoreap = do_notify_parent(tsk, signal); 861 - else 862 - autoreap = (signal == DEATH_REAP); 860 + if (unlikely(tsk->ptrace)) { 861 + int sig = thread_group_leader(tsk) && 862 + thread_group_empty(tsk) && 863 + !ptrace_reparented(tsk) ? 864 + tsk->exit_signal : SIGCHLD; 865 + autoreap = do_notify_parent(tsk, sig); 866 + } else if (thread_group_leader(tsk)) { 867 + autoreap = thread_group_empty(tsk) && 868 + do_notify_parent(tsk, tsk->exit_signal); 869 + } else { 870 + autoreap = true; 871 + } 863 872 864 873 tsk->exit_state = autoreap ? EXIT_DEAD : EXIT_ZOMBIE; 865 874