Commit ebf5ebe3 authored by Ingo Molnar's avatar Ingo Molnar Committed by Linus Torvalds

[PATCH] signal-fixes-2.5.59-A4

this is the current threading patchset, which accumulated up during the
past two weeks. It consists of a biggest set of changes from Roland, to
make threaded signals work. There were still tons of testcases and
boundary conditions (mostly in the signal/exit/ptrace area) that we did
not handle correctly.

Roland's thread-signal semantics/behavior/ptrace fixes:

 - fix signal delivery race with do_exit() => signals are re-queued to the
   'process' if do_exit() finds pending unhandled ones. This prevents
   signals getting lost upon thread-sys_exit().

 - a non-main thread has died on one processor and gone to TASK_ZOMBIE,
   but before it's gotten to release_task a sys_wait4 on the other
   processor reaps it.  It's only because it's ptraced that this gets
   through eligible_child.  Somewhere in there the main thread is also
   dying so it reparents the child thread to hit that case.  This means
   that there is a race where P might be totally invalid.

 - forget_original_parent is not doing the right thing when the group
   leader dies, i.e. reparenting threads to init when there is a zombie
   group leader.  Perhaps it doesn't matter for any practical purpose
   without ptrace, though it makes for ppid=1 for each thread in core
   dumps, which looks funny. Incidentally, SIGCHLD here really should be
   p->exit_signal.

 - one of the gdb tests makes a questionable assumption about what kill
   will do when it has some threads stopped by ptrace and others running.

exit races:

1. Processor A is in sys_wait4 case TASK_STOPPED considering task P.
   Processor B is about to resume P and then switch to it.

   While A is inside that case block, B starts running P and it clears
   P->exit_code, or takes a pending fatal signal and sets it to a new
   value. Depending on the interleaving, the possible failure modes are:
        a. A gets to its put_user after B has cleared P->exit_code
           => returns with WIFSTOPPED, WSTOPSIG==0
        b. A gets to its put_user after B has set P->exit_code anew
           => returns with e.g. WIFSTOPPED, WSTOPSIG==SIGKILL

   A can spend an arbitrarily long time in that case block, because
   there's getrusage and put_user that can take page faults, and
   write_lock'ing of the tasklist_lock that can block.  But even if it's
   short the race is there in principle.

2. This is new with NPTL, i.e. CLONE_THREAD.
   Two processors A and B are both in sys_wait4 case TASK_STOPPED
   considering task P.

   Both get through their tests and fetches of P->exit_code before either
   gets to P->exit_code = 0.  => two threads return the same pid from
   waitpid.

   In other interleavings where one processor gets to its put_user after
   the other has cleared P->exit_code, it's like case 1(a).


3. SMP races with stop/cont signals

   First, take:

        kill(pid, SIGSTOP);
        kill(pid, SIGCONT);

   or:

        kill(pid, SIGSTOP);
        kill(pid, SIGKILL);

   It's possible for this to leave the process stopped with a pending
   SIGCONT/SIGKILL.  That's a state that should never be possible.
   Moreover, kill(pid, SIGKILL) without any repetition should always be
   enough to kill a process.  (Likewise SIGCONT when you know it's
   sequenced after the last stop signal, must be sufficient to resume a
   process.)

4. take:

        kill(pid, SIGKILL);     // or any fatal signal
        kill(pid, SIGCONT);     // or SIGKILL

    it's possible for this to cause pid to be reaped with status 0
    instead of its true termination status.  The equivalent scenario
    happens when the process being killed is in an _exit call or a
    trap-induced fatal signal before the kills.

plus i've done stability fixes for bugs that popped up during
beta-testing, and minor tidying of Roland's changes:

 - a rare tasklist corruption during exec, causing some very spurious and
   colorful crashes.

 - a copy_process()-related dereference of already freed thread structure
   if hit with a SIGKILL in the wrong moment.

 - SMP spinlock deadlocks in the signal code

