Commit 878068ea authored by Kan Liang's avatar Kan Liang Committed by Ingo Molnar

perf/x86: Support outputting XMM registers

Starting from Icelake, XMM registers can be collected in PEBS record.
But current code only output the pt_regs.

Add a new struct x86_perf_regs for both pt_regs and xmm_regs. The
xmm_regs will be used later to keep a pointer to PEBS record which has
XMM information.

XMM registers are 128 bit. To simplify the code, they are handled like
two different registers, which means setting two bits in the register
bitmap. This also allows only sampling the lower 64bit bits in XMM.

The index of XMM registers starts from 32. There are 16 XMM registers.
So all reserved space for regs are used. Remove REG_RESERVED.

Add PERF_REG_X86_XMM_MAX, which stands for the max number of all x86
regs including both GPRs and XMM.

Add REG_NOSUPPORT for 32bit to exclude unsupported registers.

Previous platforms can not collect XMM information in PEBS record.
Adding pebs_no_xmm_regs to indicate the unsupported platforms.

The common code still validates the supported registers. However, it
cannot check model specific registers, e.g. XMM. Add extra check in
x86_pmu_hw_config() to reject invalid config of regs_user and regs_intr.
The regs_user never supports XMM collection.
The regs_intr only supports XMM collection when sampling PEBS event on
icelake and later platforms.
Originally-by: default avatarAndi Kleen <ak@linux.intel.com>
Suggested-by: default avatarPeter Zijlstra (Intel) <peterz@infradead.org>
Signed-off-by: default avatarKan Liang <kan.liang@linux.intel.com>
Signed-off-by: default avatarPeter 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>
Cc: Vince Weaver <vincent.weaver@maine.edu>
Cc: acme@kernel.org
Cc: jolsa@kernel.org
Link: https://lkml.kernel.org/r/20190402194509.2832-3-kan.liang@linux.intel.comSigned-off-by: default avatarIngo Molnar <mingo@kernel.org>
parent f447e4eb
...@@ -560,6 +560,21 @@ int x86_pmu_hw_config(struct perf_event *event) ...@@ -560,6 +560,21 @@ int x86_pmu_hw_config(struct perf_event *event)
return -EINVAL; return -EINVAL;
} }
/* sample_regs_user never support XMM registers */
if (unlikely(event->attr.sample_regs_user & PEBS_XMM_REGS))
return -EINVAL;
/*
* Besides the general purpose registers, XMM registers may
* be collected in PEBS on some platforms, e.g. Icelake
*/
if (unlikely(event->attr.sample_regs_intr & PEBS_XMM_REGS)) {
if (x86_pmu.pebs_no_xmm_regs)
return -EINVAL;
if (!event->attr.precise_ip)
return -EINVAL;
}
return x86_setup_perfctr(event); return x86_setup_perfctr(event);
} }
......
...@@ -1628,8 +1628,10 @@ void __init intel_ds_init(void) ...@@ -1628,8 +1628,10 @@ void __init intel_ds_init(void)
x86_pmu.bts = boot_cpu_has(X86_FEATURE_BTS); x86_pmu.bts = boot_cpu_has(X86_FEATURE_BTS);
x86_pmu.pebs = boot_cpu_has(X86_FEATURE_PEBS); x86_pmu.pebs = boot_cpu_has(X86_FEATURE_PEBS);
x86_pmu.pebs_buffer_size = PEBS_BUFFER_SIZE; x86_pmu.pebs_buffer_size = PEBS_BUFFER_SIZE;
if (x86_pmu.version <= 4) if (x86_pmu.version <= 4) {
x86_pmu.pebs_no_isolation = 1; x86_pmu.pebs_no_isolation = 1;
x86_pmu.pebs_no_xmm_regs = 1;
}
if (x86_pmu.pebs) { if (x86_pmu.pebs) {
char pebs_type = x86_pmu.intel_cap.pebs_trap ? '+' : '-'; char pebs_type = x86_pmu.intel_cap.pebs_trap ? '+' : '-';
int format = x86_pmu.intel_cap.pebs_format; int format = x86_pmu.intel_cap.pebs_format;
......
...@@ -115,6 +115,24 @@ struct amd_nb { ...@@ -115,6 +115,24 @@ struct amd_nb {
(1ULL << PERF_REG_X86_R14) | \ (1ULL << PERF_REG_X86_R14) | \
(1ULL << PERF_REG_X86_R15)) (1ULL << PERF_REG_X86_R15))
#define PEBS_XMM_REGS \
((1ULL << PERF_REG_X86_XMM0) | \
(1ULL << PERF_REG_X86_XMM1) | \
(1ULL << PERF_REG_X86_XMM2) | \
(1ULL << PERF_REG_X86_XMM3) | \
(1ULL << PERF_REG_X86_XMM4) | \
(1ULL << PERF_REG_X86_XMM5) | \
(1ULL << PERF_REG_X86_XMM6) | \
(1ULL << PERF_REG_X86_XMM7) | \
(1ULL << PERF_REG_X86_XMM8) | \
(1ULL << PERF_REG_X86_XMM9) | \
(1ULL << PERF_REG_X86_XMM10) | \
(1ULL << PERF_REG_X86_XMM11) | \
(1ULL << PERF_REG_X86_XMM12) | \
(1ULL << PERF_REG_X86_XMM13) | \
(1ULL << PERF_REG_X86_XMM14) | \
(1ULL << PERF_REG_X86_XMM15))
/* /*
* Per register state. * Per register state.
*/ */
...@@ -612,7 +630,8 @@ struct x86_pmu { ...@@ -612,7 +630,8 @@ struct x86_pmu {
pebs_broken :1, pebs_broken :1,
pebs_prec_dist :1, pebs_prec_dist :1,
pebs_no_tlb :1, pebs_no_tlb :1,
pebs_no_isolation :1; pebs_no_isolation :1,
pebs_no_xmm_regs :1;
int pebs_record_size; int pebs_record_size;
int pebs_buffer_size; int pebs_buffer_size;
void (*drain_pebs)(struct pt_regs *regs); void (*drain_pebs)(struct pt_regs *regs);
......
...@@ -248,6 +248,11 @@ extern void perf_events_lapic_init(void); ...@@ -248,6 +248,11 @@ extern void perf_events_lapic_init(void);
#define PERF_EFLAGS_VM (1UL << 5) #define PERF_EFLAGS_VM (1UL << 5)
struct pt_regs; struct pt_regs;
struct x86_perf_regs {
struct pt_regs regs;
u64 *xmm_regs;
};
extern unsigned long perf_instruction_pointer(struct pt_regs *regs); extern unsigned long perf_instruction_pointer(struct pt_regs *regs);
extern unsigned long perf_misc_flags(struct pt_regs *regs); extern unsigned long perf_misc_flags(struct pt_regs *regs);
#define perf_misc_flags(regs) perf_misc_flags(regs) #define perf_misc_flags(regs) perf_misc_flags(regs)
......
...@@ -27,8 +27,29 @@ enum perf_event_x86_regs { ...@@ -27,8 +27,29 @@ enum perf_event_x86_regs {
PERF_REG_X86_R13, PERF_REG_X86_R13,
PERF_REG_X86_R14, PERF_REG_X86_R14,
PERF_REG_X86_R15, PERF_REG_X86_R15,
/* These are the limits for the GPRs. */
PERF_REG_X86_32_MAX = PERF_REG_X86_GS + 1, PERF_REG_X86_32_MAX = PERF_REG_X86_GS + 1,
PERF_REG_X86_64_MAX = PERF_REG_X86_R15 + 1, PERF_REG_X86_64_MAX = PERF_REG_X86_R15 + 1,
/* These all need two bits set because they are 128bit */
PERF_REG_X86_XMM0 = 32,
PERF_REG_X86_XMM1 = 34,
PERF_REG_X86_XMM2 = 36,
PERF_REG_X86_XMM3 = 38,
PERF_REG_X86_XMM4 = 40,
PERF_REG_X86_XMM5 = 42,
PERF_REG_X86_XMM6 = 44,
PERF_REG_X86_XMM7 = 46,
PERF_REG_X86_XMM8 = 48,
PERF_REG_X86_XMM9 = 50,
PERF_REG_X86_XMM10 = 52,
PERF_REG_X86_XMM11 = 54,
PERF_REG_X86_XMM12 = 56,
PERF_REG_X86_XMM13 = 58,
PERF_REG_X86_XMM14 = 60,
PERF_REG_X86_XMM15 = 62,
/* These include both GPRs and XMMX registers */
PERF_REG_X86_XMM_MAX = PERF_REG_X86_XMM15 + 2,
}; };
#endif /* _ASM_X86_PERF_REGS_H */ #endif /* _ASM_X86_PERF_REGS_H */
...@@ -59,18 +59,34 @@ static unsigned int pt_regs_offset[PERF_REG_X86_MAX] = { ...@@ -59,18 +59,34 @@ static unsigned int pt_regs_offset[PERF_REG_X86_MAX] = {
u64 perf_reg_value(struct pt_regs *regs, int idx) u64 perf_reg_value(struct pt_regs *regs, int idx)
{ {
struct x86_perf_regs *perf_regs;
if (idx >= PERF_REG_X86_XMM0 && idx < PERF_REG_X86_XMM_MAX) {
perf_regs = container_of(regs, struct x86_perf_regs, regs);
if (!perf_regs->xmm_regs)
return 0;
return perf_regs->xmm_regs[idx - PERF_REG_X86_XMM0];
}
if (WARN_ON_ONCE(idx >= ARRAY_SIZE(pt_regs_offset))) if (WARN_ON_ONCE(idx >= ARRAY_SIZE(pt_regs_offset)))
return 0; return 0;
return regs_get_register(regs, pt_regs_offset[idx]); return regs_get_register(regs, pt_regs_offset[idx]);
} }
#define REG_RESERVED (~((1ULL << PERF_REG_X86_MAX) - 1ULL))
#ifdef CONFIG_X86_32 #ifdef CONFIG_X86_32
#define REG_NOSUPPORT ((1ULL << PERF_REG_X86_R8) | \
(1ULL << PERF_REG_X86_R9) | \
(1ULL << PERF_REG_X86_R10) | \
(1ULL << PERF_REG_X86_R11) | \
(1ULL << PERF_REG_X86_R12) | \
(1ULL << PERF_REG_X86_R13) | \
(1ULL << PERF_REG_X86_R14) | \
(1ULL << PERF_REG_X86_R15))
int perf_reg_validate(u64 mask) int perf_reg_validate(u64 mask)
{ {
if (!mask || mask & REG_RESERVED) if (!mask || (mask & REG_NOSUPPORT))
return -EINVAL; return -EINVAL;
return 0; return 0;
...@@ -96,10 +112,7 @@ void perf_get_regs_user(struct perf_regs *regs_user, ...@@ -96,10 +112,7 @@ void perf_get_regs_user(struct perf_regs *regs_user,
int perf_reg_validate(u64 mask) int perf_reg_validate(u64 mask)
{ {
if (!mask || mask & REG_RESERVED) if (!mask || (mask & REG_NOSUPPORT))
return -EINVAL;
if (mask & REG_NOSUPPORT)
return -EINVAL; return -EINVAL;
return 0; return 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment