diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-01-06 08:02:58 -0800 | 
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-01-06 08:02:58 -0800 | 
| commit | 35b740e4662ef386f0c60e1b60aaf5b44db9914c (patch) | |
| tree | 502a8f9499bc1b4cb3300d666dab2d01a1921224 /drivers/oprofile | |
| parent | 423d091dfe58d3109d84c408810a7cfa82f6f184 (diff) | |
| parent | 9e183426bfb52bb44bf3c443d6587e4d02478603 (diff) | |
| download | linux-35b740e4662ef386f0c60e1b60aaf5b44db9914c.tar.bz2 | |
Merge branch 'perf-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
* 'perf-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (106 commits)
  perf kvm: Fix copy & paste error in description
  perf script: Kill script_spec__delete
  perf top: Fix a memory leak
  perf stat: Introduce get_ratio_color() helper
  perf session: Remove impossible condition check
  perf tools: Fix feature-bits rework fallout, remove unused variable
  perf script: Add generic perl handler to process events
  perf tools: Use for_each_set_bit() to iterate over feature flags
  perf tools: Unify handling of features when writing feature section
  perf report: Accept fifos as input file
  perf tools: Moving code in some files
  perf tools: Fix out-of-bound access to struct perf_session
  perf tools: Continue processing header on unknown features
  perf tools: Improve macros for struct feature_ops
  perf: builtin-record: Document and check that mmap_pages must be a power of two.
  perf: builtin-record: Provide advice if mmap'ing fails with EPERM.
  perf tools: Fix truncated annotation
  perf script: look up thread using tid instead of pid
  perf tools: Look up thread names for system wide profiling
  perf tools: Fix comm for processes with named threads
  ...
