Commit 2b2a1ff64afbadac842bbc58c5166962cf4f7664
Committed by
Linus Torvalds
1 parent
fa00b80b3c
Exists in
master
and in
7 other branches
tracehook: death
This moves the ptrace logic in task death (exit_notify) into tracehook.h inlines. Some code is rearranged slightly to make things nicer. There is no change, only cleanup. There is one hook called with the tasklist_lock write-locked, as ptrace needs. There is also a new hook called after exit_state changes and without locks. This is a better place for tracing work to be in the future, since it doesn't delay the whole system with locking. Signed-off-by: Roland McGrath <roland@redhat.com> Cc: Oleg Nesterov <oleg@tv-sign.ru> Reviewed-by: Ingo Molnar <mingo@elte.hu> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Showing 4 changed files with 69 additions and 21 deletions Side-by-side Diff
include/linux/sched.h
... | ... | @@ -1796,7 +1796,7 @@ |
1796 | 1796 | extern int kill_pgrp(struct pid *pid, int sig, int priv); |
1797 | 1797 | extern int kill_pid(struct pid *pid, int sig, int priv); |
1798 | 1798 | extern int kill_proc_info(int, struct siginfo *, pid_t); |
1799 | -extern void do_notify_parent(struct task_struct *, int); | |
1799 | +extern int do_notify_parent(struct task_struct *, int); | |
1800 | 1800 | extern void force_sig(int, struct task_struct *); |
1801 | 1801 | extern void force_sig_specific(int, struct task_struct *); |
1802 | 1802 | extern int send_sig(int, struct task_struct *, int); |
include/linux/tracehook.h
... | ... | @@ -471,5 +471,57 @@ |
471 | 471 | return notify || (current->ptrace & PT_PTRACED); |
472 | 472 | } |
473 | 473 | |
474 | +/** | |
475 | + * tracehook_notify_death - task is dead, ready to notify parent | |
476 | + * @task: @current task now exiting | |
477 | + * @death_cookie: value to pass to tracehook_report_death() | |
478 | + * @group_dead: nonzero if this was the last thread in the group to die | |
479 | + * | |
480 | + * Return the signal number to send our parent with do_notify_parent(), or | |
481 | + * zero to send no signal and leave a zombie, or -1 to self-reap right now. | |
482 | + * | |
483 | + * Called with write_lock_irq(&tasklist_lock) held. | |
484 | + */ | |
485 | +static inline int tracehook_notify_death(struct task_struct *task, | |
486 | + void **death_cookie, int group_dead) | |
487 | +{ | |
488 | + if (task->exit_signal == -1) | |
489 | + return task->ptrace ? SIGCHLD : -1; | |
490 | + | |
491 | + /* | |
492 | + * If something other than our normal parent is ptracing us, then | |
493 | + * send it a SIGCHLD instead of honoring exit_signal. exit_signal | |
494 | + * only has special meaning to our real parent. | |
495 | + */ | |
496 | + if (thread_group_empty(task) && !ptrace_reparented(task)) | |
497 | + return task->exit_signal; | |
498 | + | |
499 | + return task->ptrace ? SIGCHLD : 0; | |
500 | +} | |
501 | + | |
502 | +/** | |
503 | + * tracehook_report_death - task is dead and ready to be reaped | |
504 | + * @task: @current task now exiting | |
505 | + * @signal: signal number sent to parent, or 0 or -1 | |
506 | + * @death_cookie: value passed back from tracehook_notify_death() | |
507 | + * @group_dead: nonzero if this was the last thread in the group to die | |
508 | + * | |
509 | + * Thread has just become a zombie or is about to self-reap. If positive, | |
510 | + * @signal is the signal number just sent to the parent (usually %SIGCHLD). | |
511 | + * If @signal is -1, this thread will self-reap. If @signal is 0, this is | |
512 | + * a delayed_group_leader() zombie. The @death_cookie was passed back by | |
513 | + * tracehook_notify_death(). | |
514 | + * | |
515 | + * If normal reaping is not inhibited, @task->exit_state might be changing | |
516 | + * in parallel. | |
517 | + * | |
518 | + * Called without locks. | |
519 | + */ | |
520 | +static inline void tracehook_report_death(struct task_struct *task, | |
521 | + int signal, void *death_cookie, | |
522 | + int group_dead) | |
523 | +{ | |
524 | +} | |
525 | + | |
474 | 526 | #endif /* <linux/tracehook.h> */ |
kernel/exit.c
... | ... | @@ -885,7 +885,8 @@ |
885 | 885 | */ |
886 | 886 | static void exit_notify(struct task_struct *tsk, int group_dead) |
887 | 887 | { |
888 | - int state; | |
888 | + int signal; | |
889 | + void *cookie; | |
889 | 890 | |
890 | 891 | /* |
891 | 892 | * This does two things: |
892 | 893 | |
... | ... | @@ -922,22 +923,11 @@ |
922 | 923 | !capable(CAP_KILL)) |
923 | 924 | tsk->exit_signal = SIGCHLD; |
924 | 925 | |
925 | - /* If something other than our normal parent is ptracing us, then | |
926 | - * send it a SIGCHLD instead of honoring exit_signal. exit_signal | |
927 | - * only has special meaning to our real parent. | |
928 | - */ | |
929 | - if (!task_detached(tsk) && thread_group_empty(tsk)) { | |
930 | - int signal = ptrace_reparented(tsk) ? | |
931 | - SIGCHLD : tsk->exit_signal; | |
932 | - do_notify_parent(tsk, signal); | |
933 | - } else if (tsk->ptrace) { | |
934 | - do_notify_parent(tsk, SIGCHLD); | |
935 | - } | |
926 | + signal = tracehook_notify_death(tsk, &cookie, group_dead); | |
927 | + if (signal > 0) | |
928 | + signal = do_notify_parent(tsk, signal); | |
936 | 929 | |
937 | - state = EXIT_ZOMBIE; | |
938 | - if (task_detached(tsk) && likely(!tsk->ptrace)) | |
939 | - state = EXIT_DEAD; | |
940 | - tsk->exit_state = state; | |
930 | + tsk->exit_state = signal < 0 ? EXIT_DEAD : EXIT_ZOMBIE; | |
941 | 931 | |
942 | 932 | /* mt-exec, de_thread() is waiting for us */ |
943 | 933 | if (thread_group_leader(tsk) && |
944 | 934 | |
... | ... | @@ -947,8 +937,10 @@ |
947 | 937 | |
948 | 938 | write_unlock_irq(&tasklist_lock); |
949 | 939 | |
940 | + tracehook_report_death(tsk, signal, cookie, group_dead); | |
941 | + | |
950 | 942 | /* If the process is dead, release it - nobody will wait for it */ |
951 | - if (state == EXIT_DEAD) | |
943 | + if (signal < 0) | |
952 | 944 | release_task(tsk); |
953 | 945 | } |
954 | 946 |
kernel/signal.c
... | ... | @@ -1326,9 +1326,11 @@ |
1326 | 1326 | /* |
1327 | 1327 | * Let a parent know about the death of a child. |
1328 | 1328 | * For a stopped/continued status change, use do_notify_parent_cldstop instead. |
1329 | + * | |
1330 | + * Returns -1 if our parent ignored us and so we've switched to | |
1331 | + * self-reaping, or else @sig. | |
1329 | 1332 | */ |
1330 | - | |
1331 | -void do_notify_parent(struct task_struct *tsk, int sig) | |
1333 | +int do_notify_parent(struct task_struct *tsk, int sig) | |
1332 | 1334 | { |
1333 | 1335 | struct siginfo info; |
1334 | 1336 | unsigned long flags; |
1335 | 1337 | |
... | ... | @@ -1399,12 +1401,14 @@ |
1399 | 1401 | */ |
1400 | 1402 | tsk->exit_signal = -1; |
1401 | 1403 | if (psig->action[SIGCHLD-1].sa.sa_handler == SIG_IGN) |
1402 | - sig = 0; | |
1404 | + sig = -1; | |
1403 | 1405 | } |
1404 | 1406 | if (valid_signal(sig) && sig > 0) |
1405 | 1407 | __group_send_sig_info(sig, &info, tsk->parent); |
1406 | 1408 | __wake_up_parent(tsk, tsk->parent); |
1407 | 1409 | spin_unlock_irqrestore(&psig->siglock, flags); |
1410 | + | |
1411 | + return sig; | |
1408 | 1412 | } |
1409 | 1413 | |
1410 | 1414 | static void do_notify_parent_cldstop(struct task_struct *tsk, int why) |