diff options
author | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2010-03-02 10:01:10 -0500 |
---|---|---|
committer | Robert Richter <robert.richter@amd.com> | 2010-03-02 11:03:20 -0500 |
commit | bc078e4eab65f11bbaeed380593ab8151b30d703 (patch) | |
tree | 8d61457355fad4e5b5938cd7c01a1dd379778789 | |
parent | cfc9c0b450176a077205ef39092f0dc1a04e020a (diff) |
oprofile: convert oprofile from timer_hook to hrtimer
Oprofile is currently broken on systems running with NOHZ enabled.
A maximum of 1 tick is accounted via the timer_hook if a cpu sleeps
for a longer period of time. This does bad things to the percentages
in the profiler output. To solve this problem convert oprofile to
use a restarting hrtimer instead of the timer_hook.
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: Robert Richter <robert.richter@amd.com>
-rw-r--r-- | drivers/oprofile/oprof.c | 12 | ||||
-rw-r--r-- | drivers/oprofile/oprof.h | 3 | ||||
-rw-r--r-- | drivers/oprofile/timer_int.c | 78 |
3 files changed, 79 insertions, 14 deletions
diff --git a/drivers/oprofile/oprof.c b/drivers/oprofile/oprof.c index dc8a0428260d..b336cd9ee7a1 100644 --- a/drivers/oprofile/oprof.c +++ b/drivers/oprofile/oprof.c | |||
@@ -253,22 +253,26 @@ static int __init oprofile_init(void) | |||
253 | int err; | 253 | int err; |
254 | 254 | ||
255 | err = oprofile_arch_init(&oprofile_ops); | 255 | err = oprofile_arch_init(&oprofile_ops); |
256 | |||
257 | if (err < 0 || timer) { | 256 | if (err < 0 || timer) { |
258 | printk(KERN_INFO "oprofile: using timer interrupt.\n"); | 257 | printk(KERN_INFO "oprofile: using timer interrupt.\n"); |
259 | oprofile_timer_init(&oprofile_ops); | 258 | err = oprofile_timer_init(&oprofile_ops); |
259 | if (err) | ||
260 | goto out_arch; | ||
260 | } | 261 | } |
261 | |||
262 | err = oprofilefs_register(); | 262 | err = oprofilefs_register(); |
263 | if (err) | 263 | if (err) |
264 | oprofile_arch_exit(); | 264 | goto out_arch; |
265 | return 0; | ||
265 | 266 | ||
267 | out_arch: | ||
268 | oprofile_arch_exit(); | ||
266 | return err; | 269 | return err; |
267 | } | 270 | } |
268 | 271 | ||
269 | 272 | ||
270 | static void __exit oprofile_exit(void) | 273 | static void __exit oprofile_exit(void) |
271 | { | 274 | { |
275 | oprofile_timer_exit(); | ||
272 | oprofilefs_unregister(); | 276 | oprofilefs_unregister(); |
273 | oprofile_arch_exit(); | 277 | oprofile_arch_exit(); |
274 | } | 278 | } |
diff --git a/drivers/oprofile/oprof.h b/drivers/oprofile/oprof.h index cb92f5c98c1a..47e12cb4ee8b 100644 --- a/drivers/oprofile/oprof.h +++ b/drivers/oprofile/oprof.h | |||
@@ -34,7 +34,8 @@ struct super_block; | |||
34 | struct dentry; | 34 | struct dentry; |
35 | 35 | ||
36 | void oprofile_create_files(struct super_block *sb, struct dentry *root); | 36 | void oprofile_create_files(struct super_block *sb, struct dentry *root); |
37 | void oprofile_timer_init(struct oprofile_operations *ops); | 37 | int oprofile_timer_init(struct oprofile_operations *ops); |
38 | void oprofile_timer_exit(void); | ||
38 | 39 | ||
39 | int oprofile_set_backtrace(unsigned long depth); | 40 | int oprofile_set_backtrace(unsigned long depth); |
40 | int oprofile_set_timeout(unsigned long time); | 41 | int oprofile_set_timeout(unsigned long time); |
diff --git a/drivers/oprofile/timer_int.c b/drivers/oprofile/timer_int.c index 333f915568c7..dc0ae4d14dff 100644 --- a/drivers/oprofile/timer_int.c +++ b/drivers/oprofile/timer_int.c | |||
@@ -13,34 +13,94 @@ | |||
13 | #include <linux/oprofile.h> | 13 | #include <linux/oprofile.h> |
14 | #include <linux/profile.h> | 14 | #include <linux/profile.h> |
15 | #include <linux/init.h> | 15 | #include <linux/init.h> |
16 | #include <linux/cpu.h> | ||
17 | #include <linux/hrtimer.h> | ||
18 | #include <asm/irq_regs.h> | ||
16 | #include <asm/ptrace.h> | 19 | #include <asm/ptrace.h> |
17 | 20 | ||
18 | #include "oprof.h" | 21 | #include "oprof.h" |
19 | 22 | ||
20 | static int timer_notify(struct pt_regs *regs) | 23 | static DEFINE_PER_CPU(struct hrtimer, oprofile_hrtimer); |
24 | |||
25 | static enum hrtimer_restart oprofile_hrtimer_notify(struct hrtimer *hrtimer) | ||
26 | { | ||
27 | oprofile_add_sample(get_irq_regs(), 0); | ||
28 | hrtimer_forward_now(hrtimer, ns_to_ktime(TICK_NSEC)); | ||
29 | return HRTIMER_RESTART; | ||
30 | } | ||
31 | |||
32 | static void __oprofile_hrtimer_start(void *unused) | ||
33 | { | ||
34 | struct hrtimer *hrtimer = &__get_cpu_var(oprofile_hrtimer); | ||
35 | |||
36 | hrtimer_init(hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); | ||
37 | hrtimer->function = oprofile_hrtimer_notify; | ||
38 | |||
39 | hrtimer_start(hrtimer, ns_to_ktime(TICK_NSEC), | ||
40 | HRTIMER_MODE_REL_PINNED); | ||
41 | } | ||
42 | |||
43 | static int oprofile_hrtimer_start(void) | ||
21 | { | 44 | { |
22 | oprofile_add_sample(regs, 0); | 45 | on_each_cpu(__oprofile_hrtimer_start, NULL, 1); |
23 | return 0; | 46 | return 0; |
24 | } | 47 | } |
25 | 48 | ||
26 | static int timer_start(void) | 49 | static void __oprofile_hrtimer_stop(int cpu) |
27 | { | 50 | { |
28 | return register_timer_hook(timer_notify); | 51 | struct hrtimer *hrtimer = &per_cpu(oprofile_hrtimer, cpu); |
52 | |||
53 | hrtimer_cancel(hrtimer); | ||
29 | } | 54 | } |
30 | 55 | ||
56 | static void oprofile_hrtimer_stop(void) | ||
57 | { | ||
58 | int cpu; | ||
59 | |||
60 | for_each_online_cpu(cpu) | ||
61 | __oprofile_hrtimer_stop(cpu); | ||
62 | } | ||
31 | 63 | ||
32 | static void timer_stop(void) | 64 | static int __cpuinit oprofile_cpu_notify(struct notifier_block *self, |
65 | unsigned long action, void *hcpu) | ||
33 | { | 66 | { |
34 | unregister_timer_hook(timer_notify); | 67 | long cpu = (long) hcpu; |
68 | |||
69 | switch (action) { | ||
70 | case CPU_ONLINE: | ||
71 | case CPU_ONLINE_FROZEN: | ||
72 | smp_call_function_single(cpu, __oprofile_hrtimer_start, | ||
73 | NULL, 1); | ||
74 | break; | ||
75 | case CPU_DEAD: | ||
76 | case CPU_DEAD_FROZEN: | ||
77 | __oprofile_hrtimer_stop(cpu); | ||
78 | break; | ||
79 | } | ||
80 | return NOTIFY_OK; | ||
35 | } | 81 | } |
36 | 82 | ||
83 | static struct notifier_block __refdata oprofile_cpu_notifier = { | ||
84 | .notifier_call = oprofile_cpu_notify, | ||
85 | }; | ||
37 | 86 | ||
38 | void __init oprofile_timer_init(struct oprofile_operations *ops) | 87 | int __init oprofile_timer_init(struct oprofile_operations *ops) |
39 | { | 88 | { |
89 | int rc; | ||
90 | |||
91 | rc = register_hotcpu_notifier(&oprofile_cpu_notifier); | ||
92 | if (rc) | ||
93 | return rc; | ||
40 | ops->create_files = NULL; | 94 | ops->create_files = NULL; |
41 | ops->setup = NULL; | 95 | ops->setup = NULL; |
42 | ops->shutdown = NULL; | 96 | ops->shutdown = NULL; |
43 | ops->start = timer_start; | 97 | ops->start = oprofile_hrtimer_start; |
44 | ops->stop = timer_stop; | 98 | ops->stop = oprofile_hrtimer_stop; |
45 | ops->cpu_type = "timer"; | 99 | ops->cpu_type = "timer"; |
100 | return 0; | ||
101 | } | ||
102 | |||
103 | void __exit oprofile_timer_exit(void) | ||
104 | { | ||
105 | unregister_hotcpu_notifier(&oprofile_cpu_notifier); | ||
46 | } | 106 | } |