diff options
author | Roland McGrath <roland@redhat.com> | 2008-07-25 22:45:54 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-07-26 15:00:09 -0400 |
commit | 2b2a1ff64afbadac842bbc58c5166962cf4f7664 (patch) | |
tree | 660640eb95117ce6f7436331f23f73805abced10 | |
parent | fa00b80b3c41a845b3d56f866fb40a2e98754c51 (diff) |
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>
-rw-r--r-- | include/linux/sched.h | 2 | ||||
-rw-r--r-- | include/linux/tracehook.h | 52 | ||||
-rw-r--r-- | kernel/exit.c | 26 | ||||
-rw-r--r-- | kernel/signal.c | 10 |
4 files changed, 69 insertions, 21 deletions
diff --git a/include/linux/sched.h b/include/linux/sched.h index adb8077dc463..a95d84d0da95 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h | |||
@@ -1796,7 +1796,7 @@ extern int kill_pid_info_as_uid(int, struct siginfo *, struct pid *, uid_t, uid_ | |||
1796 | extern int kill_pgrp(struct pid *pid, int sig, int priv); | 1796 | extern int kill_pgrp(struct pid *pid, int sig, int priv); |
1797 | extern int kill_pid(struct pid *pid, int sig, int priv); | 1797 | extern int kill_pid(struct pid *pid, int sig, int priv); |
1798 | extern int kill_proc_info(int, struct siginfo *, pid_t); | 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 | extern void force_sig(int, struct task_struct *); | 1800 | extern void force_sig(int, struct task_struct *); |
1801 | extern void force_sig_specific(int, struct task_struct *); | 1801 | extern void force_sig_specific(int, struct task_struct *); |
1802 | extern int send_sig(int, struct task_struct *, int); | 1802 | extern int send_sig(int, struct task_struct *, int); |
diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h index 6dc428dd2f38..4c50e1b57349 100644 --- a/include/linux/tracehook.h +++ b/include/linux/tracehook.h | |||
@@ -471,4 +471,56 @@ static inline int tracehook_notify_jctl(int notify, int why) | |||
471 | return notify || (current->ptrace & PT_PTRACED); | 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 | #endif /* <linux/tracehook.h> */ | 526 | #endif /* <linux/tracehook.h> */ |
diff --git a/kernel/exit.c b/kernel/exit.c index da28745f7c38..6cdf60712bd2 100644 --- a/kernel/exit.c +++ b/kernel/exit.c | |||
@@ -885,7 +885,8 @@ static void forget_original_parent(struct task_struct *father) | |||
885 | */ | 885 | */ |
886 | static void exit_notify(struct task_struct *tsk, int group_dead) | 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 | * This does two things: | 892 | * This does two things: |
@@ -922,22 +923,11 @@ static void exit_notify(struct task_struct *tsk, int group_dead) | |||
922 | !capable(CAP_KILL)) | 923 | !capable(CAP_KILL)) |
923 | tsk->exit_signal = SIGCHLD; | 924 | tsk->exit_signal = SIGCHLD; |
924 | 925 | ||
925 | /* If something other than our normal parent is ptracing us, then | 926 | signal = tracehook_notify_death(tsk, &cookie, group_dead); |
926 | * send it a SIGCHLD instead of honoring exit_signal. exit_signal | 927 | if (signal > 0) |
927 | * only has special meaning to our real parent. | 928 | signal = do_notify_parent(tsk, signal); |
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 | } | ||
936 | 929 | ||
937 | state = EXIT_ZOMBIE; | 930 | tsk->exit_state = signal < 0 ? EXIT_DEAD : EXIT_ZOMBIE; |
938 | if (task_detached(tsk) && likely(!tsk->ptrace)) | ||
939 | state = EXIT_DEAD; | ||
940 | tsk->exit_state = state; | ||
941 | 931 | ||
942 | /* mt-exec, de_thread() is waiting for us */ | 932 | /* mt-exec, de_thread() is waiting for us */ |
943 | if (thread_group_leader(tsk) && | 933 | if (thread_group_leader(tsk) && |
@@ -947,8 +937,10 @@ static void exit_notify(struct task_struct *tsk, int group_dead) | |||
947 | 937 | ||
948 | write_unlock_irq(&tasklist_lock); | 938 | write_unlock_irq(&tasklist_lock); |
949 | 939 | ||
940 | tracehook_report_death(tsk, signal, cookie, group_dead); | ||
941 | |||
950 | /* If the process is dead, release it - nobody will wait for it */ | 942 | /* If the process is dead, release it - nobody will wait for it */ |
951 | if (state == EXIT_DEAD) | 943 | if (signal < 0) |
952 | release_task(tsk); | 944 | release_task(tsk); |
953 | } | 945 | } |
954 | 946 | ||
diff --git a/kernel/signal.c b/kernel/signal.c index e9e699f4b1bd..0e862d3130ff 100644 --- a/kernel/signal.c +++ b/kernel/signal.c | |||
@@ -1326,9 +1326,11 @@ static inline void __wake_up_parent(struct task_struct *p, | |||
1326 | /* | 1326 | /* |
1327 | * Let a parent know about the death of a child. | 1327 | * Let a parent know about the death of a child. |
1328 | * For a stopped/continued status change, use do_notify_parent_cldstop instead. | 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 | 1333 | int do_notify_parent(struct task_struct *tsk, int sig) | |
1331 | void do_notify_parent(struct task_struct *tsk, int sig) | ||
1332 | { | 1334 | { |
1333 | struct siginfo info; | 1335 | struct siginfo info; |
1334 | unsigned long flags; | 1336 | unsigned long flags; |
@@ -1399,12 +1401,14 @@ void do_notify_parent(struct task_struct *tsk, int sig) | |||
1399 | */ | 1401 | */ |
1400 | tsk->exit_signal = -1; | 1402 | tsk->exit_signal = -1; |
1401 | if (psig->action[SIGCHLD-1].sa.sa_handler == SIG_IGN) | 1403 | if (psig->action[SIGCHLD-1].sa.sa_handler == SIG_IGN) |
1402 | sig = 0; | 1404 | sig = -1; |
1403 | } | 1405 | } |
1404 | if (valid_signal(sig) && sig > 0) | 1406 | if (valid_signal(sig) && sig > 0) |
1405 | __group_send_sig_info(sig, &info, tsk->parent); | 1407 | __group_send_sig_info(sig, &info, tsk->parent); |
1406 | __wake_up_parent(tsk, tsk->parent); | 1408 | __wake_up_parent(tsk, tsk->parent); |
1407 | spin_unlock_irqrestore(&psig->siglock, flags); | 1409 | spin_unlock_irqrestore(&psig->siglock, flags); |
1410 | |||
1411 | return sig; | ||
1408 | } | 1412 | } |
1409 | 1413 | ||
1410 | static void do_notify_parent_cldstop(struct task_struct *tsk, int why) | 1414 | static void do_notify_parent_cldstop(struct task_struct *tsk, int why) |