lkml.org 
[lkml]   [2000]   [Sep]   [28]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
Date
From
SubjectRe: [patch] enabling APIC and NMI watchdog on UP systems

On Fri, 29 Sep 2000, Keith Owens wrote:

> Using performance counter 1 for NMI conflicts with using the
> performance counters for tuning. IMHO it is better to default to NMI
> off for UP so we do not disturb people who are doing performance
> monitoring. [...]

the number of people experiencing hard lockups, and the importance of
debugging these (annoying) lockups IMO far outweighs the potential
inconvenience caused by having to disable the NMI oopser for performance
tuning.

> That then requires a mechanism for configuring the UP NMI and
> activating it on the fly, which complicates the code.

no.

> set_nmi_counter_local() has to be called after every NMI to reset the

oops - i *am* reprogramming the counter (i obviously tested wether the NMI
oopser works on a non-SMP P6 system, and checked the NMI counter in
/proc/interrupts), but it was left out of the patch by accident [forgot to
create a .orig file]. I've attached the -C3 patch.

Ingo
--- linux/include/asm-i386/apic.h.orig Thu Sep 28 05:18:49 2000
+++ linux/include/asm-i386/apic.h Thu Sep 28 05:29:14 2000
@@ -71,6 +71,7 @@
extern int verify_local_APIC (void);
extern void cache_APIC_registers (void);
extern void sync_Arb_IDs(void);
+extern int detect_init_APIC (void);
extern void setup_local_APIC (void);
extern void init_apic_mappings(void);
extern void smp_local_timer_interrupt(struct pt_regs * regs);
--- linux/include/asm-i386/msr.h.orig Thu Oct 7 10:17:09 1999
+++ linux/include/asm-i386/msr.h Thu Sep 28 05:33:02 2000
@@ -30,3 +30,21 @@
: "=a" (low), "=d" (high) \
: "c" (counter))

+#define MCG_STATUS_MSR 0x17a
+#define MCG_CAP 0x179
+#define MCG_CTL 0x17b
+#define MC0_BASE 0x400
+#define MC0_CTL_OFFSET 0x0
+#define MC0_STATUS_OFFSET 0x1
+#define MC0_ADDR_OFFSET 0x2
+#define MC0_MISC_OFFSET 0x3
+#define MC0_BANK_COUNT 0x4
+#define DEBUGCTLMSR 0x1d9
+#define LASTBRANCHFROMIP 0x1db
+#define LASTBRANCHTOIP 0x1dc
+#define LASTINTFROMIP 0x1dd
+#define LASTINTTOIP 0x1de
+#define PERFCTR0 0xc1
+#define PERFCTR1 0xc2
+#define EVNTSEL0 0x186
+#define EVNTSEL1 0x187
--- linux/include/asm-i386/processor.h.orig Thu Sep 28 06:39:06 2000
+++ linux/include/asm-i386/processor.h Thu Sep 28 06:39:27 2000
@@ -127,6 +127,8 @@
(boot_cpu_data.x86_capability & X86_FEATURE_FXSR)
#define cpu_has_xmm \
(boot_cpu_data.x86_capability & X86_FEATURE_XMM)
+#define cpu_has_apic \
+ (boot_cpu_data.x86_capability & X86_FEATURE_APIC)

extern char ignore_irq13;

--- linux/include/asm-i386/timex.h.orig Thu Sep 28 06:47:10 2000
+++ linux/include/asm-i386/timex.h Thu Sep 28 06:47:15 2000
@@ -45,4 +45,6 @@
#endif
}

+extern unsigned long cpu_khz;
+
#endif
--- linux/arch/i386/kernel/apic.c.orig Thu Sep 28 05:07:18 2000
+++ linux/arch/i386/kernel/apic.c Thu Sep 28 07:06:44 2000
@@ -84,15 +84,7 @@
void __init connect_bsp_APIC(void)
{
if (pic_mode) {
- /*
- * Do not trust the local APIC being empty at bootup.
- */
- clear_local_APIC();
- /*
- * PIC mode, enable symmetric IO mode in the IMCR,
- * i.e. connect BSP's local APIC to INT and NMI lines.
- */
- printk("leaving PIC mode, enabling symmetric IO mode.\n");
+ printk("leaving PIC mode, enabling APIC mode.\n");
outb(0x70, 0x22);
outb(0x01, 0x23);
}
@@ -107,7 +99,7 @@
* interrupts, including IPIs, won't work beyond
* this point! The only exception are INIT IPIs.
*/
- printk("disabling symmetric IO mode, entering PIC mode.\n");
+ printk("disabling APIC mode, entering PIC mode.\n");
outb(0x70, 0x22);
outb(0x00, 0x23);
}
@@ -201,12 +193,98 @@
| APIC_DM_INIT);
}