this patchset has been tested quite well in the 2.4 backport of the
threading changes - and i've done some stresstesting on 2.5.59 SMP as
well, and did an x86 UP testcompile + testboot as well.
parent 44a5a59c
...@@ -587,7 +587,7 @@ static inline int de_thread(struct signal_struct *oldsig) ...@@ -587,7 +587,7 @@ static inline int de_thread(struct signal_struct *oldsig)
return -EAGAIN; return -EAGAIN;
} }
oldsig->group_exit = 1; oldsig->group_exit = 1;
__broadcast_thread_group(current, SIGKILL); zap_other_threads(current);
/* /*
* Account for the thread group leader hanging around: * Account for the thread group leader hanging around:
...@@ -659,7 +659,8 @@ static inline int de_thread(struct signal_struct *oldsig) ...@@ -659,7 +659,8 @@ static inline int de_thread(struct signal_struct *oldsig)
current->ptrace = ptrace; current->ptrace = ptrace;
__ptrace_link(current, parent); __ptrace_link(current, parent);
} }
list_del(&current->tasks);
list_add_tail(&current->tasks, &init_task.tasks); list_add_tail(&current->tasks, &init_task.tasks);
current->exit_signal = SIGCHLD; current->exit_signal = SIGCHLD;
state = leader->state; state = leader->state;
...@@ -680,6 +681,7 @@ static inline int de_thread(struct signal_struct *oldsig) ...@@ -680,6 +681,7 @@ static inline int de_thread(struct signal_struct *oldsig)
newsig->group_exit = 0; newsig->group_exit = 0;
newsig->group_exit_code = 0; newsig->group_exit_code = 0;
newsig->group_exit_task = NULL; newsig->group_exit_task = NULL;
newsig->group_stop_count = 0;
memcpy(newsig->action, current->sig->action, sizeof(newsig->action)); memcpy(newsig->action, current->sig->action, sizeof(newsig->action));
init_sigpending(&newsig->shared_pending); init_sigpending(&newsig->shared_pending);
......
...@@ -235,6 +235,9 @@ struct signal_struct { ...@@ -235,6 +235,9 @@ struct signal_struct {
int group_exit; int group_exit;
int group_exit_code; int group_exit_code;
struct task_struct *group_exit_task; struct task_struct *group_exit_task;
/* thread group stop support, overloads group_exit_code too */
int group_stop_count;
}; };
/* /*
...@@ -508,7 +511,6 @@ extern int in_egroup_p(gid_t); ...@@ -508,7 +511,6 @@ extern int in_egroup_p(gid_t);
extern void proc_caches_init(void); extern void proc_caches_init(void);
extern void flush_signals(struct task_struct *); extern void flush_signals(struct task_struct *);
extern void flush_signal_handlers(struct task_struct *); extern void flush_signal_handlers(struct task_struct *);
extern void sig_exit(int, int, struct siginfo *);
extern int dequeue_signal(sigset_t *mask, siginfo_t *info); extern int dequeue_signal(sigset_t *mask, siginfo_t *info);
extern void block_all_signals(int (*notifier)(void *priv), void *priv, extern void block_all_signals(int (*notifier)(void *priv), void *priv,
sigset_t *mask); sigset_t *mask);
...@@ -525,7 +527,7 @@ extern void do_notify_parent(struct task_struct *, int); ...@@ -525,7 +527,7 @@ extern void do_notify_parent(struct task_struct *, int);
extern void force_sig(int, struct task_struct *); extern void force_sig(int, struct task_struct *);
extern void force_sig_specific(int, struct task_struct *); extern void force_sig_specific(int, struct task_struct *);
extern int send_sig(int, struct task_struct *, int); extern int send_sig(int, struct task_struct *, int);
extern int __broadcast_thread_group(struct task_struct *p, int sig); extern void zap_other_threads(struct task_struct *p);
extern int kill_pg(pid_t, int, int); extern int kill_pg(pid_t, int, int);
extern int kill_sl(pid_t, int, int); extern int kill_sl(pid_t, int, int);
extern int kill_proc(pid_t, int, int); extern int kill_proc(pid_t, int, int);
...@@ -590,6 +592,8 @@ extern void exit_files(struct task_struct *); ...@@ -590,6 +592,8 @@ extern void exit_files(struct task_struct *);
extern void exit_sighand(struct task_struct *); extern void exit_sighand(struct task_struct *);
extern void __exit_sighand(struct task_struct *); extern void __exit_sighand(struct task_struct *);
extern NORET_TYPE void do_group_exit(int);
extern void reparent_to_init(void); extern void reparent_to_init(void);
extern void daemonize(void); extern void daemonize(void);
extern task_t *child_reaper; extern task_t *child_reaper;
...@@ -762,6 +766,8 @@ static inline void cond_resched_lock(spinlock_t * lock) ...@@ -762,6 +766,8 @@ static inline void cond_resched_lock(spinlock_t * lock)
extern FASTCALL(void recalc_sigpending_tsk(struct task_struct *t)); extern FASTCALL(void recalc_sigpending_tsk(struct task_struct *t));
extern void recalc_sigpending(void); extern void recalc_sigpending(void);
extern void signal_wake_up(struct task_struct *t, int resume_stopped);
/* /*
* Wrappers for p->thread_info->cpu access. No-op on UP. * Wrappers for p->thread_info->cpu access. No-op on UP.
*/ */
......
...@@ -647,7 +647,7 @@ NORET_TYPE void do_exit(long code) ...@@ -647,7 +647,7 @@ NORET_TYPE void do_exit(long code)
exit_namespace(tsk); exit_namespace(tsk);
exit_thread(); exit_thread();
if (current->leader) if (tsk->leader)
disassociate_ctty(1); disassociate_ctty(1);
module_put(tsk->thread_info->exec_domain->module); module_put(tsk->thread_info->exec_domain->module);
...@@ -657,8 +657,31 @@ NORET_TYPE void do_exit(long code) ...@@ -657,8 +657,31 @@ NORET_TYPE void do_exit(long code)
tsk->exit_code = code; tsk->exit_code = code;
exit_notify(); exit_notify();
preempt_disable(); preempt_disable();
if (current->exit_signal == -1) if (signal_pending(tsk) && !tsk->sig->group_exit
release_task(current); && !thread_group_empty(tsk)) {
/*
* This occurs when there was a race between our exit
* syscall and a group signal choosing us as the one to
* wake up. It could be that we are the only thread
* alerted to check for pending signals, but another thread
* should be woken now to take the signal since we will not.
* Now we'll wake all the threads in the group just to make
* sure someone gets all the pending signals.
*/
struct task_struct *t;
read_lock(&tasklist_lock);
spin_lock_irq(&tsk->sig->siglock);
for (t = next_thread(tsk); t != tsk; t = next_thread(t))
if (!signal_pending(t) && !(t->flags & PF_EXITING)) {
recalc_sigpending_tsk(t);
if (signal_pending(t))
signal_wake_up(t, 0);
}
spin_unlock_irq(&tsk->sig->siglock);
read_unlock(&tasklist_lock);
}
if (tsk->exit_signal == -1)
release_task(tsk);
schedule(); schedule();
BUG(); BUG();
/* /*
...@@ -710,31 +733,44 @@ task_t *next_thread(task_t *p) ...@@ -710,31 +733,44 @@ task_t *next_thread(task_t *p)
} }
/* /*
* this kills every thread in the thread group. Note that any externally * Take down every thread in the group. This is called by fatal signals
* wait4()-ing process will get the correct exit code - even if this * as well as by sys_exit_group (below).
* thread is not the thread group leader.
*/ */
asmlinkage long sys_exit_group(int error_code) NORET_TYPE void
do_group_exit(int exit_code)
{ {
unsigned int exit_code = (error_code & 0xff) << 8; BUG_ON(exit_code & 0x80); /* core dumps don't get here */
if (!thread_group_empty(current)) {
struct signal_struct *sig = current->sig;
if (current->sig->group_exit)
exit_code = current->sig->group_exit_code;
else if (!thread_group_empty(current)) {
struct signal_struct *const sig = current->sig;
read_lock(&tasklist_lock);
spin_lock_irq(&sig->siglock); spin_lock_irq(&sig->siglock);
if (sig->group_exit) { if (sig->group_exit)
spin_unlock_irq(&sig->siglock); /* Another thread got here before we took the lock. */
exit_code = sig->group_exit_code;
/* another thread was faster: */ else {
do_exit(sig->group_exit_code);
}
sig->group_exit = 1; sig->group_exit = 1;
sig->group_exit_code = exit_code; sig->group_exit_code = exit_code;
__broadcast_thread_group(current, SIGKILL); zap_other_threads(current);
}
spin_unlock_irq(&sig->siglock); spin_unlock_irq(&sig->siglock);
read_unlock(&tasklist_lock);
} }
do_exit(exit_code); do_exit(exit_code);
/* NOTREACHED */
}
/*
* this kills every thread in the thread group. Note that any externally
* wait4()-ing process will get the correct exit code - even if this
* thread is not the thread group leader.
*/
asmlinkage long sys_exit_group(int error_code)
{
do_group_exit((error_code & 0xff) << 8);
} }
static int eligible_child(pid_t pid, int options, task_t *p) static int eligible_child(pid_t pid, int options, task_t *p)
...@@ -800,6 +836,8 @@ asmlinkage long sys_wait4(pid_t pid,unsigned int * stat_addr, int options, struc ...@@ -800,6 +836,8 @@ asmlinkage long sys_wait4(pid_t pid,unsigned int * stat_addr, int options, struc
int ret; int ret;
list_for_each(_p,&tsk->children) { list_for_each(_p,&tsk->children) {
int exit_code;
p = list_entry(_p,struct task_struct,sibling); p = list_entry(_p,struct task_struct,sibling);
ret = eligible_child(pid, options, p); ret = eligible_child(pid, options, p);
...@@ -813,20 +851,69 @@ asmlinkage long sys_wait4(pid_t pid,unsigned int * stat_addr, int options, struc ...@@ -813,20 +851,69 @@ asmlinkage long sys_wait4(pid_t pid,unsigned int * stat_addr, int options, struc
continue; continue;
if (!(options & WUNTRACED) && !(p->ptrace & PT_PTRACED)) if (!(options & WUNTRACED) && !(p->ptrace & PT_PTRACED))
continue; continue;
if (ret == 2 && !(p->ptrace & PT_PTRACED) &&
p->sig && p->sig->group_stop_count > 0)
/*
* A group stop is in progress and
* we are the group leader. We won't
* report until all threads have
* stopped.
*/
continue;
read_unlock(&tasklist_lock); read_unlock(&tasklist_lock);
/* move to end of parent's list to avoid starvation */ /* move to end of parent's list to avoid starvation */
write_lock_irq(&tasklist_lock); write_lock_irq(&tasklist_lock);
remove_parent(p); remove_parent(p);
add_parent(p, p->parent); add_parent(p, p->parent);
/*
* This uses xchg to be atomic with
* the thread resuming and setting it.
* It must also be done with the write
* lock held to prevent a race with the
* TASK_ZOMBIE case (below).
*/
exit_code = xchg(&p->exit_code, 0);
if (unlikely(p->state > TASK_STOPPED)) {
/*
* The task resumed and then died.
* Let the next iteration catch it
* in TASK_ZOMBIE. Note that
* exit_code might already be zero
* here if it resumed and did
* _exit(0). The task itself is
* dead and won't touch exit_code
* again; other processors in
* this function are locked out.
*/
p->exit_code = exit_code;
exit_code = 0;
}
if (unlikely(exit_code == 0)) {
/*
* Another thread in this function
* got to it first, or it resumed,
* or it resumed and then died.
*/
write_unlock_irq(&tasklist_lock);
continue;
}
/*
* Make sure this doesn't get reaped out from
* under us while we are examining it below.
* We don't want to keep holding onto the
* tasklist_lock while we call getrusage and
* possibly take page faults for user memory.
*/
get_task_struct(p);
write_unlock_irq(&tasklist_lock); write_unlock_irq(&tasklist_lock);
retval = ru ? getrusage(p, RUSAGE_BOTH, ru) : 0; retval = ru ? getrusage(p, RUSAGE_BOTH, ru) : 0;
if (!retval && stat_addr) if (!retval && stat_addr)
retval = put_user((p->exit_code << 8) | 0x7f, stat_addr); retval = put_user((exit_code << 8) | 0x7f, stat_addr);
if (!retval) { if (!retval)
p->exit_code = 0;
retval = p->pid; retval = p->pid;
} put_task_struct(p);
goto end_wait4; goto end_wait4;
case TASK_ZOMBIE: case TASK_ZOMBIE:
/* /*
...@@ -841,6 +928,13 @@ asmlinkage long sys_wait4(pid_t pid,unsigned int * stat_addr, int options, struc ...@@ -841,6 +928,13 @@ asmlinkage long sys_wait4(pid_t pid,unsigned int * stat_addr, int options, struc
state = xchg(&p->state, TASK_DEAD); state = xchg(&p->state, TASK_DEAD);
if (state != TASK_ZOMBIE) if (state != TASK_ZOMBIE)
continue; continue;
if (unlikely(p->exit_signal == -1))
/*
* This can only happen in a race with
* a ptraced thread dying on another
* processor.
*/
continue;
read_unlock(&tasklist_lock); read_unlock(&tasklist_lock);
retval = ru ? getrusage(p, RUSAGE_BOTH, ru) : 0; retval = ru ? getrusage(p, RUSAGE_BOTH, ru) : 0;
...@@ -857,11 +951,17 @@ asmlinkage long sys_wait4(pid_t pid,unsigned int * stat_addr, int options, struc ...@@ -857,11 +951,17 @@ asmlinkage long sys_wait4(pid_t pid,unsigned int * stat_addr, int options, struc
retval = p->pid; retval = p->pid;
if (p->real_parent != p->parent) { if (p->real_parent != p->parent) {
write_lock_irq(&tasklist_lock); write_lock_irq(&tasklist_lock);
/* Double-check with lock held. */
if (p->real_parent != p->parent) {
__ptrace_unlink(p); __ptrace_unlink(p);
do_notify_parent(p, SIGCHLD); do_notify_parent(
p, p->exit_signal);
p->state = TASK_ZOMBIE; p->state = TASK_ZOMBIE;
p = NULL;
}
write_unlock_irq(&tasklist_lock); write_unlock_irq(&tasklist_lock);
} else }
if (p != NULL)
release_task(p); release_task(p);
goto end_wait4; goto end_wait4;
default: default:
......
...@@ -680,6 +680,7 @@ static inline int copy_sighand(unsigned long clone_flags, struct task_struct * t ...@@ -680,6 +680,7 @@ static inline int copy_sighand(unsigned long clone_flags, struct task_struct * t
sig->group_exit = 0; sig->group_exit = 0;
sig->group_exit_code = 0; sig->group_exit_code = 0;
sig->group_exit_task = NULL; sig->group_exit_task = NULL;
sig->group_stop_count = 0;
memcpy(sig->action, current->sig->action, sizeof(sig->action)); memcpy(sig->action, current->sig->action, sizeof(sig->action));
sig->curr_target = NULL; sig->curr_target = NULL;
init_sigpending(&sig->shared_pending); init_sigpending(&sig->shared_pending);
...@@ -801,7 +802,7 @@ static struct task_struct *copy_process(unsigned long clone_flags, ...@@ -801,7 +802,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,
spin_lock_init(&p->alloc_lock); spin_lock_init(&p->alloc_lock);
spin_lock_init(&p->switch_lock); spin_lock_init(&p->switch_lock);
clear_tsk_thread_flag(p,TIF_SIGPENDING); clear_tsk_thread_flag(p, TIF_SIGPENDING);
init_sigpending(&p->pending); init_sigpending(&p->pending);
p->it_real_value = p->it_virt_value = p->it_prof_value = 0; p->it_real_value = p->it_virt_value = p->it_prof_value = 0;
...@@ -910,6 +911,7 @@ static struct task_struct *copy_process(unsigned long clone_flags, ...@@ -910,6 +911,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,
*/ */
if (sigismember(&current->pending.signal, SIGKILL)) { if (sigismember(&current->pending.signal, SIGKILL)) {
write_unlock_irq(&tasklist_lock); write_unlock_irq(&tasklist_lock);
retval = -EINTR;
goto bad_fork_cleanup_namespace; goto bad_fork_cleanup_namespace;
} }
...@@ -934,6 +936,17 @@ static struct task_struct *copy_process(unsigned long clone_flags, ...@@ -934,6 +936,17 @@ static struct task_struct *copy_process(unsigned long clone_flags,
} }
p->tgid = current->tgid; p->tgid = current->tgid;
p->group_leader = current->group_leader; p->group_leader = current->group_leader;
if (current->sig->group_stop_count > 0) {
/*
* There is an all-stop in progress for the group.
* We ourselves will stop as soon as we check signals.
* Make the new thread part of that group stop too.
*/
current->sig->group_stop_count++;
set_tsk_thread_flag(p, TIF_SIGPENDING);
}
spin_unlock(&current->sig->siglock); spin_unlock(&current->sig->siglock);
} }
...@@ -1036,8 +1049,13 @@ struct task_struct *do_fork(unsigned long clone_flags, ...@@ -1036,8 +1049,13 @@ struct task_struct *do_fork(unsigned long clone_flags,
init_completion(&vfork); init_completion(&vfork);
} }
if (p->ptrace & PT_PTRACED) if (p->ptrace & PT_PTRACED) {
send_sig(SIGSTOP, p, 1); /*
* We'll start up with an immediate SIGSTOP.
*/
sigaddset(&p->pending.signal, SIGSTOP);
set_tsk_thread_flag(p, TIF_SIGPENDING);
}
wake_up_forked_process(p); /* do this last */ wake_up_forked_process(p); /* do this last */
++total_forks; ++total_forks;
......
...@@ -55,7 +55,7 @@ int max_queued_signals = 1024; ...@@ -55,7 +55,7 @@ int max_queued_signals = 1024;
| SIGALRM | load-balance | kill-all | | SIGALRM | load-balance | kill-all |
| SIGTERM | load-balance | kill-all | | SIGTERM | load-balance | kill-all |
| SIGCHLD | load-balance | ignore | | SIGCHLD | load-balance | ignore |
| SIGCONT | specific | continue-all | | SIGCONT | load-balance | ignore |
| SIGSTOP | n/a | stop-all | | SIGSTOP | n/a | stop-all |
| SIGTSTP | load-balance | stop-all | | SIGTSTP | load-balance | stop-all |
| SIGTTIN | load-balance | stop-all | | SIGTTIN | load-balance | stop-all |
...@@ -98,26 +98,11 @@ int max_queued_signals = 1024; ...@@ -98,26 +98,11 @@ int max_queued_signals = 1024;
#endif #endif
#if SIGRTMIN > BITS_PER_LONG #if SIGRTMIN > BITS_PER_LONG
#define M(sig) (1ULL << (sig)) #define M(sig) (1ULL << ((sig)-1))
#else #else
#define M(sig) (1UL << (sig)) #define M(sig) (1UL << ((sig)-1))
#endif #endif
#define T(sig, mask) (M(sig) & mask) #define T(sig, mask) (M(sig) & (mask))
#define SIG_USER_SPECIFIC_MASK (\
M(SIGILL) | M(SIGTRAP) | M(SIGABRT) | M(SIGBUS) | \
M(SIGFPE) | M(SIGSEGV) | M(SIGPIPE) | M(SIGXFSZ) | \
M(SIGPROF) | M(SIGSYS) | M_SIGSTKFLT | M(SIGCONT) | \
M_SIGEMT )
#define SIG_USER_LOAD_BALANCE_MASK (\
M(SIGHUP) | M(SIGINT) | M(SIGQUIT) | M(SIGUSR1) | \
M(SIGUSR2) | M(SIGALRM) | M(SIGTERM) | M(SIGCHLD) | \
M(SIGURG) | M(SIGVTALRM) | M(SIGPOLL) | M(SIGWINCH) | \
M(SIGPWR) | M(SIGTSTP) | M(SIGTTIN) | M(SIGTTOU) )
#define SIG_KERNEL_SPECIFIC_MASK (\
M(SIGCHLD) | M(SIGURG) | M(SIGWINCH) )
#define SIG_KERNEL_BROADCAST_MASK (\ #define SIG_KERNEL_BROADCAST_MASK (\
M(SIGHUP) | M(SIGINT) | M(SIGQUIT) | M(SIGILL) | \ M(SIGHUP) | M(SIGINT) | M(SIGQUIT) | M(SIGILL) | \
...@@ -132,34 +117,37 @@ int max_queued_signals = 1024; ...@@ -132,34 +117,37 @@ int max_queued_signals = 1024;
#define SIG_KERNEL_ONLY_MASK (\ #define SIG_KERNEL_ONLY_MASK (\
M(SIGKILL) | M(SIGSTOP) ) M(SIGKILL) | M(SIGSTOP) )
#define SIG_KERNEL_STOP_MASK (\
M(SIGSTOP) | M(SIGTSTP) | M(SIGTTIN) | M(SIGTTOU) )
#define SIG_KERNEL_COREDUMP_MASK (\ #define SIG_KERNEL_COREDUMP_MASK (\
M(SIGQUIT) | M(SIGILL) | M(SIGTRAP) | M(SIGABRT) | \ M(SIGQUIT) | M(SIGILL) | M(SIGTRAP) | M(SIGABRT) | \
M(SIGFPE) | M(SIGSEGV) | M(SIGBUS) | M(SIGSYS) | \ M(SIGFPE) | M(SIGSEGV) | M(SIGBUS) | M(SIGSYS) | \
M(SIGXCPU) | M(SIGXFSZ) | M_SIGEMT ) M(SIGXCPU) | M(SIGXFSZ) | M_SIGEMT )
#define sig_user_specific(sig) \ #define SIG_KERNEL_IGNORE_MASK (\
(((sig) < SIGRTMIN) && T(sig, SIG_USER_SPECIFIC_MASK)) M(SIGCONT) | M(SIGCHLD) | M(SIGWINCH) | M(SIGURG) )
#define sig_user_load_balance(sig) \
(((sig) >= SIGRTMIN) || T(sig, SIG_USER_LOAD_BALANCE_MASK))
#define sig_kernel_specific(sig) \
(((sig) < SIGRTMIN) && T(sig, SIG_KERNEL_SPECIFIC_MASK))
#define sig_kernel_broadcast(sig) \
(((sig) >= SIGRTMIN) || T(sig, SIG_KERNEL_BROADCAST_MASK))
#define sig_kernel_only(sig) \ #define sig_kernel_only(sig) \
(((sig) < SIGRTMIN) && T(sig, SIG_KERNEL_ONLY_MASK)) (((sig) < SIGRTMIN) && T(sig, SIG_KERNEL_ONLY_MASK))
#define sig_kernel_coredump(sig) \ #define sig_kernel_coredump(sig) \
(((sig) < SIGRTMIN) && T(sig, SIG_KERNEL_COREDUMP_MASK)) (((sig) < SIGRTMIN) && T(sig, SIG_KERNEL_COREDUMP_MASK))
#define sig_kernel_ignore(sig) \
(((sig) < SIGRTMIN) && T(sig, SIG_KERNEL_IGNORE_MASK))
#define sig_kernel_stop(sig) \
(((sig) < SIGRTMIN) && T(sig, SIG_KERNEL_STOP_MASK))
#define sig_user_defined(t, sig) \ #define sig_user_defined(t, signr) \
(((t)->sig->action[(sig)-1].sa.sa_handler != SIG_DFL) && \ (((t)->sig->action[(signr)-1].sa.sa_handler != SIG_DFL) && \
((t)->sig->action[(sig)-1].sa.sa_handler != SIG_IGN)) ((t)->sig->action[(signr)-1].sa.sa_handler != SIG_IGN))
#define sig_ignored(t, sig) \ #define sig_ignored(t, signr) \
(((sig) != SIGCHLD) && \ (!((t)->ptrace & PT_PTRACED) && \
((t)->sig->action[(sig)-1].sa.sa_handler == SIG_IGN)) (t)->sig->action[(signr)-1].sa.sa_handler == SIG_IGN)
static int #define sig_fatal(t, signr) \
__send_sig_info(int sig, struct siginfo *info, struct task_struct *p); (!T(signr, SIG_KERNEL_IGNORE_MASK|SIG_KERNEL_STOP_MASK) && \
(t)->sig->action[(signr)-1].sa.sa_handler == SIG_DFL)
/* /*
* Re-calculate pending state from the set of locally pending * Re-calculate pending state from the set of locally pending
...@@ -193,9 +181,10 @@ static inline int has_pending_signals(sigset_t *signal, sigset_t *blocked) ...@@ -193,9 +181,10 @@ static inline int has_pending_signals(sigset_t *signal, sigset_t *blocked)
#define PENDING(p,b) has_pending_signals(&(p)->signal, (b)) #define PENDING(p,b) has_pending_signals(&(p)->signal, (b))
void recalc_sigpending_tsk(struct task_struct *t) inline void recalc_sigpending_tsk(struct task_struct *t)
{ {
if (PENDING(&t->pending, &t->blocked) || if (t->sig->group_stop_count > 0 ||
PENDING(&t->pending, &t->blocked) ||
PENDING(&t->sig->shared_pending, &t->blocked)) PENDING(&t->sig->shared_pending, &t->blocked))
set_tsk_thread_flag(t, TIF_SIGPENDING); set_tsk_thread_flag(t, TIF_SIGPENDING);
else else
...@@ -204,11 +193,7 @@ void recalc_sigpending_tsk(struct task_struct *t) ...@@ -204,11 +193,7 @@ void recalc_sigpending_tsk(struct task_struct *t)
void recalc_sigpending(void) void recalc_sigpending(void)
{ {
if (PENDING(&current->pending, &current->blocked) || recalc_sigpending_tsk(current);
PENDING(&current->sig->shared_pending, &current->blocked))
set_thread_flag(TIF_SIGPENDING);
else
clear_thread_flag(TIF_SIGPENDING);
} }
/* Given the mask, find the first available signal that should be serviced. */ /* Given the mask, find the first available signal that should be serviced. */
...@@ -337,23 +322,6 @@ flush_signal_handlers(struct task_struct *t) ...@@ -337,23 +322,6 @@ flush_signal_handlers(struct task_struct *t)
} }
} }
/*
* sig_exit - cause the current task to exit due to a signal.
*/
void
sig_exit(int sig, int exit_code, struct siginfo *info)
{
sigaddset(&current->pending.signal, sig);
recalc_sigpending();
current->flags |= PF_SIGNALED;
if (current->sig->group_exit)
exit_code = current->sig->group_exit_code;
do_exit(exit_code);
/* NOTREACHED */
}
/* Notify the system that a driver wants to block all signals for this /* Notify the system that a driver wants to block all signals for this
* process, and wants to be notified if any signals at all were to be * process, and wants to be notified if any signals at all were to be
...@@ -473,32 +441,74 @@ static int __dequeue_signal(struct sigpending *pending, sigset_t *mask, ...@@ -473,32 +441,74 @@ static int __dequeue_signal(struct sigpending *pending, sigset_t *mask,
*/ */
int dequeue_signal(sigset_t *mask, siginfo_t *info) int dequeue_signal(sigset_t *mask, siginfo_t *info)
{ {
int signr = __dequeue_signal(&current->pending, mask, info);
if (!signr)
signr = __dequeue_signal(&current->sig->shared_pending,
mask, info);
return signr;
}
/*
* Tell a process that it has a new active signal..
*
* NOTE! we rely on the previous spin_lock to
* lock interrupts for us! We can only be called with
* "siglock" held, and the local interrupt must
* have been disabled when that got acquired!
*
* No need to set need_resched since signal event passing
* goes through ->blocked
*/
inline void signal_wake_up(struct task_struct *t, int resume)
{
set_tsk_thread_flag(t,TIF_SIGPENDING);
/*
* If the task is running on a different CPU
* force a reschedule on the other CPU to make
* it notice the new signal quickly.
*
* The code below is a tad loose and might occasionally
* kick the wrong CPU if we catch the process in the
* process of changing - but no harm is done by that
* other than doing an extra (lightweight) IPI interrupt.
*/
if (t->state == TASK_RUNNING)
kick_if_running(t);
/* /*
* Here we handle shared pending signals. To implement the full * If resume is set, we want to wake it up in the TASK_STOPPED case.
* semantics we need to unqueue and resend them. It will likely * We don't check for TASK_STOPPED because there is a race with it
* get into our own pending queue. * executing another processor and just now entering stopped state.
* By calling wake_up_process any time resume is set, we ensure
* the process will wake up and handle its stop or death signal.
*/ */
if (current->sig->shared_pending.head) { if ((t->state & TASK_INTERRUPTIBLE) ||
int signr = __dequeue_signal(&current->sig->shared_pending, mask, info); (resume && t->state < TASK_ZOMBIE)) {
if (signr) wake_up_process(t);
__send_sig_info(signr, info, current); return;
} }
return __dequeue_signal(&current->pending, mask, info);
} }
static int rm_from_queue(int sig, struct sigpending *s) /*
* Remove signals in mask from the pending set and queue.
* Returns 1 if any signals were found.
*
* All callers must be holding the siglock.
*/
static int rm_from_queue(unsigned long mask, struct sigpending *s)
{ {
struct sigqueue *q, **pp; struct sigqueue *q, **pp;
if (!sigismember(&s->signal, sig)) if (!sigtestsetmask(&s->signal, mask))
return 0; return 0;
sigdelset(&s->signal, sig); sigdelsetmask(&s->signal, mask);
pp = &s->head; pp = &s->head;
while ((q = *pp) != NULL) { while ((q = *pp) != NULL) {
if (q->info.si_signo == sig) { if (q->info.si_signo < SIGRTMIN &&
(mask & sigmask (q->info.si_signo))) {
if ((*pp = q->next) == NULL) if ((*pp = q->next) == NULL)
s->tail = pp; s->tail = pp;
kmem_cache_free(sigqueue_cachep,q); kmem_cache_free(sigqueue_cachep,q);
...@@ -510,112 +520,101 @@ static int rm_from_queue(int sig, struct sigpending *s) ...@@ -510,112 +520,101 @@ static int rm_from_queue(int sig, struct sigpending *s)
return 1; return 1;
} }
/*
* Remove signal sig from t->pending.
* Returns 1 if sig was found.
*
* All callers must be holding the siglock.
*/
static int rm_sig_from_queue(int sig, struct task_struct *t)
{
return rm_from_queue(sig, &t->pending);
}
/* /*
* Bad permissions for sending the signal * Bad permissions for sending the signal
*/ */
static inline int bad_signal(int sig, struct siginfo *info, struct task_struct *t) static inline int check_kill_permission(int sig, struct siginfo *info,
struct task_struct *t)
{ {
return (!info || ((unsigned long)info != 1 && int error = -EINVAL;
if (sig < 0 || sig > _NSIG)
return error;
error = -EPERM;
if ((!info || ((unsigned long)info != 1 &&
(unsigned long)info != 2 && SI_FROMUSER(info))) (unsigned long)info != 2 && SI_FROMUSER(info)))
&& ((sig != SIGCONT) || (current->session != t->session)) && ((sig != SIGCONT) || (current->session != t->session))
&& (current->euid ^ t->suid) && (current->euid ^ t->uid) && (current->euid ^ t->suid) && (current->euid ^ t->uid)
&& (current->uid ^ t->suid) && (current->uid ^ t->uid) && (current->uid ^ t->suid) && (current->uid ^ t->uid)
&& !capable(CAP_KILL); && !capable(CAP_KILL))
return error;
return security_task_kill(t, info, sig);
} }
/* forward decl */
static void do_notify_parent_cldstop(struct task_struct *tsk,
struct task_struct *parent);
/* /*
* Signal type: * Handle magic process-wide effects of stop/continue signals, and SIGKILL.
* < 0 : global action (kill - spread to all non-blocked threads) * Unlike the signal actions, these happen immediately at signal-generation
* = 0 : ignored * time regardless of blocking, ignoring, or handling. This does the
* > 0 : wake up. * actual continuing for SIGCONT, but not the actual stopping for stop
* signals. The process stop is done as a signal action for SIG_DFL.
*/ */
static int signal_type(int sig, struct signal_struct *signals) static void handle_stop_signal(int sig, struct task_struct *p)
{ {
unsigned long handler; struct task_struct *t;
if (!signals)
return 0;
handler = (unsigned long) signals->action[sig-1].sa.sa_handler;
if (handler > 1)
return 1;
/* "Ignore" handler.. Illogical, but that has an implicit handler for SIGCHLD */
if (handler == 1)
return sig == SIGCHLD;
/* Default handler. Normally lethal, but.. */
switch (sig) {
/* Ignored */
case SIGCONT: case SIGWINCH:
case SIGCHLD: case SIGURG:
return 0;
/* Implicit behaviour */
case SIGTSTP: case SIGTTIN: case SIGTTOU:
return 1;
/* Implicit actions (kill or do special stuff) */ if (sig_kernel_stop(sig)) {
default: /*
return -1; * This is a stop signal. Remove SIGCONT from all queues.
*/
rm_from_queue(sigmask(SIGCONT), &p->sig->shared_pending);
t = p;
do {
rm_from_queue(sigmask(SIGCONT), &t->pending);
t = next_thread(t);
} while (t != p);
} }
} else if (sig == SIGCONT) {
/*
* Remove all stop signals from all queues,
/* * and wake all threads.
* Determine whether a signal should be posted or not.
*
* Signals with SIG_IGN can be ignored, except for the
* special case of a SIGCHLD.
*
* Some signals with SIG_DFL default to a non-action.
*/ */
static int ignored_signal(int sig, struct task_struct *t) if (unlikely(p->sig->group_stop_count > 0)) {
{ /*
/* Don't ignore traced or blocked signals */ * There was a group stop in progress. We'll
if ((t->ptrace & PT_PTRACED) || sigismember(&t->blocked, sig)) * pretend it finished before we got here. We are
return 0; * obliged to report it to the parent: if the
* SIGSTOP happened "after" this SIGCONT, then it
return signal_type(sig, t->sig) == 0; * would have cleared this pending SIGCONT. If it
} * happened "before" this SIGCONT, then the parent
* got the SIGCHLD about the stop finishing before
/* * the continue happened. We do the notification
* Handle TASK_STOPPED cases etc implicit behaviour * now, and it's as if the stop had finished and
* of certain magical signals. * the SIGCHLD was pending on entry to this kill.
* */
* SIGKILL gets spread out to every thread. p->sig->group_stop_count = 0;
if (p->ptrace & PT_PTRACED)
do_notify_parent_cldstop(p, p->parent);
else
do_notify_parent_cldstop(
p->group_leader,
p->group_leader->real_parent);
}
rm_from_queue(SIG_KERNEL_STOP_MASK, &p->sig->shared_pending);
t = p;
do {
rm_from_queue(SIG_KERNEL_STOP_MASK, &t->pending);
if (t->state == TASK_STOPPED) {
/*
* If there is a handler for SIGCONT, we
* must make sure that no thread returns to
* user mode before we post the signal, in
* case it was the only thread eligible to
* run the signal handler--then it must not
* do anything between resuming and running
* the handler. With the TIF_SIGPENDING flag
* set, the thread will pause and acquire the
* siglock that we hold now and until we've
* queued the pending signal.
*/ */
static void handle_stop_signal(int sig, struct task_struct *t) if (sig_user_defined(p, SIGCONT))
{ set_tsk_thread_flag(t, TIF_SIGPENDING);
switch (sig) { wake_up_process(t);
case SIGKILL: case SIGCONT: }
/* Wake up the process if stopped. */ t = next_thread(t);
if (t->state == TASK_STOPPED) } while (t != p);
wake_up_process(t);
t->exit_code = 0;
rm_sig_from_queue(SIGSTOP, t);
rm_sig_from_queue(SIGTSTP, t);
rm_sig_from_queue(SIGTTOU, t);
rm_sig_from_queue(SIGTTIN, t);
break;
case SIGSTOP: case SIGTSTP:
case SIGTTIN: case SIGTTOU:
/* If we're stopping again, cancel SIGCONT */
rm_sig_from_queue(SIGCONT, t);
break;
} }
} }
...@@ -678,51 +677,12 @@ static int send_signal(int sig, struct siginfo *info, struct sigpending *signals ...@@ -678,51 +677,12 @@ static int send_signal(int sig, struct siginfo *info, struct sigpending *signals
return 0; return 0;
} }
/* #define LEGACY_QUEUE(sigptr, sig) \
* Tell a process that it has a new active signal.. (((sig) < SIGRTMIN) && sigismember(&(sigptr)->signal, (sig)))
*
* NOTE! we rely on the previous spin_lock to
* lock interrupts for us! We can only be called with
* "siglock" held, and the local interrupt must
* have been disabled when that got acquired!
*
* No need to set need_resched since signal event passing
* goes through ->blocked
*/
inline void signal_wake_up(struct task_struct *t)
{
set_tsk_thread_flag(t,TIF_SIGPENDING);
/*
* If the task is running on a different CPU
* force a reschedule on the other CPU to make
* it notice the new signal quickly.
*
* The code below is a tad loose and might occasionally
* kick the wrong CPU if we catch the process in the
* process of changing - but no harm is done by that
* other than doing an extra (lightweight) IPI interrupt.
*/
if (t->state == TASK_RUNNING)
kick_if_running(t);
if (t->state & TASK_INTERRUPTIBLE) {
wake_up_process(t);
return;
}
}
static int deliver_signal(int sig, struct siginfo *info, struct task_struct *t)
{
int retval = send_signal(sig, info, &t->pending);
if (!retval && !sigismember(&t->blocked, sig))
signal_wake_up(t);
return retval;
}
static int static int
specific_send_sig_info(int sig, struct siginfo *info, struct task_struct *t, int shared) specific_send_sig_info(int sig, struct siginfo *info, struct task_struct *t)
{ {
int ret; int ret;
...@@ -732,49 +692,21 @@ specific_send_sig_info(int sig, struct siginfo *info, struct task_struct *t, int ...@@ -732,49 +692,21 @@ specific_send_sig_info(int sig, struct siginfo *info, struct task_struct *t, int
if (!spin_is_locked(&t->sig->siglock)) if (!spin_is_locked(&t->sig->siglock))
BUG(); BUG();
#endif #endif
ret = -EINVAL;
if (sig < 0 || sig > _NSIG)
goto out;
/* The somewhat baroque permissions check... */
ret = -EPERM;
if (bad_signal(sig, info, t))
goto out;
ret = security_task_kill(t, info, sig);
if (ret)
goto out;
/* The null signal is a permissions and process existence probe.
No signal is actually delivered. Same goes for zombies. */
ret = 0;
if (!sig || !t->sig)
goto out;
handle_stop_signal(sig, t); /* Short-circuit ignored signals. */
if (sig_ignored(t, sig))
/* Optimize away the signal, if it's a signal that can be return 0;
handled immediately (ie non-blocked and untraced) and
that is ignored (either explicitly or by default). */
if (ignored_signal(sig, t))
goto out;
#define LEGACY_QUEUE(sigptr, sig) \
(((sig) < SIGRTMIN) && sigismember(&(sigptr)->signal, (sig)))
if (!shared) {
/* Support queueing exactly one non-rt signal, so that we /* Support queueing exactly one non-rt signal, so that we
can get more detailed information about the cause of can get more detailed information about the cause of
the signal. */ the signal. */
if (LEGACY_QUEUE(&t->pending, sig)) if (LEGACY_QUEUE(&t->pending, sig))
goto out; return 0;
ret = send_signal(sig, info, &t->pending);
if (!ret && !sigismember(&t->blocked, sig))
signal_wake_up(t, sig == SIGKILL);
ret = deliver_signal(sig, info, t);
} else {
if (LEGACY_QUEUE(&t->sig->shared_pending, sig))
goto out;
ret = send_signal(sig, info, &t->sig->shared_pending);
}
out:
return ret; return ret;
} }
...@@ -794,26 +726,12 @@ force_sig_info(int sig, struct siginfo *info, struct task_struct *t) ...@@ -794,26 +726,12 @@ force_sig_info(int sig, struct siginfo *info, struct task_struct *t)
t->sig->action[sig-1].sa.sa_handler = SIG_DFL; t->sig->action[sig-1].sa.sa_handler = SIG_DFL;
sigdelset(&t->blocked, sig); sigdelset(&t->blocked, sig);
recalc_sigpending_tsk(t); recalc_sigpending_tsk(t);
ret = __send_sig_info(sig, info, t); ret = specific_send_sig_info(sig, info, t);
spin_unlock_irqrestore(&t->sig->siglock, flags); spin_unlock_irqrestore(&t->sig->siglock, flags);
return ret; return ret;
} }
static int
__specific_force_sig_info(int sig, struct task_struct *t)
{
if (!t->sig)
return -ESRCH;
if (t->sig->action[sig-1].sa.sa_handler == SIG_IGN)
t->sig->action[sig-1].sa.sa_handler = SIG_DFL;
sigdelset(&t->blocked, sig);
recalc_sigpending_tsk(t);
return specific_send_sig_info(sig, (void *)2, t, 0);
}
void void
force_sig_specific(int sig, struct task_struct *t) force_sig_specific(int sig, struct task_struct *t)
{ {
...@@ -824,157 +742,182 @@ force_sig_specific(int sig, struct task_struct *t) ...@@ -824,157 +742,182 @@ force_sig_specific(int sig, struct task_struct *t)
t->sig->action[sig-1].sa.sa_handler = SIG_DFL; t->sig->action[sig-1].sa.sa_handler = SIG_DFL;
sigdelset(&t->blocked, sig); sigdelset(&t->blocked, sig);
recalc_sigpending_tsk(t); recalc_sigpending_tsk(t);
specific_send_sig_info(sig, (void *)2, t, 0); specific_send_sig_info(sig, (void *)2, t);
spin_unlock_irqrestore(&t->sig->siglock, flags); spin_unlock_irqrestore(&t->sig->siglock, flags);
} }
#define can_take_signal(p, sig) \ /*
(((unsigned long) p->sig->action[sig-1].sa.sa_handler > 1) && \ * Test if P wants to take SIG. After we've checked all threads with this,
!sigismember(&p->blocked, sig) && (task_curr(p) || !signal_pending(p))) * it's equivalent to finding no threads not blocking SIG. Any threads not
* blocking SIG were ruled out because they are not running and already
* have pending signals. Such threads will dequeue from the shared queue
* as soon as they're available, so putting the signal on the shared queue
* will be equivalent to sending it to one such thread.
*/
#define wants_signal(sig, p) (!sigismember(&(p)->blocked, sig) \
&& (p)->state < TASK_STOPPED \
&& !((p)->flags & PF_EXITING) \
&& (task_curr(p) || !signal_pending(p)))
static inline static inline int
int load_balance_thread_group(struct task_struct *p, int sig, __group_send_sig_info(int sig, struct siginfo *info, struct task_struct *p)
struct siginfo *info)
{ {
struct task_struct *tmp; struct task_struct *t;
int ret; int ret;
#if CONFIG_SMP
if (!spin_is_locked(&p->sig->siglock))
BUG();
#endif
handle_stop_signal(sig, p);
/* Short-circuit ignored signals. */
if (sig_ignored(p, sig))
return 0;
if (LEGACY_QUEUE(&p->sig->shared_pending, sig))
/* This is a non-RT signal and we already have one queued. */
return 0;
/* /*
* if the specified thread is not blocking this signal * Put this signal on the shared-pending queue, or fail with EAGAIN.
* then deliver it. * We always use the shared queue for process-wide signals,
* to avoid several races.
*/ */
if (can_take_signal(p, sig)) ret = send_signal(sig, info, &p->sig->shared_pending);
return specific_send_sig_info(sig, info, p, 0); if (unlikely(ret))
return ret;
/* /*
* Now find a thread we can wake up to take the signal off the queue.
*
* If the main thread wants the signal, it gets first crack.
* Probably the least surprising to the average bear.
*/
if (p->state < TASK_ZOMBIE &&
(sig_kernel_only(sig) || wants_signal(sig, p)))
t = p;
else if (thread_group_empty(p))
/*
* There is just one thread and it does not need to be woken.
* It will dequeue unblocked signals before it runs again.
*/
return 0;
else {
/*
* Otherwise try to find a suitable thread. * Otherwise try to find a suitable thread.
* If no such thread is found then deliver to
* the original thread.
*/ */
t = p->sig->curr_target;
tmp = p->sig->curr_target; if (t == NULL)
if (!tmp || tmp->tgid != p->tgid)
/* restart balancing at this thread */ /* restart balancing at this thread */
p->sig->curr_target = p; t = p->sig->curr_target = p;
BUG_ON(t->tgid != p->tgid);
else for (;;) {
if (thread_group_empty(p))
BUG();
if (!tmp || tmp->tgid != p->tgid)
BUG();
while (!wants_signal(sig, t)) {
t = next_thread(t);
if (t == p->sig->curr_target)
/* /*
* Do not send signals that are ignored or blocked, * No thread needs to be woken.
* or to not-running threads that are overworked: * Any eligible threads will see
* the signal in the queue soon.
*/ */
if (!can_take_signal(tmp, sig)) { return 0;
tmp = next_thread(tmp);
p->sig->curr_target = tmp;
if (tmp == p)
break;
continue;
} }
ret = specific_send_sig_info(sig, info, tmp, 0); p->sig->curr_target = t;
return ret;
} }
/* /*
* No suitable thread was found - put the signal * Found a killable thread. If the signal will be fatal,
* into the shared-pending queue. * then start taking the whole group down immediately.
*/ */
return specific_send_sig_info(sig, info, p, 1); if (sig_fatal(p, sig) && !p->sig->group_exit &&
} !sigismember(&t->real_blocked, sig) &&
(sig == SIGKILL || !(t->ptrace & PT_PTRACED))) {
int __broadcast_thread_group(struct task_struct *p, int sig) /*
{ * This signal will be fatal to the whole group.
struct task_struct *tmp; */
struct list_head *l; if (!sig_kernel_coredump(sig)) {
struct pid *pid; /*
int err = 0; * Start a group exit and wake everybody up.
* This way we don't have other threads
for_each_task_pid(p->tgid, PIDTYPE_TGID, tmp, l, pid) * running and doing things after a slower
err = __specific_force_sig_info(sig, tmp); * thread has the fatal signal pending.
*/
return err; p->sig->group_exit = 1;
} p->sig->group_exit_code = sig;
p->sig->group_stop_count = 0;
t = p;
do {
sigaddset(&t->pending.signal, SIGKILL);
signal_wake_up(t, 1);
t = next_thread(t);
} while (t != p);
return 0;
}
struct task_struct * find_unblocked_thread(struct task_struct *p, int signr) /*
{ * There will be a core dump. We make all threads other
struct task_struct *tmp; * than the chosen one go into a group stop so that nothing
struct list_head *l; * happens until it gets scheduled, takes the signal off
struct pid *pid; * the shared queue, and does the core dump. This is a
* little more complicated than strictly necessary, but it
* keeps the signal state that winds up in the core dump
* unchanged from the death state, e.g. which thread had
* the core-dump signal unblocked.
*/
rm_from_queue(SIG_KERNEL_STOP_MASK, &t->pending);
rm_from_queue(SIG_KERNEL_STOP_MASK, &p->sig->shared_pending);
p->sig->group_stop_count = 0;
p->sig->group_exit_task = t;
t = p;
do {
p->sig->group_stop_count++;
signal_wake_up(t, 0);
t = next_thread(t);
} while (t != p);
wake_up_process(p->sig->group_exit_task);
return 0;
}
for_each_task_pid(p->tgid, PIDTYPE_TGID, tmp, l, pid) /*
if (!sigismember(&tmp->blocked, signr)) * The signal is already in the shared-pending queue.
return tmp; * Tell the chosen thread to wake up and dequeue it.
return NULL; */
signal_wake_up(t, sig == SIGKILL);
return 0;
} }
static int /*
__send_sig_info(int sig, struct siginfo *info, struct task_struct *p) * Nuke all other threads in the group.
*/
void zap_other_threads(struct task_struct *p)
{ {
struct task_struct *t; struct task_struct *t;
int ret = 0;
#if CONFIG_SMP
if (!spin_is_locked(&p->sig->siglock))
BUG();
#endif
/* not a thread group - normal signal behavior */
if (thread_group_empty(p) || !sig)
goto out_send;
if (sig_user_defined(p, sig)) {
if (sig_user_specific(sig))
goto out_send;
if (sig_user_load_balance(sig)) {
ret = load_balance_thread_group(p, sig, info);
goto out_unlock;
}
/* must not happen */ p->sig->group_stop_count = 0;
BUG();
}
/* optimize away ignored signals: */
if (sig_ignored(p, sig))
goto out_unlock;
if (sig_kernel_specific(sig) || if (thread_group_empty(p))
((p->ptrace & PT_PTRACED) && !sig_kernel_only(sig))) return;
goto out_send;
/* Does any of the threads unblock the signal? */ for (t = next_thread(p); t != p; t = next_thread(t)) {
t = find_unblocked_thread(p, sig); sigaddset(&t->pending.signal, SIGKILL);
if (!t) { rm_from_queue(SIG_KERNEL_STOP_MASK, &t->pending);
ret = specific_send_sig_info(sig, info, p, 1); signal_wake_up(t, 1);
goto out_unlock;
}
if (sigismember(&t->real_blocked,sig)) {
ret = specific_send_sig_info(sig, info, t, 0);
goto out_unlock;
} }
if (sig_kernel_broadcast(sig) || sig_kernel_coredump(sig)) {
ret = __broadcast_thread_group(p, sig);
goto out_unlock;
}
/* must not happen */
BUG();
out_send:
ret = specific_send_sig_info(sig, info, p, 0);
out_unlock:
return ret;
} }
int int
send_sig_info(int sig, struct siginfo *info, struct task_struct *p) group_send_sig_info(int sig, struct siginfo *info, struct task_struct *p)
{ {
unsigned long flags; unsigned long flags;
int ret; int ret;
spin_lock_irqsave(&p->sig->siglock, flags); ret = check_kill_permission(sig, info, p);
ret = __send_sig_info(sig, info, p); if (!ret && sig && p->sig) {
spin_unlock_irqrestore(&p->sig->siglock, flags); spin_lock_irqsave(&p->sig->siglock, flags);
ret = __group_send_sig_info(sig, info, p);
spin_unlock_irqrestore(&p->sig->siglock, flags);
}
return ret; return ret;
} }
...@@ -995,7 +938,7 @@ int __kill_pg_info(int sig, struct siginfo *info, pid_t pgrp) ...@@ -995,7 +938,7 @@ int __kill_pg_info(int sig, struct siginfo *info, pid_t pgrp)
return -EINVAL; return -EINVAL;
for_each_task_pid(pgrp, PIDTYPE_PGID, p, l, pid) { for_each_task_pid(pgrp, PIDTYPE_PGID, p, l, pid) {
err = send_sig_info(sig, info, p); err = group_send_sig_info(sig, info, p);
if (retval) if (retval)
retval = err; retval = err;
} }
...@@ -1037,7 +980,7 @@ kill_sl_info(int sig, struct siginfo *info, pid_t sid) ...@@ -1037,7 +980,7 @@ kill_sl_info(int sig, struct siginfo *info, pid_t sid)
for_each_task_pid(sid, PIDTYPE_SID, p, l, pid) { for_each_task_pid(sid, PIDTYPE_SID, p, l, pid) {
if (!p->leader) if (!p->leader)
continue; continue;
err = send_sig_info(sig, info, p); err = group_send_sig_info(sig, info, p);
if (retval) if (retval)
retval = err; retval = err;
} }
...@@ -1056,7 +999,7 @@ kill_proc_info(int sig, struct siginfo *info, pid_t pid) ...@@ -1056,7 +999,7 @@ kill_proc_info(int sig, struct siginfo *info, pid_t pid)
p = find_task_by_pid(pid); p = find_task_by_pid(pid);
error = -ESRCH; error = -ESRCH;
if (p) if (p)
error = send_sig_info(sig, info, p); error = group_send_sig_info(sig, info, p);
read_unlock(&tasklist_lock); read_unlock(&tasklist_lock);
return error; return error;
} }
...@@ -1079,8 +1022,8 @@ static int kill_something_info(int sig, struct siginfo *info, int pid) ...@@ -1079,8 +1022,8 @@ static int kill_something_info(int sig, struct siginfo *info, int pid)
read_lock(&tasklist_lock); read_lock(&tasklist_lock);
for_each_process(p) { for_each_process(p) {
if (p->pid > 1 && p != current) { if (p->pid > 1 && p->tgid != current->tgid) {
int err = send_sig_info(sig, info, p); int err = group_send_sig_info(sig, info, p);
++count; ++count;
if (err != -EPERM) if (err != -EPERM)
retval = err; retval = err;
...@@ -1099,6 +1042,22 @@ static int kill_something_info(int sig, struct siginfo *info, int pid) ...@@ -1099,6 +1042,22 @@ static int kill_something_info(int sig, struct siginfo *info, int pid)
* These are for backward compatibility with the rest of the kernel source. * These are for backward compatibility with the rest of the kernel source.
*/ */
int
send_sig_info(int sig, struct siginfo *info, struct task_struct *p)
{
/* XXX should nix these interfaces and update the kernel */
if (T(sig, SIG_KERNEL_BROADCAST_MASK))
/* XXX do callers really always hold the tasklist_lock?? */
return group_send_sig_info(sig, info, p);
else {
int error;
spin_lock_irq(&p->sig->siglock);
error = specific_send_sig_info(sig, info, p);
spin_unlock_irq(&p->sig->siglock);
return error;
}
}
int int
send_sig(int sig, struct task_struct *p, int priv) send_sig(int sig, struct task_struct *p, int priv)
{ {
...@@ -1133,9 +1092,10 @@ kill_proc(pid_t pid, int sig, int priv) ...@@ -1133,9 +1092,10 @@ kill_proc(pid_t pid, int sig, int priv)
* Joy. Or not. Pthread wants us to wake up every thread * Joy. Or not. Pthread wants us to wake up every thread
* in our parent group. * in our parent group.
*/ */
static inline void __wake_up_parent(struct task_struct *p) static inline void __wake_up_parent(struct task_struct *p,
struct task_struct *parent)
{ {
struct task_struct *parent = p->parent, *tsk = parent; struct task_struct *tsk = parent;
/* /*
* Fortunately this is not necessary for thread groups: * Fortunately this is not necessary for thread groups:
...@@ -1162,6 +1122,7 @@ void do_notify_parent(struct task_struct *tsk, int sig) ...@@ -1162,6 +1122,7 @@ void do_notify_parent(struct task_struct *tsk, int sig)
struct siginfo info; struct siginfo info;
unsigned long flags; unsigned long flags;
int why, status; int why, status;
struct signal_struct *psig;
if (sig == -1) if (sig == -1)
BUG(); BUG();
...@@ -1200,10 +1161,34 @@ void do_notify_parent(struct task_struct *tsk, int sig) ...@@ -1200,10 +1161,34 @@ void do_notify_parent(struct task_struct *tsk, int sig)
info.si_code = why; info.si_code = why;
info.si_status = status; info.si_status = status;
spin_lock_irqsave(&tsk->parent->sig->siglock, flags); psig = tsk->parent->sig;
__send_sig_info(sig, &info, tsk->parent); spin_lock_irqsave(&psig->siglock, flags);
__wake_up_parent(tsk); if (sig == SIGCHLD && tsk->state != TASK_STOPPED &&
spin_unlock_irqrestore(&tsk->parent->sig->siglock, flags); (psig->action[SIGCHLD-1].sa.sa_handler == SIG_IGN ||
(psig->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDWAIT))) {
/*
* We are exiting and our parent doesn't care. POSIX.1
* defines special semantics for setting SIGCHLD to SIG_IGN
* or setting the SA_NOCLDWAIT flag: we should be reaped
* automatically and not left for our parent's wait4 call.
* Rather than having the parent do it as a magic kind of
* signal handler, we just set this to tell do_exit that we
* can be cleaned up without becoming a zombie. Note that
* we still call __wake_up_parent in this case, because a
* blocked sys_wait4 might now return -ECHILD.
*
* Whether we send SIGCHLD or not for SA_NOCLDWAIT
* is implementation-defined: we do (if you don't want
* it, just use SIG_IGN instead).
*/
tsk->exit_signal = -1;
if (psig->action[SIGCHLD-1].sa.sa_handler == SIG_IGN)
sig = 0;
}
if (sig > 0 && sig <= _NSIG)
__group_send_sig_info(sig, &info, tsk->parent);
__wake_up_parent(tsk, tsk->parent);
spin_unlock_irqrestore(&psig->siglock, flags);
} }
...@@ -1224,6 +1209,149 @@ notify_parent(struct task_struct *tsk, int sig) ...@@ -1224,6 +1209,149 @@ notify_parent(struct task_struct *tsk, int sig)
} }
} }
static void
do_notify_parent_cldstop(struct task_struct *tsk, struct task_struct *parent)
{
struct siginfo info;
unsigned long flags;
info.si_signo = SIGCHLD;
info.si_errno = 0;
info.si_pid = tsk->pid;
info.si_uid = tsk->uid;
/* FIXME: find out whether or not this is supposed to be c*time. */
info.si_utime = tsk->utime;
info.si_stime = tsk->stime;
info.si_status = tsk->exit_code & 0x7f;
info.si_code = CLD_STOPPED;
spin_lock_irqsave(&parent->sig->siglock, flags);
if (parent->sig->action[SIGCHLD-1].sa.sa_handler != SIG_IGN &&
!(parent->sig->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDSTOP))
__group_send_sig_info(SIGCHLD, &info, parent);
/*
* Even if SIGCHLD is not generated, we must wake up wait4 calls.
*/
__wake_up_parent(tsk, parent);
spin_unlock_irqrestore(&parent->sig->siglock, flags);
}
static void
finish_stop(int stop_count)
{
/*
* If there are no other threads in the group, or if there is
* a group stop in progress and we are the last to stop,
* report to the parent. When ptraced, every thread reports itself.
*/
if (stop_count < 0 || (current->ptrace & PT_PTRACED)) {
read_lock(&tasklist_lock);
do_notify_parent_cldstop(current, current->parent);
read_unlock(&tasklist_lock);
}
else if (stop_count == 0) {
read_lock(&tasklist_lock);
do_notify_parent_cldstop(current->group_leader,
current->group_leader->real_parent);
read_unlock(&tasklist_lock);
}
schedule();
/*
* Now we don't run again until continued.
*/
current->exit_code = 0;
}
/*
* This performs the stopping for SIGSTOP and other stop signals.
* We have to stop all threads in the thread group.
*/
static void
do_signal_stop(int signr)
{
struct signal_struct *sig = current->sig;
int stop_count = -1;
if (sig->group_stop_count > 0) {
/*
* There is a group stop in progress. We don't need to
* start another one.
*/
spin_lock_irq(&sig->siglock);
if (unlikely(sig->group_stop_count == 0)) {
BUG_ON(!sig->group_exit);
spin_unlock_irq(&sig->siglock);
return;
}
signr = sig->group_exit_code;
stop_count = --sig->group_stop_count;
current->exit_code = signr;
set_current_state(TASK_STOPPED);
spin_unlock_irq(&sig->siglock);
}
else if (thread_group_empty(current)) {
/*
* No locks needed in this case.
*/
current->exit_code = signr;
set_current_state(TASK_STOPPED);
}
else {
/*
* There is no group stop already in progress.
* We must initiate one now.
*/
struct task_struct *t;
read_lock(&tasklist_lock);
spin_lock_irq(&sig->siglock);
if (unlikely(sig->group_exit)) {
/*
* There is a group exit in progress now.
* We'll just ignore the stop and process the
* associated fatal signal.
*/
spin_unlock_irq(&sig->siglock);
read_unlock(&tasklist_lock);
return;
}
if (sig->group_stop_count == 0) {
sig->group_exit_code = signr;
stop_count = 0;
for (t = next_thread(current); t != current;
t = next_thread(t))
/*
* Setting state to TASK_STOPPED for a group
* stop is always done with the siglock held,
* so this check has no races.
*/
if (t->state < TASK_STOPPED) {
stop_count++;
signal_wake_up(t, 0);
}
sig->group_stop_count = stop_count;
}
else {
/* A race with another thread while unlocked. */
signr = sig->group_exit_code;
stop_count = --sig->group_stop_count;
}
current->exit_code = signr;
set_current_state(TASK_STOPPED);
spin_unlock_irq(&sig->siglock);
read_unlock(&tasklist_lock);
}
finish_stop(stop_count);
}
#ifndef HAVE_ARCH_GET_SIGNAL_TO_DELIVER #ifndef HAVE_ARCH_GET_SIGNAL_TO_DELIVER
int get_signal_to_deliver(siginfo_t *info, struct pt_regs *regs) int get_signal_to_deliver(siginfo_t *info, struct pt_regs *regs)
...@@ -1235,6 +1363,28 @@ int get_signal_to_deliver(siginfo_t *info, struct pt_regs *regs) ...@@ -1235,6 +1363,28 @@ int get_signal_to_deliver(siginfo_t *info, struct pt_regs *regs)
struct k_sigaction *ka; struct k_sigaction *ka;
spin_lock_irq(&current->sig->siglock); spin_lock_irq(&current->sig->siglock);
if (unlikely(current->sig->group_stop_count > 0)) {
int stop_count;
if (current->sig->group_exit_task == current) {
/*
* Group stop is so we can do a core dump.
*/
current->sig->group_exit_task = NULL;
goto dequeue;
}
/*
* There is a group stop in progress. We stop
* without any associated signal being in our queue.
*/
stop_count = --current->sig->group_stop_count;
signr = current->sig->group_exit_code;
current->exit_code = signr;
set_current_state(TASK_STOPPED);
spin_unlock_irq(&current->sig->siglock);
finish_stop(stop_count);
continue;
}
dequeue:
signr = dequeue_signal(mask, info); signr = dequeue_signal(mask, info);
spin_unlock_irq(&current->sig->siglock); spin_unlock_irq(&current->sig->siglock);
...@@ -1242,6 +1392,16 @@ int get_signal_to_deliver(siginfo_t *info, struct pt_regs *regs) ...@@ -1242,6 +1392,16 @@ int get_signal_to_deliver(siginfo_t *info, struct pt_regs *regs)
break; break;
if ((current->ptrace & PT_PTRACED) && signr != SIGKILL) { if ((current->ptrace & PT_PTRACED) && signr != SIGKILL) {
/*
* If there is a group stop in progress,
* we must participate in the bookkeeping.
*/
if (current->sig->group_stop_count > 0) {
spin_lock_irq(&current->sig->siglock);
--current->sig->group_stop_count;
spin_unlock_irq(&current->sig->siglock);
}
/* Let the debugger run. */ /* Let the debugger run. */
current->exit_code = signr; current->exit_code = signr;
set_current_state(TASK_STOPPED); set_current_state(TASK_STOPPED);
...@@ -1254,10 +1414,6 @@ int get_signal_to_deliver(siginfo_t *info, struct pt_regs *regs) ...@@ -1254,10 +1414,6 @@ int get_signal_to_deliver(siginfo_t *info, struct pt_regs *regs)
continue; continue;
current->exit_code = 0; current->exit_code = 0;
/* The debugger continued. Ignore SIGSTOP. */
if (signr == SIGSTOP)
continue;
/* Update the siginfo structure. Is this good? */ /* Update the siginfo structure. Is this good? */
if (signr != info->si_signo) { if (signr != info->si_signo) {
info->si_signo = signr; info->si_signo = signr;
...@@ -1269,61 +1425,69 @@ int get_signal_to_deliver(siginfo_t *info, struct pt_regs *regs) ...@@ -1269,61 +1425,69 @@ int get_signal_to_deliver(siginfo_t *info, struct pt_regs *regs)
/* If the (new) signal is now blocked, requeue it. */ /* If the (new) signal is now blocked, requeue it. */
if (sigismember(&current->blocked, signr)) { if (sigismember(&current->blocked, signr)) {
send_sig_info(signr, info, current); spin_lock_irq(&current->sig->siglock);
specific_send_sig_info(signr, info, current);
spin_unlock_irq(&current->sig->siglock);
continue; continue;
} }
} }
ka = &current->sig->action[signr-1]; ka = &current->sig->action[signr-1];
if (ka->sa.sa_handler == SIG_IGN) { if (ka->sa.sa_handler == SIG_IGN) /* Do nothing. */
if (signr != SIGCHLD)
continue;
/* Check for SIGCHLD: it's special. */
while (sys_wait4(-1, NULL, WNOHANG, NULL) > 0)
/* nothing */;
continue; continue;
} if (ka->sa.sa_handler != SIG_DFL) /* Run the handler. */
return signr;
if (ka->sa.sa_handler == SIG_DFL) { /*
int exit_code = signr; * Now we are doing the default action for this signal.
*/
if (sig_kernel_ignore(signr)) /* Default is nothing. */
continue;
/* Init gets no signals it doesn't want. */ /* Init gets no signals it doesn't want. */
if (current->pid == 1) if (current->pid == 1)
continue; continue;
switch (signr) { if (sig_kernel_stop(signr)) {
case SIGCONT: case SIGCHLD: case SIGWINCH: case SIGURG: /*
continue; * The default action is to stop all threads in
* the thread group. The job control signals
case SIGTSTP: case SIGTTIN: case SIGTTOU: * do nothing in an orphaned pgrp, but SIGSTOP
if (is_orphaned_pgrp(current->pgrp)) * always works.
continue; */
/* FALLTHRU */ if (signr == SIGSTOP ||
!is_orphaned_pgrp(current->pgrp))
case SIGSTOP: { do_signal_stop(signr);
struct signal_struct *sig;
set_current_state(TASK_STOPPED);
current->exit_code = signr;
sig = current->parent->sig;
if (sig && !(sig->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDSTOP))
notify_parent(current, SIGCHLD);
schedule();
continue; continue;
} }
case SIGQUIT: case SIGILL: case SIGTRAP: /*
case SIGABRT: case SIGFPE: case SIGSEGV: * Anything else is fatal, maybe with a core dump.
case SIGBUS: case SIGSYS: case SIGXCPU: case SIGXFSZ: */
if (do_coredump(signr, exit_code, regs)) current->flags |= PF_SIGNALED;
exit_code |= 0x80; if (sig_kernel_coredump(signr) &&
/* FALLTHRU */ do_coredump(signr, signr, regs)) {
/*
default: * That killed all other threads in the group and
sig_exit(signr, exit_code, info); * synchronized with their demise, so there can't
* be any more left to kill now. The group_exit
* flags are set by do_coredump. Note that
* thread_group_empty won't always be true yet,
* because those threads were blocked in __exit_mm
* and we just let them go to finish dying.
*/
const int code = signr | 0x80;
BUG_ON(!current->sig->group_exit);
BUG_ON(current->sig->group_exit_code != code);
do_exit(code);
/* NOTREACHED */ /* NOTREACHED */
} }
}
return signr; /*
* Death signals, no core dump.
*/
do_group_exit(signr);
/* NOTREACHED */
} }
return 0; return 0;
} }
...@@ -1435,12 +1599,17 @@ long do_sigpending(void *set, unsigned long sigsetsize) ...@@ -1435,12 +1599,17 @@ long do_sigpending(void *set, unsigned long sigsetsize)
goto out; goto out;
spin_lock_irq(&current->sig->siglock); spin_lock_irq(&current->sig->siglock);
sigandsets(&pending, &current->blocked, &current->pending.signal); sigorsets(&pending, &current->pending.signal,
&current->sig->shared_pending.signal);
spin_unlock_irq(&current->sig->siglock); spin_unlock_irq(&current->sig->siglock);
/* Outside the lock because only this thread touches it. */
sigandsets(&pending, &current->blocked, &pending);
error = -EFAULT; error = -EFAULT;
if (!copy_to_user(set, &pending, sigsetsize)) if (!copy_to_user(set, &pending, sigsetsize))
error = 0; error = 0;
out: out:
return error; return error;
} }
...@@ -1628,9 +1797,17 @@ sys_tkill(int pid, int sig) ...@@ -1628,9 +1797,17 @@ sys_tkill(int pid, int sig)
p = find_task_by_pid(pid); p = find_task_by_pid(pid);
error = -ESRCH; error = -ESRCH;
if (p) { if (p) {
spin_lock_irq(&p->sig->siglock); error = check_kill_permission(sig, &info, p);
error = specific_send_sig_info(sig, &info, p, 0); /*
spin_unlock_irq(&p->sig->siglock); * The null signal is a permissions and process existence
* probe. No signal is actually delivered.
*/
if (!error && sig && p->sig) {
spin_lock_irq(&p->sig->siglock);
handle_stop_signal(sig, p);
error = specific_send_sig_info(sig, &info, p);
spin_unlock_irq(&p->sig->siglock);
}
} }
read_unlock(&tasklist_lock); read_unlock(&tasklist_lock);
return error; return error;
...@@ -1664,7 +1841,17 @@ do_sigaction(int sig, const struct k_sigaction *act, struct k_sigaction *oact) ...@@ -1664,7 +1841,17 @@ do_sigaction(int sig, const struct k_sigaction *act, struct k_sigaction *oact)
k = &current->sig->action[sig-1]; k = &current->sig->action[sig-1];
read_lock(&tasklist_lock);
spin_lock_irq(&current->sig->siglock); spin_lock_irq(&current->sig->siglock);
if (signal_pending(current)) {
/*
* If there might be a fatal signal pending on multiple
* threads, make sure we take it before changing the action.
*/
spin_unlock_irq(&current->sig->siglock);
read_unlock(&tasklist_lock);
return -ERESTARTSYS;
}
if (oact) if (oact)
*oact = *k; *oact = *k;
...@@ -1683,25 +1870,22 @@ do_sigaction(int sig, const struct k_sigaction *act, struct k_sigaction *oact) ...@@ -1683,25 +1870,22 @@ do_sigaction(int sig, const struct k_sigaction *act, struct k_sigaction *oact)
* pending and whose default action is to ignore the signal * pending and whose default action is to ignore the signal
* (for example, SIGCHLD), shall cause the pending signal to * (for example, SIGCHLD), shall cause the pending signal to
* be discarded, whether or not it is blocked" * be discarded, whether or not it is blocked"
*
* Note the silly behaviour of SIGCHLD: SIG_IGN means that the
* signal isn't actually ignored, but does automatic child
* reaping, while SIG_DFL is explicitly said by POSIX to force
* the signal to be ignored.
*/ */
if (k->sa.sa_handler == SIG_IGN if (k->sa.sa_handler == SIG_IGN ||
|| (k->sa.sa_handler == SIG_DFL (k->sa.sa_handler == SIG_DFL && sig_kernel_ignore(sig))) {
&& (sig == SIGCONT || struct task_struct *t = current;
sig == SIGCHLD || rm_from_queue(sigmask(sig), &t->sig->shared_pending);
sig == SIGWINCH || do {
sig == SIGURG))) { rm_from_queue(sigmask(sig), &t->pending);
if (rm_sig_from_queue(sig, current)) recalc_sigpending_tsk(t);
recalc_sigpending(); t = next_thread(t);
} while (t != current);
} }
} }
spin_unlock_irq(&current->sig->siglock); spin_unlock_irq(&current->sig->siglock);
read_unlock(&tasklist_lock);
return 0; return 0;
} }
......
...@@ -65,7 +65,6 @@ ...@@ -65,7 +65,6 @@
#include <asm/pgtable.h> #include <asm/pgtable.h>
#include <asm/io.h> #include <asm/io.h>
extern void signal_wake_up(struct task_struct *t);
extern int sys_sync(void); extern int sys_sync(void);
unsigned char software_suspend_enabled = 0; unsigned char software_suspend_enabled = 0;
...@@ -220,7 +219,7 @@ int freeze_processes(void) ...@@ -220,7 +219,7 @@ int freeze_processes(void)
without locking */ without locking */
p->flags |= PF_FREEZE; p->flags |= PF_FREEZE;
spin_lock_irqsave(&p->sig->siglock, flags); spin_lock_irqsave(&p->sig->siglock, flags);
signal_wake_up(p); signal_wake_up(p, 0);
spin_unlock_irqrestore(&p->sig->siglock, flags); spin_unlock_irqrestore(&p->sig->siglock, flags);
todo++; todo++;
} while_each_thread(g, p); } while_each_thread(g, p);
......
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