From: Jan Kiszka <jan.kiszka@domain.hid>
To: xenomai@xenomai.org
Cc: Jan Kiszka <jan.kiszka@domain.hid>
Subject: [Xenomai-core] [PATCH 2/3] NMI watchdog support for x86-64
Date: Sun, 26 Oct 2008 15:43:53 +0100 [thread overview]
Message-ID: <20081026144352.338906449@domain.hid> (raw)
In-Reply-To: 20081026144351.950914436@domain.hid
No need to lock the NMI away from x86-64 boxes, it just takes a bit
refactoring.
NOTE: Whoever applies this to SVN, make sure to MOVE nmi_32.c to nmi.c!
Signed-off-by: Jan Kiszka <jan.kiszka@domain.hid>
---
include/asm-x86/hal.h | 2
include/asm-x86/hal_32.h | 2
ksrc/arch/x86/Kconfig | 4
ksrc/arch/x86/Makefile | 4
ksrc/arch/x86/hal-common.c | 49 ++++++
ksrc/arch/x86/hal_32.c | 45 ------
ksrc/arch/x86/nmi.c | 326 +++++++++++++++++++++++++++++++++++++++++++++
ksrc/arch/x86/nmi_32.c | 326 ---------------------------------------------
8 files changed, 379 insertions(+), 379 deletions(-)
Index: b/include/asm-x86/hal.h
===================================================================
--- a/include/asm-x86/hal.h
+++ b/include/asm-x86/hal.h
@@ -69,6 +69,8 @@ typedef int (*compat_emutick_t)(unsigned
extern enum rthal_ktimer_mode rthal_ktimer_saved_mode;
+void rthal_latency_above_max(struct pt_regs *regs);
+
#ifdef __i386__
#include "hal_32.h"
#else
Index: b/include/asm-x86/hal_32.h
===================================================================
--- a/include/asm-x86/hal_32.h
+++ b/include/asm-x86/hal_32.h
@@ -228,6 +228,4 @@ static inline void rthal_setup_oneshot_a
long rthal_strncpy_from_user(char *dst, const char __user * src, long count);
-void rthal_latency_above_max(struct pt_regs *regs);
-
#endif /* !_XENO_ASM_X86_HAL_32_H */
Index: b/ksrc/arch/x86/Kconfig
===================================================================
--- a/ksrc/arch/x86/Kconfig
+++ b/ksrc/arch/x86/Kconfig
@@ -26,8 +26,6 @@ config XENO_HW_FPU
Float-Point Unit on the x86 platform at the following URL:
http://www.intel.com/design/intarch/techinfo/Pentium/fpu.htm
-if !X86_64
-
menu "NMI watchdog"
config XENO_HW_NMI_DEBUG_LATENCY
@@ -58,8 +56,6 @@ config XENO_HW_NMI_DEBUG_LATENCY_MAX
endmenu
-endif
-
menu "SMI workaround"
config XENO_HW_SMI_DETECT_DISABLE
Index: b/ksrc/arch/x86/hal_32.c
===================================================================
--- a/ksrc/arch/x86/hal_32.c
+++ b/ksrc/arch/x86/hal_32.c
@@ -97,51 +97,6 @@ unsigned long rthal_timer_calibrate(void
return rthal_imuldiv(dt, 20, RTHAL_CPU_FREQ);
}
-#ifdef CONFIG_XENO_HW_NMI_DEBUG_LATENCY
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
-
-#include <linux/vt_kern.h>
-
-extern void show_registers(struct pt_regs *regs);
-
-extern spinlock_t nmi_print_lock;
-
-void die_nmi(struct pt_regs *regs, const char *msg)
-{
- spin_lock(&nmi_print_lock);
- /*
- * We are in trouble anyway, lets at least try
- * to get a message out.
- */
- bust_spinlocks(1);
- printk(msg);
- show_registers(regs);
- printk("console shuts up ...\n");
- console_silent();
- spin_unlock(&nmi_print_lock);
- bust_spinlocks(0);
- do_exit(SIGSEGV);
-}
-
-#endif /* Linux < 2.6 */
-
-void rthal_latency_above_max(struct pt_regs *regs)
-{
- /* Try to report via latency tracer first, then fall back to panic. */
- if (rthal_trace_user_freeze(rthal_maxlat_us, 1) < 0) {
- char buf[128];
-
- snprintf(buf,
- sizeof(buf),
- "NMI watchdog detected timer latency above %u us\n",
- rthal_maxlat_us);
- die_nmi(regs, buf);
- }
-}
-
-#endif /* CONFIG_XENO_HW_NMI_DEBUG_LATENCY */
-
#else /* !CONFIG_X86_LOCAL_APIC */
unsigned long rthal_timer_calibrate(void)
Index: b/ksrc/arch/x86/hal-common.c
===================================================================
--- a/ksrc/arch/x86/hal-common.c
+++ b/ksrc/arch/x86/hal-common.c
@@ -278,6 +278,55 @@ void rthal_timer_release(int cpu)
rthal_timer_set_oneshot(0);
}
+#ifdef CONFIG_XENO_HW_NMI_DEBUG_LATENCY
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+
+#include <linux/vt_kern.h>
+
+extern void show_registers(struct pt_regs *regs);
+
+extern spinlock_t nmi_print_lock;
+
+void die_nmi(struct pt_regs *regs, const char *msg)
+{
+ spin_lock(&nmi_print_lock);
+ /*
+ * We are in trouble anyway, lets at least try
+ * to get a message out.
+ */
+ bust_spinlocks(1);
+ printk(msg);
+ show_registers(regs);
+ printk("console shuts up ...\n");
+ console_silent();
+ spin_unlock(&nmi_print_lock);
+ bust_spinlocks(0);
+ do_exit(SIGSEGV);
+}
+
+#endif /* Linux < 2.6 */
+
+#ifdef CONFIG_X86_64
+#include <asm/nmi.h>
+#define die_nmi(regs, msg) die_nmi(msg, regs, 1)
+#endif /* CONFIG_X86_64 */
+
+void rthal_latency_above_max(struct pt_regs *regs)
+{
+ /* Try to report via latency tracer first, then fall back to panic. */
+ if (rthal_trace_user_freeze(rthal_maxlat_us, 1) < 0) {
+ char buf[128];
+
+ snprintf(buf,
+ sizeof(buf),
+ "NMI watchdog detected timer latency above %u us\n",
+ rthal_maxlat_us);
+ die_nmi(regs, buf);
+ }
+}
+
+#endif /* CONFIG_XENO_HW_NMI_DEBUG_LATENCY */
#endif /* CONFIG_X86_LOCAL_APIC */
Index: b/ksrc/arch/x86/Makefile
===================================================================
--- a/ksrc/arch/x86/Makefile
+++ b/ksrc/arch/x86/Makefile
@@ -12,7 +12,7 @@ obj-$(CONFIG_XENOMAI) += xeno_hal.o
xeno_hal-y := hal_$(X86_MODE).o hal-common.o usercopy_$(X86_MODE).o
-xeno_hal-$(CONFIG_XENO_HW_NMI_DEBUG_LATENCY) += nmi_$(X86_MODE).o
+xeno_hal-$(CONFIG_XENO_HW_NMI_DEBUG_LATENCY) += nmi.o
xeno_hal-$(CONFIG_XENO_HW_SMI_DETECT) += smi.o
@@ -28,7 +28,7 @@ O_TARGET := built-in.o
obj-y := hal_32.o hal-common.o
-obj-$(CONFIG_XENO_HW_NMI_DEBUG_LATENCY) += nmi_32.o
+obj-$(CONFIG_XENO_HW_NMI_DEBUG_LATENCY) += nmi.o
obj-$(CONFIG_XENO_HW_SMI_DETECT) += smi.o
Index: b/ksrc/arch/x86/nmi.c
===================================================================
--- /dev/null
+++ b/ksrc/arch/x86/nmi.c
@@ -0,0 +1,326 @@
+/**
+ * @ingroup hal
+ * @file
+ *
+ * NMI watchdog for x86, from linux/arch/i386/kernel/nmi.c
+ *
+ * Original authors:
+ * Ingo Molnar, Mikael Pettersson, Pavel Machek.
+ *
+ * Adaptation to Xenomai by Gilles Chanteperdrix
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,
+ * USA; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/nmi.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19)
+#include <asm/intel_arch_perfmon.h>
+#endif /* Linux < 2.6.19 */
+#include <asm/nmi.h>
+#endif /* Linux < 2.6 */
+#include <asm/msr.h>
+#include <asm/xenomai/hal.h>
+
+#define NMI_WD_ARMED 0x0001
+#define NMI_WD_31BITS 0x1000
+#define NMI_WD_P4 0x2000
+#define NMI_WD_P6_OR_LATER 0x4000
+
+#define P4_ESCR_EVENT_SELECT(N) ((N)<<25)
+#define P4_ESCR_OS (1<<3)
+#define P4_ESCR_USR (1<<2)
+#define P4_CCCR_OVF_PMI0 (1<<26)
+#define P4_CCCR_OVF_PMI1 (1<<27)
+#define P4_CCCR_THRESHOLD(N) ((N)<<20)
+#define P4_CCCR_COMPLEMENT (1<<19)
+#define P4_CCCR_COMPARE (1<<18)
+#define P4_CCCR_REQUIRED (3<<16)
+#define P4_CCCR_ESCR_SELECT(N) ((N)<<13)
+#define P4_CCCR_ENABLE (1<<12)
+/* Set up IQ_COUNTER0 to behave like a clock, by having IQ_CCCR0 filter
+ CRU_ESCR0 (with any non-null event selector) through a complemented
+ max threshold. [IA32-Vol3, Section 14.9.9] */
+#define MSR_P4_IQ_COUNTER0 0x30C
+#define P4_NMI_CRU_ESCR0 (P4_ESCR_EVENT_SELECT(0x3F)|P4_ESCR_OS|P4_ESCR_USR)
+#define P4_NMI_IQ_CCCR0 \
+ (P4_CCCR_OVF_PMI0|P4_CCCR_THRESHOLD(15)|P4_CCCR_COMPLEMENT| \
+ P4_CCCR_COMPARE|P4_CCCR_REQUIRED|P4_CCCR_ESCR_SELECT(4)|P4_CCCR_ENABLE)
+
+typedef union {
+ struct {
+ /* Xenomai watchdog data. */
+ unsigned int flags;
+ unsigned long perfctr_msr;
+ unsigned long long next_linux_check;
+ unsigned int p4_cccr_val;
+
+ unsigned early_shots;
+ unsigned long long tick_date;
+ };
+ char __pad[SMP_CACHE_BYTES];
+} rthal_nmi_wd_t ____cacheline_aligned;
+
+static rthal_nmi_wd_t rthal_nmi_wds[NR_CPUS];
+static void (*rthal_nmi_emergency) (struct pt_regs *);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
+#define MSR_ARCH_PERFMON_PERFCTR0 0xc1
+#define MSR_ARCH_PERFMON_PERFCTR1 0xc2
+static void (*rthal_linux_nmi_tick) (struct pt_regs *);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+#define MSR_P4_IQ_CCCR0 0x36C
+#define rthal_nmi_active (nmi_watchdog != NMI_NONE)
+static inline void wrmsrl(unsigned long msr, unsigned long long val)
+{
+ unsigned long lo, hi;
+ lo = (unsigned long)val;
+ hi = val >> 32;
+ wrmsr(msr, lo, hi);
+}
+#else /* Linux 2.6.0..18 */
+extern int nmi_active;
+#define rthal_nmi_active nmi_active
+#endif /* Linux 2.6.0..18 */
+
+#else /* Linux >= 2.6.19 */
+static int (*rthal_linux_nmi_tick) (struct pt_regs *, unsigned);
+#define rthal_nmi_active atomic_read(&nmi_active)
+#endif /* Linux >= 2.6.19 */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
+#define CALL_LINUX_NMI rthal_linux_nmi_tick(regs)
+#define NMI_RETURN return
+static void rthal_nmi_watchdog_tick(struct pt_regs *regs)
+#else /* Linux >= 2.6.19 */
+#define CALL_LINUX_NMI rthal_linux_nmi_tick(regs, reason)
+#define NMI_RETURN return 1
+static int rthal_nmi_watchdog_tick(struct pt_regs *regs, unsigned reason)
+#endif /* Linux >= 2.6.19 */
+{
+ int cpu = rthal_processor_id();
+ rthal_nmi_wd_t *wd = &rthal_nmi_wds[cpu];
+ unsigned long long now;
+
+ if (wd->flags & NMI_WD_ARMED) {
+ if (rthal_rdtsc() - wd->tick_date < rthal_maxlat_tsc) {
+ ++wd->early_shots;
+ wd->next_linux_check = wd->tick_date + rthal_maxlat_tsc;
+ } else {
+ printk("NMI early shots: %d\n", wd->early_shots);
+ rthal_nmi_emergency(regs);
+ }
+ }
+
+ now = rthal_rdtsc();
+
+ if ((long long)(now - wd->next_linux_check) >= 0) {
+
+ CALL_LINUX_NMI;
+
+ do {
+ wd->next_linux_check += RTHAL_CPU_FREQ;
+ } while ((long long)(now - wd->next_linux_check) >= 0);
+ }
+
+ if (wd->flags & NMI_WD_P4) {
+ /*
+ * 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, wd->p4_cccr_val, 0);
+ apic_write(APIC_LVTPC, APIC_DM_NMI);
+ } else if (wd->flags & NMI_WD_P6_OR_LATER) {
+ /* P6 based Pentium M need to re-unmask
+ * the apic vector but it doesn't hurt
+ * other P6 variant.
+ * ArchPerfom/Core Duo also needs this */
+ apic_write(APIC_LVTPC, APIC_DM_NMI);
+ }
+
+ if (wd->flags & NMI_WD_31BITS)
+ wrmsr(wd->perfctr_msr, (u32)(now - wd->next_linux_check), 0);
+ else
+ wrmsrl(wd->perfctr_msr, now - wd->next_linux_check);
+
+ NMI_RETURN;
+}
+
+#ifdef CONFIG_PROC_FS
+static int earlyshots_read_proc(char *page,
+ char **start,
+ off_t off, int count, int *eof, void *data)
+{
+ int i, len = 0;
+
+ for_each_online_cpu(i)
+ len += sprintf(page + len, "CPU#%d: %u\n",
+ i, rthal_nmi_wds[i].early_shots);
+ len -= off;
+ if (len <= off + count)
+ *eof = 1;
+ *start = page + off;
+ if (len > count)
+ len = count;
+ if (len < 0)
+ len = 0;
+
+ return len;
+}
+#endif /* CONFIG_PROC_FS */
+
+int rthal_nmi_request(void (*emergency) (struct pt_regs *))
+{
+ unsigned long long next_linux_check;
+ unsigned long perfctr_msr;
+ unsigned int wd_flags = 0;
+ unsigned int p4_cccr_val = 0;
+ int i;
+
+ if (!rthal_nmi_active || !nmi_watchdog_tick)
+ return -ENODEV;
+
+ if (rthal_linux_nmi_tick)
+ return -EBUSY;
+
+ switch (boot_cpu_data.x86_vendor) {
+ case X86_VENDOR_AMD:
+ perfctr_msr = MSR_K7_PERFCTR0;
+ break;
+ case X86_VENDOR_INTEL:
+ if (cpu_has(&boot_cpu_data, X86_FEATURE_ARCH_PERFMON)) {
+ if (boot_cpu_data.x86 == 6 &&
+ boot_cpu_data.x86_model == 14)
+ perfctr_msr = MSR_ARCH_PERFMON_PERFCTR0;
+ else
+ perfctr_msr = MSR_ARCH_PERFMON_PERFCTR1;
+ wd_flags = NMI_WD_P6_OR_LATER | NMI_WD_31BITS;
+ } else
+ switch (boot_cpu_data.x86) {
+ case 6:
+ perfctr_msr = MSR_P6_PERFCTR0;
+ wd_flags = NMI_WD_P6_OR_LATER | NMI_WD_31BITS;
+ break;
+ case 15:
+ perfctr_msr = MSR_P4_IQ_COUNTER0;
+ p4_cccr_val = P4_NMI_IQ_CCCR0;
+#ifdef CONFIG_SMP
+ if (smp_num_siblings == 2)
+ p4_cccr_val |= P4_CCCR_OVF_PMI1;
+#endif
+ break;
+ default:
+ return -ENODEV;
+ }
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ rthal_nmi_emergency = emergency;
+
+ next_linux_check = rthal_rdtsc() + RTHAL_CPU_FREQ;
+ for (i = 0; i < NR_CPUS; i++) {
+ rthal_nmi_wd_t *wd = &rthal_nmi_wds[i];
+
+ wd->flags = wd_flags;
+ wd->perfctr_msr = perfctr_msr;
+ wd->p4_cccr_val = p4_cccr_val;
+ wd->next_linux_check = next_linux_check;
+ }
+
+ rthal_linux_nmi_tick = nmi_watchdog_tick;
+ wmb();
+ nmi_watchdog_tick = &rthal_nmi_watchdog_tick;
+
+#ifdef CONFIG_PROC_FS
+ rthal_add_proc_leaf("nmi_early_shots",
+ &earlyshots_read_proc,
+ NULL, NULL, rthal_proc_root);
+#endif /* CONFIG_PROC_FS */
+
+ return 0;
+}
+
+void rthal_nmi_release(void)
+{
+ rthal_nmi_wd_t *wd = &rthal_nmi_wds[rthal_processor_id()];
+
+ if (!rthal_linux_nmi_tick)
+ return;
+
+#ifdef CONFIG_PROC_FS
+ remove_proc_entry("nmi_early_shots", rthal_proc_root);
+#endif /* CONFIG_PROC_FS */
+
+ if (wd->flags & NMI_WD_31BITS)
+ wrmsr(wd->perfctr_msr, (u32)(0 - RTHAL_CPU_FREQ), 0);
+ else
+ wrmsrl(wd->perfctr_msr, 0 - RTHAL_CPU_FREQ);
+ touch_nmi_watchdog();
+ wmb();
+ nmi_watchdog_tick = rthal_linux_nmi_tick;
+ rthal_linux_nmi_tick = NULL;
+}
+
+void rthal_nmi_arm(unsigned long delay)
+{
+ rthal_nmi_wd_t *wd = &rthal_nmi_wds[rthal_processor_id()];
+
+ if (!wd->perfctr_msr)
+ return;
+
+ /* If linux watchdog could tick now, make it tick now. */
+ if ((long long) (rthal_rdtsc() - wd->next_linux_check) >= 0) {
+ unsigned long flags;
+
+ /* Protect from an interrupt handler calling rthal_nmi_arm. */
+ rthal_local_irq_save(flags);
+ wd->flags &= ~NMI_WD_ARMED;
+ wmb();
+ if (wd->flags & NMI_WD_31BITS)
+ wrmsr(wd->perfctr_msr, (u32)-1, 0);
+ else
+ wrmsrl(wd->perfctr_msr, -1);
+ asm("nop");
+ rthal_local_irq_restore(flags);
+ }
+
+ wd->tick_date = rthal_rdtsc() + (delay - rthal_maxlat_tsc);
+ wmb();
+ if (wd->flags & NMI_WD_31BITS)
+ wrmsr(wd->perfctr_msr, (u32)(0 - delay), 0);
+ else
+ wrmsrl(wd->perfctr_msr, 0 - delay);
+ wmb();
+ wd->flags |= NMI_WD_ARMED;
+}
+
+void rthal_nmi_disarm(void)
+{
+ rthal_nmi_wds[rthal_processor_id()].flags &= ~NMI_WD_ARMED;
+}
+
+EXPORT_SYMBOL(rthal_nmi_request);
+EXPORT_SYMBOL(rthal_nmi_release);
+EXPORT_SYMBOL(rthal_nmi_arm);
+EXPORT_SYMBOL(rthal_nmi_disarm);
Index: b/ksrc/arch/x86/nmi_32.c
===================================================================
--- a/ksrc/arch/x86/nmi_32.c
+++ /dev/null
@@ -1,326 +0,0 @@
-/**
- * @ingroup hal
- * @file
- *
- * NMI watchdog for x86, from linux/arch/i386/kernel/nmi.c
- *
- * Original authors:
- * Ingo Molnar, Mikael Pettersson, Pavel Machek.
- *
- * Adaptation to Xenomai by Gilles Chanteperdrix
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,
- * USA; either version 2 of the License, or (at your option) any later
- * version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#include <linux/module.h>
-#include <linux/version.h>
-#include <linux/nmi.h>
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19)
-#include <asm/intel_arch_perfmon.h>
-#endif /* Linux < 2.6.19 */
-#include <asm/nmi.h>
-#endif /* Linux < 2.6 */
-#include <asm/msr.h>
-#include <asm/xenomai/hal.h>
-
-#define NMI_WD_ARMED 0x0001
-#define NMI_WD_31BITS 0x1000
-#define NMI_WD_P4 0x2000
-#define NMI_WD_P6_OR_LATER 0x4000
-
-#define P4_ESCR_EVENT_SELECT(N) ((N)<<25)
-#define P4_ESCR_OS (1<<3)
-#define P4_ESCR_USR (1<<2)
-#define P4_CCCR_OVF_PMI0 (1<<26)
-#define P4_CCCR_OVF_PMI1 (1<<27)
-#define P4_CCCR_THRESHOLD(N) ((N)<<20)
-#define P4_CCCR_COMPLEMENT (1<<19)
-#define P4_CCCR_COMPARE (1<<18)
-#define P4_CCCR_REQUIRED (3<<16)
-#define P4_CCCR_ESCR_SELECT(N) ((N)<<13)
-#define P4_CCCR_ENABLE (1<<12)
-/* Set up IQ_COUNTER0 to behave like a clock, by having IQ_CCCR0 filter
- CRU_ESCR0 (with any non-null event selector) through a complemented
- max threshold. [IA32-Vol3, Section 14.9.9] */
-#define MSR_P4_IQ_COUNTER0 0x30C
-#define P4_NMI_CRU_ESCR0 (P4_ESCR_EVENT_SELECT(0x3F)|P4_ESCR_OS|P4_ESCR_USR)
-#define P4_NMI_IQ_CCCR0 \
- (P4_CCCR_OVF_PMI0|P4_CCCR_THRESHOLD(15)|P4_CCCR_COMPLEMENT| \
- P4_CCCR_COMPARE|P4_CCCR_REQUIRED|P4_CCCR_ESCR_SELECT(4)|P4_CCCR_ENABLE)
-
-typedef union {
- struct {
- /* Xenomai watchdog data. */
- unsigned int flags;
- unsigned long perfctr_msr;
- unsigned long long next_linux_check;
- unsigned int p4_cccr_val;
-
- unsigned early_shots;
- unsigned long long tick_date;
- };
- char __pad[SMP_CACHE_BYTES];
-} rthal_nmi_wd_t ____cacheline_aligned;
-
-static rthal_nmi_wd_t rthal_nmi_wds[NR_CPUS];
-static void (*rthal_nmi_emergency) (struct pt_regs *);
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
-#define MSR_ARCH_PERFMON_PERFCTR0 0xc1
-#define MSR_ARCH_PERFMON_PERFCTR1 0xc2
-static void (*rthal_linux_nmi_tick) (struct pt_regs *);
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
-#define MSR_P4_IQ_CCCR0 0x36C
-#define rthal_nmi_active (nmi_watchdog != NMI_NONE)
-static inline void wrmsrl(unsigned long msr, unsigned long long val)
-{
- unsigned long lo, hi;
- lo = (unsigned long)val;
- hi = val >> 32;
- wrmsr(msr, lo, hi);
-}
-#else /* Linux 2.6.0..18 */
-extern int nmi_active;
-#define rthal_nmi_active nmi_active
-#endif /* Linux 2.6.0..18 */
-
-#else /* Linux >= 2.6.19 */
-static int (*rthal_linux_nmi_tick) (struct pt_regs *, unsigned);
-#define rthal_nmi_active atomic_read(&nmi_active)
-#endif /* Linux >= 2.6.19 */
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
-#define CALL_LINUX_NMI rthal_linux_nmi_tick(regs)
-#define NMI_RETURN return
-static void rthal_nmi_watchdog_tick(struct pt_regs *regs)
-#else /* Linux >= 2.6.19 */
-#define CALL_LINUX_NMI rthal_linux_nmi_tick(regs, reason)
-#define NMI_RETURN return 1
-static int rthal_nmi_watchdog_tick(struct pt_regs *regs, unsigned reason)
-#endif /* Linux >= 2.6.19 */
-{
- int cpu = rthal_processor_id();
- rthal_nmi_wd_t *wd = &rthal_nmi_wds[cpu];
- unsigned long long now;
-
- if (wd->flags & NMI_WD_ARMED) {
- if (rthal_rdtsc() - wd->tick_date < rthal_maxlat_tsc) {
- ++wd->early_shots;
- wd->next_linux_check = wd->tick_date + rthal_maxlat_tsc;
- } else {
- printk("NMI early shots: %d\n", wd->early_shots);
- rthal_nmi_emergency(regs);
- }
- }
-
- now = rthal_rdtsc();
-
- if ((long long)(now - wd->next_linux_check) >= 0) {
-
- CALL_LINUX_NMI;
-
- do {
- wd->next_linux_check += RTHAL_CPU_FREQ;
- } while ((long long)(now - wd->next_linux_check) >= 0);
- }
-
- if (wd->flags & NMI_WD_P4) {
- /*
- * 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, wd->p4_cccr_val, 0);
- apic_write(APIC_LVTPC, APIC_DM_NMI);
- } else if (wd->flags & NMI_WD_P6_OR_LATER) {
- /* P6 based Pentium M need to re-unmask
- * the apic vector but it doesn't hurt
- * other P6 variant.
- * ArchPerfom/Core Duo also needs this */
- apic_write(APIC_LVTPC, APIC_DM_NMI);
- }
-
- if (wd->flags & NMI_WD_31BITS)
- wrmsr(wd->perfctr_msr, (u32)(now - wd->next_linux_check), 0);
- else
- wrmsrl(wd->perfctr_msr, now - wd->next_linux_check);
-
- NMI_RETURN;
-}
-
-#ifdef CONFIG_PROC_FS
-static int earlyshots_read_proc(char *page,
- char **start,
- off_t off, int count, int *eof, void *data)
-{
- int i, len = 0;
-
- for_each_online_cpu(i)
- len += sprintf(page + len, "CPU#%d: %u\n",
- i, rthal_nmi_wds[i].early_shots);
- len -= off;
- if (len <= off + count)
- *eof = 1;
- *start = page + off;
- if (len > count)
- len = count;
- if (len < 0)
- len = 0;
-
- return len;
-}
-#endif /* CONFIG_PROC_FS */
-
-int rthal_nmi_request(void (*emergency) (struct pt_regs *))
-{
- unsigned long long next_linux_check;
- unsigned long perfctr_msr;
- unsigned int wd_flags = 0;
- unsigned int p4_cccr_val = 0;
- int i;
-
- if (!rthal_nmi_active || !nmi_watchdog_tick)
- return -ENODEV;
-
- if (rthal_linux_nmi_tick)
- return -EBUSY;
-
- switch (boot_cpu_data.x86_vendor) {
- case X86_VENDOR_AMD:
- perfctr_msr = MSR_K7_PERFCTR0;
- break;
- case X86_VENDOR_INTEL:
- if (cpu_has(&boot_cpu_data, X86_FEATURE_ARCH_PERFMON)) {
- if (boot_cpu_data.x86 == 6 &&
- boot_cpu_data.x86_model == 14)
- perfctr_msr = MSR_ARCH_PERFMON_PERFCTR0;
- else
- perfctr_msr = MSR_ARCH_PERFMON_PERFCTR1;
- wd_flags = NMI_WD_P6_OR_LATER | NMI_WD_31BITS;
- } else
- switch (boot_cpu_data.x86) {
- case 6:
- perfctr_msr = MSR_P6_PERFCTR0;
- wd_flags = NMI_WD_P6_OR_LATER | NMI_WD_31BITS;
- break;
- case 15:
- perfctr_msr = MSR_P4_IQ_COUNTER0;
- p4_cccr_val = P4_NMI_IQ_CCCR0;
-#ifdef CONFIG_SMP
- if (smp_num_siblings == 2)
- p4_cccr_val |= P4_CCCR_OVF_PMI1;
-#endif
- break;
- default:
- return -ENODEV;
- }
- break;
- default:
- return -ENODEV;
- }
-
- rthal_nmi_emergency = emergency;
-
- next_linux_check = rthal_rdtsc() + RTHAL_CPU_FREQ;
- for (i = 0; i < NR_CPUS; i++) {
- rthal_nmi_wd_t *wd = &rthal_nmi_wds[i];
-
- wd->flags = wd_flags;
- wd->perfctr_msr = perfctr_msr;
- wd->p4_cccr_val = p4_cccr_val;
- wd->next_linux_check = next_linux_check;
- }
-
- rthal_linux_nmi_tick = nmi_watchdog_tick;
- wmb();
- nmi_watchdog_tick = &rthal_nmi_watchdog_tick;
-
-#ifdef CONFIG_PROC_FS
- rthal_add_proc_leaf("nmi_early_shots",
- &earlyshots_read_proc,
- NULL, NULL, rthal_proc_root);
-#endif /* CONFIG_PROC_FS */
-
- return 0;
-}
-
-void rthal_nmi_release(void)
-{
- rthal_nmi_wd_t *wd = &rthal_nmi_wds[rthal_processor_id()];
-
- if (!rthal_linux_nmi_tick)
- return;
-
-#ifdef CONFIG_PROC_FS
- remove_proc_entry("nmi_early_shots", rthal_proc_root);
-#endif /* CONFIG_PROC_FS */
-
- if (wd->flags & NMI_WD_31BITS)
- wrmsr(wd->perfctr_msr, (u32)(0 - RTHAL_CPU_FREQ), 0);
- else
- wrmsrl(wd->perfctr_msr, 0 - RTHAL_CPU_FREQ);
- touch_nmi_watchdog();
- wmb();
- nmi_watchdog_tick = rthal_linux_nmi_tick;
- rthal_linux_nmi_tick = NULL;
-}
-
-void rthal_nmi_arm(unsigned long delay)
-{
- rthal_nmi_wd_t *wd = &rthal_nmi_wds[rthal_processor_id()];
-
- if (!wd->perfctr_msr)
- return;
-
- /* If linux watchdog could tick now, make it tick now. */
- if ((long long) (rthal_rdtsc() - wd->next_linux_check) >= 0) {
- unsigned long flags;
-
- /* Protect from an interrupt handler calling rthal_nmi_arm. */
- rthal_local_irq_save(flags);
- wd->flags &= ~NMI_WD_ARMED;
- wmb();
- if (wd->flags & NMI_WD_31BITS)
- wrmsr(wd->perfctr_msr, (u32)-1, 0);
- else
- wrmsrl(wd->perfctr_msr, -1);
- asm("nop");
- rthal_local_irq_restore(flags);
- }
-
- wd->tick_date = rthal_rdtsc() + (delay - rthal_maxlat_tsc);
- wmb();
- if (wd->flags & NMI_WD_31BITS)
- wrmsr(wd->perfctr_msr, (u32)(0 - delay), 0);
- else
- wrmsrl(wd->perfctr_msr, 0 - delay);
- wmb();
- wd->flags |= NMI_WD_ARMED;
-}
-
-void rthal_nmi_disarm(void)
-{
- rthal_nmi_wds[rthal_processor_id()].flags &= ~NMI_WD_ARMED;
-}
-
-EXPORT_SYMBOL(rthal_nmi_request);
-EXPORT_SYMBOL(rthal_nmi_release);
-EXPORT_SYMBOL(rthal_nmi_arm);
-EXPORT_SYMBOL(rthal_nmi_disarm);
next prev parent reply other threads:[~2008-10-26 14:43 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2008-10-26 14:43 [Xenomai-core] [PATCH 0/3] x86: Fix & update NMI watchdog Jan Kiszka
2008-10-26 14:43 ` [Xenomai-core] [PATCH 1/3] Update NMI watchdog for latest Intel CPUs Jan Kiszka
2008-10-26 14:43 ` Jan Kiszka [this message]
2008-10-26 14:43 ` [Xenomai-core] [PATCH 3/3] Rework x86 NMI watchdog pass-through Jan Kiszka
-- strict thread matches above, loose matches on Subject: below --
2008-12-19 8:44 [Xenomai-core] [PATCH 0/3] NMI watchdog fixes / enhancements Jan Kiszka
2008-12-19 8:44 ` [Xenomai-core] [PATCH 2/3] NMI watchdog support for x86-64 Jan Kiszka
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20081026144352.338906449@domain.hid \
--to=jan.kiszka@domain.hid \
--cc=xenomai@xenomai.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.