+/*
+ * Detect and enable local APICs on non-SMP boards.
+ * Original code written by Keir Fraser
+ */
+
+int detect_init_APIC (void)
+{
+ u32 h, l, dummy, features;
+
+ if (boot_cpu_data.x86 < 5) {
+ printk("No local APIC on pre-Pentium Intel processors\n");
+ return -1;
+ }
+
+ if (boot_cpu_data.x86 == 6) {
+ /*
+ * Some BIOSes disable the local APIC in the APIC_BASE MSR.
+ * This can only be done in software for PPro and above.
+ */
+ rdmsr(0x1b, l, h);
+ if (!(l & 0x800)) {
+ printk("Local APIC disabled by BIOS -- reenabling.\n");
+ l |= 0x800;
+ wrmsr(0x1b, l, h);
+
+ /*
+ * The APIC feature bit should now be enabled
+ * in `cpuid'
+ */
+ cpuid(1, &dummy, &dummy, &dummy, &features);
+ if (!(features & X86_FEATURE_APIC)) {
+ printk("Could not enable APIC!\n");
+ return -1;
+ }
+ }
+ }
+
+ /*
+ * Connect the BSP APIC to INT and NMI lines.
+ */
+ pic_mode = 1;
+ mp_lapic_addr = APIC_DEFAULT_PHYS_BASE;
+
+ printk("found and enabled local APIC!\n");
+
+ return 0;
+}
+
+/*
+ * Activate the NMI watchdog via the local APIC.
+ * Original code written by Keith Owens.
+ */
+
+static void setup_apic_nmi_watchdog (void)
+{
+ int value;
+
+ /* clear performance counters 0, 1 */
+
+ wrmsr(EVNTSEL0, 0, 0);
+ wrmsr(EVNTSEL1, 0, 0);
+ wrmsr(PERFCTR0, 0, 0);
+ wrmsr(PERFCTR1, 0, 0);
+
+ /* send local-APIC timer counter overflow event as an NMI */
+
+ value = 1 << 20 /* Interrupt on overflow */
+ | 1 << 17 /* OS mode */
+ | 1 << 16 /* User mode */
+ | 0x79; /* Event, cpu clocks not halted */
+ wrmsr(EVNTSEL1, value, 0);
+ printk("PERFCTR1: %08lx\n", -(cpu_khz/HZ*1000));
+ wrmsr(PERFCTR1, -(cpu_khz/HZ*1000), 0);
+
+ /* Enable performance counters, only using ctr1 */
+
+ apic_write_around(APIC_LVTPC, APIC_DM_NMI);
+ value = 1 << 22;
+ wrmsr(EVNTSEL0, value, 0);
+}
+
extern void __error_in_apic_c (void);

void __init setup_local_APIC (void)
{
unsigned long value, ver, maxlvt;

+ /*
+ * Do not trust the local APIC being empty at bootup.
+ */
+ clear_local_APIC();
+
value = apic_read(APIC_LVR);
ver = GET_APIC_VERSION(value);

@@ -323,26 +401,52 @@
* Must be "all ones" explicitly for 82489DX.
*/
apic_write_around(APIC_DFR, 0xffffffff);
+
+ if (boot_cpu_data.x86 == 6)
+ setup_apic_nmi_watchdog();
}

