oprofile: convert oprofile from timer_hook to hrtimer
Martin Schwidefsky [Tue, 2 Mar 2010 15:01:10 +0000 (16:01 +0100)]
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>

drivers/oprofile/oprof.c
drivers/oprofile/oprof.h
drivers/oprofile/timer_int.c

index dc8a042..b336cd9 100644 (file)
@@ -253,22 +253,26 @@ static int __init oprofile_init(void)
        int err;
 
        err = oprofile_arch_init(&oprofile_ops);
-
        if (err < 0 || timer) {
                printk(KERN_INFO "oprofile: using timer interrupt.\n");
-               oprofile_timer_init(&oprofile_ops);
+               err = oprofile_timer_init(&oprofile_ops);
+               if (err)
+                       goto out_arch;
        }
-
        err = oprofilefs_register();
        if (err)
-               oprofile_arch_exit();
+               goto out_arch;
+       return 0;
 
+out_arch:
+       oprofile_arch_exit();
        return err;
 }
 
 
 static void __exit oprofile_exit(void)
 {
+       oprofile_timer_exit();
        oprofilefs_unregister();
        oprofile_arch_exit();
 }
index cb92f5c..47e12cb 100644 (file)
@@ -34,7 +34,8 @@ struct super_block;
 struct dentry;
 
 void oprofile_create_files(struct super_block *sb, struct dentry *root);
-void oprofile_timer_init(struct oprofile_operations *ops);
+int oprofile_timer_init(struct oprofile_operations *ops);
+void oprofile_timer_exit(void);
 
 int oprofile_set_backtrace(unsigned long depth);
 int oprofile_set_timeout(unsigned long time);
index 333f915..dc0ae4d 100644 (file)
 #include <linux/oprofile.h>
 #include <linux/profile.h>
 #include <linux/init.h>
+#include <linux/cpu.h>
+#include <linux/hrtimer.h>
+#include <asm/irq_regs.h>
 #include <asm/ptrace.h>
 
 #include "oprof.h"
 
-static int timer_notify(struct pt_regs *regs)
+static DEFINE_PER_CPU(struct hrtimer, oprofile_hrtimer);
+
+static enum hrtimer_restart oprofile_hrtimer_notify(struct hrtimer *hrtimer)
+{
+       oprofile_add_sample(get_irq_regs(), 0);
+       hrtimer_forward_now(hrtimer, ns_to_ktime(TICK_NSEC));
+       return HRTIMER_RESTART;
+}
+
+static void __oprofile_hrtimer_start(void *unused)
+{
+       struct hrtimer *hrtimer = &__get_cpu_var(oprofile_hrtimer);
+
+       hrtimer_init(hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+       hrtimer->function = oprofile_hrtimer_notify;
+
+       hrtimer_start(hrtimer, ns_to_ktime(TICK_NSEC),
+                     HRTIMER_MODE_REL_PINNED);
+}
+
+static int oprofile_hrtimer_start(void)
 {
-       oprofile_add_sample(regs, 0);
+       on_each_cpu(__oprofile_hrtimer_start, NULL, 1);
        return 0;
 }
 
-static int timer_start(void)
+static void __oprofile_hrtimer_stop(int cpu)
 {
-       return register_timer_hook(timer_notify);
+       struct hrtimer *hrtimer = &per_cpu(oprofile_hrtimer, cpu);
+
+       hrtimer_cancel(hrtimer);
 }
 
+static void oprofile_hrtimer_stop(void)
+{
+       int cpu;
+
+       for_each_online_cpu(cpu)
+               __oprofile_hrtimer_stop(cpu);
+}
 
-static void timer_stop(void)
+static int __cpuinit oprofile_cpu_notify(struct notifier_block *self,
+                                        unsigned long action, void *hcpu)
 {
-       unregister_timer_hook(timer_notify);
+       long cpu = (long) hcpu;
+
+       switch (action) {
+       case CPU_ONLINE:
+       case CPU_ONLINE_FROZEN:
+               smp_call_function_single(cpu, __oprofile_hrtimer_start,
+                                        NULL, 1);
+               break;
+       case CPU_DEAD:
+       case CPU_DEAD_FROZEN:
+               __oprofile_hrtimer_stop(cpu);
+               break;
+       }
+       return NOTIFY_OK;
 }
 
+static struct notifier_block __refdata oprofile_cpu_notifier = {
+       .notifier_call = oprofile_cpu_notify,
+};
 
-void __init oprofile_timer_init(struct oprofile_operations *ops)
+int __init oprofile_timer_init(struct oprofile_operations *ops)
 {
+       int rc;
+
+       rc = register_hotcpu_notifier(&oprofile_cpu_notifier);
+       if (rc)
+               return rc;
        ops->create_files = NULL;
        ops->setup = NULL;
        ops->shutdown = NULL;
-       ops->start = timer_start;
-       ops->stop = timer_stop;
+       ops->start = oprofile_hrtimer_start;
+       ops->stop = oprofile_hrtimer_stop;
        ops->cpu_type = "timer";
+       return 0;
+}
+
+void __exit oprofile_timer_exit(void)
+{
+       unregister_hotcpu_notifier(&oprofile_cpu_notifier);
 }