diff options
author | David S. Miller <davem@davemloft.net> | 2009-01-30 00:22:47 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-01-30 03:03:53 -0500 |
commit | e5553a6d04421eec326a629571d696e8e745a0e4 (patch) | |
tree | b6fe49a18135dbe27a464fb78828b2150c679689 /arch/sparc/oprofile/init.c | |
parent | c3cf5e8cc56d272f828a66610bb78bbb727b2ce1 (diff) |
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 @@ | |||
13 | #include <linux/init.h> | 13 | #include <linux/init.h> |
14 | 14 | ||
15 | #ifdef CONFIG_SPARC64 | 15 | #ifdef CONFIG_SPARC64 |
16 | #include <asm/hypervisor.h> | 16 | #include <linux/notifier.h> |
17 | #include <asm/spitfire.h> | 17 | #include <linux/rcupdate.h> |
18 | #include <asm/cpudata.h> | 18 | #include <linux/kdebug.h> |
19 | #include <asm/irq.h> | 19 | #include <asm/nmi.h> |
20 | #include <asm/pcr.h> | ||
21 | 20 | ||
22 | static int nmi_enabled; | 21 | static int profile_timer_exceptions_notify(struct notifier_block *self, |
23 | 22 | unsigned long val, void *data) | |
24 | /* In order to commonize as much of the implementation as | ||
25 | * possible, we use PICH as our counter. Mostly this is | ||
26 | * to accomodate Niagara-1 which can only count insn cycles | ||
27 | * in PICH. | ||
28 | */ | ||
29 | static u64 picl_value(void) | ||
30 | { | ||
31 | u32 delta = local_cpu_data().clock_tick / HZ; | ||
32 | |||
33 | return ((u64)((0 - delta) & 0xffffffff)) << 32; | ||
34 | } | ||
35 | |||
36 | #define PCR_SUN4U_ENABLE (PCR_PIC_PRIV | PCR_STRACE | PCR_UTRACE) | ||
37 | #define PCR_N2_ENABLE (PCR_PIC_PRIV | PCR_STRACE | PCR_UTRACE | \ | ||
38 | PCR_N2_TOE_OV1 | \ | ||
39 | (2 << PCR_N2_SL1_SHIFT) | \ | ||
40 | (0xff << PCR_N2_MASK1_SHIFT)) | ||
41 | |||
42 | static u64 pcr_enable; | ||
43 | |||
44 | static void nmi_handler(struct pt_regs *regs) | ||
45 | { | 23 | { |
46 | pcr_ops->write(PCR_PIC_PRIV); | 24 | struct die_args *args = (struct die_args *)data; |
25 | int ret = NOTIFY_DONE; | ||
47 | 26 | ||
48 | if (nmi_enabled) { | 27 | switch (val) { |
49 | oprofile_add_sample(regs, 0); | 28 | case DIE_NMI: |
50 | 29 | oprofile_add_sample(args->regs, 0); | |
51 | write_pic(picl_value()); | 30 | ret = NOTIFY_STOP; |
52 | pcr_ops->write(pcr_enable); | 31 | break; |
32 | default: | ||
33 | break; | ||
53 | } | 34 | } |
35 | return ret; | ||
54 | } | 36 | } |
55 | 37 | ||
56 | /* We count "clock cycle" events in the lower 32-bit PIC. | 38 | static struct notifier_block profile_timer_exceptions_nb = { |
57 | * Then configure it such that it overflows every HZ, and thus | 39 | .notifier_call = profile_timer_exceptions_notify, |
58 | * generates a level 15 interrupt at that frequency. | 40 | }; |
59 | */ | ||
60 | static void cpu_nmi_start(void *_unused) | ||
61 | { | ||
62 | pcr_ops->write(PCR_PIC_PRIV); | ||
63 | write_pic(picl_value()); | ||
64 | |||
65 | pcr_ops->write(pcr_enable); | ||
66 | } | ||
67 | 41 | ||
68 | static void cpu_nmi_stop(void *_unused) | 42 | static int timer_start(void) |
69 | { | 43 | { |
70 | pcr_ops->write(PCR_PIC_PRIV); | 44 | if (register_die_notifier(&profile_timer_exceptions_nb)) |
45 | return 1; | ||
46 | nmi_adjust_hz(HZ); | ||
47 | return 0; | ||
71 | } | 48 | } |
72 | 49 | ||
73 | static int nmi_start(void) | ||
74 | { | ||
75 | int err = register_perfctr_intr(nmi_handler); | ||
76 | |||
77 | if (!err) { | ||
78 | nmi_enabled = 1; | ||
79 | wmb(); | ||
80 | err = on_each_cpu(cpu_nmi_start, NULL, 1); | ||
81 | if (err) { | ||
82 | nmi_enabled = 0; | ||
83 | wmb(); | ||
84 | on_each_cpu(cpu_nmi_stop, NULL, 1); | ||
85 | release_perfctr_intr(nmi_handler); | ||
86 | } | ||
87 | } | ||
88 | |||
89 | return err; | ||
90 | } | ||
91 | 50 | ||
92 | static void nmi_stop(void) | 51 | static void timer_stop(void) |
93 | { | 52 | { |
94 | nmi_enabled = 0; | 53 | nmi_adjust_hz(1); |
95 | wmb(); | 54 | unregister_die_notifier(&profile_timer_exceptions_nb); |
96 | 55 | synchronize_sched(); /* Allow already-started NMIs to complete. */ | |
97 | on_each_cpu(cpu_nmi_stop, NULL, 1); | ||
98 | release_perfctr_intr(nmi_handler); | ||
99 | synchronize_sched(); | ||
100 | } | 56 | } |
101 | 57 | ||
102 | static int oprofile_nmi_init(struct oprofile_operations *ops) | 58 | static int op_nmi_timer_init(struct oprofile_operations *ops) |
103 | { | 59 | { |
104 | switch (tlb_type) { | 60 | if (!nmi_usable) |
105 | case hypervisor: | ||
106 | pcr_enable = PCR_N2_ENABLE; | ||
107 | break; | ||
108 | |||
109 | case cheetah: | ||
110 | case cheetah_plus: | ||
111 | pcr_enable = PCR_SUN4U_ENABLE; | ||
112 | break; | ||
113 | |||
114 | default: | ||
115 | return -ENODEV; | 61 | return -ENODEV; |
116 | } | ||
117 | 62 | ||
118 | ops->create_files = NULL; | 63 | ops->start = timer_start; |
119 | ops->setup = NULL; | 64 | ops->stop = timer_stop; |
120 | ops->shutdown = NULL; | ||
121 | ops->start = nmi_start; | ||
122 | ops->stop = nmi_stop; | ||
123 | ops->cpu_type = "timer"; | 65 | ops->cpu_type = "timer"; |
124 | 66 | printk(KERN_INFO "oprofile: Using perfctr NMI timer interrupt.\n"); | |
125 | printk(KERN_INFO "oprofile: Using perfctr based NMI timer interrupt.\n"); | ||
126 | |||
127 | return 0; | 67 | return 0; |
128 | } | 68 | } |
129 | #endif | 69 | #endif |
@@ -133,7 +73,7 @@ int __init oprofile_arch_init(struct oprofile_operations *ops) | |||
133 | int ret = -ENODEV; | 73 | int ret = -ENODEV; |
134 | 74 | ||
135 | #ifdef CONFIG_SPARC64 | 75 | #ifdef CONFIG_SPARC64 |
136 | ret = oprofile_nmi_init(ops); | 76 | ret = op_nmi_timer_init(ops); |
137 | if (!ret) | 77 | if (!ret) |
138 | return ret; | 78 | return ret; |
139 | #endif | 79 | #endif |