summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/char/tty_io.c4
-rw-r--r--drivers/isdn/i4l/isdn_tty.c2
-rw-r--r--fs/fcntl.c4
-rw-r--r--include/linux/sched.h6
-rw-r--r--kernel/exit.c2
-rw-r--r--kernel/itimer.c2
-rw-r--r--kernel/signal.c171
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);