mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-24 02:18:18 +07:00
c3d266c8a9
This patch tries to fix a PEBS warning found in my stress test. The following perf command can easily trigger the pebs warning or spurious NMI error on Skylake/Broadwell/Haswell platforms: sudo perf record -e 'cpu/umask=0x04,event=0xc4/pp,cycles,branches,ref-cycles,cache-misses,cache-references' --call-graph fp -b -c1000 -a Also the NMI watchdog must be enabled. For this case, the events number is larger than counter number. So perf has to do multiplexing. In perf_mux_hrtimer_handler, it does perf_pmu_disable(), schedule out old events, rotate_ctx, schedule in new events and finally perf_pmu_enable(). If the old events include precise event, the MSR_IA32_PEBS_ENABLE should be cleared when perf_pmu_disable(). The MSR_IA32_PEBS_ENABLE should keep 0 until the perf_pmu_enable() is called and the new event is precise event. However, there is a corner case which could restore PEBS_ENABLE to stale value during the above period. In perf_pmu_disable(), GLOBAL_CTRL will be set to 0 to stop overflow and followed PMI. But there may be pending PMI from an earlier overflow, which cannot be stopped. So even GLOBAL_CTRL is cleared, the kernel still be possible to get PMI. At the end of the PMI handler, __intel_pmu_enable_all() will be called, which will restore the stale values if old events haven't scheduled out. Once the stale pebs value is set, it's impossible to be corrected if the new events are non-precise. Because the pebs_enabled will be set to 0. x86_pmu.enable_all() will ignore the MSR_IA32_PEBS_ENABLE setting. As a result, the following NMI with stale PEBS_ENABLE trigger pebs warning. The pending PMI after enabled=0 will become harmless if the NMI handler does not change the state. This patch checks cpuc->enabled in pmi and only restore the state when PMU is active. Here is the dump: Call Trace: <NMI> [<ffffffff813c3a2e>] dump_stack+0x63/0x85 [<ffffffff810a46f2>] warn_slowpath_common+0x82/0xc0 [<ffffffff810a483a>] warn_slowpath_null+0x1a/0x20 [<ffffffff8100fe2e>] intel_pmu_drain_pebs_nhm+0x2be/0x320 [<ffffffff8100caa9>] intel_pmu_handle_irq+0x279/0x460 [<ffffffff810639b6>] ? native_write_msr_safe+0x6/0x40 [<ffffffff811f290d>] ? vunmap_page_range+0x20d/0x330 [<ffffffff811f2f11>] ? unmap_kernel_range_noflush+0x11/0x20 [<ffffffff8148379f>] ? ghes_copy_tofrom_phys+0x10f/0x2a0 [<ffffffff814839c8>] ? ghes_read_estatus+0x98/0x170 [<ffffffff81005a7d>] perf_event_nmi_handler+0x2d/0x50 [<ffffffff810310b9>] nmi_handle+0x69/0x120 [<ffffffff810316f6>] default_do_nmi+0xe6/0x100 [<ffffffff810317f2>] do_nmi+0xe2/0x130 [<ffffffff817aea71>] end_repeat_nmi+0x1a/0x1e [<ffffffff810639b6>] ? native_write_msr_safe+0x6/0x40 [<ffffffff810639b6>] ? native_write_msr_safe+0x6/0x40 [<ffffffff810639b6>] ? native_write_msr_safe+0x6/0x40 <<EOE>> <IRQ> [<ffffffff81006df8>] ? x86_perf_event_set_period+0xd8/0x180 [<ffffffff81006eec>] x86_pmu_start+0x4c/0x100 [<ffffffff8100722d>] x86_pmu_enable+0x28d/0x300 [<ffffffff811994d7>] perf_pmu_enable.part.81+0x7/0x10 [<ffffffff8119cb70>] perf_mux_hrtimer_handler+0x200/0x280 [<ffffffff8119c970>] ? __perf_install_in_context+0xc0/0xc0 [<ffffffff8110f92d>] __hrtimer_run_queues+0xfd/0x280 [<ffffffff811100d8>] hrtimer_interrupt+0xa8/0x190 [<ffffffff81199080>] ? __perf_read_group_add.part.61+0x1a0/0x1a0 [<ffffffff81051bd8>] local_apic_timer_interrupt+0x38/0x60 [<ffffffff817af01d>] smp_apic_timer_interrupt+0x3d/0x50 [<ffffffff817ad15c>] apic_timer_interrupt+0x8c/0xa0 <EOI> [<ffffffff81199080>] ? __perf_read_group_add.part.61+0x1a0/0x1a0 [<ffffffff81123de5>] ? smp_call_function_single+0xd5/0x130 [<ffffffff81123ddb>] ? smp_call_function_single+0xcb/0x130 [<ffffffff81199080>] ? __perf_read_group_add.part.61+0x1a0/0x1a0 [<ffffffff8119765a>] event_function_call+0x10a/0x120 [<ffffffff8119c660>] ? ctx_resched+0x90/0x90 [<ffffffff811971e0>] ? cpu_clock_event_read+0x30/0x30 [<ffffffff811976d0>] ? _perf_event_disable+0x60/0x60 [<ffffffff8119772b>] _perf_event_enable+0x5b/0x70 [<ffffffff81197388>] perf_event_for_each_child+0x38/0xa0 [<ffffffff811976d0>] ? _perf_event_disable+0x60/0x60 [<ffffffff811a0ffd>] perf_ioctl+0x12d/0x3c0 [<ffffffff8134d855>] ? selinux_file_ioctl+0x95/0x1e0 [<ffffffff8124a3a1>] do_vfs_ioctl+0xa1/0x5a0 [<ffffffff81036d29>] ? sched_clock+0x9/0x10 [<ffffffff8124a919>] SyS_ioctl+0x79/0x90 [<ffffffff817ac4b2>] entry_SYSCALL_64_fastpath+0x1a/0xa4 ---[ end trace aef202839fe9a71d ]--- Uhhuh. NMI received for unknown reason 2d on CPU 2. Do you have a strange power saving mode enabled? Signed-off-by: Kan Liang <kan.liang@intel.com> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Cc: <stable@vger.kernel.org> Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com> Cc: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: Jiri Olsa <jolsa@redhat.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Stephane Eranian <eranian@google.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Vince Weaver <vincent.weaver@maine.edu> Link: http://lkml.kernel.org/r/1457046448-6184-1-git-send-email-kan.liang@intel.com [ Fixed various typos and other small details. ] Signed-off-by: Ingo Molnar <mingo@kernel.org>
322 lines
8.2 KiB
C
322 lines
8.2 KiB
C
/* Driver for Intel Xeon Phi "Knights Corner" PMU */
|
|
|
|
#include <linux/perf_event.h>
|
|
#include <linux/types.h>
|
|
|
|
#include <asm/hardirq.h>
|
|
|
|
#include "../perf_event.h"
|
|
|
|
static const u64 knc_perfmon_event_map[] =
|
|
{
|
|
[PERF_COUNT_HW_CPU_CYCLES] = 0x002a,
|
|
[PERF_COUNT_HW_INSTRUCTIONS] = 0x0016,
|
|
[PERF_COUNT_HW_CACHE_REFERENCES] = 0x0028,
|
|
[PERF_COUNT_HW_CACHE_MISSES] = 0x0029,
|
|
[PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = 0x0012,
|
|
[PERF_COUNT_HW_BRANCH_MISSES] = 0x002b,
|
|
};
|
|
|
|
static const u64 __initconst knc_hw_cache_event_ids
|
|
[PERF_COUNT_HW_CACHE_MAX]
|
|
[PERF_COUNT_HW_CACHE_OP_MAX]
|
|
[PERF_COUNT_HW_CACHE_RESULT_MAX] =
|
|
{
|
|
[ C(L1D) ] = {
|
|
[ C(OP_READ) ] = {
|
|
/* On Xeon Phi event "0" is a valid DATA_READ */
|
|
/* (L1 Data Cache Reads) Instruction. */
|
|
/* We code this as ARCH_PERFMON_EVENTSEL_INT as this */
|
|
/* bit will always be set in x86_pmu_hw_config(). */
|
|
[ C(RESULT_ACCESS) ] = ARCH_PERFMON_EVENTSEL_INT,
|
|
/* DATA_READ */
|
|
[ C(RESULT_MISS) ] = 0x0003, /* DATA_READ_MISS */
|
|
},
|
|
[ C(OP_WRITE) ] = {
|
|
[ C(RESULT_ACCESS) ] = 0x0001, /* DATA_WRITE */
|
|
[ C(RESULT_MISS) ] = 0x0004, /* DATA_WRITE_MISS */
|
|
},
|
|
[ C(OP_PREFETCH) ] = {
|
|
[ C(RESULT_ACCESS) ] = 0x0011, /* L1_DATA_PF1 */
|
|
[ C(RESULT_MISS) ] = 0x001c, /* L1_DATA_PF1_MISS */
|
|
},
|
|
},
|
|
[ C(L1I ) ] = {
|
|
[ C(OP_READ) ] = {
|
|
[ C(RESULT_ACCESS) ] = 0x000c, /* CODE_READ */
|
|
[ C(RESULT_MISS) ] = 0x000e, /* CODE_CACHE_MISS */
|
|
},
|
|
[ C(OP_WRITE) ] = {
|
|
[ C(RESULT_ACCESS) ] = -1,
|
|
[ C(RESULT_MISS) ] = -1,
|
|
},
|
|
[ C(OP_PREFETCH) ] = {
|
|
[ C(RESULT_ACCESS) ] = 0x0,
|
|
[ C(RESULT_MISS) ] = 0x0,
|
|
},
|
|
},
|
|
[ C(LL ) ] = {
|
|
[ C(OP_READ) ] = {
|
|
[ C(RESULT_ACCESS) ] = 0,
|
|
[ C(RESULT_MISS) ] = 0x10cb, /* L2_READ_MISS */
|
|
},
|
|
[ C(OP_WRITE) ] = {
|
|
[ C(RESULT_ACCESS) ] = 0x10cc, /* L2_WRITE_HIT */
|
|
[ C(RESULT_MISS) ] = 0,
|
|
},
|
|
[ C(OP_PREFETCH) ] = {
|
|
[ C(RESULT_ACCESS) ] = 0x10fc, /* L2_DATA_PF2 */
|
|
[ C(RESULT_MISS) ] = 0x10fe, /* L2_DATA_PF2_MISS */
|
|
},
|
|
},
|
|
[ C(DTLB) ] = {
|
|
[ C(OP_READ) ] = {
|
|
[ C(RESULT_ACCESS) ] = ARCH_PERFMON_EVENTSEL_INT,
|
|
/* DATA_READ */
|
|
/* see note on L1 OP_READ */
|
|
[ C(RESULT_MISS) ] = 0x0002, /* DATA_PAGE_WALK */
|
|
},
|
|
[ C(OP_WRITE) ] = {
|
|
[ C(RESULT_ACCESS) ] = 0x0001, /* DATA_WRITE */
|
|
[ C(RESULT_MISS) ] = 0x0002, /* DATA_PAGE_WALK */
|
|
},
|
|
[ C(OP_PREFETCH) ] = {
|
|
[ C(RESULT_ACCESS) ] = 0x0,
|
|
[ C(RESULT_MISS) ] = 0x0,
|
|
},
|
|
},
|
|
[ C(ITLB) ] = {
|
|
[ C(OP_READ) ] = {
|
|
[ C(RESULT_ACCESS) ] = 0x000c, /* CODE_READ */
|
|
[ C(RESULT_MISS) ] = 0x000d, /* CODE_PAGE_WALK */
|
|
},
|
|
[ C(OP_WRITE) ] = {
|
|
[ C(RESULT_ACCESS) ] = -1,
|
|
[ C(RESULT_MISS) ] = -1,
|
|
},
|
|
[ C(OP_PREFETCH) ] = {
|
|
[ C(RESULT_ACCESS) ] = -1,
|
|
[ C(RESULT_MISS) ] = -1,
|
|
},
|
|
},
|
|
[ C(BPU ) ] = {
|
|
[ C(OP_READ) ] = {
|
|
[ C(RESULT_ACCESS) ] = 0x0012, /* BRANCHES */
|
|
[ C(RESULT_MISS) ] = 0x002b, /* BRANCHES_MISPREDICTED */
|
|
},
|
|
[ C(OP_WRITE) ] = {
|
|
[ C(RESULT_ACCESS) ] = -1,
|
|
[ C(RESULT_MISS) ] = -1,
|
|
},
|
|
[ C(OP_PREFETCH) ] = {
|
|
[ C(RESULT_ACCESS) ] = -1,
|
|
[ C(RESULT_MISS) ] = -1,
|
|
},
|
|
},
|
|
};
|
|
|
|
|
|
static u64 knc_pmu_event_map(int hw_event)
|
|
{
|
|
return knc_perfmon_event_map[hw_event];
|
|
}
|
|
|
|
static struct event_constraint knc_event_constraints[] =
|
|
{
|
|
INTEL_EVENT_CONSTRAINT(0xc3, 0x1), /* HWP_L2HIT */
|
|
INTEL_EVENT_CONSTRAINT(0xc4, 0x1), /* HWP_L2MISS */
|
|
INTEL_EVENT_CONSTRAINT(0xc8, 0x1), /* L2_READ_HIT_E */
|
|
INTEL_EVENT_CONSTRAINT(0xc9, 0x1), /* L2_READ_HIT_M */
|
|
INTEL_EVENT_CONSTRAINT(0xca, 0x1), /* L2_READ_HIT_S */
|
|
INTEL_EVENT_CONSTRAINT(0xcb, 0x1), /* L2_READ_MISS */
|
|
INTEL_EVENT_CONSTRAINT(0xcc, 0x1), /* L2_WRITE_HIT */
|
|
INTEL_EVENT_CONSTRAINT(0xce, 0x1), /* L2_STRONGLY_ORDERED_STREAMING_VSTORES_MISS */
|
|
INTEL_EVENT_CONSTRAINT(0xcf, 0x1), /* L2_WEAKLY_ORDERED_STREAMING_VSTORE_MISS */
|
|
INTEL_EVENT_CONSTRAINT(0xd7, 0x1), /* L2_VICTIM_REQ_WITH_DATA */
|
|
INTEL_EVENT_CONSTRAINT(0xe3, 0x1), /* SNP_HITM_BUNIT */
|
|
INTEL_EVENT_CONSTRAINT(0xe6, 0x1), /* SNP_HIT_L2 */
|
|
INTEL_EVENT_CONSTRAINT(0xe7, 0x1), /* SNP_HITM_L2 */
|
|
INTEL_EVENT_CONSTRAINT(0xf1, 0x1), /* L2_DATA_READ_MISS_CACHE_FILL */
|
|
INTEL_EVENT_CONSTRAINT(0xf2, 0x1), /* L2_DATA_WRITE_MISS_CACHE_FILL */
|
|
INTEL_EVENT_CONSTRAINT(0xf6, 0x1), /* L2_DATA_READ_MISS_MEM_FILL */
|
|
INTEL_EVENT_CONSTRAINT(0xf7, 0x1), /* L2_DATA_WRITE_MISS_MEM_FILL */
|
|
INTEL_EVENT_CONSTRAINT(0xfc, 0x1), /* L2_DATA_PF2 */
|
|
INTEL_EVENT_CONSTRAINT(0xfd, 0x1), /* L2_DATA_PF2_DROP */
|
|
INTEL_EVENT_CONSTRAINT(0xfe, 0x1), /* L2_DATA_PF2_MISS */
|
|
INTEL_EVENT_CONSTRAINT(0xff, 0x1), /* L2_DATA_HIT_INFLIGHT_PF2 */
|
|
EVENT_CONSTRAINT_END
|
|
};
|
|
|
|
#define MSR_KNC_IA32_PERF_GLOBAL_STATUS 0x0000002d
|
|
#define MSR_KNC_IA32_PERF_GLOBAL_OVF_CONTROL 0x0000002e
|
|
#define MSR_KNC_IA32_PERF_GLOBAL_CTRL 0x0000002f
|
|
|
|
#define KNC_ENABLE_COUNTER0 0x00000001
|
|
#define KNC_ENABLE_COUNTER1 0x00000002
|
|
|
|
static void knc_pmu_disable_all(void)
|
|
{
|
|
u64 val;
|
|
|
|
rdmsrl(MSR_KNC_IA32_PERF_GLOBAL_CTRL, val);
|
|
val &= ~(KNC_ENABLE_COUNTER0|KNC_ENABLE_COUNTER1);
|
|
wrmsrl(MSR_KNC_IA32_PERF_GLOBAL_CTRL, val);
|
|
}
|
|
|
|
static void knc_pmu_enable_all(int added)
|
|
{
|
|
u64 val;
|
|
|
|
rdmsrl(MSR_KNC_IA32_PERF_GLOBAL_CTRL, val);
|
|
val |= (KNC_ENABLE_COUNTER0|KNC_ENABLE_COUNTER1);
|
|
wrmsrl(MSR_KNC_IA32_PERF_GLOBAL_CTRL, val);
|
|
}
|
|
|
|
static inline void
|
|
knc_pmu_disable_event(struct perf_event *event)
|
|
{
|
|
struct hw_perf_event *hwc = &event->hw;
|
|
u64 val;
|
|
|
|
val = hwc->config;
|
|
val &= ~ARCH_PERFMON_EVENTSEL_ENABLE;
|
|
|
|
(void)wrmsrl_safe(hwc->config_base + hwc->idx, val);
|
|
}
|
|
|
|
static void knc_pmu_enable_event(struct perf_event *event)
|
|
{
|
|
struct hw_perf_event *hwc = &event->hw;
|
|
u64 val;
|
|
|
|
val = hwc->config;
|
|
val |= ARCH_PERFMON_EVENTSEL_ENABLE;
|
|
|
|
(void)wrmsrl_safe(hwc->config_base + hwc->idx, val);
|
|
}
|
|
|
|
static inline u64 knc_pmu_get_status(void)
|
|
{
|
|
u64 status;
|
|
|
|
rdmsrl(MSR_KNC_IA32_PERF_GLOBAL_STATUS, status);
|
|
|
|
return status;
|
|
}
|
|
|
|
static inline void knc_pmu_ack_status(u64 ack)
|
|
{
|
|
wrmsrl(MSR_KNC_IA32_PERF_GLOBAL_OVF_CONTROL, ack);
|
|
}
|
|
|
|
static int knc_pmu_handle_irq(struct pt_regs *regs)
|
|
{
|
|
struct perf_sample_data data;
|
|
struct cpu_hw_events *cpuc;
|
|
int handled = 0;
|
|
int bit, loops;
|
|
u64 status;
|
|
|
|
cpuc = this_cpu_ptr(&cpu_hw_events);
|
|
|
|
knc_pmu_disable_all();
|
|
|
|
status = knc_pmu_get_status();
|
|
if (!status) {
|
|
knc_pmu_enable_all(0);
|
|
return handled;
|
|
}
|
|
|
|
loops = 0;
|
|
again:
|
|
knc_pmu_ack_status(status);
|
|
if (++loops > 100) {
|
|
WARN_ONCE(1, "perf: irq loop stuck!\n");
|
|
perf_event_print_debug();
|
|
goto done;
|
|
}
|
|
|
|
inc_irq_stat(apic_perf_irqs);
|
|
|
|
for_each_set_bit(bit, (unsigned long *)&status, X86_PMC_IDX_MAX) {
|
|
struct perf_event *event = cpuc->events[bit];
|
|
|
|
handled++;
|
|
|
|
if (!test_bit(bit, cpuc->active_mask))
|
|
continue;
|
|
|
|
if (!intel_pmu_save_and_restart(event))
|
|
continue;
|
|
|
|
perf_sample_data_init(&data, 0, event->hw.last_period);
|
|
|
|
if (perf_event_overflow(event, &data, regs))
|
|
x86_pmu_stop(event, 0);
|
|
}
|
|
|
|
/*
|
|
* Repeat if there is more work to be done:
|
|
*/
|
|
status = knc_pmu_get_status();
|
|
if (status)
|
|
goto again;
|
|
|
|
done:
|
|
/* Only restore PMU state when it's active. See x86_pmu_disable(). */
|
|
if (cpuc->enabled)
|
|
knc_pmu_enable_all(0);
|
|
|
|
return handled;
|
|
}
|
|
|
|
|
|
PMU_FORMAT_ATTR(event, "config:0-7" );
|
|
PMU_FORMAT_ATTR(umask, "config:8-15" );
|
|
PMU_FORMAT_ATTR(edge, "config:18" );
|
|
PMU_FORMAT_ATTR(inv, "config:23" );
|
|
PMU_FORMAT_ATTR(cmask, "config:24-31" );
|
|
|
|
static struct attribute *intel_knc_formats_attr[] = {
|
|
&format_attr_event.attr,
|
|
&format_attr_umask.attr,
|
|
&format_attr_edge.attr,
|
|
&format_attr_inv.attr,
|
|
&format_attr_cmask.attr,
|
|
NULL,
|
|
};
|
|
|
|
static const struct x86_pmu knc_pmu __initconst = {
|
|
.name = "knc",
|
|
.handle_irq = knc_pmu_handle_irq,
|
|
.disable_all = knc_pmu_disable_all,
|
|
.enable_all = knc_pmu_enable_all,
|
|
.enable = knc_pmu_enable_event,
|
|
.disable = knc_pmu_disable_event,
|
|
.hw_config = x86_pmu_hw_config,
|
|
.schedule_events = x86_schedule_events,
|
|
.eventsel = MSR_KNC_EVNTSEL0,
|
|
.perfctr = MSR_KNC_PERFCTR0,
|
|
.event_map = knc_pmu_event_map,
|
|
.max_events = ARRAY_SIZE(knc_perfmon_event_map),
|
|
.apic = 1,
|
|
.max_period = (1ULL << 39) - 1,
|
|
.version = 0,
|
|
.num_counters = 2,
|
|
.cntval_bits = 40,
|
|
.cntval_mask = (1ULL << 40) - 1,
|
|
.get_event_constraints = x86_get_event_constraints,
|
|
.event_constraints = knc_event_constraints,
|
|
.format_attrs = intel_knc_formats_attr,
|
|
};
|
|
|
|
__init int knc_pmu_init(void)
|
|
{
|
|
x86_pmu = knc_pmu;
|
|
|
|
memcpy(hw_cache_event_ids, knc_hw_cache_event_ids,
|
|
sizeof(hw_cache_event_ids));
|
|
|
|
return 0;
|
|
}
|