aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/kernel-parameters.txt3
-rw-r--r--arch/Kconfig4
-rw-r--r--arch/x86/oprofile/Makefile3
-rw-r--r--arch/x86/oprofile/init.c25
-rw-r--r--arch/x86/oprofile/nmi_int.c27
-rw-r--r--arch/x86/oprofile/nmi_timer_int.c50
-rw-r--r--drivers/oprofile/nmi_timer_int.c173
-rw-r--r--drivers/oprofile/oprof.c21
-rw-r--r--drivers/oprofile/oprof.h9
-rw-r--r--drivers/oprofile/timer_int.c29
-rw-r--r--kernel/events/core.c2
11 files changed, 252 insertions, 94 deletions
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index 854ed5ca7e3f..f7735a125f4b 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -1848,6 +1848,9 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
1848 arch_perfmon: [X86] Force use of architectural 1848 arch_perfmon: [X86] Force use of architectural
1849 perfmon on Intel CPUs instead of the 1849 perfmon on Intel CPUs instead of the
1850 CPU specific event set. 1850 CPU specific event set.
1851 timer: [X86] Force use of architectural NMI
1852 timer mode (see also oprofile.timer
1853 for generic hr timer mode)
1851 1854
1852 oops=panic Always panic on oopses. Default is to just kill the 1855 oops=panic Always panic on oopses. Default is to just kill the
1853 process, but there is a small probability of 1856 process, but there is a small probability of
diff --git a/arch/Kconfig b/arch/Kconfig
index 4b0669cbb3b0..2505740b81d2 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -30,6 +30,10 @@ config OPROFILE_EVENT_MULTIPLEX
30config HAVE_OPROFILE 30config HAVE_OPROFILE
31 bool 31 bool
32 32
33config OPROFILE_NMI_TIMER
34 def_bool y
35 depends on PERF_EVENTS && HAVE_PERF_EVENTS_NMI
36
33config KPROBES 37config KPROBES
34 bool "Kprobes" 38 bool "Kprobes"
35 depends on MODULES 39 depends on MODULES
diff --git a/arch/x86/oprofile/Makefile b/arch/x86/oprofile/Makefile
index 446902b2a6b6..1599f568f0e2 100644
--- a/arch/x86/oprofile/Makefile
+++ b/arch/x86/oprofile/Makefile
@@ -4,9 +4,8 @@ DRIVER_OBJS = $(addprefix ../../../drivers/oprofile/, \
4 oprof.o cpu_buffer.o buffer_sync.o \ 4 oprof.o cpu_buffer.o buffer_sync.o \
5 event_buffer.o oprofile_files.o \ 5 event_buffer.o oprofile_files.o \
6 oprofilefs.o oprofile_stats.o \ 6 oprofilefs.o oprofile_stats.o \
7 timer_int.o ) 7 timer_int.o nmi_timer_int.o )
8 8
9oprofile-y := $(DRIVER_OBJS) init.o backtrace.o 9oprofile-y := $(DRIVER_OBJS) init.o backtrace.o
10oprofile-$(CONFIG_X86_LOCAL_APIC) += nmi_int.o op_model_amd.o \ 10oprofile-$(CONFIG_X86_LOCAL_APIC) += nmi_int.o op_model_amd.o \
11 op_model_ppro.o op_model_p4.o 11 op_model_ppro.o op_model_p4.o
12oprofile-$(CONFIG_X86_IO_APIC) += nmi_timer_int.o
diff --git a/arch/x86/oprofile/init.c b/arch/x86/oprofile/init.c
index cdfe4c54deca..9e138d00ad36 100644
--- a/arch/x86/oprofile/init.c
+++ b/arch/x86/oprofile/init.c
@@ -16,34 +16,23 @@
16 * with the NMI mode driver. 16 * with the NMI mode driver.
17 */ 17 */
18 18
19#ifdef CONFIG_X86_LOCAL_APIC
19extern int op_nmi_init(struct oprofile_operations *ops); 20extern int op_nmi_init(struct oprofile_operations *ops);
20extern int op_nmi_timer_init(struct oprofile_operations *ops);
21extern void op_nmi_exit(void); 21extern void op_nmi_exit(void);
22extern void x86_backtrace(struct pt_regs * const regs, unsigned int depth); 22#else
23static int op_nmi_init(struct oprofile_operations *ops) { return -ENODEV; }
24static void op_nmi_exit(void) { }
25#endif
23 26
27extern void x86_backtrace(struct pt_regs * const regs, unsigned int depth);
24 28
25int __init oprofile_arch_init(struct oprofile_operations *ops) 29int __init oprofile_arch_init(struct oprofile_operations *ops)
26{ 30{
27 int ret;
28
29 ret = -ENODEV;
30
31#ifdef CONFIG_X86_LOCAL_APIC
32 ret = op_nmi_init(ops);
33#endif
34#ifdef CONFIG_X86_IO_APIC
35 if (ret < 0)
36 ret = op_nmi_timer_init(ops);
37#endif
38 ops->backtrace = x86_backtrace; 31 ops->backtrace = x86_backtrace;
39 32 return op_nmi_init(ops);
40 return ret;
41} 33}
42 34
43
44void oprofile_arch_exit(void) 35void oprofile_arch_exit(void)
45{ 36{
46#ifdef CONFIG_X86_LOCAL_APIC
47 op_nmi_exit(); 37 op_nmi_exit();
48#endif
49} 38}
diff --git a/arch/x86/oprofile/nmi_int.c b/arch/x86/oprofile/nmi_int.c
index adf8fb31aa7b..990c35bfa88f 100644
--- a/arch/x86/oprofile/nmi_int.c
+++ b/arch/x86/oprofile/nmi_int.c
@@ -597,24 +597,36 @@ static int __init p4_init(char **cpu_type)
597 return 0; 597 return 0;
598} 598}
599 599
600static int force_arch_perfmon; 600enum __force_cpu_type {
601static int force_cpu_type(const char *str, struct kernel_param *kp) 601 reserved = 0, /* do not force */
602 timer,
603 arch_perfmon,
604};
605
606static int force_cpu_type;
607
608static int set_cpu_type(const char *str, struct kernel_param *kp)
602{ 609{
603 if (!strcmp(str, "arch_perfmon")) { 610 if (!strcmp(str, "timer")) {
604 force_arch_perfmon = 1; 611 force_cpu_type = timer;
612 printk(KERN_INFO "oprofile: forcing NMI timer mode\n");
613 } else if (!strcmp(str, "arch_perfmon")) {
614 force_cpu_type = arch_perfmon;
605 printk(KERN_INFO "oprofile: forcing architectural perfmon\n"); 615 printk(KERN_INFO "oprofile: forcing architectural perfmon\n");
616 } else {
617 force_cpu_type = 0;
606 } 618 }
607 619
608 return 0; 620 return 0;
609} 621}
610module_param_call(cpu_type, force_cpu_type, NULL, NULL, 0); 622module_param_call(cpu_type, set_cpu_type, NULL, NULL, 0);
611 623
612static int __init ppro_init(char **cpu_type) 624static int __init ppro_init(char **cpu_type)
613{ 625{
614 __u8 cpu_model = boot_cpu_data.x86_model; 626 __u8 cpu_model = boot_cpu_data.x86_model;
615 struct op_x86_model_spec *spec = &op_ppro_spec; /* default */ 627 struct op_x86_model_spec *spec = &op_ppro_spec; /* default */
616 628
617 if (force_arch_perfmon && cpu_has_arch_perfmon) 629 if (force_cpu_type == arch_perfmon && cpu_has_arch_perfmon)
618 return 0; 630 return 0;
619 631
620 /* 632 /*
@@ -681,6 +693,9 @@ int __init op_nmi_init(struct oprofile_operations *ops)
681 if (!cpu_has_apic) 693 if (!cpu_has_apic)
682 return -ENODEV; 694 return -ENODEV;
683 695
696 if (force_cpu_type == timer)
697 return -ENODEV;
698
684 switch (vendor) { 699 switch (vendor) {
685 case X86_VENDOR_AMD: 700 case X86_VENDOR_AMD:
686 /* Needs to be at least an Athlon (or hammer in 32bit mode) */ 701 /* Needs to be at least an Athlon (or hammer in 32bit mode) */
diff --git a/arch/x86/oprofile/nmi_timer_int.c b/arch/x86/oprofile/nmi_timer_int.c
deleted file mode 100644
index 7f8052cd6620..000000000000
--- a/arch/x86/oprofile/nmi_timer_int.c
+++ /dev/null
@@ -1,50 +0,0 @@
1/**
2 * @file nmi_timer_int.c
3 *
4 * @remark Copyright 2003 OProfile authors
5 * @remark Read the file COPYING
6 *
7 * @author Zwane Mwaikambo <zwane@linuxpower.ca>
8 */
9
10#include <linux/init.h>
11#include <linux/smp.h>
12#include <linux/errno.h>
13#include <linux/oprofile.h>
14#include <linux/rcupdate.h>
15#include <linux/kdebug.h>
16
17#include <asm/nmi.h>
18#include <asm/apic.h>
19#include <asm/ptrace.h>
20
21static int profile_timer_exceptions_notify(unsigned int val, struct pt_regs *regs)
22{
23 oprofile_add_sample(regs, 0);
24 return NMI_HANDLED;
25}
26
27static int timer_start(void)
28{
29 if (register_nmi_handler(NMI_LOCAL, profile_timer_exceptions_notify,
30 0, "oprofile-timer"))
31 return 1;
32 return 0;
33}
34
35
36static void timer_stop(void)
37{
38 unregister_nmi_handler(NMI_LOCAL, "oprofile-timer");
39 synchronize_sched(); /* Allow already-started NMIs to complete. */
40}
41
42
43int __init op_nmi_timer_init(struct oprofile_operations *ops)
44{
45 ops->start = timer_start;
46 ops->stop = timer_stop;
47 ops->cpu_type = "timer";
48 printk(KERN_INFO "oprofile: using NMI timer interrupt.\n");
49 return 0;
50}
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 @@
1/**
2 * @file nmi_timer_int.c
3 *
4 * @remark Copyright 2011 Advanced Micro Devices, Inc.
5 *
6 * @author Robert Richter <robert.richter@amd.com>
7 */
8
9#include <linux/init.h>
10#include <linux/smp.h>
11#include <linux/errno.h>
12#include <linux/oprofile.h>
13#include <linux/perf_event.h>
14
15#ifdef CONFIG_OPROFILE_NMI_TIMER
16
17static DEFINE_PER_CPU(struct perf_event *, nmi_timer_events);
18static int ctr_running;
19
20static struct perf_event_attr nmi_timer_attr = {
21 .type = PERF_TYPE_HARDWARE,
22 .config = PERF_COUNT_HW_CPU_CYCLES,
23 .size = sizeof(struct perf_event_attr),
24 .pinned = 1,
25 .disabled = 1,
26};
27
28static void nmi_timer_callback(struct perf_event *event,
29 struct perf_sample_data *data,
30 struct pt_regs *regs)
31{
32 event->hw.interrupts = 0; /* don't throttle interrupts */
33 oprofile_add_sample(regs, 0);
34}
35
36static int nmi_timer_start_cpu(int cpu)
37{
38 struct perf_event *event = per_cpu(nmi_timer_events, cpu);
39
40 if (!event) {
41 event = perf_event_create_kernel_counter(&nmi_timer_attr, cpu, NULL,
42 nmi_timer_callback, NULL);
43 if (IS_ERR(event))
44 return PTR_ERR(event);
45 per_cpu(nmi_timer_events, cpu) = event;
46 }
47
48 if (event && ctr_running)
49 perf_event_enable(event);
50
51 return 0;
52}
53
54static void nmi_timer_stop_cpu(int cpu)
55{
56 struct perf_event *event = per_cpu(nmi_timer_events, cpu);
57
58 if (event && ctr_running)
59 perf_event_disable(event);
60}
61
62static int nmi_timer_cpu_notifier(struct notifier_block *b, unsigned long action,
63 void *data)
64{
65 int cpu = (unsigned long)data;
66 switch (action) {
67 case CPU_DOWN_FAILED:
68 case CPU_ONLINE:
69 nmi_timer_start_cpu(cpu);
70 break;
71 case CPU_DOWN_PREPARE:
72 nmi_timer_stop_cpu(cpu);
73 break;
74 }
75 return NOTIFY_DONE;
76}
77
78static struct notifier_block nmi_timer_cpu_nb = {
79 .notifier_call = nmi_timer_cpu_notifier
80};
81
82static int nmi_timer_start(void)
83{
84 int cpu;
85
86 get_online_cpus();
87 ctr_running = 1;
88 for_each_online_cpu(cpu)
89 nmi_timer_start_cpu(cpu);
90 put_online_cpus();
91
92 return 0;
93}
94
95static void nmi_timer_stop(void)
96{
97 int cpu;
98
99 get_online_cpus();
100 for_each_online_cpu(cpu)
101 nmi_timer_stop_cpu(cpu);
102 ctr_running = 0;
103 put_online_cpus();
104}
105
106static void nmi_timer_shutdown(void)
107{
108 struct perf_event *event;
109 int cpu;
110
111 get_online_cpus();
112 unregister_cpu_notifier(&nmi_timer_cpu_nb);
113 for_each_possible_cpu(cpu) {
114 event = per_cpu(nmi_timer_events, cpu);
115 if (!event)
116 continue;
117 perf_event_disable(event);
118 per_cpu(nmi_timer_events, cpu) = NULL;
119 perf_event_release_kernel(event);
120 }
121
122 put_online_cpus();
123}
124
125static int nmi_timer_setup(void)
126{
127 int cpu, err;
128 u64 period;
129
130 /* clock cycles per tick: */
131 period = (u64)cpu_khz * 1000;
132 do_div(period, HZ);
133 nmi_timer_attr.sample_period = period;
134
135 get_online_cpus();
136 err = register_cpu_notifier(&nmi_timer_cpu_nb);
137 if (err)
138 goto out;
139 /* can't attach events to offline cpus: */
140 for_each_online_cpu(cpu) {
141 err = nmi_timer_start_cpu(cpu);
142 if (err)
143 break;
144 }
145 if (err)
146 nmi_timer_shutdown();
147out:
148 put_online_cpus();
149 return err;
150}
151
152int __init op_nmi_timer_init(struct oprofile_operations *ops)
153{
154 int err = 0;
155
156 err = nmi_timer_setup();
157 if (err)
158 return err;
159 nmi_timer_shutdown(); /* only check, don't alloc */
160
161 ops->create_files = NULL;
162 ops->setup = nmi_timer_setup;
163 ops->shutdown = nmi_timer_shutdown;
164 ops->start = nmi_timer_start;
165 ops->stop = nmi_timer_stop;
166 ops->cpu_type = "timer";
167
168 printk(KERN_INFO "oprofile: using NMI timer interrupt.\n");
169
170 return 0;
171}
172
173#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)
239 return err; 239 return err;
240} 240}
241 241
242static int timer_mode;
243
242static int __init oprofile_init(void) 244static int __init oprofile_init(void)
243{ 245{
244 int err; 246 int err;
245 247
248 /* always init architecture to setup backtrace support */
249 timer_mode = 0;
246 err = oprofile_arch_init(&oprofile_ops); 250 err = oprofile_arch_init(&oprofile_ops);
247 if (err < 0 || timer) { 251 if (!err) {
248 printk(KERN_INFO "oprofile: using timer interrupt.\n"); 252 if (!timer && !oprofilefs_register())
253 return 0;
254 oprofile_arch_exit();
255 }
256
257 /* setup timer mode: */
258 timer_mode = 1;
259 /* no nmi timer mode if oprofile.timer is set */
260 if (timer || op_nmi_timer_init(&oprofile_ops)) {
249 err = oprofile_timer_init(&oprofile_ops); 261 err = oprofile_timer_init(&oprofile_ops);
250 if (err) 262 if (err)
251 return err; 263 return err;
252 } 264 }
265
253 return oprofilefs_register(); 266 return oprofilefs_register();
254} 267}
255 268
256 269
257static void __exit oprofile_exit(void) 270static void __exit oprofile_exit(void)
258{ 271{
259 oprofile_timer_exit();
260 oprofilefs_unregister(); 272 oprofilefs_unregister();
261 oprofile_arch_exit(); 273 if (!timer_mode)
274 oprofile_arch_exit();
262} 275}
263 276
264 277
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;
36void oprofile_create_files(struct super_block *sb, struct dentry *root); 36void oprofile_create_files(struct super_block *sb, struct dentry *root);
37int oprofile_timer_init(struct oprofile_operations *ops); 37int oprofile_timer_init(struct oprofile_operations *ops);
38void oprofile_timer_exit(void); 38void oprofile_timer_exit(void);
39#ifdef CONFIG_OPROFILE_NMI_TIMER
40int op_nmi_timer_init(struct oprofile_operations *ops);
41#else
42static inline int op_nmi_timer_init(struct oprofile_operations *ops)
43{
44 return -ENODEV;
45}
46#endif
47
39 48
40int oprofile_set_ulong(unsigned long *addr, unsigned long val); 49int oprofile_set_ulong(unsigned long *addr, unsigned long val);
41int oprofile_set_timeout(unsigned long time); 50int 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 = {
97 .notifier_call = oprofile_cpu_notify, 97 .notifier_call = oprofile_cpu_notify,
98}; 98};
99 99
100int oprofile_timer_init(struct oprofile_operations *ops) 100static int oprofile_hrtimer_setup(void)
101{ 101{
102 int rc; 102 return register_hotcpu_notifier(&oprofile_cpu_notifier);
103
104 rc = register_hotcpu_notifier(&oprofile_cpu_notifier);
105 if (rc)
106 return rc;
107 ops->create_files = NULL;
108 ops->setup = NULL;
109 ops->shutdown = NULL;
110 ops->start = oprofile_hrtimer_start;
111 ops->stop = oprofile_hrtimer_stop;
112 ops->cpu_type = "timer";
113 return 0;
114} 103}
115 104
116void oprofile_timer_exit(void) 105static void oprofile_hrtimer_shutdown(void)
117{ 106{
118 unregister_hotcpu_notifier(&oprofile_cpu_notifier); 107 unregister_hotcpu_notifier(&oprofile_cpu_notifier);
119} 108}
109
110int oprofile_timer_init(struct oprofile_operations *ops)
111{
112 ops->create_files = NULL;
113 ops->setup = oprofile_hrtimer_setup;
114 ops->shutdown = oprofile_hrtimer_shutdown;
115 ops->start = oprofile_hrtimer_start;
116 ops->stop = oprofile_hrtimer_stop;
117 ops->cpu_type = "timer";
118 printk(KERN_INFO "oprofile: using timer interrupt.\n");
119 return 0;
120}
diff --git a/kernel/events/core.c b/kernel/events/core.c
index d1a1bee35228..d2e28bdd523a 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -1322,6 +1322,7 @@ retry:
1322 } 1322 }
1323 raw_spin_unlock_irq(&ctx->lock); 1323 raw_spin_unlock_irq(&ctx->lock);
1324} 1324}
1325EXPORT_SYMBOL_GPL(perf_event_disable);
1325 1326
1326static void perf_set_shadow_time(struct perf_event *event, 1327static void perf_set_shadow_time(struct perf_event *event,
1327 struct perf_event_context *ctx, 1328 struct perf_event_context *ctx,
@@ -1806,6 +1807,7 @@ retry:
1806out: 1807out:
1807 raw_spin_unlock_irq(&ctx->lock); 1808 raw_spin_unlock_irq(&ctx->lock);
1808} 1809}
1810EXPORT_SYMBOL_GPL(perf_event_enable);
1809 1811
1810int perf_event_refresh(struct perf_event *event, int refresh) 1812int perf_event_refresh(struct perf_event *event, int refresh)
1811{ 1813{