diff options
Diffstat (limited to 'arch/sparc/kernel/nmi.c')
| -rw-r--r-- | arch/sparc/kernel/nmi.c | 74 |
1 files changed, 54 insertions, 20 deletions
diff --git a/arch/sparc/kernel/nmi.c b/arch/sparc/kernel/nmi.c index 2c0cc72d295b..b129611590a4 100644 --- a/arch/sparc/kernel/nmi.c +++ b/arch/sparc/kernel/nmi.c | |||
| @@ -19,6 +19,7 @@ | |||
| 19 | #include <linux/delay.h> | 19 | #include <linux/delay.h> |
| 20 | #include <linux/smp.h> | 20 | #include <linux/smp.h> |
| 21 | 21 | ||
| 22 | #include <asm/perf_event.h> | ||
| 22 | #include <asm/ptrace.h> | 23 | #include <asm/ptrace.h> |
| 23 | #include <asm/local.h> | 24 | #include <asm/local.h> |
| 24 | #include <asm/pcr.h> | 25 | #include <asm/pcr.h> |
| @@ -31,13 +32,19 @@ | |||
| 31 | * level 14 as our IRQ off level. | 32 | * level 14 as our IRQ off level. |
| 32 | */ | 33 | */ |
| 33 | 34 | ||
| 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); |
| @@ -45,7 +52,7 @@ static DEFINE_PER_CPU(int, nmi_touch); | |||
| 45 | 52 | ||
| 46 | void touch_nmi_watchdog(void) | 53 | void touch_nmi_watchdog(void) |
| 47 | { | 54 | { |
| 48 | if (nmi_watchdog_active) { | 55 | if (atomic_read(&nmi_active)) { |
| 49 | int cpu; | 56 | int cpu; |
| 50 | 57 | ||
| 51 | for_each_present_cpu(cpu) { | 58 | for_each_present_cpu(cpu) { |
| @@ -78,6 +85,7 @@ static void die_nmi(const char *str, struct pt_regs *regs, int do_panic) | |||
| 78 | if (do_panic || panic_on_oops) | 85 | if (do_panic || panic_on_oops) |
| 79 | panic("Non maskable interrupt"); | 86 | panic("Non maskable interrupt"); |
| 80 | 87 | ||
| 88 | nmi_exit(); | ||
| 81 | local_irq_enable(); | 89 | local_irq_enable(); |
| 82 | do_exit(SIGBUS); | 90 | do_exit(SIGBUS); |
| 83 | } | 91 | } |
| @@ -92,6 +100,8 @@ notrace __kprobes void perfctr_irq(int irq, struct pt_regs *regs) | |||
| 92 | 100 | ||
| 93 | local_cpu_data().__nmi_count++; | 101 | local_cpu_data().__nmi_count++; |
| 94 | 102 | ||
| 103 | nmi_enter(); | ||
| 104 | |||
| 95 | if (notify_die(DIE_NMI, "nmi", regs, 0, | 105 | if (notify_die(DIE_NMI, "nmi", regs, 0, |
| 96 | pt_regs_trap_type(regs), SIGINT) == NOTIFY_STOP) | 106 | pt_regs_trap_type(regs), SIGINT) == NOTIFY_STOP) |
| 97 | touched = 1; | 107 | touched = 1; |
| @@ -103,17 +113,19 @@ notrace __kprobes void perfctr_irq(int irq, struct pt_regs *regs) | |||
| 103 | } | 113 | } |
| 104 | if (!touched && __get_cpu_var(last_irq_sum) == sum) { | 114 | if (!touched && __get_cpu_var(last_irq_sum) == sum) { |
| 105 | local_inc(&__get_cpu_var(alert_counter)); | 115 | local_inc(&__get_cpu_var(alert_counter)); |
| 106 | if (local_read(&__get_cpu_var(alert_counter)) == 5 * nmi_hz) | 116 | if (local_read(&__get_cpu_var(alert_counter)) == 30 * nmi_hz) |
| 107 | die_nmi("BUG: NMI Watchdog detected LOCKUP", | 117 | die_nmi("BUG: NMI Watchdog detected LOCKUP", |
| 108 | regs, panic_on_timeout); | 118 | regs, panic_on_timeout); |
| 109 | } else { | 119 | } else { |
| 110 | __get_cpu_var(last_irq_sum) = sum; | 120 | __get_cpu_var(last_irq_sum) = sum; |
| 111 | local_set(&__get_cpu_var(alert_counter), 0); | 121 | local_set(&__get_cpu_var(alert_counter), 0); |
| 112 | } | 122 | } |
| 113 | if (nmi_usable) { | 123 | if (__get_cpu_var(wd_enabled)) { |
| 114 | write_pic(picl_value(nmi_hz)); | 124 | write_pic(picl_value(nmi_hz)); |
| 115 | pcr_ops->write(pcr_enable); | 125 | pcr_ops->write(pcr_enable); |
| 116 | } | 126 | } |
| 127 | |||
| 128 | nmi_exit(); | ||
| 117 | } | 129 | } |
| 118 | 130 | ||
| 119 | static inline unsigned int get_nmi_count(int cpu) | 131 | static inline unsigned int get_nmi_count(int cpu) |
| @@ -121,8 +133,6 @@ static inline unsigned int get_nmi_count(int cpu) | |||
| 121 | return cpu_data(cpu).__nmi_count; | 133 | return cpu_data(cpu).__nmi_count; |
| 122 | } | 134 | } |
| 123 | 135 | ||
| 124 | static int endflag __initdata; | ||
| 125 | |||
| 126 | static __init void nmi_cpu_busy(void *data) | 136 | static __init void nmi_cpu_busy(void *data) |
| 127 | { | 137 | { |
| 128 | local_irq_enable_in_hardirq(); | 138 | local_irq_enable_in_hardirq(); |
| @@ -143,12 +153,15 @@ static void report_broken_nmi(int cpu, int *prev_nmi_count) | |||
| 143 | printk(KERN_WARNING | 153 | printk(KERN_WARNING |
| 144 | "and attach the output of the 'dmesg' command.\n"); | 154 | "and attach the output of the 'dmesg' command.\n"); |
| 145 | 155 | ||
| 146 | nmi_usable = 0; | 156 | per_cpu(wd_enabled, cpu) = 0; |
| 157 | atomic_dec(&nmi_active); | ||
| 147 | } | 158 | } |
| 148 | 159 | ||
| 149 | static void stop_watchdog(void *unused) | 160 | void stop_nmi_watchdog(void *unused) |
| 150 | { | 161 | { |
| 151 | pcr_ops->write(PCR_PIC_PRIV); | 162 | pcr_ops->write(PCR_PIC_PRIV); |
| 163 | __get_cpu_var(wd_enabled) = 0; | ||
| 164 | atomic_dec(&nmi_active); | ||
| 152 | } | 165 | } |
| 153 | 166 | ||
| 154 | static int __init check_nmi_watchdog(void) | 167 | static int __init check_nmi_watchdog(void) |
| @@ -156,6 +169,9 @@ static int __init check_nmi_watchdog(void) | |||
| 156 | unsigned int *prev_nmi_count; | 169 | unsigned int *prev_nmi_count; |
| 157 | int cpu, err; | 170 | int cpu, err; |
| 158 | 171 | ||
| 172 | if (!atomic_read(&nmi_active)) | ||
| 173 | return 0; | ||
| 174 | |||
| 159 | prev_nmi_count = kmalloc(nr_cpu_ids * sizeof(unsigned int), GFP_KERNEL); | 175 | prev_nmi_count = kmalloc(nr_cpu_ids * sizeof(unsigned int), GFP_KERNEL); |
| 160 | if (!prev_nmi_count) { | 176 | if (!prev_nmi_count) { |
| 161 | err = -ENOMEM; | 177 | err = -ENOMEM; |
| @@ -172,12 +188,15 @@ static int __init check_nmi_watchdog(void) | |||
| 172 | mdelay((20 * 1000) / nmi_hz); /* wait 20 ticks */ | 188 | mdelay((20 * 1000) / nmi_hz); /* wait 20 ticks */ |
| 173 | 189 | ||
| 174 | for_each_online_cpu(cpu) { | 190 | for_each_online_cpu(cpu) { |
| 191 | if (!per_cpu(wd_enabled, cpu)) | ||
| 192 | continue; | ||
| 175 | if (get_nmi_count(cpu) - prev_nmi_count[cpu] <= 5) | 193 | if (get_nmi_count(cpu) - prev_nmi_count[cpu] <= 5) |
| 176 | report_broken_nmi(cpu, prev_nmi_count); | 194 | report_broken_nmi(cpu, prev_nmi_count); |
| 177 | } | 195 | } |
| 178 | endflag = 1; | 196 | endflag = 1; |
| 179 | if (!nmi_usable) { | 197 | if (!atomic_read(&nmi_active)) { |
| 180 | kfree(prev_nmi_count); | 198 | kfree(prev_nmi_count); |
| 199 | atomic_set(&nmi_active, -1); | ||
| 181 | err = -ENODEV; | 200 | err = -ENODEV; |
| 182 | goto error; | 201 | goto error; |
| 183 | } | 202 | } |
| @@ -188,12 +207,26 @@ static int __init check_nmi_watchdog(void) | |||
| 188 | kfree(prev_nmi_count); | 207 | kfree(prev_nmi_count); |
| 189 | return 0; | 208 | return 0; |
| 190 | error: | 209 | error: |
| 191 | on_each_cpu(stop_watchdog, NULL, 1); | 210 | on_each_cpu(stop_nmi_watchdog, NULL, 1); |
| 192 | return err; | 211 | return err; |
| 193 | } | 212 | } |
| 194 | 213 | ||
| 195 | static void start_watchdog(void *unused) | 214 | void start_nmi_watchdog(void *unused) |
| 196 | { | 215 | { |
| 216 | __get_cpu_var(wd_enabled) = 1; | ||
| 217 | atomic_inc(&nmi_active); | ||
| 218 | |||
| 219 | pcr_ops->write(PCR_PIC_PRIV); | ||
| 220 | write_pic(picl_value(nmi_hz)); | ||
| 221 | |||
| 222 | pcr_ops->write(pcr_enable); | ||
| 223 | } | ||
| 224 | |||
| 225 | static void nmi_adjust_hz_one(void *unused) | ||
| 226 | { | ||
| 227 | if (!__get_cpu_var(wd_enabled)) | ||
| 228 | return; | ||
| 229 | |||
| 197 | pcr_ops->write(PCR_PIC_PRIV); | 230 | pcr_ops->write(PCR_PIC_PRIV); |
| 198 | write_pic(picl_value(nmi_hz)); | 231 | write_pic(picl_value(nmi_hz)); |
| 199 | 232 | ||
| @@ -203,13 +236,13 @@ static void start_watchdog(void *unused) | |||
| 203 | void nmi_adjust_hz(unsigned int new_hz) | 236 | void nmi_adjust_hz(unsigned int new_hz) |
| 204 | { | 237 | { |
| 205 | nmi_hz = new_hz; | 238 | nmi_hz = new_hz; |
| 206 | on_each_cpu(start_watchdog, NULL, 1); | 239 | on_each_cpu(nmi_adjust_hz_one, NULL, 1); |
| 207 | } | 240 | } |
| 208 | EXPORT_SYMBOL_GPL(nmi_adjust_hz); | 241 | EXPORT_SYMBOL_GPL(nmi_adjust_hz); |
| 209 | 242 | ||
| 210 | static int nmi_shutdown(struct notifier_block *nb, unsigned long cmd, void *p) | 243 | static int nmi_shutdown(struct notifier_block *nb, unsigned long cmd, void *p) |
| 211 | { | 244 | { |
| 212 | on_each_cpu(stop_watchdog, NULL, 1); | 245 | on_each_cpu(stop_nmi_watchdog, NULL, 1); |
| 213 | return 0; | 246 | return 0; |
| 214 | } | 247 | } |
| 215 | 248 | ||
| @@ -221,18 +254,19 @@ int __init nmi_init(void) | |||
| 221 | { | 254 | { |
| 222 | int err; | 255 | int err; |
| 223 | 256 | ||
| 224 | nmi_usable = 1; | 257 | on_each_cpu(start_nmi_watchdog, NULL, 1); |
| 225 | |||
| 226 | on_each_cpu(start_watchdog, NULL, 1); | ||
| 227 | 258 | ||
| 228 | err = check_nmi_watchdog(); | 259 | err = check_nmi_watchdog(); |
| 229 | if (!err) { | 260 | if (!err) { |
| 230 | err = register_reboot_notifier(&nmi_reboot_notifier); | 261 | err = register_reboot_notifier(&nmi_reboot_notifier); |
| 231 | if (err) { | 262 | if (err) { |
| 232 | nmi_usable = 0; | 263 | on_each_cpu(stop_nmi_watchdog, NULL, 1); |
| 233 | on_each_cpu(stop_watchdog, NULL, 1); | 264 | atomic_set(&nmi_active, -1); |
| 234 | } | 265 | } |
| 235 | } | 266 | } |
| 267 | if (!err) | ||
| 268 | init_hw_perf_events(); | ||
| 269 | |||
| 236 | return err; | 270 | return err; |
| 237 | } | 271 | } |
| 238 | 272 | ||
