perf/x86: Disable extended registers for non-supported PMUs

The perf fuzzer caused Skylake machine to crash:

[ 9680.085831] Call Trace:
[ 9680.088301] <IRQ>
[ 9680.090363] perf_output_sample_regs+0x43/0xa0
[ 9680.094928] perf_output_sample+0x3aa/0x7a0
[ 9680.099181] perf_event_output_forward+0x53/0x80
[ 9680.103917] __perf_event_overflow+0x52/0xf0
[ 9680.108266] ? perf_trace_run_bpf_submit+0xc0/0xc0
[ 9680.113108] perf_swevent_hrtimer+0xe2/0x150
[ 9680.117475] ? check_preempt_wakeup+0x181/0x230
[ 9680.122091] ? check_preempt_curr+0x62/0x90
[ 9680.126361] ? ttwu_do_wakeup+0x19/0x140
[ 9680.130355] ? try_to_wake_up+0x54/0x460
[ 9680.134366] ? reweight_entity+0x15b/0x1a0
[ 9680.138559] ? __queue_work+0x103/0x3f0
[ 9680.142472] ? update_dl_rq_load_avg+0x1cd/0x270
[ 9680.147194] ? timerqueue_del+0x1e/0x40
[ 9680.151092] ? __remove_hrtimer+0x35/0x70
[ 9680.155191] __hrtimer_run_queues+0x100/0x280
[ 9680.159658] hrtimer_interrupt+0x100/0x220
[ 9680.163835] smp_apic_timer_interrupt+0x6a/0x140
[ 9680.168555] apic_timer_interrupt+0xf/0x20
[ 9680.172756] </IRQ>

The XMM registers can only be collected by PEBS hardware events on the
platforms with PEBS baseline support, e.g. Icelake, not software/probe
events.

Add capabilities flag PERF_PMU_CAP_EXTENDED_REGS to indicate the PMU
which support extended registers. For X86, the extended registers are
XMM registers.

Add has_extended_regs() to check if extended registers are applied.

The generic code define the mask of extended registers as 0 if arch
headers haven't overridden it.

Originally-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reported-by: Vince Weaver <vincent.weaver@maine.edu>
Signed-off-by: Kan Liang <kan.liang@linux.intel.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.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>
Fixes: 878068ea270e ("perf/x86: Support outputting XMM registers")
Link: https://lkml.kernel.org/r/1559081314-9714-1-git-send-email-kan.liang@linux.intel.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>

authored by

Kan Liang and committed by
Ingo Molnar
e321d02d 913a90bc

+27 -4
+1
arch/x86/events/intel/ds.c
··· 2020 2020 PERF_SAMPLE_TIME; 2021 2021 x86_pmu.flags |= PMU_FL_PEBS_ALL; 2022 2022 pebs_qual = "-baseline"; 2023 + x86_get_pmu()->capabilities |= PERF_PMU_CAP_EXTENDED_REGS; 2023 2024 } else { 2024 2025 /* Only basic record supported */ 2025 2026 x86_pmu.pebs_no_xmm_regs = 1;
+3
arch/x86/include/uapi/asm/perf_regs.h
··· 52 52 /* These include both GPRs and XMMX registers */ 53 53 PERF_REG_X86_XMM_MAX = PERF_REG_X86_XMM15 + 2, 54 54 }; 55 + 56 + #define PERF_REG_EXTENDED_MASK (~((1ULL << PERF_REG_X86_XMM0) - 1)) 57 + 55 58 #endif /* _ASM_X86_PERF_REGS_H */
+1
include/linux/perf_event.h
··· 241 241 #define PERF_PMU_CAP_NO_INTERRUPT 0x01 242 242 #define PERF_PMU_CAP_NO_NMI 0x02 243 243 #define PERF_PMU_CAP_AUX_NO_SG 0x04 244 + #define PERF_PMU_CAP_EXTENDED_REGS 0x08 244 245 #define PERF_PMU_CAP_EXCLUSIVE 0x10 245 246 #define PERF_PMU_CAP_ITRACE 0x20 246 247 #define PERF_PMU_CAP_HETEROGENEOUS_CPUS 0x40
+8
include/linux/perf_regs.h
··· 11 11 12 12 #ifdef CONFIG_HAVE_PERF_REGS 13 13 #include <asm/perf_regs.h> 14 + 15 + #ifndef PERF_REG_EXTENDED_MASK 16 + #define PERF_REG_EXTENDED_MASK 0 17 + #endif 18 + 14 19 u64 perf_reg_value(struct pt_regs *regs, int idx); 15 20 int perf_reg_validate(u64 mask); 16 21 u64 perf_reg_abi(struct task_struct *task); ··· 23 18 struct pt_regs *regs, 24 19 struct pt_regs *regs_user_copy); 25 20 #else 21 + 22 + #define PERF_REG_EXTENDED_MASK 0 23 + 26 24 static inline u64 perf_reg_value(struct pt_regs *regs, int idx) 27 25 { 28 26 return 0;
+14 -4
kernel/events/core.c
··· 10036 10036 } 10037 10037 EXPORT_SYMBOL_GPL(perf_pmu_unregister); 10038 10038 10039 + static inline bool has_extended_regs(struct perf_event *event) 10040 + { 10041 + return (event->attr.sample_regs_user & PERF_REG_EXTENDED_MASK) || 10042 + (event->attr.sample_regs_intr & PERF_REG_EXTENDED_MASK); 10043 + } 10044 + 10039 10045 static int perf_try_init_event(struct pmu *pmu, struct perf_event *event) 10040 10046 { 10041 10047 struct perf_event_context *ctx = NULL; ··· 10073 10067 perf_event_ctx_unlock(event->group_leader, ctx); 10074 10068 10075 10069 if (!ret) { 10070 + if (!(pmu->capabilities & PERF_PMU_CAP_EXTENDED_REGS) && 10071 + has_extended_regs(event)) 10072 + ret = -EOPNOTSUPP; 10073 + 10076 10074 if (pmu->capabilities & PERF_PMU_CAP_NO_EXCLUDE && 10077 - event_has_any_exclude_flag(event)) { 10078 - if (event->destroy) 10079 - event->destroy(event); 10075 + event_has_any_exclude_flag(event)) 10080 10076 ret = -EINVAL; 10081 - } 10077 + 10078 + if (ret && event->destroy) 10079 + event->destroy(event); 10082 10080 } 10083 10081 10084 10082 if (ret)