]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
x86: perf: prevent spurious PMU NMIs on Haswell systems
authorDan Duval <dan.duval@oracle.com>
Fri, 24 Oct 2014 19:14:14 +0000 (15:14 -0400)
committerSantosh Shilimkar <santosh.shilimkar@oracle.com>
Mon, 29 Jun 2015 15:36:56 +0000 (08:36 -0700)
Orabug: 20996846

When "perf" is run on Haswell-based systems under UEK3, we've
noticed that "extra" NMIs are being generated by the Performance
Monitoring Unit (PMU).

The PMU contains counters that can count occurrences of certain
kinds of events, such as branch misses or instructions retired.
These counters can be programmed to issue an interrupt when they
reach certain pre-set values. linux uses vector 2, the NMI vector,
for these interrupts, so the PMU interrupts behave just like other
sources of NMIs such as watchdog timers. Each consumer of NMIs
within the kernel is responsible for identifying the interrupts
it's interested in.

In the current case, the linux PMU-support code is failing to
"claim" certain of the NMIs that are originating in the PMU.
What happens when no piece of kernel code claims an NMI is that
an ugly kernel message gets generated and, if the sysctl variable
"unknown_nmi_panic" is set nonzero (as it is by default on Exadata
systems), the system panics.

The current UEK3 PMU handler attempts to determine whether a given
NMI belongs to it by scanning the PMU hardware's potential NMI
sources to find out whether any of them has triggered. Apparently,
Haswell has potential NMI sources that are indeed getting triggered,
but of which the PMU handler is not aware.

This commit contains two measures designed to prevent these
extra NMIs.

First, we've moved the write to the local APIC's APIC_LVTPC register
from near the beginning of the PMU NMI handler to near the end.
Upstream has discovered empirically that this helps elminate
the spurious NMIs.  See:

    http://lists.openwall.net/linux-kernel/2013/06/19/712

for the original commit.

Second, this change takes advantage of a bit in the APIC_LVTPC
register that gets set when (and only when) a PMU-originated NMI
is being delivered to the CPU core.  This bit is a "mask" bit,
which when set, disables delivery of these NMIs to the core.
Having processed an NMI, system software must clear this bit in
order to enable delivery of the next one.

The fix involves sampling this bit and claiming the NMI if it's a
PMU NMI, even if its origin has not been otherwise determined.

Note that this change also helps render the PMU NMI handler immune
to the addition of more sources to the PMUs on future CPUs.

Signed-off-by: Dan Duval <dan.duval@oracle.com>
Reviewed-by: Santosh Shilimkar <santosh.shilimkar@oracle.com>
Signed-off-by: Guangyu Sun <guangyu.sun@oracle.com>
Signed-off-by: Dan Duval <dan.duval@oracle.com>
(cherry picked from commit ed921c01bcd2cad94dbd659ad2031a877e85acb8)

Conflict:

arch/x86/kernel/cpu/perf_event_intel.c

Signed-off-by: Santosh Shilimkar <santosh.shilimkar@oracle.com>
arch/x86/kernel/cpu/perf_event_intel.c

index a1e35c9f06b9522af32b79cd4837f3a93a083f6b..2e06274924612cf12b79b87fe957565243f191fa 100644 (file)
@@ -1576,6 +1576,7 @@ static int intel_pmu_handle_irq(struct pt_regs *regs)
        int bit, loops;
        u64 status;
        int handled;
+       u32 lvtpc;
 
        cpuc = this_cpu_ptr(&cpu_hw_events);
 
@@ -1586,6 +1587,7 @@ static int intel_pmu_handle_irq(struct pt_regs *regs)
        if (!x86_pmu.late_ack)
                apic_write(APIC_LVTPC, APIC_DM_NMI);
        __intel_pmu_disable_all();
+
        handled = intel_pmu_drain_bts_buffer();
        handled += intel_bts_interrupt();
        status = intel_pmu_get_status();
@@ -1670,6 +1672,25 @@ again:
                goto again;
 
 done:
+       lvtpc = apic_read(APIC_LVTPC);
+
+       /*
+        * Haswell processors sometimes generate PMIs that are not handled
+        * by other code in this handler.  The following ensures that any
+        * NMI that is generated by the PMU is at least "claimed" by this
+        * handler, rather than showing up as an unknown NMI and possibly
+        * causing a crash.
+        */
+       if(handled == 0 && (lvtpc & APIC_LVT_MASKED))
+               handled = 1;
+
+       /*
+        * Only unmask the NMI after the overflow counters
+        * have been reset. This avoids spurious NMIs on
+        * Haswell CPUs.
+        */
+       apic_write(APIC_LVTPC, APIC_DM_NMI);
+
        __intel_pmu_enable_all(0, true);
        /*
         * Only unmask the NMI after the overflow counters