diff options
Diffstat (limited to 'kernel/signal.c')
| -rw-r--r-- | kernel/signal.c | 108 | 
1 files changed, 52 insertions, 56 deletions
diff --git a/kernel/signal.c b/kernel/signal.c index ded28b91fa5..a4077e90f19 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -33,6 +33,8 @@  #include <linux/uprobes.h>  #include <linux/compat.h>  #include <linux/cn_proc.h> +#include <linux/compiler.h> +  #define CREATE_TRACE_POINTS  #include <trace/events/signal.h> @@ -275,6 +277,7 @@ void task_clear_jobctl_trapping(struct task_struct *task)  {  	if (unlikely(task->jobctl & JOBCTL_TRAPPING)) {  		task->jobctl &= ~JOBCTL_TRAPPING; +		smp_mb();	/* advised by wake_up_bit() */  		wake_up_bit(&task->jobctl, JOBCTL_TRAPPING_BIT);  	}  } @@ -703,11 +706,8 @@ void signal_wake_up_state(struct task_struct *t, unsigned int state)   * Returns 1 if any signals were found.   *   * All callers must be holding the siglock. - * - * This version takes a sigset mask and looks at all signals, - * not just those in the first mask word.   */ -static int rm_from_queue_full(sigset_t *mask, struct sigpending *s) +static int flush_sigqueue_mask(sigset_t *mask, struct sigpending *s)  {  	struct sigqueue *q, *n;  	sigset_t m; @@ -725,29 +725,6 @@ static int rm_from_queue_full(sigset_t *mask, struct sigpending *s)  	}  	return 1;  } -/* - * 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, *n; - -	if (!sigtestsetmask(&s->signal, mask)) -		return 0; - -	sigdelsetmask(&s->signal, mask); -	list_for_each_entry_safe(q, n, &s->list, list) { -		if (q->info.si_signo < SIGRTMIN && -		    (mask & sigmask(q->info.si_signo))) { -			list_del_init(&q->list); -			__sigqueue_free(q); -		} -	} -	return 1; -}  static inline int is_si_special(const struct siginfo *info)  { @@ -859,6 +836,7 @@ static bool prepare_signal(int sig, struct task_struct *p, bool force)  {  	struct signal_struct *signal = p->signal;  	struct task_struct *t; +	sigset_t flush;  	if (signal->flags & (SIGNAL_GROUP_EXIT | SIGNAL_GROUP_COREDUMP)) {  		if (signal->flags & SIGNAL_GROUP_COREDUMP) @@ -870,26 +848,25 @@ static bool prepare_signal(int sig, struct task_struct *p, bool force)  		/*  		 * This is a stop signal.  Remove SIGCONT from all queues.  		 */ -		rm_from_queue(sigmask(SIGCONT), &signal->shared_pending); -		t = p; -		do { -			rm_from_queue(sigmask(SIGCONT), &t->pending); -		} while_each_thread(p, t); +		siginitset(&flush, sigmask(SIGCONT)); +		flush_sigqueue_mask(&flush, &signal->shared_pending); +		for_each_thread(p, t) +			flush_sigqueue_mask(&flush, &t->pending);  	} else if (sig == SIGCONT) {  		unsigned int why;  		/*  		 * Remove all stop signals from all queues, wake all threads.  		 */ -		rm_from_queue(SIG_KERNEL_STOP_MASK, &signal->shared_pending); -		t = p; -		do { +		siginitset(&flush, SIG_KERNEL_STOP_MASK); +		flush_sigqueue_mask(&flush, &signal->shared_pending); +		for_each_thread(p, t) { +			flush_sigqueue_mask(&flush, &t->pending);  			task_clear_jobctl_pending(t, JOBCTL_STOP_PENDING); -			rm_from_queue(SIG_KERNEL_STOP_MASK, &t->pending);  			if (likely(!(t->ptrace & PT_SEIZED)))  				wake_up_state(t, __TASK_STOPPED);  			else  				ptrace_trap_notify(t); -		} while_each_thread(p, t); +		}  		/*  		 * Notify the parent with CLD_CONTINUED if we were stopped. @@ -2047,8 +2024,8 @@ static bool do_signal_stop(int signr)  		if (task_set_jobctl_pending(current, signr | gstop))  			sig->group_stop_count++; -		for (t = next_thread(current); t != current; -		     t = next_thread(t)) { +		t = current; +		while_each_thread(current, t) {  			/*  			 * Setting state to TASK_STOPPED for a group  			 * stop is always done with the siglock held, @@ -2382,7 +2359,7 @@ relock:   * @regs:		user register state   * @stepping:		nonzero if debugger single-step or block-step in use   * - * This function should be called when a signal has succesfully been + * This function should be called when a signal has successfully been   * delivered. It updates the blocked signals accordingly (@ka->sa.sa_mask   * is always blocked, and the signal itself is blocked unless %SA_NODEFER   * is set in @ka->sa.sa_flags.  Tracing is notified. @@ -2723,7 +2700,7 @@ COMPAT_SYSCALL_DEFINE2(rt_sigpending, compat_sigset_t __user *, uset,  #ifndef HAVE_ARCH_COPY_SIGINFO_TO_USER -int copy_siginfo_to_user(siginfo_t __user *to, siginfo_t *from) +int copy_siginfo_to_user(siginfo_t __user *to, const siginfo_t *from)  {  	int err; @@ -2852,7 +2829,7 @@ int do_sigtimedwait(const sigset_t *which, siginfo_t *info,  		spin_lock_irq(&tsk->sighand->siglock);  		__set_task_blocked(tsk, &tsk->real_blocked); -		siginitset(&tsk->real_blocked, 0); +		sigemptyset(&tsk->real_blocked);  		sig = dequeue_signal(tsk, &mask, info);  	}  	spin_unlock_irq(&tsk->sighand->siglock); @@ -3089,18 +3066,39 @@ COMPAT_SYSCALL_DEFINE4(rt_tgsigqueueinfo,  }  #endif +/* + * For kthreads only, must not be used if cloned with CLONE_SIGHAND + */ +void kernel_sigaction(int sig, __sighandler_t action) +{ +	spin_lock_irq(¤t->sighand->siglock); +	current->sighand->action[sig - 1].sa.sa_handler = action; +	if (action == SIG_IGN) { +		sigset_t mask; + +		sigemptyset(&mask); +		sigaddset(&mask, sig); + +		flush_sigqueue_mask(&mask, ¤t->signal->shared_pending); +		flush_sigqueue_mask(&mask, ¤t->pending); +		recalc_sigpending(); +	} +	spin_unlock_irq(¤t->sighand->siglock); +} +EXPORT_SYMBOL(kernel_sigaction); +  int do_sigaction(int sig, struct k_sigaction *act, struct k_sigaction *oact)  { -	struct task_struct *t = current; +	struct task_struct *p = current, *t;  	struct k_sigaction *k;  	sigset_t mask;  	if (!valid_signal(sig) || sig < 1 || (act && sig_kernel_only(sig)))  		return -EINVAL; -	k = &t->sighand->action[sig-1]; +	k = &p->sighand->action[sig-1]; -	spin_lock_irq(¤t->sighand->siglock); +	spin_lock_irq(&p->sighand->siglock);  	if (oact)  		*oact = *k; @@ -3119,22 +3117,20 @@ int do_sigaction(int sig, struct k_sigaction *act, struct k_sigaction *oact)  		 *   (for example, SIGCHLD), shall cause the pending signal to  		 *   be discarded, whether or not it is blocked"  		 */ -		if (sig_handler_ignored(sig_handler(t, sig), sig)) { +		if (sig_handler_ignored(sig_handler(p, sig), sig)) {  			sigemptyset(&mask);  			sigaddset(&mask, sig); -			rm_from_queue_full(&mask, &t->signal->shared_pending); -			do { -				rm_from_queue_full(&mask, &t->pending); -				t = next_thread(t); -			} while (t != current); +			flush_sigqueue_mask(&mask, &p->signal->shared_pending); +			for_each_thread(p, t) +				flush_sigqueue_mask(&mask, &t->pending);  		}  	} -	spin_unlock_irq(¤t->sighand->siglock); +	spin_unlock_irq(&p->sighand->siglock);  	return 0;  } -static int  +static int  do_sigaltstack (const stack_t __user *uss, stack_t __user *uoss, unsigned long sp)  {  	stack_t oss; @@ -3495,7 +3491,7 @@ COMPAT_SYSCALL_DEFINE3(sigaction, int, sig,  }  #endif -#ifdef __ARCH_WANT_SYS_SGETMASK +#ifdef CONFIG_SGETMASK_SYSCALL  /*   * For backwards compatibility.  Functionality superseded by sigprocmask. @@ -3516,7 +3512,7 @@ SYSCALL_DEFINE1(ssetmask, int, newmask)  	return old;  } -#endif /* __ARCH_WANT_SGETMASK */ +#endif /* CONFIG_SGETMASK_SYSCALL */  #ifdef __ARCH_WANT_SYS_SIGNAL  /* @@ -3619,7 +3615,7 @@ SYSCALL_DEFINE3(sigsuspend, int, unused1, int, unused2, old_sigset_t, mask)  }  #endif -__attribute__((weak)) const char *arch_vma_name(struct vm_area_struct *vma) +__weak const char *arch_vma_name(struct vm_area_struct *vma)  {  	return NULL;  }  | 
