diff options
Diffstat (limited to 'kernel/taskstats.c')
| -rw-r--r-- | kernel/taskstats.c | 30 | 
1 files changed, 19 insertions, 11 deletions
diff --git a/kernel/taskstats.c b/kernel/taskstats.c index 13a0f2e6ebc2..e2ac0e37c4ae 100644 --- a/kernel/taskstats.c +++ b/kernel/taskstats.c @@ -554,25 +554,33 @@ static int taskstats_user_cmd(struct sk_buff *skb, struct genl_info *info)  static struct taskstats *taskstats_tgid_alloc(struct task_struct *tsk)  {  	struct signal_struct *sig = tsk->signal; -	struct taskstats *stats; +	struct taskstats *stats_new, *stats; -	if (sig->stats || thread_group_empty(tsk)) -		goto ret; +	/* Pairs with smp_store_release() below. */ +	stats = smp_load_acquire(&sig->stats); +	if (stats || thread_group_empty(tsk)) +		return stats;  	/* No problem if kmem_cache_zalloc() fails */ -	stats = kmem_cache_zalloc(taskstats_cache, GFP_KERNEL); +	stats_new = kmem_cache_zalloc(taskstats_cache, GFP_KERNEL);  	spin_lock_irq(&tsk->sighand->siglock); -	if (!sig->stats) { -		sig->stats = stats; -		stats = NULL; +	stats = sig->stats; +	if (!stats) { +		/* +		 * Pairs with smp_store_release() above and order the +		 * kmem_cache_zalloc(). +		 */ +		smp_store_release(&sig->stats, stats_new); +		stats = stats_new; +		stats_new = NULL;  	}  	spin_unlock_irq(&tsk->sighand->siglock); -	if (stats) -		kmem_cache_free(taskstats_cache, stats); -ret: -	return sig->stats; +	if (stats_new) +		kmem_cache_free(taskstats_cache, stats_new); + +	return stats;  }  /* Send pid data out on exit */  | 