Diffstat (limited to 'drivers/oprofile')
| -rw-r--r-- | drivers/oprofile/nmi_timer_int.c | 173 | ||||
| -rw-r--r-- | drivers/oprofile/oprof.c | 30 | ||||
| -rw-r--r-- | drivers/oprofile/oprof.h | 10 | ||||
| -rw-r--r-- | drivers/oprofile/timer_int.c | 30 | 
4 files changed, 209 insertions, 34 deletions
| diff --git a/drivers/oprofile/nmi_timer_int.c b/drivers/oprofile/nmi_timer_int.c new file mode 100644 index 000000000000..76f1c9357f39 --- /dev/null +++ b/drivers/oprofile/nmi_timer_int.c @@ -0,0 +1,173 @@ +/** + * @file nmi_timer_int.c + * + * @remark Copyright 2011 Advanced Micro Devices, Inc. + * + * @author Robert Richter <robert.richter@amd.com> + */ + +#include <linux/init.h> +#include <linux/smp.h> +#include <linux/errno.h> +#include <linux/oprofile.h> +#include <linux/perf_event.h> + +#ifdef CONFIG_OPROFILE_NMI_TIMER + +static DEFINE_PER_CPU(struct perf_event *, nmi_timer_events); +static int ctr_running; + +static struct perf_event_attr nmi_timer_attr = { +	.type           = PERF_TYPE_HARDWARE, +	.config         = PERF_COUNT_HW_CPU_CYCLES, +	.size           = sizeof(struct perf_event_attr), +	.pinned         = 1, +	.disabled       = 1, +}; + +static void nmi_timer_callback(struct perf_event *event, +			       struct perf_sample_data *data, +			       struct pt_regs *regs) +{ +	event->hw.interrupts = 0;       /* don't throttle interrupts */ +	oprofile_add_sample(regs, 0); +} + +static int nmi_timer_start_cpu(int cpu) +{ +	struct perf_event *event = per_cpu(nmi_timer_events, cpu); + +	if (!event) { +		event = perf_event_create_kernel_counter(&nmi_timer_attr, cpu, NULL, +							 nmi_timer_callback, NULL); +		if (IS_ERR(event)) +			return PTR_ERR(event); +		per_cpu(nmi_timer_events, cpu) = event; +	} + +	if (event && ctr_running) +		perf_event_enable(event); + +	return 0; +} + +static void nmi_timer_stop_cpu(int cpu) +{ +	struct perf_event *event = per_cpu(nmi_timer_events, cpu); + +	if (event && ctr_running) +		perf_event_disable(event); +} + +static int nmi_timer_cpu_notifier(struct notifier_block *b, unsigned long action, +				  void *data) +{ +	int cpu = (unsigned long)data; +	switch (action) { +	case CPU_DOWN_FAILED: +	case CPU_ONLINE: +		nmi_timer_start_cpu(cpu); +		break; +	case CPU_DOWN_PREPARE: +		nmi_timer_stop_cpu(cpu); +		break; +	} +	return NOTIFY_DONE; +} + +static struct notifier_block nmi_timer_cpu_nb = { +	.notifier_call = nmi_timer_cpu_notifier +}; + +static int nmi_timer_start(void) +{ +	int cpu; + +	get_online_cpus(); +	ctr_running = 1; +	for_each_online_cpu(cpu) +		nmi_timer_start_cpu(cpu); +	put_online_cpus(); + +	return 0; +} + +static void nmi_timer_stop(void) +{ +	int cpu; + +	get_online_cpus(); +	for_each_online_cpu(cpu) +		nmi_timer_stop_cpu(cpu); +	ctr_running = 0; +	put_online_cpus(); +} + +static void nmi_timer_shutdown(void) +{ +	struct perf_event *event; +	int cpu; + +	get_online_cpus(); +	unregister_cpu_notifier(&nmi_timer_cpu_nb); +	for_each_possible_cpu(cpu) { +		event = per_cpu(nmi_timer_events, cpu); +		if (!event) +			continue; +		perf_event_disable(event); +		per_cpu(nmi_timer_events, cpu) = NULL; +		perf_event_release_kernel(event); +	} + +	put_online_cpus(); +} + +static int nmi_timer_setup(void) +{ +	int cpu, err; +	u64 period; + +	/* clock cycles per tick: */ +	period = (u64)cpu_khz * 1000; +	do_div(period, HZ); +	nmi_timer_attr.sample_period = period; + +	get_online_cpus(); +	err = register_cpu_notifier(&nmi_timer_cpu_nb); +	if (err) +		goto out; +	/* can't attach events to offline cpus: */ +	for_each_online_cpu(cpu) { +		err = nmi_timer_start_cpu(cpu); +		if (err) +			break; +	} +	if (err) +		nmi_timer_shutdown(); +out: +	put_online_cpus(); +	return err; +} + +int __init op_nmi_timer_init(struct oprofile_operations *ops) +{ +	int err = 0; + +	err = nmi_timer_setup(); +	if (err) +		return err; +	nmi_timer_shutdown();		/* only check, don't alloc */ + +	ops->create_files	= NULL; +	ops->setup		= nmi_timer_setup; +	ops->shutdown		= nmi_timer_shutdown; +	ops->start		= nmi_timer_start; +	ops->stop		= nmi_timer_stop; +	ops->cpu_type		= "timer"; + +	printk(KERN_INFO "oprofile: using NMI timer interrupt.\n"); + +	return 0; +} + +#endif diff --git a/drivers/oprofile/oprof.c b/drivers/oprofile/oprof.c index f8c752e408a6..ed2c3ec07024 100644 --- a/drivers/oprofile/oprof.c +++ b/drivers/oprofile/oprof.c @@ -246,37 +246,31 @@ static int __init oprofile_init(void)  	int err;  	/* always init architecture to setup backtrace support */ +	timer_mode = 0;  	err = oprofile_arch_init(&oprofile_ops); +	if (!err) { +		if (!timer && !oprofilefs_register()) +			return 0; +		oprofile_arch_exit(); +	} -	timer_mode = err || timer;	/* fall back to timer mode on errors */ -	if (timer_mode) { -		if (!err) -			oprofile_arch_exit(); +	/* setup timer mode: */ +	timer_mode = 1; +	/* no nmi timer mode if oprofile.timer is set */ +	if (timer || op_nmi_timer_init(&oprofile_ops)) {  		err = oprofile_timer_init(&oprofile_ops);  		if (err)  			return err;  	} -	err = oprofilefs_register(); -	if (!err) -		return 0; - -	/* failed */ -	if (timer_mode) -		oprofile_timer_exit(); -	else -		oprofile_arch_exit(); - -	return err; +	return oprofilefs_register();  }  static void __exit oprofile_exit(void)  {  	oprofilefs_unregister(); -	if (timer_mode) -		oprofile_timer_exit(); -	else +	if (!timer_mode)  		oprofile_arch_exit();  } diff --git a/drivers/oprofile/oprof.h b/drivers/oprofile/oprof.h index 177b73de5e5f..d32ef816337c 100644 --- a/drivers/oprofile/oprof.h +++ b/drivers/oprofile/oprof.h @@ -35,7 +35,15 @@ struct dentry;  void oprofile_create_files(struct super_block *sb, struct dentry *root);  int oprofile_timer_init(struct oprofile_operations *ops); -void oprofile_timer_exit(void); +#ifdef CONFIG_OPROFILE_NMI_TIMER +int op_nmi_timer_init(struct oprofile_operations *ops); +#else +static inline int op_nmi_timer_init(struct oprofile_operations *ops) +{ +	return -ENODEV; +} +#endif +  int oprofile_set_ulong(unsigned long *addr, unsigned long val);  int oprofile_set_timeout(unsigned long time); diff --git a/drivers/oprofile/timer_int.c b/drivers/oprofile/timer_int.c index 878fba126582..93404f72dfa8 100644 --- a/drivers/oprofile/timer_int.c +++ b/drivers/oprofile/timer_int.c @@ -97,24 +97,24 @@ static struct notifier_block __refdata oprofile_cpu_notifier = {  	.notifier_call = oprofile_cpu_notify,  }; -int oprofile_timer_init(struct oprofile_operations *ops) +static int oprofile_hrtimer_setup(void)  { -	int rc; - -	rc = register_hotcpu_notifier(&oprofile_cpu_notifier); -	if (rc) -		return rc; -	ops->create_files = NULL; -	ops->setup = NULL; -	ops->shutdown = NULL; -	ops->start = oprofile_hrtimer_start; -	ops->stop = oprofile_hrtimer_stop; -	ops->cpu_type = "timer"; -	printk(KERN_INFO "oprofile: using timer interrupt.\n"); -	return 0; +	return register_hotcpu_notifier(&oprofile_cpu_notifier);  } -void oprofile_timer_exit(void) +static void oprofile_hrtimer_shutdown(void)  {  	unregister_hotcpu_notifier(&oprofile_cpu_notifier);  } + +int oprofile_timer_init(struct oprofile_operations *ops) +{ +	ops->create_files	= NULL; +	ops->setup		= oprofile_hrtimer_setup; +	ops->shutdown		= oprofile_hrtimer_shutdown; +	ops->start		= oprofile_hrtimer_start; +	ops->stop		= oprofile_hrtimer_stop; +	ops->cpu_type		= "timer"; +	printk(KERN_INFO "oprofile: using timer interrupt.\n"); +	return 0; +} |