diff options
| author | Ingo Molnar <mingo@elte.hu> | 2011-11-15 11:05:18 +0100 | 
|---|---|---|
| committer | Ingo Molnar <mingo@elte.hu> | 2011-11-15 11:05:18 +0100 | 
| commit | c23205c8488f11cb9ebe7a7b5851a1d8a0171011 (patch) | |
| tree | 239dd82daddb1ce08b50ea9145d8f2a4685ebead /drivers/oprofile | |
| parent | 5d81e5cfb37a174e8ddc0413e2e70cdf05807ace (diff) | |
| parent | de346b6949063aa040ef607943b072835294f4b3 (diff) | |
| download | linux-c23205c8488f11cb9ebe7a7b5851a1d8a0171011.tar.bz2 | |
Merge branch 'core' of git://amd64.org/linux/rric into perf/core
Diffstat (limited to 'drivers/oprofile')
| -rw-r--r-- | drivers/oprofile/nmi_timer_int.c | 173 | ||||
| -rw-r--r-- | drivers/oprofile/oprof.c | 21 | ||||
| -rw-r--r-- | drivers/oprofile/oprof.h | 9 | ||||
| -rw-r--r-- | drivers/oprofile/timer_int.c | 29 | 
4 files changed, 214 insertions, 18 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 dccd8636095c..ed2c3ec07024 100644 --- a/drivers/oprofile/oprof.c +++ b/drivers/oprofile/oprof.c @@ -239,26 +239,39 @@ int oprofile_set_ulong(unsigned long *addr, unsigned long val)  	return err;  } +static int timer_mode; +  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 < 0 || timer) { -		printk(KERN_INFO "oprofile: using timer interrupt.\n"); +	if (!err) { +		if (!timer && !oprofilefs_register()) +			return 0; +		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;  	} +  	return oprofilefs_register();  }  static void __exit oprofile_exit(void)  { -	oprofile_timer_exit();  	oprofilefs_unregister(); -	oprofile_arch_exit(); +	if (!timer_mode) +		oprofile_arch_exit();  } diff --git a/drivers/oprofile/oprof.h b/drivers/oprofile/oprof.h index 177b73de5e5f..769fb0fcac44 100644 --- a/drivers/oprofile/oprof.h +++ b/drivers/oprofile/oprof.h @@ -36,6 +36,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 3ef44624f510..93404f72dfa8 100644 --- a/drivers/oprofile/timer_int.c +++ b/drivers/oprofile/timer_int.c @@ -97,23 +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"; -	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; +} |