| 
									
										
										
										
											2011-10-16 17:15:04 +02:00
										 |  |  |  | /*
 | 
					
						
							|  |  |  |  |  * Performance events callchain code, extracted from core.c: | 
					
						
							|  |  |  |  |  * | 
					
						
							|  |  |  |  |  *  Copyright (C) 2008 Thomas Gleixner <tglx@linutronix.de> | 
					
						
							|  |  |  |  |  *  Copyright (C) 2008-2011 Red Hat, Inc., Ingo Molnar | 
					
						
							|  |  |  |  |  *  Copyright (C) 2008-2011 Red Hat, Inc., Peter Zijlstra <pzijlstr@redhat.com> | 
					
						
							|  |  |  |  |  *  Copyright  <EFBFBD>  2009 Paul Mackerras, IBM Corp. <paulus@au1.ibm.com> | 
					
						
							|  |  |  |  |  * | 
					
						
							|  |  |  |  |  * For licensing details see kernel-base/COPYING | 
					
						
							|  |  |  |  |  */ | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | #include <linux/perf_event.h>
 | 
					
						
							|  |  |  |  | #include <linux/slab.h>
 | 
					
						
							|  |  |  |  | #include "internal.h"
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | struct callchain_cpus_entries { | 
					
						
							|  |  |  |  | 	struct rcu_head			rcu_head; | 
					
						
							|  |  |  |  | 	struct perf_callchain_entry	*cpu_entries[0]; | 
					
						
							|  |  |  |  | }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static DEFINE_PER_CPU(int, callchain_recursion[PERF_NR_CONTEXTS]); | 
					
						
							|  |  |  |  | static atomic_t nr_callchain_events; | 
					
						
							|  |  |  |  | static DEFINE_MUTEX(callchain_mutex); | 
					
						
							|  |  |  |  | static struct callchain_cpus_entries *callchain_cpus_entries; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | __weak void perf_callchain_kernel(struct perf_callchain_entry *entry, | 
					
						
							|  |  |  |  | 				  struct pt_regs *regs) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | __weak void perf_callchain_user(struct perf_callchain_entry *entry, | 
					
						
							|  |  |  |  | 				struct pt_regs *regs) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static void release_callchain_buffers_rcu(struct rcu_head *head) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  | 	struct callchain_cpus_entries *entries; | 
					
						
							|  |  |  |  | 	int cpu; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	entries = container_of(head, struct callchain_cpus_entries, rcu_head); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	for_each_possible_cpu(cpu) | 
					
						
							|  |  |  |  | 		kfree(entries->cpu_entries[cpu]); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	kfree(entries); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static void release_callchain_buffers(void) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  | 	struct callchain_cpus_entries *entries; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	entries = callchain_cpus_entries; | 
					
						
							|  |  |  |  | 	rcu_assign_pointer(callchain_cpus_entries, NULL); | 
					
						
							|  |  |  |  | 	call_rcu(&entries->rcu_head, release_callchain_buffers_rcu); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static int alloc_callchain_buffers(void) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  | 	int cpu; | 
					
						
							|  |  |  |  | 	int size; | 
					
						
							|  |  |  |  | 	struct callchain_cpus_entries *entries; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	/*
 | 
					
						
							|  |  |  |  | 	 * We can't use the percpu allocation API for data that can be | 
					
						
							|  |  |  |  | 	 * accessed from NMI. Use a temporary manual per cpu allocation | 
					
						
							|  |  |  |  | 	 * until that gets sorted out. | 
					
						
							|  |  |  |  | 	 */ | 
					
						
							|  |  |  |  | 	size = offsetof(struct callchain_cpus_entries, cpu_entries[nr_cpu_ids]); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	entries = kzalloc(size, GFP_KERNEL); | 
					
						
							|  |  |  |  | 	if (!entries) | 
					
						
							|  |  |  |  | 		return -ENOMEM; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	size = sizeof(struct perf_callchain_entry) * PERF_NR_CONTEXTS; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	for_each_possible_cpu(cpu) { | 
					
						
							|  |  |  |  | 		entries->cpu_entries[cpu] = kmalloc_node(size, GFP_KERNEL, | 
					
						
							|  |  |  |  | 							 cpu_to_node(cpu)); | 
					
						
							|  |  |  |  | 		if (!entries->cpu_entries[cpu]) | 
					
						
							|  |  |  |  | 			goto fail; | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	rcu_assign_pointer(callchain_cpus_entries, entries); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	return 0; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | fail: | 
					
						
							|  |  |  |  | 	for_each_possible_cpu(cpu) | 
					
						
							|  |  |  |  | 		kfree(entries->cpu_entries[cpu]); | 
					
						
							|  |  |  |  | 	kfree(entries); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	return -ENOMEM; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | int get_callchain_buffers(void) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  | 	int err = 0; | 
					
						
							|  |  |  |  | 	int count; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	mutex_lock(&callchain_mutex); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	count = atomic_inc_return(&nr_callchain_events); | 
					
						
							|  |  |  |  | 	if (WARN_ON_ONCE(count < 1)) { | 
					
						
							|  |  |  |  | 		err = -EINVAL; | 
					
						
							|  |  |  |  | 		goto exit; | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	if (count > 1) { | 
					
						
							|  |  |  |  | 		/* If the allocation failed, give up */ | 
					
						
							|  |  |  |  | 		if (!callchain_cpus_entries) | 
					
						
							|  |  |  |  | 			err = -ENOMEM; | 
					
						
							|  |  |  |  | 		goto exit; | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	err = alloc_callchain_buffers(); | 
					
						
							|  |  |  |  | exit: | 
					
						
							| 
									
										
										
										
											2013-07-23 02:31:00 +02:00
										 |  |  |  | 	if (err) | 
					
						
							|  |  |  |  | 		atomic_dec(&nr_callchain_events); | 
					
						
							| 
									
										
										
										
											2011-10-16 17:15:04 +02:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-08-02 18:29:54 +02:00
										 |  |  |  | 	mutex_unlock(&callchain_mutex); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-10-16 17:15:04 +02:00
										 |  |  |  | 	return err; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | void put_callchain_buffers(void) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  | 	if (atomic_dec_and_mutex_lock(&nr_callchain_events, &callchain_mutex)) { | 
					
						
							|  |  |  |  | 		release_callchain_buffers(); | 
					
						
							|  |  |  |  | 		mutex_unlock(&callchain_mutex); | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static struct perf_callchain_entry *get_callchain_entry(int *rctx) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  | 	int cpu; | 
					
						
							|  |  |  |  | 	struct callchain_cpus_entries *entries; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	*rctx = get_recursion_context(__get_cpu_var(callchain_recursion)); | 
					
						
							|  |  |  |  | 	if (*rctx == -1) | 
					
						
							|  |  |  |  | 		return NULL; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	entries = rcu_dereference(callchain_cpus_entries); | 
					
						
							|  |  |  |  | 	if (!entries) | 
					
						
							|  |  |  |  | 		return NULL; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	cpu = smp_processor_id(); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	return &entries->cpu_entries[cpu][*rctx]; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static void | 
					
						
							|  |  |  |  | put_callchain_entry(int rctx) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  | 	put_recursion_context(__get_cpu_var(callchain_recursion), rctx); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-07-11 18:14:58 +04:00
										 |  |  |  | struct perf_callchain_entry * | 
					
						
							|  |  |  |  | perf_callchain(struct perf_event *event, struct pt_regs *regs) | 
					
						
							| 
									
										
										
										
											2011-10-16 17:15:04 +02:00
										 |  |  |  | { | 
					
						
							|  |  |  |  | 	int rctx; | 
					
						
							|  |  |  |  | 	struct perf_callchain_entry *entry; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-08-07 15:20:41 +02:00
										 |  |  |  | 	int kernel = !event->attr.exclude_callchain_kernel; | 
					
						
							|  |  |  |  | 	int user   = !event->attr.exclude_callchain_user; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	if (!kernel && !user) | 
					
						
							|  |  |  |  | 		return NULL; | 
					
						
							| 
									
										
										
										
											2011-10-16 17:15:04 +02:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 	entry = get_callchain_entry(&rctx); | 
					
						
							|  |  |  |  | 	if (rctx == -1) | 
					
						
							|  |  |  |  | 		return NULL; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	if (!entry) | 
					
						
							|  |  |  |  | 		goto exit_put; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	entry->nr = 0; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-08-07 15:20:41 +02:00
										 |  |  |  | 	if (kernel && !user_mode(regs)) { | 
					
						
							| 
									
										
										
										
											2011-10-16 17:15:04 +02:00
										 |  |  |  | 		perf_callchain_store(entry, PERF_CONTEXT_KERNEL); | 
					
						
							|  |  |  |  | 		perf_callchain_kernel(entry, regs); | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-08-07 15:20:41 +02:00
										 |  |  |  | 	if (user) { | 
					
						
							|  |  |  |  | 		if (!user_mode(regs)) { | 
					
						
							|  |  |  |  | 			if  (current->mm) | 
					
						
							|  |  |  |  | 				regs = task_pt_regs(current); | 
					
						
							|  |  |  |  | 			else | 
					
						
							|  |  |  |  | 				regs = NULL; | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 		if (regs) { | 
					
						
							|  |  |  |  | 			/*
 | 
					
						
							|  |  |  |  | 			 * Disallow cross-task user callchains. | 
					
						
							|  |  |  |  | 			 */ | 
					
						
							|  |  |  |  | 			if (event->ctx->task && event->ctx->task != current) | 
					
						
							|  |  |  |  | 				goto exit_put; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 			perf_callchain_store(entry, PERF_CONTEXT_USER); | 
					
						
							|  |  |  |  | 			perf_callchain_user(entry, regs); | 
					
						
							|  |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2011-10-16 17:15:04 +02:00
										 |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | exit_put: | 
					
						
							|  |  |  |  | 	put_callchain_entry(rctx); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	return entry; | 
					
						
							|  |  |  |  | } |