Commit da334d91 authored by Roland McGrath's avatar Roland McGrath Committed by Linus Torvalds

[PATCH] linux-2.5.66-signal-cleanup.patch

Here is the cleanup patch I promised back in February.  Sorry it took a
while.

The effects should be purely cosmetic in 2.5.66.  However, the new
interface for the proper way to send thread-specific of process-global
signals from inside the kernel is needed for correct implementation of
some fixes to timer stuff that Ulrich told me about.

This cleans up some obsolete comments and macros in kernel/signal.c,
restores send_sig_info to its original behavior, and adds a global entry
point send_group_sig_info.  I checked all the uses of send_sig and
send_sig_info and changed a few to send_group_sig_info.

I think it would be cleanest if the whole mess of *_sig* entry points were
reduced to two or three, but I did the change that minimized the number of
callers I had to fix up.

There should be no discernible difference, since the 2.5.66 send_sig_info
function did group semantics for those signals by number already.  The only
exception to that is pdeath_signal, which I guess can be any signal number
but I deemed ought to be process-wide.

I did not change any of the calls using SIGKILL, though that does have
process-wide semantics.  There is no need to change it since SIGKILL always
kills the whole group, though the code path for send_sig(SIGKILL,...) calls
in multithreaded processes will be different now.
parent 7777d006
......@@ -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;
}
......
......@@ -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
......
......@@ -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)
......
......@@ -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)
......
......@@ -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)) {
......
......@@ -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)
......
......@@ -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.
*/
#ifdef SIGSTKFLT
#define M_SIGSTKFLT M(SIGSTKFLT)
#else
#define M_SIGSTKFLT 0
#endif
/*
* 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 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);
}
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);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment