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 | } |
