Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

sparc64: Implement NMI watchdog on capable cpus.

Signed-off-by: David S. Miller <davem@davemloft.net>

+319 -173
+1 -1
arch/sparc/include/asm/cpudata_64.h
··· 17 17 typedef struct { 18 18 /* Dcache line 1 */ 19 19 unsigned int __softirq_pending; /* must be 1st, see rtrap.S */ 20 - unsigned int __pad0; 20 + unsigned int __nmi_count; 21 21 unsigned long clock_tick; /* %tick's per second */ 22 22 unsigned long __pad; 23 23 unsigned int __pad1;
+1 -3
arch/sparc/include/asm/irq_64.h
··· 66 66 extern void __init init_IRQ(void); 67 67 extern void fixup_irqs(void); 68 68 69 - extern int register_perfctr_intr(void (*handler)(struct pt_regs *)); 70 - extern void release_perfctr_intr(void (*handler)(struct pt_regs *)); 71 - 72 69 static inline void set_softint(unsigned long bits) 73 70 { 74 71 __asm__ __volatile__("wr %0, 0x0, %%set_softint" ··· 95 98 extern void *hardirq_stack[NR_CPUS]; 96 99 extern void *softirq_stack[NR_CPUS]; 97 100 #define __ARCH_HAS_DO_SOFTIRQ 101 + #define ARCH_HAS_NMI_WATCHDOG 98 102 99 103 #endif
+2
arch/sparc/include/asm/kdebug_64.h
··· 14 14 DIE_TRAP, 15 15 DIE_TRAP_TL1, 16 16 DIE_CALL, 17 + DIE_NMI, 18 + DIE_NMIWATCHDOG, 17 19 }; 18 20 19 21 #endif
+10
arch/sparc/include/asm/nmi.h
··· 1 + #ifndef __NMI_H 2 + #define __NMI_H 3 + 4 + extern int __init nmi_init(void); 5 + extern void perfctr_irq(int irq, struct pt_regs *regs); 6 + extern void nmi_adjust_hz(unsigned int new_hz); 7 + 8 + extern int nmi_usable; 9 + 10 + #endif /* __NMI_H */
+16
arch/sparc/include/asm/pcr.h
··· 27 27 #define PCR_N2_SL1_SHIFT 27 28 28 #define PCR_N2_OV1 0x80000000 29 29 30 + extern unsigned int picl_shift; 31 + 32 + /* In order to commonize as much of the implementation as 33 + * possible, we use PICH as our counter. Mostly this is 34 + * to accomodate Niagara-1 which can only count insn cycles 35 + * in PICH. 36 + */ 37 + static inline u64 picl_value(unsigned int nmi_hz) 38 + { 39 + u32 delta = local_cpu_data().clock_tick / (nmi_hz << picl_shift); 40 + 41 + return ((u64)((0 - delta) & 0xffffffff)) << 32; 42 + } 43 + 44 + extern u64 pcr_enable; 45 + 30 46 #endif /* __PCR_H */
+1
arch/sparc/kernel/Makefile
··· 53 53 obj-$(CONFIG_SPARC64) += sstate.o 54 54 obj-$(CONFIG_SPARC64) += mdesc.o 55 55 obj-$(CONFIG_SPARC64) += pcr.o 56 + obj-$(CONFIG_SPARC64) += nmi.o 56 57 57 58 # sparc32 do not use GENERIC_HARDIRQS but uses the generic devres implementation 58 59 obj-$(CONFIG_SPARC32) += devres.o
+5 -63
arch/sparc/kernel/irq_64.c
··· 196 196 seq_putc(p, '\n'); 197 197 skip: 198 198 spin_unlock_irqrestore(&irq_desc[i].lock, flags); 199 + } else if (i == NR_IRQS) { 200 + seq_printf(p, "NMI: "); 201 + for_each_online_cpu(j) 202 + seq_printf(p, "%10u ", cpu_data(j).__nmi_count); 203 + seq_printf(p, " Non-maskable interrupts\n"); 199 204 } 200 205 return 0; 201 206 } ··· 782 777 783 778 local_irq_restore(flags); 784 779 } 785 - 786 - static void unhandled_perf_irq(struct pt_regs *regs) 787 - { 788 - unsigned long pcr, pic; 789 - 790 - read_pcr(pcr); 791 - read_pic(pic); 792 - 793 - write_pcr(0); 794 - 795 - printk(KERN_EMERG "CPU %d: Got unexpected perf counter IRQ.\n", 796 - smp_processor_id()); 797 - printk(KERN_EMERG "CPU %d: PCR[%016lx] PIC[%016lx]\n", 798 - smp_processor_id(), pcr, pic); 799 - } 800 - 801 - /* Almost a direct copy of the powerpc PMC code. */ 802 - static DEFINE_SPINLOCK(perf_irq_lock); 803 - static void *perf_irq_owner_caller; /* mostly for debugging */ 804 - static void (*perf_irq)(struct pt_regs *regs) = unhandled_perf_irq; 805 - 806 - /* Invoked from level 15 PIL handler in trap table. */ 807 - void perfctr_irq(int irq, struct pt_regs *regs) 808 - { 809 - clear_softint(1 << irq); 810 - perf_irq(regs); 811 - } 812 - 813 - int register_perfctr_intr(void (*handler)(struct pt_regs *)) 814 - { 815 - int ret; 816 - 817 - if (!handler) 818 - return -EINVAL; 819 - 820 - spin_lock(&perf_irq_lock); 821 - if (perf_irq != unhandled_perf_irq) { 822 - printk(KERN_WARNING "register_perfctr_intr: " 823 - "perf IRQ busy (reserved by caller %p)\n", 824 - perf_irq_owner_caller); 825 - ret = -EBUSY; 826 - goto out; 827 - } 828 - 829 - perf_irq_owner_caller = __builtin_return_address(0); 830 - perf_irq = handler; 831 - 832 - ret = 0; 833 - out: 834 - spin_unlock(&perf_irq_lock); 835 - 836 - return ret; 837 - } 838 - EXPORT_SYMBOL_GPL(register_perfctr_intr); 839 - 840 - void release_perfctr_intr(void (*handler)(struct pt_regs *)) 841 - { 842 - spin_lock(&perf_irq_lock); 843 - perf_irq_owner_caller = NULL; 844 - perf_irq = unhandled_perf_irq; 845 - spin_unlock(&perf_irq_lock); 846 - } 847 - EXPORT_SYMBOL_GPL(release_perfctr_intr); 848 780 849 781 #ifdef CONFIG_HOTPLUG_CPU 850 782 void fixup_irqs(void)
+224
arch/sparc/kernel/nmi.c
··· 1 + /* Pseudo NMI support on sparc64 systems. 2 + * 3 + * Copyright (C) 2009 David S. Miller <davem@davemloft.net> 4 + * 5 + * The NMI watchdog support and infrastructure is based almost 6 + * entirely upon the x86 NMI support code. 7 + */ 8 + #include <linux/kernel.h> 9 + #include <linux/param.h> 10 + #include <linux/init.h> 11 + #include <linux/percpu.h> 12 + #include <linux/nmi.h> 13 + #include <linux/module.h> 14 + #include <linux/kprobes.h> 15 + #include <linux/kernel_stat.h> 16 + #include <linux/slab.h> 17 + #include <linux/kdebug.h> 18 + #include <linux/delay.h> 19 + #include <linux/smp.h> 20 + 21 + #include <asm/ptrace.h> 22 + #include <asm/local.h> 23 + #include <asm/pcr.h> 24 + 25 + /* We don't have a real NMI on sparc64, but we can fake one 26 + * up using profiling counter overflow interrupts and interrupt 27 + * levels. 28 + * 29 + * The profile overflow interrupts at level 15, so we use 30 + * level 14 as our IRQ off level. 31 + */ 32 + 33 + static int nmi_watchdog_active; 34 + static int panic_on_timeout; 35 + 36 + int nmi_usable; 37 + EXPORT_SYMBOL_GPL(nmi_usable); 38 + 39 + static unsigned int nmi_hz = HZ; 40 + 41 + static DEFINE_PER_CPU(unsigned int, last_irq_sum); 42 + static DEFINE_PER_CPU(local_t, alert_counter); 43 + static DEFINE_PER_CPU(int, nmi_touch); 44 + 45 + void touch_nmi_watchdog(void) 46 + { 47 + if (nmi_watchdog_active) { 48 + int cpu; 49 + 50 + for_each_present_cpu(cpu) { 51 + if (per_cpu(nmi_touch, cpu) != 1) 52 + per_cpu(nmi_touch, cpu) = 1; 53 + } 54 + } 55 + 56 + touch_softlockup_watchdog(); 57 + } 58 + EXPORT_SYMBOL(touch_nmi_watchdog); 59 + 60 + static void die_nmi(const char *str, struct pt_regs *regs, int do_panic) 61 + { 62 + if (notify_die(DIE_NMIWATCHDOG, str, regs, 0, 63 + pt_regs_trap_type(regs), SIGINT) == NOTIFY_STOP) 64 + return; 65 + 66 + console_verbose(); 67 + bust_spinlocks(1); 68 + 69 + printk(KERN_EMERG "%s", str); 70 + printk(" on CPU%d, ip %08lx, registers:\n", 71 + smp_processor_id(), regs->tpc); 72 + show_regs(regs); 73 + 74 + bust_spinlocks(0); 75 + 76 + if (do_panic || panic_on_oops) 77 + panic("Non maskable interrupt"); 78 + 79 + local_irq_enable(); 80 + do_exit(SIGBUS); 81 + } 82 + 83 + notrace __kprobes void perfctr_irq(int irq, struct pt_regs *regs) 84 + { 85 + unsigned int sum, touched = 0; 86 + int cpu = smp_processor_id(); 87 + 88 + clear_softint(1 << irq); 89 + pcr_ops->write(PCR_PIC_PRIV); 90 + 91 + local_cpu_data().__nmi_count++; 92 + 93 + if (notify_die(DIE_NMI, "nmi", regs, 0, 94 + pt_regs_trap_type(regs), SIGINT) == NOTIFY_STOP) 95 + touched = 1; 96 + 97 + sum = kstat_cpu(cpu).irqs[0]; 98 + if (__get_cpu_var(nmi_touch)) { 99 + __get_cpu_var(nmi_touch) = 0; 100 + touched = 1; 101 + } 102 + if (!touched && __get_cpu_var(last_irq_sum) == sum) { 103 + local_inc(&__get_cpu_var(alert_counter)); 104 + if (local_read(&__get_cpu_var(alert_counter)) == 5 * nmi_hz) 105 + die_nmi("BUG: NMI Watchdog detected LOCKUP", 106 + regs, panic_on_timeout); 107 + } else { 108 + __get_cpu_var(last_irq_sum) = sum; 109 + local_set(&__get_cpu_var(alert_counter), 0); 110 + } 111 + if (nmi_usable) { 112 + write_pic(picl_value(nmi_hz)); 113 + pcr_ops->write(pcr_enable); 114 + } 115 + } 116 + 117 + static inline unsigned int get_nmi_count(int cpu) 118 + { 119 + return cpu_data(cpu).__nmi_count; 120 + } 121 + 122 + static int endflag __initdata; 123 + 124 + static __init void nmi_cpu_busy(void *data) 125 + { 126 + local_irq_enable_in_hardirq(); 127 + while (endflag == 0) 128 + mb(); 129 + } 130 + 131 + static void report_broken_nmi(int cpu, int *prev_nmi_count) 132 + { 133 + printk(KERN_CONT "\n"); 134 + 135 + printk(KERN_WARNING 136 + "WARNING: CPU#%d: NMI appears to be stuck (%d->%d)!\n", 137 + cpu, prev_nmi_count[cpu], get_nmi_count(cpu)); 138 + 139 + printk(KERN_WARNING 140 + "Please report this to bugzilla.kernel.org,\n"); 141 + printk(KERN_WARNING 142 + "and attach the output of the 'dmesg' command.\n"); 143 + 144 + nmi_usable = 0; 145 + } 146 + 147 + static void stop_watchdog(void *unused) 148 + { 149 + pcr_ops->write(PCR_PIC_PRIV); 150 + } 151 + 152 + static int __init check_nmi_watchdog(void) 153 + { 154 + unsigned int *prev_nmi_count; 155 + int cpu, err; 156 + 157 + prev_nmi_count = kmalloc(nr_cpu_ids * sizeof(unsigned int), GFP_KERNEL); 158 + if (!prev_nmi_count) { 159 + err = -ENOMEM; 160 + goto error; 161 + } 162 + 163 + printk(KERN_INFO "Testing NMI watchdog ... "); 164 + 165 + smp_call_function(nmi_cpu_busy, (void *)&endflag, 0); 166 + 167 + for_each_possible_cpu(cpu) 168 + prev_nmi_count[cpu] = get_nmi_count(cpu); 169 + local_irq_enable(); 170 + mdelay((20 * 1000) / nmi_hz); /* wait 20 ticks */ 171 + 172 + for_each_online_cpu(cpu) { 173 + if (get_nmi_count(cpu) - prev_nmi_count[cpu] <= 5) 174 + report_broken_nmi(cpu, prev_nmi_count); 175 + } 176 + endflag = 1; 177 + if (!nmi_usable) { 178 + kfree(prev_nmi_count); 179 + err = -ENODEV; 180 + goto error; 181 + } 182 + printk("OK.\n"); 183 + 184 + nmi_hz = 1; 185 + 186 + kfree(prev_nmi_count); 187 + return 0; 188 + error: 189 + on_each_cpu(stop_watchdog, NULL, 1); 190 + return err; 191 + } 192 + 193 + static void start_watchdog(void *unused) 194 + { 195 + pcr_ops->write(PCR_PIC_PRIV); 196 + write_pic(picl_value(nmi_hz)); 197 + 198 + pcr_ops->write(pcr_enable); 199 + } 200 + 201 + void nmi_adjust_hz(unsigned int new_hz) 202 + { 203 + nmi_hz = new_hz; 204 + on_each_cpu(start_watchdog, NULL, 1); 205 + } 206 + EXPORT_SYMBOL_GPL(nmi_adjust_hz); 207 + 208 + int __init nmi_init(void) 209 + { 210 + nmi_usable = 1; 211 + 212 + on_each_cpu(start_watchdog, NULL, 1); 213 + 214 + return check_nmi_watchdog(); 215 + } 216 + 217 + static int __init setup_nmi_watchdog(char *str) 218 + { 219 + if (!strncmp(str, "panic", 5)) 220 + panic_on_timeout = 1; 221 + 222 + return 0; 223 + } 224 + __setup("nmi_watchdog=", setup_nmi_watchdog);
+15 -2
arch/sparc/kernel/pcr.c
··· 9 9 10 10 #include <asm/pil.h> 11 11 #include <asm/pcr.h> 12 + #include <asm/nmi.h> 12 13 13 14 /* This code is shared between various users of the performance 14 15 * counters. Users will be oprofile, pseudo-NMI watchdog, and the 15 16 * perf_counter support layer. 16 17 */ 18 + 19 + #define PCR_SUN4U_ENABLE (PCR_PIC_PRIV | PCR_STRACE | PCR_UTRACE) 20 + #define PCR_N2_ENABLE (PCR_PIC_PRIV | PCR_STRACE | PCR_UTRACE | \ 21 + PCR_N2_TOE_OV1 | \ 22 + (2 << PCR_N2_SL1_SHIFT) | \ 23 + (0xff << PCR_N2_MASK1_SHIFT)) 24 + 25 + u64 pcr_enable; 26 + unsigned int picl_shift; 17 27 18 28 /* Performance counter interrupts run unmasked at PIL level 15. 19 29 * Therefore we can't do things like wakeups and other work ··· 127 117 switch (tlb_type) { 128 118 case hypervisor: 129 119 pcr_ops = &n2_pcr_ops; 120 + pcr_enable = PCR_N2_ENABLE; 121 + picl_shift = 2; 130 122 break; 131 123 132 - case spitfire: 133 124 case cheetah: 134 125 case cheetah_plus: 126 + case spitfire: 135 127 pcr_ops = &direct_pcr_ops; 128 + pcr_enable = PCR_SUN4U_ENABLE; 136 129 break; 137 130 138 131 default: ··· 143 130 goto out_unregister; 144 131 } 145 132 146 - return 0; 133 + return nmi_init(); 147 134 148 135 out_unregister: 149 136 unregister_perf_hsvc();
+44 -104
arch/sparc/oprofile/init.c
··· 13 13 #include <linux/init.h> 14 14 15 15 #ifdef CONFIG_SPARC64 16 - #include <asm/hypervisor.h> 17 - #include <asm/spitfire.h> 18 - #include <asm/cpudata.h> 19 - #include <asm/irq.h> 20 - #include <asm/pcr.h> 16 + #include <linux/notifier.h> 17 + #include <linux/rcupdate.h> 18 + #include <linux/kdebug.h> 19 + #include <asm/nmi.h> 21 20 22 - static int nmi_enabled; 23 - 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) 21 + static int profile_timer_exceptions_notify(struct notifier_block *self, 22 + unsigned long val, void *data) 30 23 { 31 - u32 delta = local_cpu_data().clock_tick / HZ; 24 + struct die_args *args = (struct die_args *)data; 25 + int ret = NOTIFY_DONE; 32 26 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 - { 46 - pcr_ops->write(PCR_PIC_PRIV); 47 - 48 - if (nmi_enabled) { 49 - oprofile_add_sample(regs, 0); 50 - 51 - write_pic(picl_value()); 52 - pcr_ops->write(pcr_enable); 53 - } 54 - } 55 - 56 - /* We count "clock cycle" events in the lower 32-bit PIC. 57 - * Then configure it such that it overflows every HZ, and thus 58 - * generates a level 15 interrupt at that frequency. 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 - 68 - static void cpu_nmi_stop(void *_unused) 69 - { 70 - pcr_ops->write(PCR_PIC_PRIV); 71 - } 72 - 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 - 92 - static void nmi_stop(void) 93 - { 94 - nmi_enabled = 0; 95 - wmb(); 96 - 97 - on_each_cpu(cpu_nmi_stop, NULL, 1); 98 - release_perfctr_intr(nmi_handler); 99 - synchronize_sched(); 100 - } 101 - 102 - static int oprofile_nmi_init(struct oprofile_operations *ops) 103 - { 104 - switch (tlb_type) { 105 - case hypervisor: 106 - pcr_enable = PCR_N2_ENABLE; 27 + switch (val) { 28 + case DIE_NMI: 29 + oprofile_add_sample(args->regs, 0); 30 + ret = NOTIFY_STOP; 107 31 break; 108 - 109 - case cheetah: 110 - case cheetah_plus: 111 - pcr_enable = PCR_SUN4U_ENABLE; 112 - break; 113 - 114 32 default: 115 - return -ENODEV; 33 + break; 116 34 } 35 + return ret; 36 + } 117 37 118 - ops->create_files = NULL; 119 - ops->setup = NULL; 120 - ops->shutdown = NULL; 121 - ops->start = nmi_start; 122 - ops->stop = nmi_stop; 38 + static struct notifier_block profile_timer_exceptions_nb = { 39 + .notifier_call = profile_timer_exceptions_notify, 40 + }; 41 + 42 + static int timer_start(void) 43 + { 44 + if (register_die_notifier(&profile_timer_exceptions_nb)) 45 + return 1; 46 + nmi_adjust_hz(HZ); 47 + return 0; 48 + } 49 + 50 + 51 + static void timer_stop(void) 52 + { 53 + nmi_adjust_hz(1); 54 + unregister_die_notifier(&profile_timer_exceptions_nb); 55 + synchronize_sched(); /* Allow already-started NMIs to complete. */ 56 + } 57 + 58 + static int op_nmi_timer_init(struct oprofile_operations *ops) 59 + { 60 + if (!nmi_usable) 61 + return -ENODEV; 62 + 63 + ops->start = timer_start; 64 + ops->stop = timer_stop; 123 65 ops->cpu_type = "timer"; 124 - 125 - printk(KERN_INFO "oprofile: Using perfctr based NMI timer interrupt.\n"); 126 - 66 + printk(KERN_INFO "oprofile: Using perfctr NMI timer interrupt.\n"); 127 67 return 0; 128 68 } 129 69 #endif ··· 73 133 int ret = -ENODEV; 74 134 75 135 #ifdef CONFIG_SPARC64 76 - ret = oprofile_nmi_init(ops); 136 + ret = op_nmi_timer_init(ops); 77 137 if (!ret) 78 138 return ret; 79 139 #endif