aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRobert Richter <robert.richter@amd.com>2011-10-11 11:11:08 -0400
committerRobert Richter <robert.richter@amd.com>2011-11-04 11:27:18 -0400
commitdcfce4a095932e6e95d83ad982be3609947963bc (patch)
tree2808b7cbdb57deab25cc1cd847fd0f9ffa058f92
parent75c43a20b220f885c39ffa7cdbbb1191e257a9a9 (diff)
oprofile, x86: Reimplement nmi timer mode using perf event
The legacy x86 nmi watchdog code was removed with the implementation of the perf based nmi watchdog. This broke Oprofile's nmi timer mode. To run nmi timer mode we relied on a continuous ticking nmi source which the nmi watchdog provided. The nmi tick was no longer available and current watchdog can not be used anymore since it runs with very long periods in the range of seconds. This patch reimplements the nmi timer mode using a perf counter nmi source. V2: * removing pr_info() * fix undefined reference to `__udivdi3' for 32 bit build * fix section mismatch of .cpuinit.data:nmi_timer_cpu_nb * removed nmi timer setup in arch/x86 * implemented function stubs for op_nmi_init/exit() * made code more readable in oprofile_init() V3: * fix architectural initialization in oprofile_init() * fix CONFIG_OPROFILE_NMI_TIMER dependencies Acked-by: Peter Zijlstra <a.p.zijlstra@chello.nl> Signed-off-by: Robert Richter <robert.richter@amd.com>
-rw-r--r--arch/Kconfig4
-rw-r--r--arch/x86/oprofile/Makefile3
-rw-r--r--arch/x86/oprofile/init.c30
-rw-r--r--arch/x86/oprofile/nmi_timer_int.c66
-rw-r--r--drivers/oprofile/nmi_timer_int.c173
-rw-r--r--drivers/oprofile/oprof.c24
-rw-r--r--drivers/oprofile/oprof.h9
-rw-r--r--kernel/events/core.c2
8 files changed, 208 insertions, 103 deletions
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 f148cf652678..9e138d00ad36 100644
--- a/arch/x86/oprofile/init.c
+++ b/arch/x86/oprofile/init.c
@@ -16,37 +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
24static int nmi_timer; 27extern void x86_backtrace(struct pt_regs * const regs, unsigned int depth);
25 28
26int __init oprofile_arch_init(struct oprofile_operations *ops) 29int __init oprofile_arch_init(struct oprofile_operations *ops)
27{ 30{
28 int ret;
29
30 ret = -ENODEV;
31
32#ifdef CONFIG_X86_LOCAL_APIC
33 ret = op_nmi_init(ops);
34#endif
35 nmi_timer = (ret != 0);
36#ifdef CONFIG_X86_IO_APIC
37 if (nmi_timer)
38 ret = op_nmi_timer_init(ops);
39#endif
40 ops->backtrace = x86_backtrace; 31 ops->backtrace = x86_backtrace;
41 32 return op_nmi_init(ops);
42 return ret;
43} 33}
44 34
45
46void oprofile_arch_exit(void) 35void oprofile_arch_exit(void)
47{ 36{
48#ifdef CONFIG_X86_LOCAL_APIC 37 op_nmi_exit();
49 if (!nmi_timer)
50 op_nmi_exit();
51#endif
52} 38}
diff --git a/arch/x86/oprofile/nmi_timer_int.c b/arch/x86/oprofile/nmi_timer_int.c
deleted file mode 100644
index 720bf5a53c51..000000000000
--- a/arch/x86/oprofile/nmi_timer_int.c
+++ /dev/null
@@ -1,66 +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(struct notifier_block *self,
22 unsigned long val, void *data)
23{
24 struct die_args *args = (struct die_args *)data;
25 int ret = NOTIFY_DONE;
26
27 switch (val) {
28 case DIE_NMI:
29 oprofile_add_sample(args->regs, 0);
30 ret = NOTIFY_STOP;
31 break;
32 default:
33 break;
34 }
35 return ret;
36}
37
38static struct notifier_block profile_timer_exceptions_nb = {
39 .notifier_call = profile_timer_exceptions_notify,
40 .next = NULL,
41 .priority = NMI_LOW_PRIOR,
42};
43
44static int timer_start(void)
45{
46 if (register_die_notifier(&profile_timer_exceptions_nb))
47 return 1;
48 return 0;
49}
50
51
52static void timer_stop(void)
53{
54 unregister_die_notifier(&profile_timer_exceptions_nb);
55 synchronize_sched(); /* Allow already-started NMIs to complete. */
56}
57
58
59int __init op_nmi_timer_init(struct oprofile_operations *ops)
60{
61 ops->start = timer_start;
62 ops->stop = timer_stop;
63 ops->cpu_type = "timer";
64 printk(KERN_INFO "oprofile: using NMI timer interrupt.\n");
65 return 0;
66}
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 f7cd06967aed..ed2c3ec07024 100644
--- a/drivers/oprofile/oprof.c
+++ b/drivers/oprofile/oprof.c
@@ -246,26 +246,24 @@ static int __init oprofile_init(void)
246 int err; 246 int err;
247 247
248 /* always init architecture to setup backtrace support */ 248 /* always init architecture to setup backtrace support */
249 timer_mode = 0;
249 err = oprofile_arch_init(&oprofile_ops); 250 err = oprofile_arch_init(&oprofile_ops);
251 if (!err) {
252 if (!timer && !oprofilefs_register())
253 return 0;
254 oprofile_arch_exit();
255 }
250 256
251 timer_mode = err || timer; /* fall back to timer mode on errors */ 257 /* setup timer mode: */
252 if (timer_mode) { 258 timer_mode = 1;
253 if (!err) 259 /* no nmi timer mode if oprofile.timer is set */
254 oprofile_arch_exit(); 260 if (timer || op_nmi_timer_init(&oprofile_ops)) {
255 err = oprofile_timer_init(&oprofile_ops); 261 err = oprofile_timer_init(&oprofile_ops);
256 if (err) 262 if (err)
257 return err; 263 return err;
258 } 264 }
259 265
260 err = oprofilefs_register(); 266 return oprofilefs_register();
261 if (!err)
262 return 0;
263
264 /* failed */
265 if (!timer_mode)
266 oprofile_arch_exit();
267
268 return err;
269} 267}
270 268
271 269
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/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{