diff options
author | David S. Miller <davem@davemloft.net> | 2009-09-09 02:16:06 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-09-09 02:16:06 -0400 |
commit | a8f22264550e64c0cd11fb6647284b0bd6407f9c (patch) | |
tree | e42ef7f04063bef6114c6488c70eff7089b5ea26 /arch/sparc | |
parent | 825c9fb47a0837db12fecf8d360e0e1d284ddb49 (diff) |
sparc64: Manage NMI watchdog enabling like x86.
Use a per-cpu 'wd_enabled' boolean and a global atomic_t count
of watchdog NMI enabled cpus which is set to '-1' if something
is wrong with the watchdog and it can't be used.
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'arch/sparc')
-rw-r--r-- | arch/sparc/include/asm/nmi.h | 2 | ||||
-rw-r--r-- | arch/sparc/kernel/nmi.c | 60 | ||||
-rw-r--r-- | arch/sparc/oprofile/init.c | 2 |
3 files changed, 45 insertions, 19 deletions
diff --git a/arch/sparc/include/asm/nmi.h b/arch/sparc/include/asm/nmi.h index fbd546dd4feb..c7d11e435df9 100644 --- a/arch/sparc/include/asm/nmi.h +++ b/arch/sparc/include/asm/nmi.h | |||
@@ -5,6 +5,6 @@ extern int __init nmi_init(void); | |||
5 | extern void perfctr_irq(int irq, struct pt_regs *regs); | 5 | extern void perfctr_irq(int irq, struct pt_regs *regs); |
6 | extern void nmi_adjust_hz(unsigned int new_hz); | 6 | extern void nmi_adjust_hz(unsigned int new_hz); |
7 | 7 | ||
8 | extern int nmi_usable; | 8 | extern atomic_t nmi_active; |
9 | 9 | ||
10 | #endif /* __NMI_H */ | 10 | #endif /* __NMI_H */ |
diff --git a/arch/sparc/kernel/nmi.c b/arch/sparc/kernel/nmi.c index 2c0cc72d295b..d1614e8384ae 100644 --- a/arch/sparc/kernel/nmi.c +++ b/arch/sparc/kernel/nmi.c | |||
@@ -34,10 +34,17 @@ | |||
34 | static int nmi_watchdog_active; | 34 | static int nmi_watchdog_active; |
35 | static int panic_on_timeout; | 35 | static int panic_on_timeout; |
36 | 36 | ||
37 | int nmi_usable; | 37 | /* nmi_active: |
38 | EXPORT_SYMBOL_GPL(nmi_usable); | 38 | * >0: the NMI watchdog is active, but can be disabled |
39 | * <0: the NMI watchdog has not been set up, and cannot be enabled | ||
40 | * 0: the NMI watchdog is disabled, but can be enabled | ||
41 | */ | ||
42 | atomic_t nmi_active = ATOMIC_INIT(0); /* oprofile uses this */ | ||
43 | EXPORT_SYMBOL(nmi_active); | ||
39 | 44 | ||
40 | static unsigned int nmi_hz = HZ; | 45 | static unsigned int nmi_hz = HZ; |
46 | static DEFINE_PER_CPU(short, wd_enabled); | ||
47 | static int endflag __initdata; | ||
41 | 48 | ||
42 | static DEFINE_PER_CPU(unsigned int, last_irq_sum); | 49 | static DEFINE_PER_CPU(unsigned int, last_irq_sum); |
43 | static DEFINE_PER_CPU(local_t, alert_counter); | 50 | static DEFINE_PER_CPU(local_t, alert_counter); |
@@ -110,7 +117,7 @@ notrace __kprobes void perfctr_irq(int irq, struct pt_regs *regs) | |||
110 | __get_cpu_var(last_irq_sum) = sum; | 117 | __get_cpu_var(last_irq_sum) = sum; |
111 | local_set(&__get_cpu_var(alert_counter), 0); | 118 | local_set(&__get_cpu_var(alert_counter), 0); |
112 | } | 119 | } |
113 | if (nmi_usable) { | 120 | if (__get_cpu_var(wd_enabled)) { |
114 | write_pic(picl_value(nmi_hz)); | 121 | write_pic(picl_value(nmi_hz)); |
115 | pcr_ops->write(pcr_enable); | 122 | pcr_ops->write(pcr_enable); |
116 | } | 123 | } |
@@ -121,8 +128,6 @@ static inline unsigned int get_nmi_count(int cpu) | |||
121 | return cpu_data(cpu).__nmi_count; | 128 | return cpu_data(cpu).__nmi_count; |
122 | } | 129 | } |
123 | 130 | ||
124 | static int endflag __initdata; | ||
125 | |||
126 | static __init void nmi_cpu_busy(void *data) | 131 | static __init void nmi_cpu_busy(void *data) |
127 | { | 132 | { |
128 | local_irq_enable_in_hardirq(); | 133 | local_irq_enable_in_hardirq(); |
@@ -143,12 +148,15 @@ static void report_broken_nmi(int cpu, int *prev_nmi_count) | |||
143 | printk(KERN_WARNING | 148 | printk(KERN_WARNING |
144 | "and attach the output of the 'dmesg' command.\n"); | 149 | "and attach the output of the 'dmesg' command.\n"); |
145 | 150 | ||
146 | nmi_usable = 0; | 151 | per_cpu(wd_enabled, cpu) = 0; |
152 | atomic_dec(&nmi_active); | ||
147 | } | 153 | } |
148 | 154 | ||
149 | static void stop_watchdog(void *unused) | 155 | static void stop_nmi_watchdog(void *unused) |
150 | { | 156 | { |
151 | pcr_ops->write(PCR_PIC_PRIV); | 157 | pcr_ops->write(PCR_PIC_PRIV); |
158 | __get_cpu_var(wd_enabled) = 0; | ||
159 | atomic_dec(&nmi_active); | ||
152 | } | 160 | } |
153 | 161 | ||
154 | static int __init check_nmi_watchdog(void) | 162 | static int __init check_nmi_watchdog(void) |
@@ -156,6 +164,9 @@ static int __init check_nmi_watchdog(void) | |||
156 | unsigned int *prev_nmi_count; | 164 | unsigned int *prev_nmi_count; |
157 | int cpu, err; | 165 | int cpu, err; |
158 | 166 | ||
167 | if (!atomic_read(&nmi_active)) | ||
168 | return 0; | ||
169 | |||
159 | prev_nmi_count = kmalloc(nr_cpu_ids * sizeof(unsigned int), GFP_KERNEL); | 170 | prev_nmi_count = kmalloc(nr_cpu_ids * sizeof(unsigned int), GFP_KERNEL); |
160 | if (!prev_nmi_count) { | 171 | if (!prev_nmi_count) { |
161 | err = -ENOMEM; | 172 | err = -ENOMEM; |
@@ -172,12 +183,15 @@ static int __init check_nmi_watchdog(void) | |||
172 | mdelay((20 * 1000) / nmi_hz); /* wait 20 ticks */ | 183 | mdelay((20 * 1000) / nmi_hz); /* wait 20 ticks */ |
173 | 184 | ||
174 | for_each_online_cpu(cpu) { | 185 | for_each_online_cpu(cpu) { |
186 | if (!per_cpu(wd_enabled, cpu)) | ||
187 | continue; | ||
175 | if (get_nmi_count(cpu) - prev_nmi_count[cpu] <= 5) | 188 | if (get_nmi_count(cpu) - prev_nmi_count[cpu] <= 5) |
176 | report_broken_nmi(cpu, prev_nmi_count); | 189 | report_broken_nmi(cpu, prev_nmi_count); |
177 | } | 190 | } |
178 | endflag = 1; | 191 | endflag = 1; |
179 | if (!nmi_usable) { | 192 | if (!atomic_read(&nmi_active)) { |
180 | kfree(prev_nmi_count); | 193 | kfree(prev_nmi_count); |
194 | atomic_set(&nmi_active, -1); | ||
181 | err = -ENODEV; | 195 | err = -ENODEV; |
182 | goto error; | 196 | goto error; |
183 | } | 197 | } |
@@ -188,12 +202,26 @@ static int __init check_nmi_watchdog(void) | |||
188 | kfree(prev_nmi_count); | 202 | kfree(prev_nmi_count); |
189 | return 0; | 203 | return 0; |
190 | error: | 204 | error: |
191 | on_each_cpu(stop_watchdog, NULL, 1); | 205 | on_each_cpu(stop_nmi_watchdog, NULL, 1); |
192 | return err; | 206 | return err; |
193 | } | 207 | } |
194 | 208 | ||
195 | static void start_watchdog(void *unused) | 209 | static void start_nmi_watchdog(void *unused) |
210 | { | ||
211 | __get_cpu_var(wd_enabled) = 1; | ||
212 | atomic_inc(&nmi_active); | ||
213 | |||
214 | pcr_ops->write(PCR_PIC_PRIV); | ||
215 | write_pic(picl_value(nmi_hz)); | ||
216 | |||
217 | pcr_ops->write(pcr_enable); | ||
218 | } | ||
219 | |||
220 | static void nmi_adjust_hz_one(void *unused) | ||
196 | { | 221 | { |
222 | if (!__get_cpu_var(wd_enabled)) | ||
223 | return; | ||
224 | |||
197 | pcr_ops->write(PCR_PIC_PRIV); | 225 | pcr_ops->write(PCR_PIC_PRIV); |
198 | write_pic(picl_value(nmi_hz)); | 226 | write_pic(picl_value(nmi_hz)); |
199 | 227 | ||
@@ -203,13 +231,13 @@ static void start_watchdog(void *unused) | |||
203 | void nmi_adjust_hz(unsigned int new_hz) | 231 | void nmi_adjust_hz(unsigned int new_hz) |
204 | { | 232 | { |
205 | nmi_hz = new_hz; | 233 | nmi_hz = new_hz; |
206 | on_each_cpu(start_watchdog, NULL, 1); | 234 | on_each_cpu(nmi_adjust_hz_one, NULL, 1); |
207 | } | 235 | } |
208 | EXPORT_SYMBOL_GPL(nmi_adjust_hz); | 236 | EXPORT_SYMBOL_GPL(nmi_adjust_hz); |
209 | 237 | ||
210 | static int nmi_shutdown(struct notifier_block *nb, unsigned long cmd, void *p) | 238 | static int nmi_shutdown(struct notifier_block *nb, unsigned long cmd, void *p) |
211 | { | 239 | { |
212 | on_each_cpu(stop_watchdog, NULL, 1); | 240 | on_each_cpu(stop_nmi_watchdog, NULL, 1); |
213 | return 0; | 241 | return 0; |
214 | } | 242 | } |
215 | 243 | ||
@@ -221,16 +249,14 @@ int __init nmi_init(void) | |||
221 | { | 249 | { |
222 | int err; | 250 | int err; |
223 | 251 | ||
224 | nmi_usable = 1; | 252 | on_each_cpu(start_nmi_watchdog, NULL, 1); |
225 | |||
226 | on_each_cpu(start_watchdog, NULL, 1); | ||
227 | 253 | ||
228 | err = check_nmi_watchdog(); | 254 | err = check_nmi_watchdog(); |
229 | if (!err) { | 255 | if (!err) { |
230 | err = register_reboot_notifier(&nmi_reboot_notifier); | 256 | err = register_reboot_notifier(&nmi_reboot_notifier); |
231 | if (err) { | 257 | if (err) { |
232 | nmi_usable = 0; | 258 | on_each_cpu(stop_nmi_watchdog, NULL, 1); |
233 | on_each_cpu(stop_watchdog, NULL, 1); | 259 | atomic_set(&nmi_active, -1); |
234 | } | 260 | } |
235 | } | 261 | } |
236 | return err; | 262 | return err; |
diff --git a/arch/sparc/oprofile/init.c b/arch/sparc/oprofile/init.c index d172f86439b1..9ce34fd294c9 100644 --- a/arch/sparc/oprofile/init.c +++ b/arch/sparc/oprofile/init.c | |||
@@ -57,7 +57,7 @@ static void timer_stop(void) | |||
57 | 57 | ||
58 | static int op_nmi_timer_init(struct oprofile_operations *ops) | 58 | static int op_nmi_timer_init(struct oprofile_operations *ops) |
59 | { | 59 | { |
60 | if (!nmi_usable) | 60 | if (atomic_read(&nmi_active) <= 0) |
61 | return -ENODEV; | 61 | return -ENODEV; |
62 | 62 | ||
63 | ops->start = timer_start; | 63 | ops->start = timer_start; |