diff options
| author | Russell King <[email protected]> | 2009-12-06 17:00:33 +0000 | 
|---|---|---|
| committer | Russell King <[email protected]> | 2009-12-06 17:00:33 +0000 | 
| commit | 3d14b5beba35250c548d3851a2b84fce742d8311 (patch) | |
| tree | 065e3d93c3fcbc5ee4c44fa78662393cddbdf6de /kernel/signal.c | |
| parent | 0719dc341389882cc834ed18fc9b7fc6006b2b85 (diff) | |
| parent | 1bf8e6219552d5dd27012d567ec8c4bb9c2d86b4 (diff) | |
Merge branch 'sa1100' into devel
Diffstat (limited to 'kernel/signal.c')
| -rw-r--r-- | kernel/signal.c | 73 | 
1 files changed, 53 insertions, 20 deletions
| diff --git a/kernel/signal.c b/kernel/signal.c index 6705320784fd..6b982f2cf524 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -22,12 +22,14 @@  #include <linux/ptrace.h>  #include <linux/signal.h>  #include <linux/signalfd.h> +#include <linux/ratelimit.h>  #include <linux/tracehook.h>  #include <linux/capability.h>  #include <linux/freezer.h>  #include <linux/pid_namespace.h>  #include <linux/nsproxy.h> -#include <trace/events/sched.h> +#define CREATE_TRACE_POINTS +#include <trace/events/signal.h>  #include <asm/param.h>  #include <asm/uaccess.h> @@ -41,6 +43,8 @@  static struct kmem_cache *sigqueue_cachep; +int print_fatal_signals __read_mostly; +  static void __user *sig_handler(struct task_struct *t, int sig)  {  	return t->sighand->action[sig - 1].sa.sa_handler; @@ -159,7 +163,7 @@ int next_signal(struct sigpending *pending, sigset_t *mask)  {  	unsigned long i, *s, *m, x;  	int sig = 0; -	 +  	s = pending->signal.sig;  	m = mask->sig;  	switch (_NSIG_WORDS) { @@ -184,17 +188,31 @@ int next_signal(struct sigpending *pending, sigset_t *mask)  			sig = ffz(~x) + 1;  		break;  	} -	 +  	return sig;  } +static inline void print_dropped_signal(int sig) +{ +	static DEFINE_RATELIMIT_STATE(ratelimit_state, 5 * HZ, 10); + +	if (!print_fatal_signals) +		return; + +	if (!__ratelimit(&ratelimit_state)) +		return; + +	printk(KERN_INFO "%s/%d: reached RLIMIT_SIGPENDING, dropped signal %d\n", +				current->comm, current->pid, sig); +} +  /*   * allocate a new signal queue record   * - this may be called without locks if and only if t == current, otherwise an   *   appopriate lock must be held to stop the target task from exiting   */ -static struct sigqueue *__sigqueue_alloc(struct task_struct *t, gfp_t flags, -					 int override_rlimit) +static struct sigqueue * +__sigqueue_alloc(int sig, struct task_struct *t, gfp_t flags, int override_rlimit)  {  	struct sigqueue *q = NULL;  	struct user_struct *user; @@ -207,10 +225,15 @@ static struct sigqueue *__sigqueue_alloc(struct task_struct *t, gfp_t flags,  	 */  	user = get_uid(__task_cred(t)->user);  	atomic_inc(&user->sigpending); +  	if (override_rlimit ||  	    atomic_read(&user->sigpending) <= -			t->signal->rlim[RLIMIT_SIGPENDING].rlim_cur) +			t->signal->rlim[RLIMIT_SIGPENDING].rlim_cur) {  		q = kmem_cache_alloc(sigqueue_cachep, flags); +	} else { +		print_dropped_signal(sig); +	} +  	if (unlikely(q == NULL)) {  		atomic_dec(&user->sigpending);  		free_uid(user); @@ -834,7 +857,7 @@ static int __send_signal(int sig, struct siginfo *info, struct task_struct *t,  	struct sigqueue *q;  	int override_rlimit; -	trace_sched_signal_send(sig, t); +	trace_signal_generate(sig, info, t);  	assert_spin_locked(&t->sighand->siglock); @@ -869,7 +892,7 @@ static int __send_signal(int sig, struct siginfo *info, struct task_struct *t,  	else  		override_rlimit = 0; -	q = __sigqueue_alloc(t, GFP_ATOMIC | __GFP_NOTRACK_FALSE_POSITIVE, +	q = __sigqueue_alloc(sig, t, GFP_ATOMIC | __GFP_NOTRACK_FALSE_POSITIVE,  		override_rlimit);  	if (q) {  		list_add_tail(&q->list, &pending->list); @@ -896,12 +919,21 @@ static int __send_signal(int sig, struct siginfo *info, struct task_struct *t,  			break;  		}  	} else if (!is_si_special(info)) { -		if (sig >= SIGRTMIN && info->si_code != SI_USER) -		/* -		 * Queue overflow, abort.  We may abort if the signal was rt -		 * and sent by user using something other than kill(). -		 */ +		if (sig >= SIGRTMIN && info->si_code != SI_USER) { +			/* +			 * Queue overflow, abort.  We may abort if the +			 * signal was rt and sent by user using something +			 * other than kill(). +			 */ +			trace_signal_overflow_fail(sig, group, info);  			return -EAGAIN; +		} else { +			/* +			 * This is a silent loss of information.  We still +			 * send the signal, but the *info bits are lost. +			 */ +			trace_signal_lose_info(sig, group, info); +		}  	}  out_set: @@ -925,8 +957,6 @@ static int send_signal(int sig, struct siginfo *info, struct task_struct *t,  	return __send_signal(sig, info, t, group, from_ancestor_ns);  } -int print_fatal_signals; -  static void print_fatal_signal(struct pt_regs *regs, int signr)  {  	printk("%s/%d: potentially unexpected fatal signal %d.\n", @@ -1293,19 +1323,19 @@ EXPORT_SYMBOL(kill_pid);   * These functions support sending signals using preallocated sigqueue   * structures.  This is needed "because realtime applications cannot   * afford to lose notifications of asynchronous events, like timer - * expirations or I/O completions".  In the case of Posix Timers  + * expirations or I/O completions".  In the case of Posix Timers   * we allocate the sigqueue structure from the timer_create.  If this   * allocation fails we are able to report the failure to the application   * with an EAGAIN error.   */ -   struct sigqueue *sigqueue_alloc(void)  { -	struct sigqueue *q; +	struct sigqueue *q = __sigqueue_alloc(-1, current, GFP_KERNEL, 0); -	if ((q = __sigqueue_alloc(current, GFP_KERNEL, 0))) +	if (q)  		q->flags |= SIGQUEUE_PREALLOC; -	return(q); + +	return q;  }  void sigqueue_free(struct sigqueue *q) @@ -1839,6 +1869,9 @@ relock:  			ka = &sighand->action[signr-1];  		} +		/* Trace actually delivered signals. */ +		trace_signal_deliver(signr, info, ka); +  		if (ka->sa.sa_handler == SIG_IGN) /* Do nothing.  */  			continue;  		if (ka->sa.sa_handler != SIG_DFL) { |