Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > fc11cd6e1c513a17304da94a5390f3cd > files > 3986

kernel-2.6.18-194.11.1.el5.src.rpm

From: Aristeu Rozanski <aris@redhat.com>
Date: Wed, 20 Aug 2008 13:17:09 -0400
Subject: [x86] nmi: disable LAPIC/IO APIC on unknown_nmi_panic
Message-id: 20080820171651.956553000@redhat.com
O-Subject: [RHEL5.3 PATCH 23/25] nmi: disable LAPIC and IO APIC NMIs on unknown_nmi_panic on i386
Bugzilla: 447618

https://bugzilla.redhat.com/show_bug.cgi?id=447618

in RHEL-5 (and upstream), when unknown_nmi_panic is set, the nmi watchdog is
disabled. this is necessary for IO APIC NMI watchdog because there's no way to
determine if the NMI was triggered by the watchdog or not.

upstream: the IO APIC enable/disable patch was submitted upstream, but not merged
          yet.

diff --git a/arch/i386/kernel/nmi.c b/arch/i386/kernel/nmi.c
index 0b80259..956ffb5 100644
--- a/arch/i386/kernel/nmi.c
+++ b/arch/i386/kernel/nmi.c
@@ -72,28 +72,6 @@ static __init void nmi_cpu_busy(void *data)
 }
 #endif
 
-static unsigned int adjust_for_32bit_ctr(unsigned int hz)
-{
-	u64 counter_val;
-	unsigned int retval = hz;
-
-	/*
-	 * On Intel CPUs with P6/ARCH_PERFMON only 32 bits in the counter
-	 * are writable, with higher bits sign extending from bit 31.
-	 * So, we can only program the counter with 31 bit values and
-	 * 32nd bit should be 1, for 33.. to be 1.
-	 * Find the appropriate nmi_hz
-	 */
-	counter_val = (u64)cpu_khz * 1000;
-	do_div(counter_val, retval);
-	if (counter_val > 0x7fffffffULL) {
-		u64 count = (u64)cpu_khz * 1000;
-		do_div(count, 0x7fffffffUL);
-		retval = count + 1;
-	}
-	return retval;
-}
-
 static int __init check_nmi_watchdog(void)
 {
 	unsigned int *prev_nmi_count;
@@ -272,39 +250,6 @@ 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 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 write_watchdog_counter32(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);
-	wrmsr(nmi_perfctr_msr, (u32)(-count), 0);
-}
-
 void setup_apic_nmi_watchdog (void)
 {
 	if (__get_cpu_var(wd_enabled) == 1)
@@ -340,6 +285,46 @@ void stop_apic_nmi_watchdog(void)
 		nmi_active = 0;
 }
 
+static void __acpi_nmi_enable(void *__unused)
+{
+	if (__get_cpu_var(wd_enabled) == 1)
+		return;
+
+	__get_cpu_var(wd_enabled) = 1;
+	if (atomic_inc_return(&nmi_watchdog_active) == 1)
+		nmi_active = 1;
+	apic_write(APIC_LVT0, APIC_DM_NMI);
+}
+
+/*
+ * Enable timer based NMIs on all CPUs:
+ */
+void acpi_nmi_enable(void)
+{
+	if (atomic_read(&nmi_watchdog_active) == 0)
+		on_each_cpu(__acpi_nmi_enable, NULL, 0, 1);
+	touch_nmi_watchdog();
+}
+
+static void __acpi_nmi_disable(void *__unused)
+{
+	if (__get_cpu_var(wd_enabled) == 0)
+		return;
+	apic_write(APIC_LVT0, APIC_DM_NMI | APIC_LVT_MASKED);
+	__get_cpu_var(wd_enabled) = 0;
+	if (atomic_dec_and_test(&nmi_watchdog_active))
+		nmi_active = 0;
+}
+
+/*
+ * Disable timer based NMIs on all CPUs:
+ */
+void acpi_nmi_disable(void)
+{
+	if (atomic_read(&nmi_watchdog_active))
+		on_each_cpu(__acpi_nmi_disable, NULL, 0, 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
@@ -463,14 +448,18 @@ int proc_unknown_nmi_panic(ctl_table *table, int write, struct file *file,
 	if (!!old_state == !!unknown_nmi_panic)
 		return 0;
 
-	if (unknown_nmi_panic) {
-		if (reserve_lapic_nmi() < 0) {
-			unknown_nmi_panic = 0;
-			return -EBUSY;
-		}
-	} else {
-		release_lapic_nmi();
-	}
+ 	if (unknown_nmi_panic) {
+		if (nmi_watchdog == NMI_LOCAL_APIC)
+			disable_lapic_nmi_watchdog();
+		else if (nmi_watchdog == NMI_IO_APIC)
+			acpi_nmi_disable();
+ 	} else {
+		if (nmi_watchdog == NMI_LOCAL_APIC)
+			enable_lapic_nmi_watchdog();
+		else if (nmi_watchdog == NMI_IO_APIC)
+			acpi_nmi_enable();
+ 	}
+
 	return 0;
 }