···1717typedef struct {1818 /* Dcache line 1 */1919 unsigned int __softirq_pending; /* must be 1st, see rtrap.S */2020- unsigned int __pad0;2020+ unsigned int __nmi_count;2121 unsigned long clock_tick; /* %tick's per second */2222 unsigned long __pad;2323 unsigned int __pad1;
···11+#ifndef __NMI_H22+#define __NMI_H33+44+extern int __init nmi_init(void);55+extern void perfctr_irq(int irq, struct pt_regs *regs);66+extern void nmi_adjust_hz(unsigned int new_hz);77+88+extern int nmi_usable;99+1010+#endif /* __NMI_H */
+16
arch/sparc/include/asm/pcr.h
···2727#define PCR_N2_SL1_SHIFT 272828#define PCR_N2_OV1 0x8000000029293030+extern unsigned int picl_shift;3131+3232+/* In order to commonize as much of the implementation as3333+ * possible, we use PICH as our counter. Mostly this is3434+ * to accomodate Niagara-1 which can only count insn cycles3535+ * in PICH.3636+ */3737+static inline u64 picl_value(unsigned int nmi_hz)3838+{3939+ u32 delta = local_cpu_data().clock_tick / (nmi_hz << picl_shift);4040+4141+ return ((u64)((0 - delta) & 0xffffffff)) << 32;4242+}4343+4444+extern u64 pcr_enable;4545+3046#endif /* __PCR_H */
+1
arch/sparc/kernel/Makefile
···5353obj-$(CONFIG_SPARC64) += sstate.o5454obj-$(CONFIG_SPARC64) += mdesc.o5555obj-$(CONFIG_SPARC64) += pcr.o5656+obj-$(CONFIG_SPARC64) += nmi.o56575758# sparc32 do not use GENERIC_HARDIRQS but uses the generic devres implementation5859obj-$(CONFIG_SPARC32) += devres.o
+5-63
arch/sparc/kernel/irq_64.c
···196196 seq_putc(p, '\n');197197skip:198198 spin_unlock_irqrestore(&irq_desc[i].lock, flags);199199+ } else if (i == NR_IRQS) {200200+ seq_printf(p, "NMI: ");201201+ for_each_online_cpu(j)202202+ seq_printf(p, "%10u ", cpu_data(j).__nmi_count);203203+ seq_printf(p, " Non-maskable interrupts\n");199204 }200205 return 0;201206}···782777783778 local_irq_restore(flags);784779}785785-786786-static void unhandled_perf_irq(struct pt_regs *regs)787787-{788788- unsigned long pcr, pic;789789-790790- read_pcr(pcr);791791- read_pic(pic);792792-793793- write_pcr(0);794794-795795- printk(KERN_EMERG "CPU %d: Got unexpected perf counter IRQ.\n",796796- smp_processor_id());797797- printk(KERN_EMERG "CPU %d: PCR[%016lx] PIC[%016lx]\n",798798- smp_processor_id(), pcr, pic);799799-}800800-801801-/* Almost a direct copy of the powerpc PMC code. */802802-static DEFINE_SPINLOCK(perf_irq_lock);803803-static void *perf_irq_owner_caller; /* mostly for debugging */804804-static void (*perf_irq)(struct pt_regs *regs) = unhandled_perf_irq;805805-806806-/* Invoked from level 15 PIL handler in trap table. */807807-void perfctr_irq(int irq, struct pt_regs *regs)808808-{809809- clear_softint(1 << irq);810810- perf_irq(regs);811811-}812812-813813-int register_perfctr_intr(void (*handler)(struct pt_regs *))814814-{815815- int ret;816816-817817- if (!handler)818818- return -EINVAL;819819-820820- spin_lock(&perf_irq_lock);821821- if (perf_irq != unhandled_perf_irq) {822822- printk(KERN_WARNING "register_perfctr_intr: "823823- "perf IRQ busy (reserved by caller %p)\n",824824- perf_irq_owner_caller);825825- ret = -EBUSY;826826- goto out;827827- }828828-829829- perf_irq_owner_caller = __builtin_return_address(0);830830- perf_irq = handler;831831-832832- ret = 0;833833-out:834834- spin_unlock(&perf_irq_lock);835835-836836- return ret;837837-}838838-EXPORT_SYMBOL_GPL(register_perfctr_intr);839839-840840-void release_perfctr_intr(void (*handler)(struct pt_regs *))841841-{842842- spin_lock(&perf_irq_lock);843843- perf_irq_owner_caller = NULL;844844- perf_irq = unhandled_perf_irq;845845- spin_unlock(&perf_irq_lock);846846-}847847-EXPORT_SYMBOL_GPL(release_perfctr_intr);848780849781#ifdef CONFIG_HOTPLUG_CPU850782void fixup_irqs(void)
+224
arch/sparc/kernel/nmi.c
···11+/* Pseudo NMI support on sparc64 systems.22+ *33+ * Copyright (C) 2009 David S. Miller <davem@davemloft.net>44+ *55+ * The NMI watchdog support and infrastructure is based almost66+ * entirely upon the x86 NMI support code.77+ */88+#include <linux/kernel.h>99+#include <linux/param.h>1010+#include <linux/init.h>1111+#include <linux/percpu.h>1212+#include <linux/nmi.h>1313+#include <linux/module.h>1414+#include <linux/kprobes.h>1515+#include <linux/kernel_stat.h>1616+#include <linux/slab.h>1717+#include <linux/kdebug.h>1818+#include <linux/delay.h>1919+#include <linux/smp.h>2020+2121+#include <asm/ptrace.h>2222+#include <asm/local.h>2323+#include <asm/pcr.h>2424+2525+/* We don't have a real NMI on sparc64, but we can fake one2626+ * up using profiling counter overflow interrupts and interrupt2727+ * levels.2828+ *2929+ * The profile overflow interrupts at level 15, so we use3030+ * level 14 as our IRQ off level.3131+ */3232+3333+static int nmi_watchdog_active;3434+static int panic_on_timeout;3535+3636+int nmi_usable;3737+EXPORT_SYMBOL_GPL(nmi_usable);3838+3939+static unsigned int nmi_hz = HZ;4040+4141+static DEFINE_PER_CPU(unsigned int, last_irq_sum);4242+static DEFINE_PER_CPU(local_t, alert_counter);4343+static DEFINE_PER_CPU(int, nmi_touch);4444+4545+void touch_nmi_watchdog(void)4646+{4747+ if (nmi_watchdog_active) {4848+ int cpu;4949+5050+ for_each_present_cpu(cpu) {5151+ if (per_cpu(nmi_touch, cpu) != 1)5252+ per_cpu(nmi_touch, cpu) = 1;5353+ }5454+ }5555+5656+ touch_softlockup_watchdog();5757+}5858+EXPORT_SYMBOL(touch_nmi_watchdog);5959+6060+static void die_nmi(const char *str, struct pt_regs *regs, int do_panic)6161+{6262+ if (notify_die(DIE_NMIWATCHDOG, str, regs, 0,6363+ pt_regs_trap_type(regs), SIGINT) == NOTIFY_STOP)6464+ return;6565+6666+ console_verbose();6767+ bust_spinlocks(1);6868+6969+ printk(KERN_EMERG "%s", str);7070+ printk(" on CPU%d, ip %08lx, registers:\n",7171+ smp_processor_id(), regs->tpc);7272+ show_regs(regs);7373+7474+ bust_spinlocks(0);7575+7676+ if (do_panic || panic_on_oops)7777+ panic("Non maskable interrupt");7878+7979+ local_irq_enable();8080+ do_exit(SIGBUS);8181+}8282+8383+notrace __kprobes void perfctr_irq(int irq, struct pt_regs *regs)8484+{8585+ unsigned int sum, touched = 0;8686+ int cpu = smp_processor_id();8787+8888+ clear_softint(1 << irq);8989+ pcr_ops->write(PCR_PIC_PRIV);9090+9191+ local_cpu_data().__nmi_count++;9292+9393+ if (notify_die(DIE_NMI, "nmi", regs, 0,9494+ pt_regs_trap_type(regs), SIGINT) == NOTIFY_STOP)9595+ touched = 1;9696+9797+ sum = kstat_cpu(cpu).irqs[0];9898+ if (__get_cpu_var(nmi_touch)) {9999+ __get_cpu_var(nmi_touch) = 0;100100+ touched = 1;101101+ }102102+ if (!touched && __get_cpu_var(last_irq_sum) == sum) {103103+ local_inc(&__get_cpu_var(alert_counter));104104+ if (local_read(&__get_cpu_var(alert_counter)) == 5 * nmi_hz)105105+ die_nmi("BUG: NMI Watchdog detected LOCKUP",106106+ regs, panic_on_timeout);107107+ } else {108108+ __get_cpu_var(last_irq_sum) = sum;109109+ local_set(&__get_cpu_var(alert_counter), 0);110110+ }111111+ if (nmi_usable) {112112+ write_pic(picl_value(nmi_hz));113113+ pcr_ops->write(pcr_enable);114114+ }115115+}116116+117117+static inline unsigned int get_nmi_count(int cpu)118118+{119119+ return cpu_data(cpu).__nmi_count;120120+}121121+122122+static int endflag __initdata;123123+124124+static __init void nmi_cpu_busy(void *data)125125+{126126+ local_irq_enable_in_hardirq();127127+ while (endflag == 0)128128+ mb();129129+}130130+131131+static void report_broken_nmi(int cpu, int *prev_nmi_count)132132+{133133+ printk(KERN_CONT "\n");134134+135135+ printk(KERN_WARNING136136+ "WARNING: CPU#%d: NMI appears to be stuck (%d->%d)!\n",137137+ cpu, prev_nmi_count[cpu], get_nmi_count(cpu));138138+139139+ printk(KERN_WARNING140140+ "Please report this to bugzilla.kernel.org,\n");141141+ printk(KERN_WARNING142142+ "and attach the output of the 'dmesg' command.\n");143143+144144+ nmi_usable = 0;145145+}146146+147147+static void stop_watchdog(void *unused)148148+{149149+ pcr_ops->write(PCR_PIC_PRIV);150150+}151151+152152+static int __init check_nmi_watchdog(void)153153+{154154+ unsigned int *prev_nmi_count;155155+ int cpu, err;156156+157157+ prev_nmi_count = kmalloc(nr_cpu_ids * sizeof(unsigned int), GFP_KERNEL);158158+ if (!prev_nmi_count) {159159+ err = -ENOMEM;160160+ goto error;161161+ }162162+163163+ printk(KERN_INFO "Testing NMI watchdog ... ");164164+165165+ smp_call_function(nmi_cpu_busy, (void *)&endflag, 0);166166+167167+ for_each_possible_cpu(cpu)168168+ prev_nmi_count[cpu] = get_nmi_count(cpu);169169+ local_irq_enable();170170+ mdelay((20 * 1000) / nmi_hz); /* wait 20 ticks */171171+172172+ for_each_online_cpu(cpu) {173173+ if (get_nmi_count(cpu) - prev_nmi_count[cpu] <= 5)174174+ report_broken_nmi(cpu, prev_nmi_count);175175+ }176176+ endflag = 1;177177+ if (!nmi_usable) {178178+ kfree(prev_nmi_count);179179+ err = -ENODEV;180180+ goto error;181181+ }182182+ printk("OK.\n");183183+184184+ nmi_hz = 1;185185+186186+ kfree(prev_nmi_count);187187+ return 0;188188+error:189189+ on_each_cpu(stop_watchdog, NULL, 1);190190+ return err;191191+}192192+193193+static void start_watchdog(void *unused)194194+{195195+ pcr_ops->write(PCR_PIC_PRIV);196196+ write_pic(picl_value(nmi_hz));197197+198198+ pcr_ops->write(pcr_enable);199199+}200200+201201+void nmi_adjust_hz(unsigned int new_hz)202202+{203203+ nmi_hz = new_hz;204204+ on_each_cpu(start_watchdog, NULL, 1);205205+}206206+EXPORT_SYMBOL_GPL(nmi_adjust_hz);207207+208208+int __init nmi_init(void)209209+{210210+ nmi_usable = 1;211211+212212+ on_each_cpu(start_watchdog, NULL, 1);213213+214214+ return check_nmi_watchdog();215215+}216216+217217+static int __init setup_nmi_watchdog(char *str)218218+{219219+ if (!strncmp(str, "panic", 5))220220+ panic_on_timeout = 1;221221+222222+ return 0;223223+}224224+__setup("nmi_watchdog=", setup_nmi_watchdog);
+15-2
arch/sparc/kernel/pcr.c
···991010#include <asm/pil.h>1111#include <asm/pcr.h>1212+#include <asm/nmi.h>12131314/* This code is shared between various users of the performance1415 * counters. Users will be oprofile, pseudo-NMI watchdog, and the1516 * perf_counter support layer.1617 */1818+1919+#define PCR_SUN4U_ENABLE (PCR_PIC_PRIV | PCR_STRACE | PCR_UTRACE)2020+#define PCR_N2_ENABLE (PCR_PIC_PRIV | PCR_STRACE | PCR_UTRACE | \2121+ PCR_N2_TOE_OV1 | \2222+ (2 << PCR_N2_SL1_SHIFT) | \2323+ (0xff << PCR_N2_MASK1_SHIFT))2424+2525+u64 pcr_enable;2626+unsigned int picl_shift;17271828/* Performance counter interrupts run unmasked at PIL level 15.1929 * Therefore we can't do things like wakeups and other work···127117 switch (tlb_type) {128118 case hypervisor:129119 pcr_ops = &n2_pcr_ops;120120+ pcr_enable = PCR_N2_ENABLE;121121+ picl_shift = 2;130122 break;131123132132- case spitfire:133124 case cheetah:134125 case cheetah_plus:126126+ case spitfire:135127 pcr_ops = &direct_pcr_ops;128128+ pcr_enable = PCR_SUN4U_ENABLE;136129 break;137130138131 default:···143130 goto out_unregister;144131 }145132146146- return 0;133133+ return nmi_init();147134148135out_unregister:149136 unregister_perf_hsvc();
+44-104
arch/sparc/oprofile/init.c
···1313#include <linux/init.h>14141515#ifdef CONFIG_SPARC641616-#include <asm/hypervisor.h>1717-#include <asm/spitfire.h>1818-#include <asm/cpudata.h>1919-#include <asm/irq.h>2020-#include <asm/pcr.h>1616+#include <linux/notifier.h>1717+#include <linux/rcupdate.h>1818+#include <linux/kdebug.h>1919+#include <asm/nmi.h>21202222-static int nmi_enabled;2323-2424-/* In order to commonize as much of the implementation as2525- * possible, we use PICH as our counter. Mostly this is2626- * to accomodate Niagara-1 which can only count insn cycles2727- * in PICH.2828- */2929-static u64 picl_value(void)2121+static int profile_timer_exceptions_notify(struct notifier_block *self,2222+ unsigned long val, void *data)3023{3131- u32 delta = local_cpu_data().clock_tick / HZ;2424+ struct die_args *args = (struct die_args *)data;2525+ int ret = NOTIFY_DONE;32263333- return ((u64)((0 - delta) & 0xffffffff)) << 32;3434-}3535-3636-#define PCR_SUN4U_ENABLE (PCR_PIC_PRIV | PCR_STRACE | PCR_UTRACE)3737-#define PCR_N2_ENABLE (PCR_PIC_PRIV | PCR_STRACE | PCR_UTRACE | \3838- PCR_N2_TOE_OV1 | \3939- (2 << PCR_N2_SL1_SHIFT) | \4040- (0xff << PCR_N2_MASK1_SHIFT))4141-4242-static u64 pcr_enable;4343-4444-static void nmi_handler(struct pt_regs *regs)4545-{4646- pcr_ops->write(PCR_PIC_PRIV);4747-4848- if (nmi_enabled) {4949- oprofile_add_sample(regs, 0);5050-5151- write_pic(picl_value());5252- pcr_ops->write(pcr_enable);5353- }5454-}5555-5656-/* We count "clock cycle" events in the lower 32-bit PIC.5757- * Then configure it such that it overflows every HZ, and thus5858- * generates a level 15 interrupt at that frequency.5959- */6060-static void cpu_nmi_start(void *_unused)6161-{6262- pcr_ops->write(PCR_PIC_PRIV);6363- write_pic(picl_value());6464-6565- pcr_ops->write(pcr_enable);6666-}6767-6868-static void cpu_nmi_stop(void *_unused)6969-{7070- pcr_ops->write(PCR_PIC_PRIV);7171-}7272-7373-static int nmi_start(void)7474-{7575- int err = register_perfctr_intr(nmi_handler);7676-7777- if (!err) {7878- nmi_enabled = 1;7979- wmb();8080- err = on_each_cpu(cpu_nmi_start, NULL, 1);8181- if (err) {8282- nmi_enabled = 0;8383- wmb();8484- on_each_cpu(cpu_nmi_stop, NULL, 1);8585- release_perfctr_intr(nmi_handler);8686- }8787- }8888-8989- return err;9090-}9191-9292-static void nmi_stop(void)9393-{9494- nmi_enabled = 0;9595- wmb();9696-9797- on_each_cpu(cpu_nmi_stop, NULL, 1);9898- release_perfctr_intr(nmi_handler);9999- synchronize_sched();100100-}101101-102102-static int oprofile_nmi_init(struct oprofile_operations *ops)103103-{104104- switch (tlb_type) {105105- case hypervisor:106106- pcr_enable = PCR_N2_ENABLE;2727+ switch (val) {2828+ case DIE_NMI:2929+ oprofile_add_sample(args->regs, 0);3030+ ret = NOTIFY_STOP;10731 break;108108-109109- case cheetah:110110- case cheetah_plus:111111- pcr_enable = PCR_SUN4U_ENABLE;112112- break;113113-11432 default:115115- return -ENODEV;3333+ break;11634 }3535+ return ret;3636+}11737118118- ops->create_files = NULL;119119- ops->setup = NULL;120120- ops->shutdown = NULL;121121- ops->start = nmi_start;122122- ops->stop = nmi_stop;3838+static struct notifier_block profile_timer_exceptions_nb = {3939+ .notifier_call = profile_timer_exceptions_notify,4040+};4141+4242+static int timer_start(void)4343+{4444+ if (register_die_notifier(&profile_timer_exceptions_nb))4545+ return 1;4646+ nmi_adjust_hz(HZ);4747+ return 0;4848+}4949+5050+5151+static void timer_stop(void)5252+{5353+ nmi_adjust_hz(1);5454+ unregister_die_notifier(&profile_timer_exceptions_nb);5555+ synchronize_sched(); /* Allow already-started NMIs to complete. */5656+}5757+5858+static int op_nmi_timer_init(struct oprofile_operations *ops)5959+{6060+ if (!nmi_usable)6161+ return -ENODEV;6262+6363+ ops->start = timer_start;6464+ ops->stop = timer_stop;12365 ops->cpu_type = "timer";124124-125125- printk(KERN_INFO "oprofile: Using perfctr based NMI timer interrupt.\n");126126-6666+ printk(KERN_INFO "oprofile: Using perfctr NMI timer interrupt.\n");12767 return 0;12868}12969#endif···73133 int ret = -ENODEV;7413475135#ifdef CONFIG_SPARC647676- ret = oprofile_nmi_init(ops);136136+ ret = op_nmi_timer_init(ops);77137 if (!ret)78138 return ret;79139#endif