void __init init_apic_mappings(void)
{
unsigned long apic_phys;

- if (smp_found_config) {
- apic_phys = mp_lapic_addr;
- } else {
- /*
- * set up a fake all zeroes page to simulate the
- * local APIC and another one for the IO-APIC. We
- * could use the real zero-page, but it's safer
- * this way if some buggy code writes to this page ...
- */
+ /*
+ * If no local APIC can be found then set up a fake all
+ * zeroes page to simulate the local APIC and another
+ * one for the IO-APIC.
+ */
+ if (!smp_found_config && detect_init_APIC()) {
apic_phys = (unsigned long) alloc_bootmem_pages(PAGE_SIZE);
apic_phys = __pa(apic_phys);
- }
+ } else
+ apic_phys = mp_lapic_addr;
+
set_fixmap_nocache(FIX_APIC_BASE, apic_phys);
Dprintk("mapped APIC to %08lx (%08lx)\n", APIC_BASE, apic_phys);
+
+ if (!smp_found_config && cpu_has_apic) {
+ /*
+ * Make sure that we are processor 0, and that we are indicated
+ * as present. This stuff doesn't normally get done on a
+ * uniprocessor machine.
+ */
+ phys_cpu_present_map = 1;
+ apic_read_around(APIC_ID); /* buggy P5 paranoia */
+ apic_write_around(APIC_ID, 0);
+
+ /*
+ * A final sanity check. Early P5 processors don't have a local
+ * APIC. Also, a P5 local APIC can be disabled in hardware!
+ */
+ if (apic_read(APIC_ID)) {
+ printk("Can't find local APIC: non-existent or disabled"
+ "in hardware\n");
+ return;
+ }
+
+ /* Now we know the APIC is enabled, update CPU features flags */
+ boot_cpu_data.x86_capability |= X86_FEATURE_APIC;
+ }

/*
* Fetch the APIC ID of the BSP in case we have a
--- linux/arch/i386/kernel/io_apic.c.orig Thu Sep 28 05:07:19 2000
+++ linux/arch/i386/kernel/io_apic.c Thu Sep 28 05:27:39 2000
@@ -1538,17 +1538,19 @@
}

#ifndef CONFIG_SMP
+
/*
* This initializes the IO-APIC and APIC hardware if this is
* a UP kernel.
*/
void IO_APIC_init_uniprocessor (void)
{
- if (!smp_found_config)
- return;
connect_bsp_APIC();
setup_local_APIC();
- setup_IO_APIC();
- setup_APIC_clocks();
+
+ if (smp_found_config)
+ setup_IO_APIC();
+
+ setup_APIC_clocks();
}
#endif
--- linux/arch/i386/kernel/mpparse.c.orig Thu Sep 28 05:25:46 2000
+++ linux/arch/i386/kernel/mpparse.c Thu Sep 28 07:50:52 2000
@@ -37,9 +37,10 @@
int apic_version [MAX_APICS];
int mp_bus_id_to_type [MAX_MP_BUSSES];
int mp_bus_id_to_pci_bus [MAX_MP_BUSSES] = { -1, };
-int mp_current_pci_id = 0;
+int mp_current_pci_id;
+
int pic_mode;
-unsigned long mp_lapic_addr = 0;
+unsigned long mp_lapic_addr;

/* Processor that is doing the boot up */
unsigned int boot_cpu_id = -1U;
@@ -532,6 +533,9 @@
void __init find_intel_smp (void)
{
unsigned int address;
+
+// printk("emulating a non-SMP board.\n");
+// return;

/*
* FIXME: Linux assumes you have 640K of base ram..
--- linux/arch/i386/kernel/traps.c.orig Thu Sep 28 16:01:15 2000
+++ linux/arch/i386/kernel/traps.c Thu Sep 28 07:16:30 2000
@@ -462,6 +462,8 @@

++nmi_count(smp_processor_id());
if (!(reason & 0xc0)) {
+ if (cpu_has_apic && (boot_cpu_data.x86 == 6))
+ wrmsr(PERFCTR1, -(cpu_khz/HZ*1000), 0);
#if CONFIG_X86_IO_APIC
/*
* Ok, so this is none of the documented NMI sources,
--- linux/arch/i386/kernel/smpboot.c.orig Thu Sep 28 06:47:33 2000
+++ linux/arch/i386/kernel/smpboot.c Thu Sep 28 06:47:40 2000
@@ -774,7 +774,6 @@
}

cycles_t cacheflush_time;
-extern unsigned long cpu_khz;

static void smp_tune_scheduling (void)
{
--- linux/arch/i386/kernel/setup.c.orig Thu Sep 28 06:48:18 2000
+++ linux/arch/i386/kernel/setup.c Thu Sep 28 06:48:21 2000
@@ -135,7 +135,6 @@

extern int root_mountflags;
extern char _text, _etext, _edata, _end;
-extern unsigned long cpu_khz;

/*
* This is set up by the setup-routine at boot-time
--- linux/Documentation/nmi_watchdog.txt.orig Thu Sep 28 05:38:35 2000
+++ linux/Documentation/nmi_watchdog.txt Thu Sep 28 05:39:01 2000
@@ -1,4 +1,6 @@

+NMI watchdog, started by Ingo Molnar <mingo@redhat.com>
+
Is your SMP system locking up unpredictably? No keyboard activity, just
a frustrating complete hard lockup? Do you want to help us debugging
such lockups? If all yes then this document is definitely for you.
\
 
 \ /
  Last update: 2005-03-22 12:38    [W:0.198 / U:0.168 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site