📄 nmi.c
字号:
{ if (nmi_pm_active > 0) enable_lapic_nmi_watchdog(); return 0;}static struct sysdev_class nmi_sysclass = { set_kset_name("lapic_nmi"), .resume = lapic_nmi_resume, .suspend = lapic_nmi_suspend,};static struct sys_device device_lapic_nmi = { .id = 0, .cls = &nmi_sysclass,};static int __init init_lapic_nmi_sysfs(void){ int error; if (nmi_active == 0 || nmi_watchdog != NMI_LOCAL_APIC) return 0; error = sysdev_class_register(&nmi_sysclass); if (!error) error = sysdev_register(&device_lapic_nmi); return error;}/* must come after the local APIC's device_initcall() */late_initcall(init_lapic_nmi_sysfs);#endif /* CONFIG_PM *//* * Activate the NMI watchdog via the local APIC. * Original code written by Keith Owens. */static void clear_msr_range(unsigned int base, unsigned int n){ unsigned int i; for(i = 0; i < n; ++i) wrmsr(base+i, 0, 0);}static inline void write_watchdog_counter(const char *descr){ u64 count = (u64)cpu_khz * 1000; do_div(count, nmi_hz); if(descr) Dprintk("setting %s to -0x%08Lx\n", descr, count); wrmsrl(nmi_perfctr_msr, 0 - count);}static void setup_k7_watchdog(void){ unsigned int evntsel; nmi_perfctr_msr = MSR_K7_PERFCTR0; clear_msr_range(MSR_K7_EVNTSEL0, 4); clear_msr_range(MSR_K7_PERFCTR0, 4); evntsel = K7_EVNTSEL_INT | K7_EVNTSEL_OS | K7_EVNTSEL_USR | K7_NMI_EVENT; wrmsr(MSR_K7_EVNTSEL0, evntsel, 0); write_watchdog_counter("K7_PERFCTR0"); apic_write(APIC_LVTPC, APIC_DM_NMI); evntsel |= K7_EVNTSEL_ENABLE; wrmsr(MSR_K7_EVNTSEL0, evntsel, 0);}static void setup_p6_watchdog(void){ unsigned int evntsel; nmi_perfctr_msr = MSR_P6_PERFCTR0; clear_msr_range(MSR_P6_EVNTSEL0, 2); clear_msr_range(MSR_P6_PERFCTR0, 2); evntsel = P6_EVNTSEL_INT | P6_EVNTSEL_OS | P6_EVNTSEL_USR | P6_NMI_EVENT; wrmsr(MSR_P6_EVNTSEL0, evntsel, 0); write_watchdog_counter("P6_PERFCTR0"); apic_write(APIC_LVTPC, APIC_DM_NMI); evntsel |= P6_EVNTSEL0_ENABLE; wrmsr(MSR_P6_EVNTSEL0, evntsel, 0);}static int setup_p4_watchdog(void){ unsigned int misc_enable, dummy; rdmsr(MSR_P4_MISC_ENABLE, misc_enable, dummy); if (!(misc_enable & MSR_P4_MISC_ENABLE_PERF_AVAIL)) return 0; nmi_perfctr_msr = MSR_P4_IQ_COUNTER0; nmi_p4_cccr_val = P4_NMI_IQ_CCCR0;#ifdef CONFIG_SMP if (smp_num_siblings == 2) nmi_p4_cccr_val |= P4_CCCR_OVF_PMI1;#endif if (!(misc_enable & MSR_P4_MISC_ENABLE_PEBS_UNAVAIL)) clear_msr_range(0x3F1, 2); /* MSR 0x3F0 seems to have a default value of 0xFC00, but current docs doesn't fully define it, so leave it alone for now. */ if (boot_cpu_data.x86_model >= 0x3) { /* MSR_P4_IQ_ESCR0/1 (0x3ba/0x3bb) removed */ clear_msr_range(0x3A0, 26); clear_msr_range(0x3BC, 3); } else { clear_msr_range(0x3A0, 31); } clear_msr_range(0x3C0, 6); clear_msr_range(0x3C8, 6); clear_msr_range(0x3E0, 2); clear_msr_range(MSR_P4_CCCR0, 18); clear_msr_range(MSR_P4_PERFCTR0, 18); wrmsr(MSR_P4_CRU_ESCR0, P4_NMI_CRU_ESCR0, 0); wrmsr(MSR_P4_IQ_CCCR0, P4_NMI_IQ_CCCR0 & ~P4_CCCR_ENABLE, 0); write_watchdog_counter("P4_IQ_COUNTER0"); apic_write(APIC_LVTPC, APIC_DM_NMI); wrmsr(MSR_P4_IQ_CCCR0, nmi_p4_cccr_val, 0); return 1;}void setup_apic_nmi_watchdog (void){ switch (boot_cpu_data.x86_vendor) { case X86_VENDOR_AMD: if (boot_cpu_data.x86 != 6 && boot_cpu_data.x86 != 15) return; setup_k7_watchdog(); break; case X86_VENDOR_INTEL: switch (boot_cpu_data.x86) { case 6: if (boot_cpu_data.x86_model > 0xd) return; setup_p6_watchdog(); break; case 15: if (boot_cpu_data.x86_model > 0x4) return; if (!setup_p4_watchdog()) return; break; default: return; } break; default: return; } lapic_nmi_owner = LAPIC_NMI_WATCHDOG; nmi_active = 1;}/* * the best way to detect whether a CPU has a 'hard lockup' problem * is to check it's local APIC timer IRQ counts. If they are not * changing then that CPU has some problem. * * as these watchdog NMI IRQs are generated on every CPU, we only * have to check the current processor. * * since NMIs don't listen to _any_ locks, we have to be extremely * careful not to rely on unsafe variables. The printk might lock * up though, so we have to break up any console locks first ... * [when there will be more tty-related locks, break them up * here too!] */static unsigned int last_irq_sums [NR_CPUS], alert_counter [NR_CPUS];void touch_nmi_watchdog (void){ int i; /* * Just reset the alert counters, (other CPUs might be * spinning on locks we hold): */ for (i = 0; i < NR_CPUS; i++) alert_counter[i] = 0; /* * Tickle the softlockup detector too: */ touch_softlockup_watchdog();}extern void die_nmi(struct pt_regs *, const char *msg);void nmi_watchdog_tick (struct pt_regs * regs){ /* * Since current_thread_info()-> is always on the stack, and we * always switch the stack NMI-atomically, it's safe to use * smp_processor_id(). */ int sum, cpu = smp_processor_id(); sum = per_cpu(irq_stat, cpu).apic_timer_irqs; if (last_irq_sums[cpu] == sum) { /* * Ayiee, looks like this CPU is stuck ... * wait a few IRQs (5 seconds) before doing the oops ... */ alert_counter[cpu]++; if (alert_counter[cpu] == 5*nmi_hz) /* * die_nmi will return ONLY if NOTIFY_STOP happens.. */ die_nmi(regs, "NMI Watchdog detected LOCKUP"); last_irq_sums[cpu] = sum; alert_counter[cpu] = 0; } if (nmi_perfctr_msr) { if (nmi_perfctr_msr == MSR_P4_IQ_COUNTER0) { /* * P4 quirks: * - An overflown perfctr will assert its interrupt * until the OVF flag in its CCCR is cleared. * - LVTPC is masked on interrupt and must be * unmasked by the LVTPC handler. */ wrmsr(MSR_P4_IQ_CCCR0, nmi_p4_cccr_val, 0); apic_write(APIC_LVTPC, APIC_DM_NMI); } else if (nmi_perfctr_msr == MSR_P6_PERFCTR0) { /* Only P6 based Pentium M need to re-unmask * the apic vector but it doesn't hurt * other P6 variant */ apic_write(APIC_LVTPC, APIC_DM_NMI); } write_watchdog_counter(NULL); }}#ifdef CONFIG_SYSCTLstatic int unknown_nmi_panic_callback(struct pt_regs *regs, int cpu){ unsigned char reason = get_nmi_reason(); char buf[64]; if (!(reason & 0xc0)) { sprintf(buf, "NMI received for unknown reason %02x\n", reason); die_nmi(regs, buf); } return 0;}/* * proc handler for /proc/sys/kernel/unknown_nmi_panic */int proc_unknown_nmi_panic(ctl_table *table, int write, struct file *file, void __user *buffer, size_t *length, loff_t *ppos){ int old_state; old_state = unknown_nmi_panic; proc_dointvec(table, write, file, buffer, length, ppos); if (!!old_state == !!unknown_nmi_panic) return 0; if (unknown_nmi_panic) { if (reserve_lapic_nmi() < 0) { unknown_nmi_panic = 0; return -EBUSY; } else { set_nmi_callback(unknown_nmi_panic_callback); } } else { release_lapic_nmi(); unset_nmi_callback(); } return 0;}#endifEXPORT_SYMBOL(nmi_active);EXPORT_SYMBOL(nmi_watchdog);EXPORT_SYMBOL(reserve_lapic_nmi);EXPORT_SYMBOL(release_lapic_nmi);EXPORT_SYMBOL(disable_timer_nmi_watchdog);EXPORT_SYMBOL(enable_timer_nmi_watchdog);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -