Commit bc078e4eab65f11bbaeed380593ab8151b30d703
Committed by
Robert Richter
1 parent
cfc9c0b450
Exists in
master
and in
4 other branches
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>
Showing 3 changed files with 79 additions and 14 deletions Side-by-side Diff
drivers/oprofile/oprof.c
... | ... | @@ -253,22 +253,26 @@ |
253 | 253 | int err; |
254 | 254 | |
255 | 255 | err = oprofile_arch_init(&oprofile_ops); |
256 | - | |
257 | 256 | if (err < 0 || timer) { |
258 | 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 | 262 | err = oprofilefs_register(); |
263 | 263 | if (err) |
264 | - oprofile_arch_exit(); | |
264 | + goto out_arch; | |
265 | + return 0; | |
265 | 266 | |
267 | +out_arch: | |
268 | + oprofile_arch_exit(); | |
266 | 269 | return err; |
267 | 270 | } |
268 | 271 | |
269 | 272 | |
270 | 273 | static void __exit oprofile_exit(void) |
271 | 274 | { |
275 | + oprofile_timer_exit(); | |
272 | 276 | oprofilefs_unregister(); |
273 | 277 | oprofile_arch_exit(); |
274 | 278 | } |
drivers/oprofile/oprof.h
... | ... | @@ -34,7 +34,8 @@ |
34 | 34 | struct dentry; |
35 | 35 | |
36 | 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 | 40 | int oprofile_set_backtrace(unsigned long depth); |
40 | 41 | int oprofile_set_timeout(unsigned long time); |
drivers/oprofile/timer_int.c
... | ... | @@ -13,35 +13,95 @@ |
13 | 13 | #include <linux/oprofile.h> |
14 | 14 | #include <linux/profile.h> |
15 | 15 | #include <linux/init.h> |
16 | +#include <linux/cpu.h> | |
17 | +#include <linux/hrtimer.h> | |
18 | +#include <asm/irq_regs.h> | |
16 | 19 | #include <asm/ptrace.h> |
17 | 20 | |
18 | 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) | |
21 | 26 | { |
22 | - oprofile_add_sample(regs, 0); | |
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) | |
44 | +{ | |
45 | + on_each_cpu(__oprofile_hrtimer_start, NULL, 1); | |
23 | 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; | |
31 | 59 | |
32 | -static void timer_stop(void) | |
60 | + for_each_online_cpu(cpu) | |
61 | + __oprofile_hrtimer_stop(cpu); | |
62 | +} | |
63 | + | |
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 | 94 | ops->create_files = NULL; |
41 | 95 | ops->setup = NULL; |
42 | 96 | ops->shutdown = NULL; |
43 | - ops->start = timer_start; | |
44 | - ops->stop = timer_stop; | |
97 | + ops->start = oprofile_hrtimer_start; | |
98 | + ops->stop = oprofile_hrtimer_stop; | |
45 | 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 | } |