diff options
author | David S. Miller <davem@davemloft.net> | 2009-01-30 06:22:47 +0100 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-01-30 09:03:53 +0100 |
commit | e5553a6d04421eec326a629571d696e8e745a0e4 (patch) | |
tree | b6fe49a18135dbe27a464fb78828b2150c679689 /arch/sparc/oprofile/init.c | |
parent | sparc: Probe PMU type and record in sparc_pmu_type. (diff) | |
download | linux-e5553a6d04421eec326a629571d696e8e745a0e4.tar.xz linux-e5553a6d04421eec326a629571d696e8e745a0e4.zip |
sparc64: Implement NMI watchdog on capable cpus.
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'arch/sparc/oprofile/init.c')
-rw-r--r-- | arch/sparc/oprofile/init.c | 128 |
1 files changed, 34 insertions, 94 deletions
diff --git a/arch/sparc/oprofile/init.c b/arch/sparc/oprofile/init.c index c8877a5202b0..d172f86439b1 100644 --- a/arch/sparc/oprofile/init.c +++ b/arch/sparc/oprofile/init.c @@ -13,117 +13,57 @@ #include <linux/init.h> #ifdef CONFIG_SPARC64 -#include <asm/hypervisor.h> -#include <asm/spitfire.h> -#include <asm/cpudata.h> -#include <asm/irq.h> -#include <asm/pcr.h> +#include <linux/notifier.h> +#include <linux/rcupdate.h> +#include <linux/kdebug.h> +#include <asm/nmi.h> -static int nmi_enabled; - -/* In order to commonize as much of the implementation as - * possible, we use PICH as our counter. Mostly this is - * to accomodate Niagara-1 which can only count insn cycles - * in PICH. - */ -static u64 picl_value(void) -{ - u32 delta = local_cpu_data().clock_tick / HZ; - - return ((u64)((0 - delta) & 0xffffffff)) << 32; -} - -#define PCR_SUN4U_ENABLE (PCR_PIC_PRIV | PCR_STRACE | PCR_UTRACE) -#define PCR_N2_ENABLE (PCR_PIC_PRIV | PCR_STRACE | PCR_UTRACE | \ - PCR_N2_TOE_OV1 | \ - (2 << PCR_N2_SL1_SHIFT) | \ - (0xff << PCR_N2_MASK1_SHIFT)) - -static u64 pcr_enable; - -static void nmi_handler(struct pt_regs *regs) +static int profile_timer_exceptions_notify(struct notifier_block *self, + unsigned long val, void *data) { - pcr_ops->write(PCR_PIC_PRIV); + struct die_args *args = (struct die_args *)data; + int ret = NOTIFY_DONE; - if (nmi_enabled) { - oprofile_add_sample(regs, 0); - - write_pic(picl_value()); - pcr_ops->write(pcr_enable); + switch (val) { + case DIE_NMI: + oprofile_add_sample(args->regs, 0); + ret = NOTIFY_STOP; + break; + default: + break; } + return ret; } -/* We count "clock cycle" events in the lower 32-bit PIC. - * Then configure it such that it overflows every HZ, and thus - * generates a level 15 interrupt at that frequency. - */ -static void cpu_nmi_start(void *_unused) -{ - pcr_ops->write(PCR_PIC_PRIV); - write_pic(picl_value()); - - pcr_ops->write(pcr_enable); -} +static struct notifier_block profile_timer_exceptions_nb = { + .notifier_call = profile_timer_exceptions_notify, +}; -static void cpu_nmi_stop(void *_unused) +static int timer_start(void) { - pcr_ops->write(PCR_PIC_PRIV); + if (register_die_notifier(&profile_timer_exceptions_nb)) + return 1; + nmi_adjust_hz(HZ); + return 0; } -static int nmi_start(void) -{ - int err = register_perfctr_intr(nmi_handler); - - if (!err) { - nmi_enabled = 1; - wmb(); - err = on_each_cpu(cpu_nmi_start, NULL, 1); - if (err) { - nmi_enabled = 0; - wmb(); - on_each_cpu(cpu_nmi_stop, NULL, 1); - release_perfctr_intr(nmi_handler); - } - } - - return err; -} -static void nmi_stop(void) +static void timer_stop(void) { - nmi_enabled = 0; - wmb(); - - on_each_cpu(cpu_nmi_stop, NULL, 1); - release_perfctr_intr(nmi_handler); - synchronize_sched(); + nmi_adjust_hz(1); + unregister_die_notifier(&profile_timer_exceptions_nb); + synchronize_sched(); /* Allow already-started NMIs to complete. */ } -static int oprofile_nmi_init(struct oprofile_operations *ops) +static int op_nmi_timer_init(struct oprofile_operations *ops) { - switch (tlb_type) { - case hypervisor: - pcr_enable = PCR_N2_ENABLE; - break; - - case cheetah: - case cheetah_plus: - pcr_enable = PCR_SUN4U_ENABLE; - break; - - default: + if (!nmi_usable) return -ENODEV; - } - ops->create_files = NULL; - ops->setup = NULL; - ops->shutdown = NULL; - ops->start = nmi_start; - ops->stop = nmi_stop; + ops->start = timer_start; + ops->stop = timer_stop; ops->cpu_type = "timer"; - - printk(KERN_INFO "oprofile: Using perfctr based NMI timer interrupt.\n"); - + printk(KERN_INFO "oprofile: Using perfctr NMI timer interrupt.\n"); return 0; } #endif @@ -133,7 +73,7 @@ int __init oprofile_arch_init(struct oprofile_operations *ops) int ret = -ENODEV; #ifdef CONFIG_SPARC64 - ret = oprofile_nmi_init(ops); + ret = op_nmi_timer_init(ops); if (!ret) return ret; #endif |