diff options
| -rw-r--r-- | drivers/char/tty_io.c | 4 | ||||
| -rw-r--r-- | drivers/isdn/i4l/isdn_tty.c | 2 | ||||
| -rw-r--r-- | fs/fcntl.c | 4 | ||||
| -rw-r--r-- | include/linux/sched.h | 6 | ||||
| -rw-r--r-- | kernel/exit.c | 2 | ||||
| -rw-r--r-- | kernel/itimer.c | 2 | ||||
| -rw-r--r-- | kernel/signal.c | 171 |
7 files changed, 106 insertions, 85 deletions
diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index f3c2f35381ac..c57b097ac389 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -506,8 +506,8 @@ void do_tty_hangup(void *data) p->tty = NULL; if (!p->leader) continue; - send_sig(SIGHUP, p, 1); - send_sig(SIGCONT, p, 1); + send_group_sig_info(SIGHUP, SEND_SIG_PRIV, p); + send_group_sig_info(SIGCONT, SEND_SIG_PRIV, p); if (tty->pgrp > 0) p->tty_old_pgrp = tty->pgrp; } diff --git a/drivers/isdn/i4l/isdn_tty.c b/drivers/isdn/i4l/isdn_tty.c index dd58066f7386..0053c471fb16 100644 --- a/drivers/isdn/i4l/isdn_tty.c +++ b/drivers/isdn/i4l/isdn_tty.c @@ -2036,7 +2036,7 @@ modem_write_profile(atemu * m) memcpy(m->pmsn, m->msn, ISDN_MSNLEN); memcpy(m->plmsn, m->lmsn, ISDN_LMSNLEN); if (dev->profd) - send_sig(SIGIO, dev->profd, 1); + group_send_sig_info(SIGIO, SEND_SIG_PRIV, dev->profd); } int diff --git a/fs/fcntl.c b/fs/fcntl.c index fae2022ada19..f1bf41ec99a5 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -465,7 +465,7 @@ static void send_sigio_to_task(struct task_struct *p, break; /* fall-through: fall back on the old plain SIGIO signal */ case 0: - send_sig(SIGIO, p, 1); + send_group_sig_info(SIGIO, SEND_SIG_PRIV, p); } } @@ -501,7 +501,7 @@ static void send_sigurg_to_task(struct task_struct *p, struct fown_struct *fown) { if (sigio_perm(p, fown)) - send_sig(SIGURG, p, 1); + send_group_sig_info(SIGURG, SEND_SIG_PRIV, p); } int send_sigurg(struct fown_struct *fown) diff --git a/include/linux/sched.h b/include/linux/sched.h index d001088e58b3..f3b4c5891898 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -541,6 +541,7 @@ extern void block_all_signals(int (*notifier)(void *priv), void *priv, extern void unblock_all_signals(void); extern void release_task(struct task_struct * p); extern int send_sig_info(int, struct siginfo *, struct task_struct *); +extern int send_group_sig_info(int, struct siginfo *, struct task_struct *); extern int force_sig_info(int, struct siginfo *, struct task_struct *); extern int __kill_pg_info(int sig, struct siginfo *info, pid_t pgrp); extern int kill_pg_info(int, struct siginfo *, pid_t); @@ -558,6 +559,11 @@ extern int kill_proc(pid_t, int, int); extern int do_sigaction(int, const struct k_sigaction *, struct k_sigaction *); extern int do_sigaltstack(const stack_t *, stack_t *, unsigned long); +/* These can be the second arg to send_sig_info/send_group_sig_info. */ +#define SEND_SIG_NOINFO ((struct siginfo *) 0) +#define SEND_SIG_PRIV ((struct siginfo *) 1) +#define SEND_SIG_FORCED ((struct siginfo *) 2) + /* True if we are on the alternate signal stack. */ static inline int on_sig_stack(unsigned long sp) diff --git a/kernel/exit.c b/kernel/exit.c index ffc0973ab074..b393a46247c4 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -488,7 +488,7 @@ static inline void reparent_thread(task_t *p, task_t *father, int traced) p->self_exec_id++; if (p->pdeath_signal) - send_sig(p->pdeath_signal, p, 0); + send_group_sig_info(p->pdeath_signal, 0, p); /* Move the child from its dying parent to the new one. */ if (unlikely(traced)) { diff --git a/kernel/itimer.c b/kernel/itimer.c index 4fc29f3de5e8..4db3b24ec41e 100644 --- a/kernel/itimer.c +++ b/kernel/itimer.c @@ -67,7 +67,7 @@ void it_real_fn(unsigned long __data) struct task_struct * p = (struct task_struct *) __data; unsigned long interval; - send_sig(SIGALRM, p, 1); + send_group_sig_info(SIGALRM, SEND_SIG_PRIV, p); interval = p->it_real_incr; if (interval) { if (interval > (unsigned long) LONG_MAX) diff --git a/kernel/signal.c b/kernel/signal.c index 488134eec0e2..096d58ad5237 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -33,64 +33,79 @@ static kmem_cache_t *sigqueue_cachep; atomic_t nr_queued_signals; int max_queued_signals = 1024; -/********************************************************* - - POSIX thread group signal behavior: - ----------------------------------------------------------- -| | userspace | kernel | ----------------------------------------------------------- -| SIGHUP | load-balance | kill-all | -| SIGINT | load-balance | kill-all | -| SIGQUIT | load-balance | kill-all+core | -| SIGILL | specific | kill-all+core | -| SIGTRAP | specific | kill-all+core | -| SIGABRT/SIGIOT | specific | kill-all+core | -| SIGBUS | specific | kill-all+core | -| SIGFPE | specific | kill-all+core | -| SIGKILL | n/a | kill-all | -| SIGUSR1 | load-balance | kill-all | -| SIGSEGV | specific | kill-all+core | -| SIGUSR2 | load-balance | kill-all | -| SIGPIPE | specific | kill-all | -| SIGALRM | load-balance | kill-all | -| SIGTERM | load-balance | kill-all | -| SIGCHLD | load-balance | ignore | -| SIGCONT | load-balance | ignore | -| SIGSTOP | n/a | stop-all | -| SIGTSTP | load-balance | stop-all | -| SIGTTIN | load-balance | stop-all | -| SIGTTOU | load-balance | stop-all | -| SIGURG | load-balance | ignore | -| SIGXCPU | specific | kill-all+core | -| SIGXFSZ | specific | kill-all+core | -| SIGVTALRM | load-balance | kill-all | -| SIGPROF | specific | kill-all | -| SIGPOLL/SIGIO | load-balance | kill-all | -| SIGSYS/SIGUNUSED | specific | kill-all+core | -| SIGSTKFLT | specific | kill-all | -| SIGWINCH | load-balance | ignore | -| SIGPWR | load-balance | kill-all | -| SIGRTMIN-SIGRTMAX | load-balance | kill-all | ----------------------------------------------------------- - - non-POSIX signal thread group behavior: - ----------------------------------------------------------- -| | userspace | kernel | ----------------------------------------------------------- -| SIGEMT | specific | kill-all+core | ----------------------------------------------------------- -*/ - -/* Some systems do not have a SIGSTKFLT and the kernel never - * generates such signals anyways. +/* + * In POSIX a signal is sent either to a specific thread (Linux task) + * or to the process as a whole (Linux thread group). How the signal + * is sent determines whether it's to one thread or the whole group, + * which determines which signal mask(s) are involved in blocking it + * from being delivered until later. When the signal is delivered, + * either it's caught or ignored by a user handler or it has a default + * effect that applies to the whole thread group (POSIX process). + * + * The possible effects an unblocked signal set to SIG_DFL can have are: + * ignore - Nothing Happens + * terminate - kill the process, i.e. all threads in the group, + * similar to exit_group. The group leader (only) reports + * WIFSIGNALED status to its parent. + * coredump - write a core dump file describing all threads using + * the same mm and then kill all those threads + * stop - stop all the threads in the group, i.e. TASK_STOPPED state + * + * SIGKILL and SIGSTOP cannot be caught, blocked, or ignored. + * Other signals when not blocked and set to SIG_DFL behaves as follows. + * The job control signals also have other special effects. + * + * +--------------------+------------------+ + * | POSIX signal | default action | + * +--------------------+------------------+ + * | SIGHUP | terminate | + * | SIGINT | terminate | + * | SIGQUIT | coredump | + * | SIGILL | coredump | + * | SIGTRAP | coredump | + * | SIGABRT/SIGIOT | coredump | + * | SIGBUS | coredump | + * | SIGFPE | coredump | + * | SIGKILL | terminate(+) | + * | SIGUSR1 | terminate | + * | SIGSEGV | coredump | + * | SIGUSR2 | terminate | + * | SIGPIPE | terminate | + * | SIGALRM | terminate | + * | SIGTERM | terminate | + * | SIGCHLD | ignore | + * | SIGCONT | ignore(*) | + * | SIGSTOP | stop(*)(+) | + * | SIGTSTP | stop(*) | + * | SIGTTIN | stop(*) | + * | SIGTTOU | stop(*) | + * | SIGURG | ignore | + * | SIGXCPU | coredump | + * | SIGXFSZ | coredump | + * | SIGVTALRM | terminate | + * | SIGPROF | terminate | + * | SIGPOLL/SIGIO | terminate | + * | SIGSYS/SIGUNUSED | coredump | + * | SIGSTKFLT | terminate | + * | SIGWINCH | ignore | + * | SIGPWR | terminate | + * | SIGRTMIN-SIGRTMAX | terminate | + * +--------------------+------------------+ + * | non-POSIX signal | default action | + * +--------------------+------------------+ + * | SIGEMT | coredump | + * +--------------------+------------------+ + * + * (+) For SIGKILL and SIGSTOP the action is "always", not just "default". + * (*) Special job control effects: + * When SIGCONT is sent, it resumes the process (all threads in the group) + * from TASK_STOPPED state and also clears any pending/queued stop signals + * (any of those marked with "stop(*)"). This happens regardless of blocking, + * catching, or ignoring SIGCONT. When any stop signal is sent, it clears + * any pending/queued SIGCONT signals; this happens regardless of blocking, + * catching, or ignored the stop signal, though (except for SIGSTOP) the + * default action of stopping the process may happen later or never. */ -#ifdef SIGSTKFLT -#define M_SIGSTKFLT M(SIGSTKFLT) -#else -#define M_SIGSTKFLT 0 -#endif #ifdef SIGEMT #define M_SIGEMT M(SIGEMT) @@ -105,16 +120,6 @@ int max_queued_signals = 1024; #endif #define T(sig, mask) (M(sig) & (mask)) -#define SIG_KERNEL_BROADCAST_MASK (\ - M(SIGHUP) | M(SIGINT) | M(SIGQUIT) | M(SIGILL) | \ - M(SIGTRAP) | M(SIGABRT) | M(SIGBUS) | M(SIGFPE) | \ - M(SIGKILL) | M(SIGUSR1) | M(SIGSEGV) | M(SIGUSR2) | \ - M(SIGPIPE) | M(SIGALRM) | M(SIGTERM) | M(SIGXCPU) | \ - M(SIGXFSZ) | M(SIGVTALRM) | M(SIGPROF) | M(SIGPOLL) | \ - M(SIGSYS) | M_SIGSTKFLT | M(SIGPWR) | M(SIGCONT) | \ - M(SIGSTOP) | M(SIGTSTP) | M(SIGTTIN) | M(SIGTTOU) | \ - M_SIGEMT ) - #define SIG_KERNEL_ONLY_MASK (\ M(SIGKILL) | M(SIGSTOP) ) @@ -599,7 +604,7 @@ static void do_notify_parent_cldstop(struct task_struct *tsk, struct task_struct *parent); /* - * Handle magic process-wide effects of stop/continue signals, and SIGKILL. + * Handle magic process-wide effects of stop/continue signals. * Unlike the signal actions, these happen immediately at signal-generation * time regardless of blocking, ignoring, or handling. This does the * actual continuing for SIGCONT, but not the actual stopping for stop @@ -1134,9 +1139,8 @@ static int kill_something_info(int sig, struct siginfo *info, int pid) */ /* - * XXX should probably nix these interfaces and update the kernel - * to specify explicitly whether the signal is a group signal or - * specific to a thread. + * These two are the most common entry points. They send a signal + * just to the specific thread. */ int send_sig_info(int sig, struct siginfo *info, struct task_struct *p) @@ -1150,13 +1154,9 @@ send_sig_info(int sig, struct siginfo *info, struct task_struct *p) * going away or changing from under us. */ read_lock(&tasklist_lock); - if (T(sig, SIG_KERNEL_BROADCAST_MASK)) { - ret = group_send_sig_info(sig, info, p); - } else { - spin_lock_irq(&p->sighand->siglock); - ret = specific_send_sig_info(sig, info, p); - spin_unlock_irq(&p->sighand->siglock); - } + spin_lock_irq(&p->sighand->siglock); + ret = specific_send_sig_info(sig, info, p); + spin_unlock_irq(&p->sighand->siglock); read_unlock(&tasklist_lock); return ret; } @@ -1167,6 +1167,20 @@ send_sig(int sig, struct task_struct *p, int priv) return send_sig_info(sig, (void*)(long)(priv != 0), p); } +/* + * This is the entry point for "process-wide" signals. + * They will go to an appropriate thread in the thread group. + */ +int +send_group_sig_info(int sig, struct siginfo *info, struct task_struct *p) +{ + int ret; + read_lock(&tasklist_lock); + ret = group_send_sig_info(sig, info, p); + read_unlock(&tasklist_lock); + return ret; +} + void force_sig(int sig, struct task_struct *p) { @@ -1642,6 +1656,7 @@ EXPORT_SYMBOL(kill_sl_info); EXPORT_SYMBOL(notify_parent); EXPORT_SYMBOL(send_sig); EXPORT_SYMBOL(send_sig_info); +EXPORT_SYMBOL(send_group_sig_info); EXPORT_SYMBOL(sigprocmask); EXPORT_SYMBOL(block_all_signals); EXPORT_SYMBOL(unblock_all_signals); |
