* [Patch v9 00/24] Support SIMD/eGPRs/SSP registers sampling for perf
@ 2026-07-06 1:54 Dapeng Mi
2026-07-06 1:54 ` [Patch v9 01/24] perf/x86: Fix two error-path and hybrid PMU guard issues Dapeng Mi
` (23 more replies)
0 siblings, 24 replies; 42+ messages in thread
From: Dapeng Mi @ 2026-07-06 1:54 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Thomas Gleixner, Dave Hansen, Ian Rogers,
Adrian Hunter, Jiri Olsa, Alexander Shishkin, Andi Kleen,
Eranian Stephane
Cc: Mark Rutland, broonie, Ravi Bangoria, linux-kernel,
linux-perf-users, Zide Chen, Falcon Thomas, Dapeng Mi, Xudong Hao,
Dapeng Mi
Patch layout:
- Patches 1-6: Bug fixes and cleanup needed before enabling XSAVES-based
sampling in NMI context.
- Patches 7-8: FPU-related preparation, including xsaves_nmi() and
related cleanup/optimization.
- Patches 9-12: PMI-based XMM sampling support through the existing
sample_regs_intr/sample_regs_user interfaces for both
PERF_SAMPLE_REGS_INTR and PERF_SAMPLE_REGS_USER.
- Patches 13-20: New SIMD register interface and support for
XMM/YMM/ZMM/OPMASK, APX eGPRs, and SSP through that interface.
- Patch 21: Extend arch PEBS to support YMM/ZMM/OPMASK, APX eGPRs, and
SSP with the new interface.
- Patch 22: Enable new interface-based sampling.
- Patches 23-24: arch PEBS bug fix and sanity check.
Changes since v8:
- Patch 01/24: Fix missing PMI handler unregister on PMU initialization
failure (Sashiko).
- Patch 11/24: Skip kernel-space SIMD sampling when exclude_kernel is
set, to account for PMI skid (Sashiko).
- Patch 12/24: Skip guest kernel-space SIMD sampling for user register
sampling.
- Patch 14/24: Strengthen SIMD register configuration validation in
perf_simd_reg_validate() (Sashiko).
- Patch 17/24: Strengthen PRED (OPMASK) register configuration validation
in perf_simd_reg_validate() (Sashiko).
- Patch 21/24: Enhance large-PEBS checks for SSP and APX eGPR registers
(Sashiko).
- Patch 22/24: Refine PERF_PMU_CAP_SIMD_REGS setup: advertise the
capability only when both XSAVES and Arch PEBS are supported (Peter).
- Refine patch changelogs across the series.
Changes since V7:
- Validate the return value of intel_pmu_init_hybrid() (Patch 01/23).
- Replace pt_regs with x86_perf_regs in xen_pmu_irq_handler()
(Patch 06/23).
- Improve event_has_extended_regs() (Patch 09/23).
- Explicitly ensure the allocated XSAVE area is 64-byte aligned
(Patch 10/23, Sashiko).
- Clear the SIMD register pointers in x86_user_regs to avoid exposing
stale register data to user space (Patch 11/23, Sashiko).
- Refine the SIMD register interface and sample data layout, and add the
missing SIMD data reservation in perf_prepare_sample() for non-x86
architectures (Patch 12/23, Sashiko).
- Improve perf_simd_reg_validate() for x86 (Patch 13/23, Sashiko).
- Refine SSP sampling and ensure the GPR sub-group flag is set for PEBS
(Patch 19/23, Sashiko).
- Fix the incorrect large-PEBS check for XMM (Patch 20/23, Sashiko).
- Fix missing handling in x86_pmu_handle_guest_pebs() for back-to-back
PMI detection (Patch 22/23, Sashiko).
- Strengthen the PEBS record header sanity checks to prevent invalid
memory access (Patch 23/23, Sashiko).
Changes since V6:
- Fix potential overwritten issue in hybrid PMU structure (patch 01/24)
- Restrict PEBS events work on GP counters if no PEBS baseline suggested
(patch 02/24)
- Use per-cpu x86_intr_regs for perf_event_nmi_handler() instead of
temporary variable (patch 06/24)
- Add helper update_fpu_state_and_flag() to ensure TIF_NEED_FPU_LOAD is
set after save_fpregs_to_fpstate() call (patch 09/24)
- Optimize and simplify x86_pmu_sample_xregs(), etc. (patch 11/24)
- Add macro word_for_each_set_bit() to simplify u64 set-bit iteration
(patch 13/24)
- Add sanity check for PEBS fragment size (patch 24/24)
Changes since V5:
- Introduce 3 commits to fix newly found PEBS issues (Patch 01~03/19)
- Address Peter comments, including,
* Fully support user-regs sampling of the SIMD/eGPRs/SSP registers
* Adjust newly added fields in perf_event_attr to avoid holes
* Fix the endian issue introduced by for_each_set_bit() in
event/core.c
* Remove some unnecessary macros from UAPI header perf_regs.h
* Enhance b2b NMI detection for all PEBS handlers to ensure identical
behaviors of all PEBS handlers
- Split perf-tools patches which would be posted in a separate patchset
later
Changes since V4:
- Rewrite some functions comments and commit messages (Dave)
- Add arch-PEBS based SIMD/eGPRs/SSP sampling support (Patch 15/19)
- Fix "suspecious NMI" warnning observed on PTL/NVL P-core and DMR by
activating back-to-back NMI detection mechanism (Patch 16/19)
- Fix some minor issues on perf-tool patches (Patch 18/19)
Changes since V3:
- Drop the SIMD registers if an NMI hits kernel mode for REGS_USER.
- Only dump the available regs, rather than zero and dump the
unavailable regs. It's possible that the dumped registers are a subset
of the requested registers.
- Some minor updates to address Dapeng's comments in V3.
Changes since V2:
- Use the FPU format for the x86_pmu.ext_regs_mask as well
- Add a check before invoking xsaves_nmi()
- Add perf_simd_reg_check() to retrieve the number of available
registers. If the kernel fails to get the requested registers, e.g.,
XSAVES fails, nothing dumps to the userspace (the V2 dumps all 0s).
- Add POC perf tool patches
Changes since V1:
- Apply the new interfaces to configure and dump the SIMD registers
- Utilize the existing FPU functions, e.g., xstate_calculate_size,
get_xsave_addr().
This series adds support on x86 for sampling SIMD registers, APX eGPRs,
and SSP with both PMI-based and PEBS-based sampling.
Starting with Intel Ice Lake, PEBS can sample XMM registers, but PMI-based
XMM sampling is still not available. On newer Intel platforms with
architectural PEBS support, such as Clearwater Forest and Diamond Rapids,
the hardware also gains support for sampling additional SIMD state
(XMM/YMM/ZMM/OPMASK), APX extended GPRs, and SSP.
To support these registers consistently across both PMI and PEBS, this
series makes the following changes:
1. Adds a new perf_event_attr interface for SIMD register selection.
The existing sample_regs_user/sample_regs_intr bitmaps do not have
enough space to represent the full SIMD register set, so this series
introduces dedicated fields for SIMD and predicate register masks and
element widths.
2. Introduces a new sample data layout for SIMD register data.
SIMD register payload is appended after the GPR payload, and a new ABI
flag, PERF_SAMPLE_REGS_ABI_SIMD, indicates its presence.
3. Adds xsaves_nmi() to allow SIMD/eGPR/SSP sampling from PMI handlers in
NMI context.
4. Extends the arch PEBS path to support YMM/ZMM/OPMASK, APX eGPRs, and
SSP sampling.
New perf_event_attr fields
--------------------------
This series adds the following fields to perf_event_attr:
/*
* Defines the sampling SIMD/PRED(predicate) register bitmaps and
* qword (8-byte) lengths.
*
* sample_simd_regs_enabled != 0 indicates SIMD/PRED registers are
* requested. The register bitmaps and element sizes are described by:
*
* sample_simd_{vec,pred}_reg_{intr,user}
* sample_simd_{vec,pred}_reg_qwords
*
* sample_simd_regs_enabled == 0 indicates no SIMD/PRED registers are
* requested.
*/
__u16 sample_simd_regs_enabled;
__u16 sample_simd_pred_reg_qwords;
__u16 sample_simd_vec_reg_qwords;
__u16 __reserved_4;
__u32 sample_simd_pred_reg_intr;
__u32 sample_simd_pred_reg_user;
__u64 sample_simd_vec_reg_intr;
__u64 sample_simd_vec_reg_user;
Field semantics:
- sample_simd_vec_reg_qwords: qword count for regular SIMD registers
- sample_simd_pred_reg_qwords: qword count for predicate registers
- sample_simd_vec_reg_{intr,user}: SIMD register masks for
PERF_SAMPLE_REGS_INTR and PERF_SAMPLE_REGS_USER
- sample_simd_pred_reg_{intr,user}: predicate register masks for
PERF_SAMPLE_REGS_INTR and PERF_SAMPLE_REGS_USER
- sample_simd_regs_enabled: indicates whether the new SIMD fields are in use
Examples:
To sample ZMM registers for PERF_SAMPLE_REGS_INTR:
sample_simd_regs_enabled = 1
sample_simd_vec_reg_qwords = 8 // 512 bits = 8 qwords
sample_simd_vec_reg_intr = 0xffffffff // zmm0-zmm31
To sample OPMASK registers for PERF_SAMPLE_REGS_USER:
sample_simd_regs_enabled = 1
sample_simd_pred_reg_qwords = 1 // 64 bits = 1 qword
sample_simd_pred_reg_user = 0xff // opmask0-opmask7
After introducing these fields, bits [63:32] in sample_regs_user and
sample_regs_intr are reclaimed for APX eGPRs and SSP instead of the
previous XMM0-XMM15 encoding.
Discussion of the new SIMD register interface is available at:
https://lore.kernel.org/lkml/20250617081458.GI1613376@noisy.programming.kicks-ass.net/
Sample data layout
------------------
SIMD register data is appended after the GPR data.
For PERF_SAMPLE_REGS_USER:
{ u64 abi; // enum perf_sample_regs_abi
u64 regs[weight(mask)];
struct {
u64 nr_vectors; // 0 ... weight(sample_simd_vec_reg_user)
u64 vector_qwords; // 0 ... sample_simd_vec_reg_qwords
u64 nr_pred; // 0 ... weight(sample_simd_pred_reg_user)
u64 pred_qwords; // 0 ... sample_simd_pred_reg_qwords
u64 data[nr_vectors * vector_qwords +
nr_pred * pred_qwords];
} && (abi & PERF_SAMPLE_REGS_ABI_SIMD)
}
For PERF_SAMPLE_REGS_INTR:
{ u64 abi; // enum perf_sample_regs_abi
u64 regs[weight(mask)];
struct {
u64 nr_vectors; // 0 ... weight(sample_simd_vec_reg_intr)
u64 vector_qwords; // 0 ... sample_simd_vec_reg_qwords
u64 nr_pred; // 0 ... weight(sample_simd_pred_reg_intr)
u64 pred_qwords; // 0 ... sample_simd_pred_reg_qwords
u64 data[nr_vectors * vector_qwords +
nr_pred * pred_qwords];
} && (abi & PERF_SAMPLE_REGS_ABI_SIMD)
}
PERF_SAMPLE_REGS_ABI_SIMD indicates that SIMD register data is present.
The metadata fields are encoded as u64 to keep perf tool parsing and
cross-endian support straightforward.
Example
-------
$ perf record -I?
available registers: AX BX CX DX SI DI BP SP IP FLAGS CS SS R8 R9 R10
R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27
R28 R29 R30 R31 SSP XMM0-15 YMM0-15 ZMM0-31 OPMASK0-7
$ perf record --user-regs=?
available registers: AX BX CX DX SI DI BP SP IP FLAGS CS SS R8 R9 R10
R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27
R28 R29 R30 R31 SSP XMM0-15 YMM0-15 ZMM0-31 OPMASK0-7
$ perf record -e branches:p \
-Iax,bx,r8,r16,r31,ssp,xmm,ymm,zmm,opmask \
-c 100000 ./test
$ perf report -D
...
14027761992115 0xcf30 [0x8a8]: PERF_RECORD_SAMPLE(IP, 0x1): 29964/29964:
0xffffffff9f085e24 period: 100000 addr: 0
... intr regs: mask 0x18001010003 ABI 64-bit
.... AX 0xdffffc0000000000
.... BX 0xffff8882297685e8
.... R8 0x0000000000000000
.... R16 0x0000000000000000
.... R31 0x0000000000000000
.... SSP 0x0000000000000000
... SIMD ABI nr_vectors 32 vector_qwords 8 nr_pred 8 pred_qwords 1
.... ZMM[0][0] 0x616c2f656d6f682f
.... ZMM[0][1] 0x696c2f7265737562
...
.... ZMM[31][7] 0x0000000000000000
.... OPMASK[0] 0x00000000fffffe00
....
.... OPMASK[7] 0x0000000000000000
...
Testing
-------
The following intr-regs, user-regs, and combined sampling tests were run
on DMR and NVL. The sampled register data was reported correctly and no
issues were observed.
$ ./perf record -e branches:p \
-Iax,bx,r8,r16,r31,ssp,xmm,ymm,zmm,opmask -b -c 10000 sleep 1
$ ./perf record -e branches \
-Iax,bx,r8,r16,r31,ssp,xmm,ymm,zmm,opmask -b -c 10000 sleep 1
$ ./perf record -e branches:p \
--user-regs=ax,bx,r8,r16,r31,ssp,xmm,ymm,zmm,opmask \
-b -c 10000 sleep 1
$ ./perf record -e branches \
--user-regs=ax,bx,r8,r16,r31,ssp,xmm,ymm,zmm,opmask \
-b -c 10000 sleep 1
$ ./perf record -e branches:p \
-Ixmm,ymm,zmm,opmask \
--user-regs=ax,bx,r8,r16,r31,ssp \
-b -c 10000 sleep 1
$ ./perf record -e branches:p \
--user-regs=xmm,ymm,zmm,opmask \
-Iax,bx,r8,r16,r31,ssp \
-b -c 10000 sleep 1
$ ./perf record -e branches:p \
-Iax,bx,r9,r17,r30,ssp \
--user-regs=ax,bx,r8,r16,r31,ssp \
-b -c 10000 sleep 1
$ ./perf record -e branches:p \
-Ixmm,opmask --user-regs=zmm \
-b -c 10000 taskset -c 0 sleep 1
History:
v8: https://lore.kernel.org/all/20260529075645.580362-1-dapeng1.mi@linux.intel.com/
v7: https://lore.kernel.org/all/20260324004118.3772171-1-dapeng1.mi@linux.intel.com/
v6: https://lore.kernel.org/all/20260209072047.2180332-1-dapeng1.mi@linux.intel.com/
v5: https://lore.kernel.org/all/20251203065500.2597594-1-dapeng1.mi@linux.intel.com/
v4: https://lore.kernel.org/all/20250925061213.178796-1-dapeng1.mi@linux.intel.com/
v3: https://lore.kernel.org/lkml/20250815213435.1702022-1-kan.liang@linux.intel.com/
v2: https://lore.kernel.org/lkml/20250626195610.405379-1-kan.liang@linux.intel.com/
v1: https://lore.kernel.org/lkml/20250613134943.3186517-1-kan.liang@linux.intel.com/
Dapeng Mi (21):
perf/x86: Fix two error-path and hybrid PMU guard issues
perf/x86: Move hybrid PMU initialization before x86_pmu_starting_cpu()
perf/x86/intel: Enable large PEBS sampling for XMMs
perf/x86/intel: Convert x86_perf_regs to per-cpu variables
perf: Eliminate duplicate arch-specific function definitions
perf/x86: Use x86_perf_regs in NMI handlers
x86/fpu: Add update_fpu_state_and_flag() helper
perf/x86/intel: Consolidate PMU capability updates
perf/x86: Enable XMM register sampling for non-PEBS events
perf/x86: Enable XMM register sampling for REGS_USER case
perf/x86: Support XMM sampling using sample_simd_vec_reg_* fields
perf/x86: Support YMM sampling using sample_simd_vec_reg_* fields
perf/x86: Support ZMM sampling using sample_simd_vec_reg_* fields
perf/x86: Support OPMASK sampling using sample_simd_pred_reg_* fields
perf: Enhance perf_reg_validate() with simd_enabled argument
perf/x86: Support eGPRs sampling using sample_regs_* fields
perf/x86: Support SSP sampling using sample_regs_* fields
perf/x86/intel: Support arch-PEBS based SIMD/eGPRs/SSP sampling
perf/x86/intel: Enable PERF_PMU_CAP_SIMD_REGS capability
perf/x86: Activate back-to-back NMI detection for arch-PEBS induced
NMIs
perf/x86/intel: Add sanity check for PEBS fragment size
Kan Liang (3):
x86/fpu/xstate: Add xsaves_nmi() helper
perf: Move and enhance has_extended_regs() for arch-specific use
perf: Add sampling support for SIMD registers
arch/arm/kernel/perf_regs.c | 8 +-
arch/arm64/kernel/perf_regs.c | 8 +-
arch/csky/kernel/perf_regs.c | 8 +-
arch/loongarch/kernel/perf_regs.c | 8 +-
arch/mips/kernel/perf_regs.c | 8 +-
arch/parisc/kernel/perf_regs.c | 8 +-
arch/powerpc/perf/perf_regs.c | 2 +-
arch/riscv/kernel/perf_regs.c | 8 +-
arch/s390/kernel/perf_regs.c | 2 +-
arch/x86/events/core.c | 436 +++++++++++++++++++++++++-
arch/x86/events/intel/core.c | 267 +++++++++++++---
arch/x86/events/intel/ds.c | 235 +++++++++++---
arch/x86/events/perf_event.h | 122 ++++++-
arch/x86/include/asm/fpu/sched.h | 5 +-
arch/x86/include/asm/fpu/xstate.h | 3 +
arch/x86/include/asm/msr-index.h | 7 +
arch/x86/include/asm/perf_event.h | 35 ++-
arch/x86/include/uapi/asm/perf_regs.h | 53 ++++
arch/x86/kernel/fpu/core.c | 25 +-
arch/x86/kernel/fpu/xstate.c | 25 +-
arch/x86/kernel/perf_regs.c | 175 +++++++++--
arch/x86/xen/pmu.c | 5 +-
include/linux/perf_event.h | 23 ++
include/linux/perf_regs.h | 38 +--
include/uapi/linux/perf_event.h | 49 ++-
kernel/events/core.c | 194 ++++++++++--
26 files changed, 1518 insertions(+), 239 deletions(-)
base-commit: a4573a3838ae4fc73b70019cfa1dac9aaea7cc2f
--
2.34.1
^ permalink raw reply [flat|nested] 42+ messages in thread
* [Patch v9 01/24] perf/x86: Fix two error-path and hybrid PMU guard issues
2026-07-06 1:54 [Patch v9 00/24] Support SIMD/eGPRs/SSP registers sampling for perf Dapeng Mi
@ 2026-07-06 1:54 ` Dapeng Mi
2026-07-06 2:21 ` sashiko-bot
2026-07-06 1:54 ` [Patch v9 02/24] perf/x86: Move hybrid PMU initialization before x86_pmu_starting_cpu() Dapeng Mi
` (22 subsequent siblings)
23 siblings, 1 reply; 42+ messages in thread
From: Dapeng Mi @ 2026-07-06 1:54 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Thomas Gleixner, Dave Hansen, Ian Rogers,
Adrian Hunter, Jiri Olsa, Alexander Shishkin, Andi Kleen,
Eranian Stephane
Cc: Mark Rutland, broonie, Ravi Bangoria, linux-kernel,
linux-perf-users, Zide Chen, Falcon Thomas, Dapeng Mi, Xudong Hao,
Dapeng Mi
Fix an NMI handler leak in init_hw_perf_events(). When PMU
initialization fails after register_nmi_handler(), the error path
exits without calling unregister_nmi_handler(), leaving a stale
NMI_LOCAL "PMI" handler registered. Add the missing call before
clearing x86_pmu state.
Also guard the hybrid PMU cpumask update in intel_pmu_cpu_dead()
with a check on x86_pmu.num_hybrid_pmus. Without this, hybrid_pmu()
may be called when the hybrid PMU array has not been allocated,
leading to an out-of-bounds access.
Signed-off-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
---
arch/x86/events/core.c | 4 +++-
arch/x86/events/intel/core.c | 2 +-
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c
index af0b67ffb43d..872d07a5fa80 100644
--- a/arch/x86/events/core.c
+++ b/arch/x86/events/core.c
@@ -2219,7 +2219,7 @@ static int __init init_hw_perf_events(void)
err = cpuhp_setup_state(CPUHP_PERF_X86_PREPARE, "perf/x86:prepare",
x86_pmu_prepare_cpu, x86_pmu_dead_cpu);
if (err)
- return err;
+ goto pmi_unregister;
err = cpuhp_setup_state(CPUHP_AP_PERF_X86_STARTING,
"perf/x86:starting", x86_pmu_starting_cpu,
@@ -2273,6 +2273,8 @@ static int __init init_hw_perf_events(void)
cpuhp_remove_state(CPUHP_AP_PERF_X86_STARTING);
out:
cpuhp_remove_state(CPUHP_PERF_X86_PREPARE);
+pmi_unregister:
+ unregister_nmi_handler(NMI_LOCAL, "PMI");
out_bad_pmu:
memset(&x86_pmu, 0, sizeof(x86_pmu));
return err;
diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c
index b39c6ce0efb5..b8a6382dbb82 100644
--- a/arch/x86/events/intel/core.c
+++ b/arch/x86/events/intel/core.c
@@ -6479,7 +6479,7 @@ static void intel_pmu_cpu_dead(int cpu)
release_arch_pebs_buf_on_cpu(cpu);
intel_cpuc_finish(cpuc);
- if (is_hybrid() && cpuc->pmu)
+ if (is_hybrid() && x86_pmu.num_hybrid_pmus && cpuc->pmu)
cpumask_clear_cpu(cpu, &hybrid_pmu(cpuc->pmu)->supported_cpus);
}
--
2.34.1
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [Patch v9 02/24] perf/x86: Move hybrid PMU initialization before x86_pmu_starting_cpu()
2026-07-06 1:54 [Patch v9 00/24] Support SIMD/eGPRs/SSP registers sampling for perf Dapeng Mi
2026-07-06 1:54 ` [Patch v9 01/24] perf/x86: Fix two error-path and hybrid PMU guard issues Dapeng Mi
@ 2026-07-06 1:54 ` Dapeng Mi
2026-07-06 2:18 ` sashiko-bot
2026-07-06 1:54 ` [Patch v9 03/24] perf/x86/intel: Enable large PEBS sampling for XMMs Dapeng Mi
` (21 subsequent siblings)
23 siblings, 1 reply; 42+ messages in thread
From: Dapeng Mi @ 2026-07-06 1:54 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Thomas Gleixner, Dave Hansen, Ian Rogers,
Adrian Hunter, Jiri Olsa, Alexander Shishkin, Andi Kleen,
Eranian Stephane
Cc: Mark Rutland, broonie, Ravi Bangoria, linux-kernel,
linux-perf-users, Zide Chen, Falcon Thomas, Dapeng Mi, Xudong Hao,
Dapeng Mi
The current approach initializes hybrid PMU structures immediately before
registering them. This is risky as it can lead to key fields, such as
'capabilities', being inadvertently overwritten.
Although no issues have arisen so far, this method is not ideal. It makes
the PMU structure fields susceptible to being overwritten, especially with
future changes that might initialize fields like 'capabilities' within
init_hybrid_pmu() called by x86_pmu_starting_cpu().
To mitigate this potential problem, move the default hybrid structure
initialization before calling x86_pmu_starting_cpu().
Signed-off-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
---
arch/x86/events/core.c | 15 +++++++++++++--
1 file changed, 13 insertions(+), 2 deletions(-)
diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c
index 872d07a5fa80..0888d3b0923e 100644
--- a/arch/x86/events/core.c
+++ b/arch/x86/events/core.c
@@ -2195,8 +2195,20 @@ static int __init init_hw_perf_events(void)
pmu.attr_update = x86_pmu.attr_update;
- if (!is_hybrid())
+ if (!is_hybrid()) {
x86_pmu_show_pmu_cap(NULL);
+ } else {
+ int i;
+
+ /*
+ * Init default ops.
+ * Must be called before registering x86_pmu_starting_cpu(),
+ * otherwise some key PMU fields, e.g., capabilities
+ * initialized in x86_pmu_starting_cpu(), would be overwritten.
+ */
+ for (i = 0; i < x86_pmu.num_hybrid_pmus; i++)
+ x86_pmu.hybrid_pmu[i].pmu = pmu;
+ }
if (!x86_pmu.read)
x86_pmu.read = _x86_pmu_read;
@@ -2243,7 +2255,6 @@ static int __init init_hw_perf_events(void)
for (i = 0; i < x86_pmu.num_hybrid_pmus; i++) {
hybrid_pmu = &x86_pmu.hybrid_pmu[i];
- hybrid_pmu->pmu = pmu;
hybrid_pmu->pmu.type = -1;
hybrid_pmu->pmu.attr_update = x86_pmu.attr_update;
hybrid_pmu->pmu.capabilities |= PERF_PMU_CAP_EXTENDED_HW_TYPE;
--
2.34.1
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [Patch v9 03/24] perf/x86/intel: Enable large PEBS sampling for XMMs
2026-07-06 1:54 [Patch v9 00/24] Support SIMD/eGPRs/SSP registers sampling for perf Dapeng Mi
2026-07-06 1:54 ` [Patch v9 01/24] perf/x86: Fix two error-path and hybrid PMU guard issues Dapeng Mi
2026-07-06 1:54 ` [Patch v9 02/24] perf/x86: Move hybrid PMU initialization before x86_pmu_starting_cpu() Dapeng Mi
@ 2026-07-06 1:54 ` Dapeng Mi
2026-07-06 1:54 ` [Patch v9 04/24] perf/x86/intel: Convert x86_perf_regs to per-cpu variables Dapeng Mi
` (20 subsequent siblings)
23 siblings, 0 replies; 42+ messages in thread
From: Dapeng Mi @ 2026-07-06 1:54 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Thomas Gleixner, Dave Hansen, Ian Rogers,
Adrian Hunter, Jiri Olsa, Alexander Shishkin, Andi Kleen,
Eranian Stephane
Cc: Mark Rutland, broonie, Ravi Bangoria, linux-kernel,
linux-perf-users, Zide Chen, Falcon Thomas, Dapeng Mi, Xudong Hao,
Dapeng Mi
Modern PEBS hardware supports directly sampling XMM registers, then
large PEBS can be enabled for XMM registers just like other GPRs.
Reported-by: Xudong Hao <xudong.hao@intel.com>
Signed-off-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
---
arch/x86/events/intel/core.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c
index b8a6382dbb82..2e658b4bc83a 100644
--- a/arch/x86/events/intel/core.c
+++ b/arch/x86/events/intel/core.c
@@ -4701,7 +4701,8 @@ static unsigned long intel_pmu_large_pebs_flags(struct perf_event *event)
flags &= ~PERF_SAMPLE_REGS_USER;
if (event->attr.sample_regs_user & ~PEBS_GP_REGS)
flags &= ~PERF_SAMPLE_REGS_USER;
- if (event->attr.sample_regs_intr & ~PEBS_GP_REGS)
+ if (event->attr.sample_regs_intr &
+ ~(PEBS_GP_REGS | PERF_REG_EXTENDED_MASK))
flags &= ~PERF_SAMPLE_REGS_INTR;
return flags;
}
--
2.34.1
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [Patch v9 04/24] perf/x86/intel: Convert x86_perf_regs to per-cpu variables
2026-07-06 1:54 [Patch v9 00/24] Support SIMD/eGPRs/SSP registers sampling for perf Dapeng Mi
` (2 preceding siblings ...)
2026-07-06 1:54 ` [Patch v9 03/24] perf/x86/intel: Enable large PEBS sampling for XMMs Dapeng Mi
@ 2026-07-06 1:54 ` Dapeng Mi
2026-07-06 1:54 ` [Patch v9 05/24] perf: Eliminate duplicate arch-specific function definitions Dapeng Mi
` (19 subsequent siblings)
23 siblings, 0 replies; 42+ messages in thread
From: Dapeng Mi @ 2026-07-06 1:54 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Thomas Gleixner, Dave Hansen, Ian Rogers,
Adrian Hunter, Jiri Olsa, Alexander Shishkin, Andi Kleen,
Eranian Stephane
Cc: Mark Rutland, broonie, Ravi Bangoria, linux-kernel,
linux-perf-users, Zide Chen, Falcon Thomas, Dapeng Mi, Xudong Hao,
Dapeng Mi
Currently, the drain_pebs() helpers, e.g., intel_pmu_drain_arch_pebs()
define an on-stack x86_perf_regs. Upcoming patches will add new fields
like *ymm_regs and *zmm_regs to the x86_perf_regs structure to support
sampling for these SIMD registers. This would increase the stack size
consumed by these helpers, potentially triggering the warning:
"the frame size of 1048 bytes is larger than 1024 bytes
[-Wframe-larger-than=]".
To eliminate this warning, convert x86_perf_regs to per-cpu variables.
Please note drain_pebs() can't be interrupted by other NMIs since
either it's already in NMI context or PMU is already disabled.
Signed-off-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
---
arch/x86/events/intel/ds.c | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/arch/x86/events/intel/ds.c b/arch/x86/events/intel/ds.c
index e86e4ba91e1b..7b69f8c8d0c2 100644
--- a/arch/x86/events/intel/ds.c
+++ b/arch/x86/events/intel/ds.c
@@ -2917,6 +2917,8 @@ __intel_pmu_pebs_last_event(struct perf_event *event,
}
}
+static DEFINE_PER_CPU(struct x86_perf_regs, x86_pebs_regs);
+
static __always_inline void
__intel_pmu_pebs_events(struct perf_event *event,
struct pt_regs *iregs,
@@ -2926,8 +2928,8 @@ __intel_pmu_pebs_events(struct perf_event *event,
setup_fn setup_sample)
{
struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
- struct x86_perf_regs perf_regs;
- struct pt_regs *regs = &perf_regs.regs;
+ struct x86_perf_regs *perf_regs = this_cpu_ptr(&x86_pebs_regs);
+ struct pt_regs *regs = &perf_regs->regs;
void *at = get_next_pebs_record_by_bit(base, top, bit);
int cnt = count;
@@ -3175,8 +3177,8 @@ static void intel_pmu_drain_pebs_icl(struct pt_regs *iregs, struct perf_sample_d
void *last[INTEL_PMC_IDX_FIXED + MAX_FIXED_PEBS_EVENTS];
struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
struct debug_store *ds = cpuc->ds;
- struct x86_perf_regs perf_regs;
- struct pt_regs *regs = &perf_regs.regs;
+ struct x86_perf_regs *perf_regs = this_cpu_ptr(&x86_pebs_regs);
+ struct pt_regs *regs = &perf_regs->regs;
struct pebs_basic *basic;
void *base, *at, *top;
u64 mask;
@@ -3226,8 +3228,8 @@ static void intel_pmu_drain_arch_pebs(struct pt_regs *iregs,
void *last[INTEL_PMC_IDX_FIXED + MAX_FIXED_PEBS_EVENTS];
struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
union arch_pebs_index index;
- struct x86_perf_regs perf_regs;
- struct pt_regs *regs = &perf_regs.regs;
+ struct x86_perf_regs *perf_regs = this_cpu_ptr(&x86_pebs_regs);
+ struct pt_regs *regs = &perf_regs->regs;
void *base, *at, *top;
u64 mask;
--
2.34.1
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [Patch v9 05/24] perf: Eliminate duplicate arch-specific function definitions
2026-07-06 1:54 [Patch v9 00/24] Support SIMD/eGPRs/SSP registers sampling for perf Dapeng Mi
` (3 preceding siblings ...)
2026-07-06 1:54 ` [Patch v9 04/24] perf/x86/intel: Convert x86_perf_regs to per-cpu variables Dapeng Mi
@ 2026-07-06 1:54 ` Dapeng Mi
2026-07-06 1:54 ` [Patch v9 06/24] perf/x86: Use x86_perf_regs in NMI handlers Dapeng Mi
` (18 subsequent siblings)
23 siblings, 0 replies; 42+ messages in thread
From: Dapeng Mi @ 2026-07-06 1:54 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Thomas Gleixner, Dave Hansen, Ian Rogers,
Adrian Hunter, Jiri Olsa, Alexander Shishkin, Andi Kleen,
Eranian Stephane
Cc: Mark Rutland, broonie, Ravi Bangoria, linux-kernel,
linux-perf-users, Zide Chen, Falcon Thomas, Dapeng Mi, Xudong Hao,
Dapeng Mi
Define default common __weak functions for perf_reg_value(),
perf_reg_validate(), perf_reg_abi() and perf_get_regs_user(). This helps
to eliminate the duplicated arch-specific definitions.
No function changes intended.
Signed-off-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
---
arch/arm/kernel/perf_regs.c | 6 ------
arch/arm64/kernel/perf_regs.c | 6 ------
arch/csky/kernel/perf_regs.c | 6 ------
arch/loongarch/kernel/perf_regs.c | 6 ------
arch/mips/kernel/perf_regs.c | 6 ------
arch/parisc/kernel/perf_regs.c | 6 ------
arch/riscv/kernel/perf_regs.c | 6 ------
arch/x86/kernel/perf_regs.c | 6 ------
include/linux/perf_regs.h | 32 ++++++-------------------------
kernel/events/core.c | 22 +++++++++++++++++++++
10 files changed, 28 insertions(+), 74 deletions(-)
diff --git a/arch/arm/kernel/perf_regs.c b/arch/arm/kernel/perf_regs.c
index 0529f90395c9..d575a4c3ca56 100644
--- a/arch/arm/kernel/perf_regs.c
+++ b/arch/arm/kernel/perf_regs.c
@@ -31,9 +31,3 @@ u64 perf_reg_abi(struct task_struct *task)
return PERF_SAMPLE_REGS_ABI_32;
}
-void perf_get_regs_user(struct perf_regs *regs_user,
- struct pt_regs *regs)
-{
- regs_user->regs = task_pt_regs(current);
- regs_user->abi = perf_reg_abi(current);
-}
diff --git a/arch/arm64/kernel/perf_regs.c b/arch/arm64/kernel/perf_regs.c
index b4eece3eb17d..70e2f13f587f 100644
--- a/arch/arm64/kernel/perf_regs.c
+++ b/arch/arm64/kernel/perf_regs.c
@@ -98,9 +98,3 @@ u64 perf_reg_abi(struct task_struct *task)
return PERF_SAMPLE_REGS_ABI_64;
}
-void perf_get_regs_user(struct perf_regs *regs_user,
- struct pt_regs *regs)
-{
- regs_user->regs = task_pt_regs(current);
- regs_user->abi = perf_reg_abi(current);
-}
diff --git a/arch/csky/kernel/perf_regs.c b/arch/csky/kernel/perf_regs.c
index 09b7f88a2d6a..94601f37b596 100644
--- a/arch/csky/kernel/perf_regs.c
+++ b/arch/csky/kernel/perf_regs.c
@@ -31,9 +31,3 @@ u64 perf_reg_abi(struct task_struct *task)
return PERF_SAMPLE_REGS_ABI_32;
}
-void perf_get_regs_user(struct perf_regs *regs_user,
- struct pt_regs *regs)
-{
- regs_user->regs = task_pt_regs(current);
- regs_user->abi = perf_reg_abi(current);
-}
diff --git a/arch/loongarch/kernel/perf_regs.c b/arch/loongarch/kernel/perf_regs.c
index 263ac4ab5af6..8dd604f01745 100644
--- a/arch/loongarch/kernel/perf_regs.c
+++ b/arch/loongarch/kernel/perf_regs.c
@@ -45,9 +45,3 @@ u64 perf_reg_value(struct pt_regs *regs, int idx)
return regs->regs[idx];
}
-void perf_get_regs_user(struct perf_regs *regs_user,
- struct pt_regs *regs)
-{
- regs_user->regs = task_pt_regs(current);
- regs_user->abi = perf_reg_abi(current);
-}
diff --git a/arch/mips/kernel/perf_regs.c b/arch/mips/kernel/perf_regs.c
index e686780d1647..7736d3c5ebd2 100644
--- a/arch/mips/kernel/perf_regs.c
+++ b/arch/mips/kernel/perf_regs.c
@@ -60,9 +60,3 @@ u64 perf_reg_value(struct pt_regs *regs, int idx)
return (s64)v; /* Sign extend if 32-bit. */
}
-void perf_get_regs_user(struct perf_regs *regs_user,
- struct pt_regs *regs)
-{
- regs_user->regs = task_pt_regs(current);
- regs_user->abi = perf_reg_abi(current);
-}
diff --git a/arch/parisc/kernel/perf_regs.c b/arch/parisc/kernel/perf_regs.c
index 10a1a5f06a18..b9fe1f2fcb9b 100644
--- a/arch/parisc/kernel/perf_regs.c
+++ b/arch/parisc/kernel/perf_regs.c
@@ -53,9 +53,3 @@ u64 perf_reg_abi(struct task_struct *task)
return PERF_SAMPLE_REGS_ABI_64;
}
-void perf_get_regs_user(struct perf_regs *regs_user,
- struct pt_regs *regs)
-{
- regs_user->regs = task_pt_regs(current);
- regs_user->abi = perf_reg_abi(current);
-}
diff --git a/arch/riscv/kernel/perf_regs.c b/arch/riscv/kernel/perf_regs.c
index fd304a248de6..3bba8deababb 100644
--- a/arch/riscv/kernel/perf_regs.c
+++ b/arch/riscv/kernel/perf_regs.c
@@ -35,9 +35,3 @@ u64 perf_reg_abi(struct task_struct *task)
#endif
}
-void perf_get_regs_user(struct perf_regs *regs_user,
- struct pt_regs *regs)
-{
- regs_user->regs = task_pt_regs(current);
- regs_user->abi = perf_reg_abi(current);
-}
diff --git a/arch/x86/kernel/perf_regs.c b/arch/x86/kernel/perf_regs.c
index 624703af80a1..81204cb7f723 100644
--- a/arch/x86/kernel/perf_regs.c
+++ b/arch/x86/kernel/perf_regs.c
@@ -100,12 +100,6 @@ u64 perf_reg_abi(struct task_struct *task)
return PERF_SAMPLE_REGS_ABI_32;
}
-void perf_get_regs_user(struct perf_regs *regs_user,
- struct pt_regs *regs)
-{
- regs_user->regs = task_pt_regs(current);
- regs_user->abi = perf_reg_abi(current);
-}
#else /* CONFIG_X86_64 */
#define REG_NOSUPPORT ((1ULL << PERF_REG_X86_DS) | \
(1ULL << PERF_REG_X86_ES) | \
diff --git a/include/linux/perf_regs.h b/include/linux/perf_regs.h
index f632c5725f16..144bcc3ff19f 100644
--- a/include/linux/perf_regs.h
+++ b/include/linux/perf_regs.h
@@ -9,6 +9,12 @@ struct perf_regs {
struct pt_regs *regs;
};
+u64 perf_reg_value(struct pt_regs *regs, int idx);
+int perf_reg_validate(u64 mask);
+u64 perf_reg_abi(struct task_struct *task);
+void perf_get_regs_user(struct perf_regs *regs_user,
+ struct pt_regs *regs);
+
#ifdef CONFIG_HAVE_PERF_REGS
#include <asm/perf_regs.h>
@@ -16,35 +22,9 @@ struct perf_regs {
#define PERF_REG_EXTENDED_MASK 0
#endif
-u64 perf_reg_value(struct pt_regs *regs, int idx);
-int perf_reg_validate(u64 mask);
-u64 perf_reg_abi(struct task_struct *task);
-void perf_get_regs_user(struct perf_regs *regs_user,
- struct pt_regs *regs);
#else
#define PERF_REG_EXTENDED_MASK 0
-static inline u64 perf_reg_value(struct pt_regs *regs, int idx)
-{
- return 0;
-}
-
-static inline int perf_reg_validate(u64 mask)
-{
- return mask ? -ENOSYS : 0;
-}
-
-static inline u64 perf_reg_abi(struct task_struct *task)
-{
- return PERF_SAMPLE_REGS_ABI_NONE;
-}
-
-static inline void perf_get_regs_user(struct perf_regs *regs_user,
- struct pt_regs *regs)
-{
- regs_user->regs = task_pt_regs(current);
- regs_user->abi = perf_reg_abi(current);
-}
#endif /* CONFIG_HAVE_PERF_REGS */
#endif /* _LINUX_PERF_REGS_H */
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 0f29d17a600d..82009bfb4fd9 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -7807,6 +7807,28 @@ unsigned long perf_instruction_pointer(struct perf_event *event,
0 : perf_arch_instruction_pointer(regs);
}
+u64 __weak perf_reg_value(struct pt_regs *regs, int idx)
+{
+ return 0;
+}
+
+int __weak perf_reg_validate(u64 mask)
+{
+ return mask ? -ENOSYS : 0;
+}
+
+u64 __weak perf_reg_abi(struct task_struct *task)
+{
+ return PERF_SAMPLE_REGS_ABI_NONE;
+}
+
+void __weak perf_get_regs_user(struct perf_regs *regs_user,
+ struct pt_regs *regs)
+{
+ regs_user->regs = task_pt_regs(current);
+ regs_user->abi = perf_reg_abi(current);
+}
+
static void
perf_output_sample_regs(struct perf_output_handle *handle,
struct pt_regs *regs, u64 mask)
--
2.34.1
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [Patch v9 06/24] perf/x86: Use x86_perf_regs in NMI handlers
2026-07-06 1:54 [Patch v9 00/24] Support SIMD/eGPRs/SSP registers sampling for perf Dapeng Mi
` (4 preceding siblings ...)
2026-07-06 1:54 ` [Patch v9 05/24] perf: Eliminate duplicate arch-specific function definitions Dapeng Mi
@ 2026-07-06 1:54 ` Dapeng Mi
2026-07-06 2:31 ` sashiko-bot
2026-07-06 1:54 ` [Patch v9 07/24] x86/fpu/xstate: Add xsaves_nmi() helper Dapeng Mi
` (17 subsequent siblings)
23 siblings, 1 reply; 42+ messages in thread
From: Dapeng Mi @ 2026-07-06 1:54 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Thomas Gleixner, Dave Hansen, Ian Rogers,
Adrian Hunter, Jiri Olsa, Alexander Shishkin, Andi Kleen,
Eranian Stephane
Cc: Mark Rutland, broonie, Ravi Bangoria, linux-kernel,
linux-perf-users, Zide Chen, Falcon Thomas, Dapeng Mi, Xudong Hao,
Dapeng Mi, Kan Liang
Support for sampling additional register state in NMI context
(e.g. vector registers and SSP) requires an x86-specific register
container. The generic pt_regs structure cannot represent all of
the required x86 register data, so switch x86 NMI handlers to
x86_perf_regs.
pt_regs is still passed to x86_pmu_handle_irq(), so there is no
functional change to existing handling.
AMD IBS NMI handling does not use x86_pmu_handle_irq(), so this
conversion does not apply to IBS. IBS support for extended register
sampling can be added separately in follow-up patches.
Co-developed-by: Kan Liang <kan.liang@linux.intel.com>
Signed-off-by: Kan Liang <kan.liang@linux.intel.com>
Signed-off-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
---
arch/x86/events/core.c | 5 ++++-
arch/x86/xen/pmu.c | 5 ++++-
2 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c
index 0888d3b0923e..d83ea02e2457 100644
--- a/arch/x86/events/core.c
+++ b/arch/x86/events/core.c
@@ -1788,9 +1788,11 @@ void perf_put_guest_lvtpc(void)
EXPORT_SYMBOL_FOR_KVM(perf_put_guest_lvtpc);
#endif /* CONFIG_PERF_GUEST_MEDIATED_PMU */
+static DEFINE_PER_CPU(struct x86_perf_regs, x86_intr_regs);
static int
perf_event_nmi_handler(unsigned int cmd, struct pt_regs *regs)
{
+ struct x86_perf_regs *x86_regs = this_cpu_ptr(&x86_intr_regs);
u64 start_clock;
u64 finish_clock;
int ret;
@@ -1814,7 +1816,8 @@ perf_event_nmi_handler(unsigned int cmd, struct pt_regs *regs)
return NMI_DONE;
start_clock = sched_clock();
- ret = static_call(x86_pmu_handle_irq)(regs);
+ x86_regs->regs = *regs;
+ ret = static_call(x86_pmu_handle_irq)(&x86_regs->regs);
finish_clock = sched_clock();
perf_sample_event_took(finish_clock - start_clock);
diff --git a/arch/x86/xen/pmu.c b/arch/x86/xen/pmu.c
index 5f50a3ee08f5..3f4dd3f50f56 100644
--- a/arch/x86/xen/pmu.c
+++ b/arch/x86/xen/pmu.c
@@ -456,12 +456,14 @@ static void xen_convert_regs(const struct xen_pmu_regs *xen_regs,
}
}
+static DEFINE_PER_CPU(struct x86_perf_regs, x86_xen_intr_regs);
irqreturn_t xen_pmu_irq_handler(int irq, void *dev_id)
{
int err, ret = IRQ_NONE;
struct pt_regs regs = {0};
const struct xen_pmu_data *xenpmu_data = get_xenpmu_data();
uint8_t xenpmu_flags = get_xenpmu_flags();
+ struct x86_perf_regs *x86_regs = this_cpu_ptr(&x86_xen_intr_regs);
if (!xenpmu_data) {
pr_warn_once("%s: pmudata not initialized\n", __func__);
@@ -472,7 +474,8 @@ irqreturn_t xen_pmu_irq_handler(int irq, void *dev_id)
xenpmu_flags | XENPMU_IRQ_PROCESSING;
xen_convert_regs(&xenpmu_data->pmu.r.regs, ®s,
xenpmu_data->pmu.pmu_flags);
- if (x86_pmu.handle_irq(®s))
+ x86_regs->regs = regs;
+ if (x86_pmu.handle_irq(&x86_regs->regs))
ret = IRQ_HANDLED;
/* Write out cached context to HW */
--
2.34.1
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [Patch v9 07/24] x86/fpu/xstate: Add xsaves_nmi() helper
2026-07-06 1:54 [Patch v9 00/24] Support SIMD/eGPRs/SSP registers sampling for perf Dapeng Mi
` (5 preceding siblings ...)
2026-07-06 1:54 ` [Patch v9 06/24] perf/x86: Use x86_perf_regs in NMI handlers Dapeng Mi
@ 2026-07-06 1:54 ` Dapeng Mi
2026-07-06 2:18 ` sashiko-bot
2026-07-06 1:54 ` [Patch v9 08/24] x86/fpu: Add update_fpu_state_and_flag() helper Dapeng Mi
` (16 subsequent siblings)
23 siblings, 1 reply; 42+ messages in thread
From: Dapeng Mi @ 2026-07-06 1:54 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Thomas Gleixner, Dave Hansen, Ian Rogers,
Adrian Hunter, Jiri Olsa, Alexander Shishkin, Andi Kleen,
Eranian Stephane
Cc: Mark Rutland, broonie, Ravi Bangoria, linux-kernel,
linux-perf-users, Zide Chen, Falcon Thomas, Dapeng Mi, Xudong Hao,
Kan Liang, Dapeng Mi
From: Kan Liang <kan.liang@linux.intel.com>
Add xsaves_nmi() to save supported xsave states in NMI handler.
This function is similar to xsaves(), but should only be called within
the NMI handler. This function returns the actual register contents at
the moment the NMI occurs.
Currently the perf subsystem is the sole user of this helper. It uses
this function to snapshot SIMD (XMM/YMM/ZMM) and APX eGPRs registers
which would be added in subsequent patches.
Signed-off-by: Kan Liang <kan.liang@linux.intel.com>
Signed-off-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
---
arch/x86/include/asm/fpu/xstate.h | 1 +
arch/x86/kernel/fpu/xstate.c | 23 +++++++++++++++++++++++
2 files changed, 24 insertions(+)
diff --git a/arch/x86/include/asm/fpu/xstate.h b/arch/x86/include/asm/fpu/xstate.h
index 7a7dc9d56027..38fa8ff26559 100644
--- a/arch/x86/include/asm/fpu/xstate.h
+++ b/arch/x86/include/asm/fpu/xstate.h
@@ -110,6 +110,7 @@ int xfeature_size(int xfeature_nr);
void xsaves(struct xregs_state *xsave, u64 mask);
void xrstors(struct xregs_state *xsave, u64 mask);
+void xsaves_nmi(struct xregs_state *xsave, u64 mask);
int xfd_enable_feature(u64 xfd_err);
diff --git a/arch/x86/kernel/fpu/xstate.c b/arch/x86/kernel/fpu/xstate.c
index a7b6524a9dea..d39c01546dee 100644
--- a/arch/x86/kernel/fpu/xstate.c
+++ b/arch/x86/kernel/fpu/xstate.c
@@ -1474,6 +1474,29 @@ void xrstors(struct xregs_state *xstate, u64 mask)
WARN_ON_ONCE(err);
}
+/**
+ * xsaves_nmi - Save selected components to a kernel xstate buffer in NMI
+ * @xstate: Pointer to the buffer
+ * @mask: Feature mask to select the components to save
+ *
+ * This function is similar to xsaves(), but should only be called within
+ * the NMI handler. This function returns the actual register contents at
+ * the moment the NMI occurs.
+ *
+ * Currently, the perf subsystem is the sole user of this helper. It uses
+ * the function to snapshot SIMD (XMM/YMM/ZMM) and APX eGPRs registers.
+ */
+void xsaves_nmi(struct xregs_state *xstate, u64 mask)
+{
+ int err;
+
+ if (!in_nmi())
+ return;
+
+ XSTATE_OP(XSAVES, xstate, (u32)mask, (u32)(mask >> 32), err);
+ WARN_ON_ONCE(err);
+}
+
#if IS_ENABLED(CONFIG_KVM)
void fpstate_clear_xstate_component(struct fpstate *fpstate, unsigned int xfeature)
{
--
2.34.1
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [Patch v9 08/24] x86/fpu: Add update_fpu_state_and_flag() helper
2026-07-06 1:54 [Patch v9 00/24] Support SIMD/eGPRs/SSP registers sampling for perf Dapeng Mi
` (6 preceding siblings ...)
2026-07-06 1:54 ` [Patch v9 07/24] x86/fpu/xstate: Add xsaves_nmi() helper Dapeng Mi
@ 2026-07-06 1:54 ` Dapeng Mi
2026-07-06 2:22 ` sashiko-bot
2026-07-06 1:54 ` [Patch v9 09/24] perf: Move and enhance has_extended_regs() for arch-specific use Dapeng Mi
` (15 subsequent siblings)
23 siblings, 1 reply; 42+ messages in thread
From: Dapeng Mi @ 2026-07-06 1:54 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Thomas Gleixner, Dave Hansen, Ian Rogers,
Adrian Hunter, Jiri Olsa, Alexander Shishkin, Andi Kleen,
Eranian Stephane
Cc: Mark Rutland, broonie, Ravi Bangoria, linux-kernel,
linux-perf-users, Zide Chen, Falcon Thomas, Dapeng Mi, Xudong Hao,
Dapeng Mi
Add update_fpu_state_and_flag() as suggested by Peter and Dave.
The helper saves user FPU state and then sets TIF_NEED_FPU_LOAD,
ensuring the task FPU state is saved whenever the flag is set.
Subsequent patches will use this guarantee in NMI context by checking
TIF_NEED_FPU_LOAD before retrieving user FPU state from the saved
task FPU state.
Also add barrier() in the host/guest FPU state switch path so
fpu->__task_fpstate is always observed as host FPU state when non-NULL.
Link: https://lore.kernel.org/all/20251204154721.GB2619703@noisy.programming.kicks-ass.net/
Signed-off-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
---
arch/x86/include/asm/fpu/sched.h | 5 +++--
arch/x86/kernel/fpu/core.c | 25 +++++++++++++++++++------
2 files changed, 22 insertions(+), 8 deletions(-)
diff --git a/arch/x86/include/asm/fpu/sched.h b/arch/x86/include/asm/fpu/sched.h
index 89004f4ca208..dcb2fa5f06d6 100644
--- a/arch/x86/include/asm/fpu/sched.h
+++ b/arch/x86/include/asm/fpu/sched.h
@@ -10,6 +10,8 @@
#include <asm/trace/fpu.h>
extern void save_fpregs_to_fpstate(struct fpu *fpu);
+extern void update_fpu_state_and_flag(struct fpu *fpu,
+ struct task_struct *task);
extern void fpu__drop(struct task_struct *tsk);
extern int fpu_clone(struct task_struct *dst, u64 clone_flags, bool minimal,
unsigned long shstk_addr);
@@ -36,8 +38,7 @@ static inline void switch_fpu(struct task_struct *old, int cpu)
!(old->flags & (PF_KTHREAD | PF_USER_WORKER))) {
struct fpu *old_fpu = x86_task_fpu(old);
- set_tsk_thread_flag(old, TIF_NEED_FPU_LOAD);
- save_fpregs_to_fpstate(old_fpu);
+ update_fpu_state_and_flag(old_fpu, old);
/*
* The save operation preserved register state, so the
* fpu_fpregs_owner_ctx is still @old_fpu. Store the
diff --git a/arch/x86/kernel/fpu/core.c b/arch/x86/kernel/fpu/core.c
index 584fb9913be4..fb78e0ecd5fa 100644
--- a/arch/x86/kernel/fpu/core.c
+++ b/arch/x86/kernel/fpu/core.c
@@ -213,6 +213,19 @@ void restore_fpregs_from_fpstate(struct fpstate *fpstate, u64 mask)
}
}
+/*
+ * Save the FPU register state in fpu->fpstate->regs and set
+ * TIF_NEED_FPU_LOAD subsequently.
+ *
+ * Must be called with fpregs_lock() held, ensuring flag
+ * TIF_NEED_FPU_LOAD is set last.
+ */
+void update_fpu_state_and_flag(struct fpu *fpu, struct task_struct *task)
+{
+ save_fpregs_to_fpstate(fpu);
+ set_tsk_thread_flag(task, TIF_NEED_FPU_LOAD);
+}
+
void fpu_reset_from_exception_fixup(void)
{
restore_fpregs_from_fpstate(&init_fpstate, XFEATURE_MASK_FPSTATE);
@@ -383,13 +396,15 @@ int fpu_swap_kvm_fpstate(struct fpu_guest *guest_fpu, bool enter_guest)
/* Swap fpstate */
if (enter_guest) {
- fpu->__task_fpstate = cur_fps;
+ WRITE_ONCE(fpu->__task_fpstate, cur_fps);
+ barrier();
fpu->fpstate = guest_fps;
guest_fps->in_use = true;
} else {
guest_fps->in_use = false;
fpu->fpstate = fpu->__task_fpstate;
- fpu->__task_fpstate = NULL;
+ barrier();
+ WRITE_ONCE(fpu->__task_fpstate, NULL);
}
cur_fps = fpu->fpstate;
@@ -481,10 +496,8 @@ void kernel_fpu_begin_mask(unsigned int kfpu_mask)
this_cpu_write(kernel_fpu_allowed, false);
if (!(current->flags & (PF_KTHREAD | PF_USER_WORKER)) &&
- !test_thread_flag(TIF_NEED_FPU_LOAD)) {
- set_thread_flag(TIF_NEED_FPU_LOAD);
- save_fpregs_to_fpstate(x86_task_fpu(current));
- }
+ !test_thread_flag(TIF_NEED_FPU_LOAD))
+ update_fpu_state_and_flag(x86_task_fpu(current), current);
__cpu_invalidate_fpregs_state();
/* Put sane initial values into the control registers. */
--
2.34.1
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [Patch v9 09/24] perf: Move and enhance has_extended_regs() for arch-specific use
2026-07-06 1:54 [Patch v9 00/24] Support SIMD/eGPRs/SSP registers sampling for perf Dapeng Mi
` (7 preceding siblings ...)
2026-07-06 1:54 ` [Patch v9 08/24] x86/fpu: Add update_fpu_state_and_flag() helper Dapeng Mi
@ 2026-07-06 1:54 ` Dapeng Mi
2026-07-06 1:54 ` [Patch v9 10/24] perf/x86/intel: Consolidate PMU capability updates Dapeng Mi
` (14 subsequent siblings)
23 siblings, 0 replies; 42+ messages in thread
From: Dapeng Mi @ 2026-07-06 1:54 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Thomas Gleixner, Dave Hansen, Ian Rogers,
Adrian Hunter, Jiri Olsa, Alexander Shishkin, Andi Kleen,
Eranian Stephane
Cc: Mark Rutland, broonie, Ravi Bangoria, linux-kernel,
linux-perf-users, Zide Chen, Falcon Thomas, Dapeng Mi, Xudong Hao,
Kan Liang, Dapeng Mi
From: Kan Liang <kan.liang@linux.intel.com>
Move has_extended_regs() to include/linux/perf_event.h so it can be used
by arch-specific code.
While moving it, enhance the check logic and rename it to
event_has_extended_regs() to match existing perf event helper naming.
Signed-off-by: Kan Liang <kan.liang@linux.intel.com>
Signed-off-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
---
include/linux/perf_event.h | 10 ++++++++++
kernel/events/core.c | 8 +-------
2 files changed, 11 insertions(+), 7 deletions(-)
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h
index 48d851fbd8ea..fb38affa7352 100644
--- a/include/linux/perf_event.h
+++ b/include/linux/perf_event.h
@@ -1534,6 +1534,16 @@ perf_event__output_id_sample(struct perf_event *event,
extern void
perf_log_lost_samples(struct perf_event *event, u64 lost);
+static inline bool event_has_extended_regs(struct perf_event *event)
+{
+ struct perf_event_attr *attr = &event->attr;
+
+ return ((attr->sample_type & PERF_SAMPLE_REGS_USER) &&
+ (attr->sample_regs_user & PERF_REG_EXTENDED_MASK)) ||
+ ((attr->sample_type & PERF_SAMPLE_REGS_INTR) &&
+ (attr->sample_regs_intr & PERF_REG_EXTENDED_MASK));
+}
+
static inline bool event_has_any_exclude_flag(struct perf_event *event)
{
struct perf_event_attr *attr = &event->attr;
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 82009bfb4fd9..0239864029be 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -13116,12 +13116,6 @@ int perf_pmu_unregister(struct pmu *pmu)
}
EXPORT_SYMBOL_GPL(perf_pmu_unregister);
-static inline bool has_extended_regs(struct perf_event *event)
-{
- return (event->attr.sample_regs_user & PERF_REG_EXTENDED_MASK) ||
- (event->attr.sample_regs_intr & PERF_REG_EXTENDED_MASK);
-}
-
static int perf_try_init_event(struct pmu *pmu, struct perf_event *event)
{
struct perf_event_context *ctx = NULL;
@@ -13156,7 +13150,7 @@ static int perf_try_init_event(struct pmu *pmu, struct perf_event *event)
goto err_pmu;
if (!(pmu->capabilities & PERF_PMU_CAP_EXTENDED_REGS) &&
- has_extended_regs(event)) {
+ event_has_extended_regs(event)) {
ret = -EOPNOTSUPP;
goto err_destroy;
}
--
2.34.1
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [Patch v9 10/24] perf/x86/intel: Consolidate PMU capability updates
2026-07-06 1:54 [Patch v9 00/24] Support SIMD/eGPRs/SSP registers sampling for perf Dapeng Mi
` (8 preceding siblings ...)
2026-07-06 1:54 ` [Patch v9 09/24] perf: Move and enhance has_extended_regs() for arch-specific use Dapeng Mi
@ 2026-07-06 1:54 ` Dapeng Mi
2026-07-06 1:54 ` [Patch v9 11/24] perf/x86: Enable XMM register sampling for non-PEBS events Dapeng Mi
` (13 subsequent siblings)
23 siblings, 0 replies; 42+ messages in thread
From: Dapeng Mi @ 2026-07-06 1:54 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Thomas Gleixner, Dave Hansen, Ian Rogers,
Adrian Hunter, Jiri Olsa, Alexander Shishkin, Andi Kleen,
Eranian Stephane
Cc: Mark Rutland, broonie, Ravi Bangoria, linux-kernel,
linux-perf-users, Zide Chen, Falcon Thomas, Dapeng Mi, Xudong Hao,
Dapeng Mi
Currently, the PERF_PMU_CAP_EXTENDED_REGS capability is set in two
different places: intel_ds_pebs_init() and __intel_update_pmu_caps()
for adaptive and architectural PEBS. The upcoming XSAVES-based SIMD
register sampling will also require setting and validating this
capability. Managing it across multiple locations introduces
unnecessary complexity and potential conflicts.
To centralize and simplify the PERF_PMU_CAP_EXTENDED_REGS logic,
consolidate its initialization into a single helper function,
__intel_update_pmu_xregs_caps(), handling both adaptive and
architectural PEBS.
Additionally, optimize the capability update paths by moving the
initialization of intel_cap.capabilities out of the update_pmu_cap()
helper. The helper is guarded by archPerfmonExt support, whereas
intel_cap.capabilities is independent of it.
Finally, introduce a new wrapper function, intel_update_pmu_caps(),
to cleanly encapsulate all these PMU capability updates.
Signed-off-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
---
arch/x86/events/intel/core.c | 54 ++++++++++++++++++++++++------------
arch/x86/events/intel/ds.c | 1 -
2 files changed, 36 insertions(+), 19 deletions(-)
diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c
index 2e658b4bc83a..11a0c4dd2026 100644
--- a/arch/x86/events/intel/core.c
+++ b/arch/x86/events/intel/core.c
@@ -6143,19 +6143,27 @@ static void intel_pmu_check_extra_regs(struct extra_reg *extra_regs);
static inline bool intel_pmu_broken_perf_cap(void)
{
- /* The Perf Metric (Bit 15) is always cleared */
- if (boot_cpu_data.x86_vfm == INTEL_METEORLAKE ||
+ /*
+ * The Perf Metric (Bit 15) is always cleared on P-core of
+ * PRL and MTL. Details can be found in RPL018 Errata Details.
+ */
+ if (boot_cpu_data.x86_vfm == INTEL_RAPTORLAKE ||
+ boot_cpu_data.x86_vfm == INTEL_RAPTORLAKE_P ||
+ boot_cpu_data.x86_vfm == INTEL_RAPTORLAKE_S ||
+ boot_cpu_data.x86_vfm == INTEL_METEORLAKE ||
boot_cpu_data.x86_vfm == INTEL_METEORLAKE_L)
return true;
return false;
}
-static inline void __intel_update_pmu_caps(struct pmu *pmu)
+static inline void __intel_update_pmu_xregs_caps(struct pmu *pmu)
{
struct pmu *dest_pmu = pmu ? pmu : x86_get_pmu(smp_processor_id());
+ u64 caps = hybrid(pmu, arch_pebs_cap).caps;
- if (hybrid(pmu, arch_pebs_cap).caps & ARCH_PEBS_VECR_XMM)
+ if ((x86_pmu.arch_pebs && (caps & ARCH_PEBS_VECR_XMM)) ||
+ (x86_pmu.intel_cap.pebs_format >= 4 && x86_pmu.intel_cap.pebs_baseline))
dest_pmu->capabilities |= PERF_PMU_CAP_EXTENDED_REGS;
}
@@ -6179,7 +6187,7 @@ static inline void __intel_update_large_pebs_flags(struct pmu *pmu)
#define counter_mask(_gp, _fixed) ((_gp) | ((u64)(_fixed) << INTEL_PMC_IDX_FIXED))
-static void update_pmu_cap(struct pmu *pmu)
+static void update_pmu_cap_from_perfmonext(struct pmu *pmu)
{
unsigned int eax, ebx, ecx, edx;
union cpuid35_eax eax_0;
@@ -6227,21 +6235,34 @@ static void update_pmu_cap(struct pmu *pmu)
hybrid(pmu, arch_pebs_cap).counters = pebs_mask;
hybrid(pmu, arch_pebs_cap).pdists = pdists_mask;
- if (WARN_ON((pebs_mask | pdists_mask) & ~cntrs_mask)) {
+ if (WARN_ON((pebs_mask | pdists_mask) & ~cntrs_mask))
x86_pmu.arch_pebs = 0;
- } else {
- __intel_update_pmu_caps(pmu);
+ else
__intel_update_large_pebs_flags(pmu);
- }
} else {
WARN_ON(x86_pmu.arch_pebs == 1);
x86_pmu.arch_pebs = 0;
}
+}
- if (!intel_pmu_broken_perf_cap()) {
- /* Perf Metric (Bit 15) and PEBS via PT (Bit 16) are hybrid enumeration */
- rdmsrq(MSR_IA32_PERF_CAPABILITIES, hybrid(pmu, intel_cap).capabilities);
+static void intel_update_pmu_caps(struct pmu *pmu)
+{
+ if (this_cpu_has(X86_FEATURE_ARCH_PERFMON_EXT))
+ update_pmu_cap_from_perfmonext(pmu);
+
+ if (is_hybrid() && this_cpu_has(X86_FEATURE_PDCM)) {
+ rdmsrq(MSR_IA32_PERF_CAPABILITIES,
+ hybrid(pmu, intel_cap).capabilities);
+
+ /*
+ * Restore perf_metrics on platforms with broken
+ * perf_capablities.
+ */
+ if (intel_pmu_broken_perf_cap() &&
+ hybrid_pmu(pmu)->pmu_type == hybrid_big)
+ hybrid(pmu, intel_cap).perf_metrics = 1;
}
+ __intel_update_pmu_xregs_caps(pmu);
}
static void intel_pmu_check_hybrid_pmus(struct x86_hybrid_pmu *pmu)
@@ -6325,8 +6346,7 @@ static bool init_hybrid_pmu(int cpu)
if (!cpumask_empty(&pmu->supported_cpus))
goto end;
- if (this_cpu_has(X86_FEATURE_ARCH_PERFMON_EXT))
- update_pmu_cap(&pmu->pmu);
+ intel_update_pmu_caps(&pmu->pmu);
intel_pmu_check_hybrid_pmus(pmu);
@@ -6395,8 +6415,6 @@ static void intel_pmu_cpu_starting(int cpu)
}
}
- __intel_update_pmu_caps(cpuc->pmu);
-
if (!cpuc->shared_regs)
return;
@@ -8821,8 +8839,8 @@ __init int intel_pmu_init(void)
* from the leaf 0xa. The core specific update will be done later
* when a new type is online.
*/
- if (!is_hybrid() && boot_cpu_has(X86_FEATURE_ARCH_PERFMON_EXT))
- update_pmu_cap(NULL);
+ if (!is_hybrid())
+ intel_update_pmu_caps(NULL);
if (x86_pmu.arch_pebs) {
static_call_update(intel_pmu_disable_event_ext,
diff --git a/arch/x86/events/intel/ds.c b/arch/x86/events/intel/ds.c
index 7b69f8c8d0c2..78b9c0dcb14d 100644
--- a/arch/x86/events/intel/ds.c
+++ b/arch/x86/events/intel/ds.c
@@ -3396,7 +3396,6 @@ static void __init intel_ds_pebs_init(void)
x86_pmu.flags |= PMU_FL_PEBS_ALL;
x86_pmu.pebs_capable = ~0ULL;
pebs_qual = "-baseline";
- x86_get_pmu(smp_processor_id())->capabilities |= PERF_PMU_CAP_EXTENDED_REGS;
} else {
/* Only basic record supported */
x86_pmu.large_pebs_flags &=
--
2.34.1
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [Patch v9 11/24] perf/x86: Enable XMM register sampling for non-PEBS events
2026-07-06 1:54 [Patch v9 00/24] Support SIMD/eGPRs/SSP registers sampling for perf Dapeng Mi
` (9 preceding siblings ...)
2026-07-06 1:54 ` [Patch v9 10/24] perf/x86/intel: Consolidate PMU capability updates Dapeng Mi
@ 2026-07-06 1:54 ` Dapeng Mi
2026-07-06 2:34 ` sashiko-bot
2026-07-06 1:54 ` [Patch v9 12/24] perf/x86: Enable XMM register sampling for REGS_USER case Dapeng Mi
` (12 subsequent siblings)
23 siblings, 1 reply; 42+ messages in thread
From: Dapeng Mi @ 2026-07-06 1:54 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Thomas Gleixner, Dave Hansen, Ian Rogers,
Adrian Hunter, Jiri Olsa, Alexander Shishkin, Andi Kleen,
Eranian Stephane
Cc: Mark Rutland, broonie, Ravi Bangoria, linux-kernel,
linux-perf-users, Zide Chen, Falcon Thomas, Dapeng Mi, Xudong Hao,
Dapeng Mi, Kan Liang
Previously, XMM register sampling was only available for PEBS events
starting from Icelake. Extend the support to non-PEBS events using
the xsaves instruction, thereby completing the feature set.
To implement this, a 64-byte aligned buffer is required. A per-CPU
ext_regs_buf is introduced to store SIMD and other registers, with an
approximate size of 2K. The buffer is allocated using kzalloc_node(),
ensuring natural and 64-byte alignment for all kmalloc() allocations
with powers of 2.
XMM sampling for non-PEBS events is supported in the REGS_INTR case.
Support for REGS_USER will be added in a subsequent patch. For PEBS
events, XMM register sampling data is directly retrieved from PEBS
records.
Future support for additional vector registers (YMM/ZMM/OPMASK) is
planned. An ext_regs_mask is added to track the supported vector
register groups.
Co-developed-by: Kan Liang <kan.liang@linux.intel.com>
Signed-off-by: Kan Liang <kan.liang@linux.intel.com>
Signed-off-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
---
arch/x86/events/core.c | 183 ++++++++++++++++++++++++++++--
arch/x86/events/intel/core.c | 32 ++++++
arch/x86/events/intel/ds.c | 12 +-
arch/x86/events/perf_event.h | 13 +++
arch/x86/include/asm/fpu/xstate.h | 2 +
arch/x86/include/asm/perf_event.h | 5 +-
arch/x86/kernel/fpu/xstate.c | 2 +-
7 files changed, 234 insertions(+), 15 deletions(-)
diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c
index d83ea02e2457..3e52610510cd 100644
--- a/arch/x86/events/core.c
+++ b/arch/x86/events/core.c
@@ -410,6 +410,56 @@ set_ext_hw_attr(struct hw_perf_event *hwc, struct perf_event *event)
return x86_pmu_extra_regs(val, event);
}
+static DEFINE_PER_CPU(struct xregs_state *, ext_regs_buf);
+
+static void release_ext_regs_buffers(void)
+{
+ int cpu;
+
+ if (!x86_pmu.ext_regs_mask)
+ return;
+
+ for_each_possible_cpu(cpu) {
+ kfree(per_cpu(ext_regs_buf, cpu));
+ per_cpu(ext_regs_buf, cpu) = NULL;
+ }
+}
+
+static void reserve_ext_regs_buffers(void)
+{
+ bool compacted = cpu_feature_enabled(X86_FEATURE_XCOMPACTED);
+ unsigned int size;
+ int cpu;
+
+ if (!x86_pmu.ext_regs_mask)
+ return;
+
+ /* +64 bytes for the 64 bytes alignment request of xsave area. */
+ size = xstate_calculate_size(x86_pmu.ext_regs_mask, compacted) + 64;
+
+ for_each_possible_cpu(cpu) {
+ per_cpu(ext_regs_buf, cpu) = kzalloc_node(size, GFP_KERNEL,
+ cpu_to_node(cpu));
+ if (WARN_ON_ONCE(!per_cpu(ext_regs_buf, cpu)))
+ goto err;
+ }
+
+ return;
+
+err:
+ release_ext_regs_buffers();
+}
+
+static inline struct xregs_state *get_ext_regs_buf(int cpu)
+{
+ void *buf = per_cpu(ext_regs_buf, cpu);
+ struct xregs_state *xsave;
+
+ xsave = buf ? PTR_ALIGN(buf, 64) : NULL;
+
+ return xsave;
+}
+
int x86_reserve_hardware(void)
{
int err = 0;
@@ -422,6 +472,7 @@ int x86_reserve_hardware(void)
} else {
reserve_ds_buffers();
reserve_lbr_buffers();
+ reserve_ext_regs_buffers();
}
}
if (!err)
@@ -438,6 +489,7 @@ void x86_release_hardware(void)
release_pmc_hardware();
release_ds_buffers();
release_lbr_buffers();
+ release_ext_regs_buffers();
mutex_unlock(&pmc_reserve_mutex);
}
}
@@ -655,18 +707,20 @@ int x86_pmu_hw_config(struct perf_event *event)
return -EINVAL;
}
- /* sample_regs_user never support XMM registers */
- if (unlikely(event->attr.sample_regs_user & PERF_REG_EXTENDED_MASK))
- 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 & PERF_REG_EXTENDED_MASK)) {
- if (!(event->pmu->capabilities & PERF_PMU_CAP_EXTENDED_REGS))
- return -EINVAL;
+ if (event->attr.sample_type & PERF_SAMPLE_REGS_INTR) {
+ /*
+ * Besides the general purpose registers, XMM registers may
+ * be collected as well.
+ */
+ if (event->attr.sample_regs_intr & PERF_REG_EXTENDED_MASK) {
+ if (!(event->pmu->capabilities & PERF_PMU_CAP_EXTENDED_REGS))
+ return -EINVAL;
+ }
+ }
- if (!event->attr.precise_ip)
+ if (event->attr.sample_type & PERF_SAMPLE_REGS_USER) {
+ /* XMM registers sampling for REGS_USER is not supported yet. */
+ if (event->attr.sample_regs_user & PERF_REG_EXTENDED_MASK)
return -EINVAL;
}
@@ -1705,6 +1759,113 @@ static void x86_pmu_del(struct perf_event *event, int flags)
static_call_cond(x86_pmu_del)(event);
}
+void x86_pmu_clear_perf_regs(struct pt_regs *regs)
+{
+ struct x86_perf_regs *perf_regs = container_of(regs, struct x86_perf_regs, regs);
+
+ perf_regs->xmm_regs = NULL;
+}
+
+static void update_perf_regs(struct x86_perf_regs *perf_regs,
+ struct xregs_state *xsave, u64 bitmap)
+{
+ u64 mask;
+
+ if (!xsave)
+ return;
+
+ /* Restrict to features actually saved by XSAVES */
+ mask = bitmap & xsave->header.xfeatures;
+
+ if (mask & XFEATURE_MASK_SSE)
+ perf_regs->xmm_space = xsave->i387.xmm_space;
+}
+
+/*
+ * The x86 specific variant of perf_sample_regs_intr().
+ * It would be extended to add more SIMD registers sampling support
+ * in later patches.
+ */
+static void x86_pmu_update_regs_intr(struct perf_event *event,
+ struct perf_sample_data *data,
+ struct pt_regs *regs,
+ bool exclude_kernel)
+{
+ if (exclude_kernel && !user_mode(regs)) {
+ data->regs_intr.regs = NULL;
+ data->regs_intr.abi = PERF_SAMPLE_REGS_ABI_NONE;
+ } else {
+ data->regs_intr.regs = regs;
+ data->regs_intr.abi = perf_reg_abi(current);
+ }
+
+ data->dyn_size += sizeof(u64);
+ if (data->regs_intr.regs) {
+ data->dyn_size += hweight64(event->attr.sample_regs_intr) *
+ sizeof(u64);
+ }
+
+ /*
+ * Set PERF_SAMPLE_REGS_INTR to bypass perf_sample_regs_intr() call
+ * in perf_prepare_sample() function.
+ */
+ data->sample_flags |= PERF_SAMPLE_REGS_INTR;
+}
+
+static void x86_pmu_sample_xregs(struct perf_event *event,
+ struct perf_sample_data *data,
+ u64 ignore_mask)
+{
+ struct xregs_state *xsave = get_ext_regs_buf(smp_processor_id());
+ u64 sample_type = event->attr.sample_type;
+ struct x86_perf_regs *perf_regs;
+ u64 intr_mask = 0;
+ u64 mask = 0;
+
+ if (WARN_ON_ONCE(!xsave) || !in_nmi())
+ return;
+
+ if ((sample_type & PERF_SAMPLE_REGS_INTR) &&
+ (event->attr.sample_regs_intr & PERF_REG_EXTENDED_MASK))
+ mask |= XFEATURE_MASK_SSE;
+
+ mask &= x86_pmu.ext_regs_mask;
+
+ if ((sample_type & PERF_SAMPLE_REGS_INTR) && data->regs_intr.regs)
+ intr_mask = mask & ~ignore_mask;
+
+ if (intr_mask) {
+ perf_regs = container_of(data->regs_intr.regs,
+ struct x86_perf_regs, regs);
+ xsave->header.xfeatures = 0;
+ xsaves_nmi(xsave, mask);
+ update_perf_regs(perf_regs, xsave, intr_mask);
+ }
+}
+
+void x86_pmu_update_perf_regs(struct perf_event *event,
+ struct perf_sample_data *data,
+ struct pt_regs *regs,
+ u64 ignore_mask)
+{
+ u64 sample_type = event->attr.sample_type;
+
+ if (!((sample_type & PERF_SAMPLE_REGS_INTR) &&
+ (event->attr.sample_regs_intr & PERF_REG_EXTENDED_MASK)))
+ return;
+
+ if (sample_type & PERF_SAMPLE_REGS_INTR) {
+ x86_pmu_update_regs_intr(event, data, regs,
+ event->attr.exclude_kernel);
+ }
+
+ /*
+ * ignore_mask indicates the PEBS sampled extended regs
+ * which are unnecessary to sample again.
+ */
+ x86_pmu_sample_xregs(event, data, ignore_mask);
+}
+
int x86_pmu_handle_irq(struct pt_regs *regs)
{
struct perf_sample_data data;
diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c
index 11a0c4dd2026..93ac6591cb3e 100644
--- a/arch/x86/events/intel/core.c
+++ b/arch/x86/events/intel/core.c
@@ -3927,6 +3927,9 @@ static int handle_pmi_common(struct pt_regs *regs, u64 status)
if (has_branch_stack(event))
intel_pmu_lbr_save_brstack(&data, cpuc, event);
+ x86_pmu_clear_perf_regs(regs);
+ x86_pmu_update_perf_regs(event, &data, regs, 0);
+
perf_event_overflow(event, &data, regs);
}
@@ -6162,9 +6165,38 @@ static inline void __intel_update_pmu_xregs_caps(struct pmu *pmu)
struct pmu *dest_pmu = pmu ? pmu : x86_get_pmu(smp_processor_id());
u64 caps = hybrid(pmu, arch_pebs_cap).caps;
+ /*
+ * Extend the vector registers support to non-PEBS.
+ * The feature is limited to newer Intel machines with
+ * PEBS V4+ or archPerfmonExt (0x23) enabled for now.
+ * In theory, the vector registers can be retrieved as
+ * long as the CPU supports. The support for the old
+ * generations may be added later if there is a
+ * requirement.
+ * Only support the extension when XSAVES is available.
+ */
+ if (!boot_cpu_has(X86_FEATURE_XSAVES))
+ return;
+
+ if (!boot_cpu_has(X86_FEATURE_XMM) ||
+ !cpu_has_xfeatures(XFEATURE_MASK_SSE, NULL))
+ return;
+
+ /*
+ * On current hybrid platforms, P-cores and E-cores expose the same
+ * XSAVE feature set. Therefore, using the global x86_pmu.ext_regs_mask
+ * is sufficient to represent the hardware-supported XSAVE features.
+ */
+ x86_pmu.ext_regs_mask |= XFEATURE_MASK_SSE;
+
+ /* PEBS supported case */
if ((x86_pmu.arch_pebs && (caps & ARCH_PEBS_VECR_XMM)) ||
(x86_pmu.intel_cap.pebs_format >= 4 && x86_pmu.intel_cap.pebs_baseline))
dest_pmu->capabilities |= PERF_PMU_CAP_EXTENDED_REGS;
+
+ /* PEBS unsupported case (e.g., guest) */
+ if (!x86_pmu.intel_cap.pebs_format)
+ dest_pmu->capabilities |= PERF_PMU_CAP_EXTENDED_REGS;
}
static inline void __intel_update_large_pebs_flags(struct pmu *pmu)
diff --git a/arch/x86/events/intel/ds.c b/arch/x86/events/intel/ds.c
index 78b9c0dcb14d..e2bb53f138ee 100644
--- a/arch/x86/events/intel/ds.c
+++ b/arch/x86/events/intel/ds.c
@@ -2508,6 +2508,7 @@ static void setup_pebs_adaptive_sample_data(struct perf_event *event,
struct pebs_meminfo *meminfo = NULL;
struct pebs_gprs *gprs = NULL;
struct x86_perf_regs *perf_regs;
+ u64 ignore_mask = 0;
u64 format_group;
u16 retire;
@@ -2515,7 +2516,7 @@ static void setup_pebs_adaptive_sample_data(struct perf_event *event,
return;
perf_regs = container_of(regs, struct x86_perf_regs, regs);
- perf_regs->xmm_regs = NULL;
+ x86_pmu_clear_perf_regs(regs);
format_group = basic->format_group;
@@ -2562,6 +2563,7 @@ static void setup_pebs_adaptive_sample_data(struct perf_event *event,
if (format_group & PEBS_DATACFG_XMMS) {
struct pebs_xmm *xmm = next_record;
+ ignore_mask |= XFEATURE_MASK_SSE;
next_record = xmm + 1;
perf_regs->xmm_regs = xmm->xmm;
}
@@ -2600,6 +2602,8 @@ static void setup_pebs_adaptive_sample_data(struct perf_event *event,
next_record += nr * sizeof(u64);
}
+ x86_pmu_update_perf_regs(event, data, regs, ignore_mask);
+
WARN_ONCE(next_record != __pebs + basic->format_size,
"PEBS record size %u, expected %llu, config %llx\n",
basic->format_size,
@@ -2625,6 +2629,7 @@ static void setup_arch_pebs_sample_data(struct perf_event *event,
struct arch_pebs_aux *meminfo = NULL;
struct arch_pebs_gprs *gprs = NULL;
struct x86_perf_regs *perf_regs;
+ u64 ignore_mask = 0;
void *next_record;
void *at = __pebs;
@@ -2632,7 +2637,7 @@ static void setup_arch_pebs_sample_data(struct perf_event *event,
return;
perf_regs = container_of(regs, struct x86_perf_regs, regs);
- perf_regs->xmm_regs = NULL;
+ x86_pmu_clear_perf_regs(regs);
__setup_perf_sample_data(event, iregs, data);
@@ -2687,6 +2692,7 @@ static void setup_arch_pebs_sample_data(struct perf_event *event,
next_record += sizeof(struct arch_pebs_xer_header);
+ ignore_mask |= XFEATURE_MASK_SSE;
xmm = next_record;
perf_regs->xmm_regs = xmm->xmm;
next_record = xmm + 1;
@@ -2734,6 +2740,8 @@ static void setup_arch_pebs_sample_data(struct perf_event *event,
at = at + header->size;
goto again;
}
+
+ x86_pmu_update_perf_regs(event, data, regs, ignore_mask);
}
static inline void *
diff --git a/arch/x86/events/perf_event.h b/arch/x86/events/perf_event.h
index a8afea8d38f0..70fa7ec21673 100644
--- a/arch/x86/events/perf_event.h
+++ b/arch/x86/events/perf_event.h
@@ -1028,6 +1028,12 @@ struct x86_pmu {
struct extra_reg *extra_regs;
unsigned int flags;
+ /*
+ * Extended regs, e.g., vector registers
+ * Utilize the same format as the XFEATURE_MASK_*
+ */
+ u64 ext_regs_mask;
+
/*
* Intel host/guest support (KVM)
*/
@@ -1314,6 +1320,13 @@ void x86_pmu_enable_event(struct perf_event *event);
int x86_pmu_handle_irq(struct pt_regs *regs);
+void x86_pmu_clear_perf_regs(struct pt_regs *regs);
+
+void x86_pmu_update_perf_regs(struct perf_event *event,
+ struct perf_sample_data *data,
+ struct pt_regs *regs,
+ u64 ignore_mask);
+
void x86_pmu_show_pmu_cap(struct pmu *pmu);
static inline int x86_pmu_num_counters(struct pmu *pmu)
diff --git a/arch/x86/include/asm/fpu/xstate.h b/arch/x86/include/asm/fpu/xstate.h
index 38fa8ff26559..19dec5f0b1c7 100644
--- a/arch/x86/include/asm/fpu/xstate.h
+++ b/arch/x86/include/asm/fpu/xstate.h
@@ -112,6 +112,8 @@ void xsaves(struct xregs_state *xsave, u64 mask);
void xrstors(struct xregs_state *xsave, u64 mask);
void xsaves_nmi(struct xregs_state *xsave, u64 mask);
+unsigned int xstate_calculate_size(u64 xfeatures, bool compacted);
+
int xfd_enable_feature(u64 xfd_err);
#ifdef CONFIG_X86_64
diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h
index 1eb13673e889..619e0ae915e1 100644
--- a/arch/x86/include/asm/perf_event.h
+++ b/arch/x86/include/asm/perf_event.h
@@ -728,7 +728,10 @@ extern void perf_events_lapic_init(void);
struct pt_regs;
struct x86_perf_regs {
struct pt_regs regs;
- u64 *xmm_regs;
+ union {
+ u64 *xmm_regs;
+ u32 *xmm_space; /* for xsaves */
+ };
};
extern unsigned long perf_arch_instruction_pointer(struct pt_regs *regs);
diff --git a/arch/x86/kernel/fpu/xstate.c b/arch/x86/kernel/fpu/xstate.c
index d39c01546dee..3e7f5fb5bfaf 100644
--- a/arch/x86/kernel/fpu/xstate.c
+++ b/arch/x86/kernel/fpu/xstate.c
@@ -587,7 +587,7 @@ static bool __init check_xstate_against_struct(int nr)
return true;
}
-static unsigned int xstate_calculate_size(u64 xfeatures, bool compacted)
+unsigned int xstate_calculate_size(u64 xfeatures, bool compacted)
{
unsigned int topmost = fls64(xfeatures) - 1;
unsigned int offset, i;
--
2.34.1
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [Patch v9 12/24] perf/x86: Enable XMM register sampling for REGS_USER case
2026-07-06 1:54 [Patch v9 00/24] Support SIMD/eGPRs/SSP registers sampling for perf Dapeng Mi
` (10 preceding siblings ...)
2026-07-06 1:54 ` [Patch v9 11/24] perf/x86: Enable XMM register sampling for non-PEBS events Dapeng Mi
@ 2026-07-06 1:54 ` Dapeng Mi
2026-07-06 2:35 ` sashiko-bot
2026-07-06 1:54 ` [Patch v9 13/24] perf: Add sampling support for SIMD registers Dapeng Mi
` (11 subsequent siblings)
23 siblings, 1 reply; 42+ messages in thread
From: Dapeng Mi @ 2026-07-06 1:54 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Thomas Gleixner, Dave Hansen, Ian Rogers,
Adrian Hunter, Jiri Olsa, Alexander Shishkin, Andi Kleen,
Eranian Stephane
Cc: Mark Rutland, broonie, Ravi Bangoria, linux-kernel,
linux-perf-users, Zide Chen, Falcon Thomas, Dapeng Mi, Xudong Hao,
Dapeng Mi, Kan Liang
Support XMM register sampling for the REGS_USER case.
To handle simultaneous sampling of XMM registers for both REGS_INTR and
REGS_USER cases, a per-CPU `x86_user_regs` is introduced to store
REGS_USER-specific XMM registers. This prevents REGS_USER-specific XMM
register data from being overwritten by REGS_INTR-specific data if they
share the same `x86_perf_regs` structure.
To sample user-space XMM registers, the `x86_pmu_update_user_xregs()`
helper function is added. It checks if the `TIF_NEED_FPU_LOAD` flag is
set. If so, the user-space XMM register data can be directly retrieved
from the cached task FPU state, as the corresponding hardware registers
have been cleared or switched to kernel-space data. Otherwise, the data
must be read from the hardware registers using the `xsaves` instruction.
For PEBS events, `x86_pmu_update_user_xregs()` checks if the PEBS-sampled
XMM register data belongs to user-space. If so, no further action is
needed. Otherwise, the user-space XMM register data needs to be
re-sampled using the same method as for non-PEBS events.
Co-developed-by: Kan Liang <kan.liang@linux.intel.com>
Signed-off-by: Kan Liang <kan.liang@linux.intel.com>
Signed-off-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
---
arch/x86/events/core.c | 157 +++++++++++++++++++++++++++++++----
arch/x86/events/intel/core.c | 6 +-
arch/x86/events/intel/ds.c | 5 +-
3 files changed, 148 insertions(+), 20 deletions(-)
diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c
index 3e52610510cd..0d42c51761f9 100644
--- a/arch/x86/events/core.c
+++ b/arch/x86/events/core.c
@@ -707,23 +707,17 @@ int x86_pmu_hw_config(struct perf_event *event)
return -EINVAL;
}
- if (event->attr.sample_type & PERF_SAMPLE_REGS_INTR) {
+ if (event->attr.sample_type & (PERF_SAMPLE_REGS_INTR | PERF_SAMPLE_REGS_USER)) {
/*
* Besides the general purpose registers, XMM registers may
* be collected as well.
*/
- if (event->attr.sample_regs_intr & PERF_REG_EXTENDED_MASK) {
+ if (event_has_extended_regs(event)) {
if (!(event->pmu->capabilities & PERF_PMU_CAP_EXTENDED_REGS))
return -EINVAL;
}
}
- if (event->attr.sample_type & PERF_SAMPLE_REGS_USER) {
- /* XMM registers sampling for REGS_USER is not supported yet. */
- if (event->attr.sample_regs_user & PERF_REG_EXTENDED_MASK)
- return -EINVAL;
- }
-
return x86_setup_perfctr(event);
}
@@ -1812,33 +1806,165 @@ static void x86_pmu_update_regs_intr(struct perf_event *event,
data->sample_flags |= PERF_SAMPLE_REGS_INTR;
}
+/*
+ * When both PERF_SAMPLE_REGS_INTR and PERF_SAMPLE_REGS_USER are set,
+ * an additional x86_perf_regs is required to save user-space registers.
+ * Without this, user-space register data may be overwritten by kernel-space
+ * registers.
+ */
+static DEFINE_PER_CPU(struct x86_perf_regs, x86_user_regs);
+static void x86_pmu_get_regs_user(struct perf_sample_data *data,
+ struct pt_regs *regs)
+{
+ struct x86_perf_regs *x86_regs_user = this_cpu_ptr(&x86_user_regs);
+ struct perf_regs regs_user;
+
+ x86_pmu_clear_perf_regs(&x86_regs_user->regs);
+
+ perf_get_regs_user(®s_user, regs);
+ data->regs_user.abi = regs_user.abi;
+ if (regs_user.regs) {
+ x86_regs_user->regs = *regs_user.regs;
+ data->regs_user.regs = &x86_regs_user->regs;
+ } else
+ data->regs_user.regs = NULL;
+}
+
+/*
+ * The x86 specific variant of perf_sample_regs_user().
+ * Update data->regs_user fields for extended registers (e.g., SIMD).
+ */
+static void x86_pmu_update_regs_user(struct perf_event *event,
+ struct perf_sample_data *data,
+ struct pt_regs *regs)
+{
+ struct perf_event_attr *attr = &event->attr;
+
+ if (user_mode(regs)) {
+ data->regs_user.abi = perf_reg_abi(current);
+ data->regs_user.regs = regs;
+ } else if (is_user_task(current)) {
+ /*
+ * It cannot guarantee that the kernel will never
+ * touch the registers outside of the pt_regs,
+ * especially when more and more registers
+ * (e.g., SIMD, eGPR) are added. The live data
+ * cannot be used.
+ */
+ x86_pmu_get_regs_user(data, regs);
+ } else {
+ data->regs_user.abi = PERF_SAMPLE_REGS_ABI_NONE;
+ data->regs_user.regs = NULL;
+ }
+
+ data->dyn_size += sizeof(u64);
+ if (data->regs_user.regs)
+ data->dyn_size += hweight64(attr->sample_regs_user) * sizeof(u64);
+
+ /*
+ * Set PERF_SAMPLE_REGS_USER to bypass perf_sample_regs_user() call
+ * in perf_prepare_sample() function.
+ */
+ data->sample_flags |= PERF_SAMPLE_REGS_USER;
+}
+
+/*
+ * This function retrieves cached user-space fpu registers (XMM/YMM/ZMM).
+ * If TIF_NEED_FPU_LOAD is set, it indicates that the user-space FPU state
+ * is cached. Otherwise, the data should be read directly from the hardware
+ * registers.
+ */
+static inline u64 x86_pmu_update_user_xregs(struct perf_sample_data *data,
+ struct pt_regs *regs,
+ u64 mask, u64 ignore_mask)
+{
+ struct x86_perf_regs *perf_regs;
+ struct xregs_state *xsave;
+ unsigned int guest_state;
+ struct fpu *fpu;
+ struct fpstate *fps;
+ u64 user_mask = mask;
+
+ if (data->regs_user.abi == PERF_SAMPLE_REGS_ABI_NONE)
+ return 0;
+
+ /*
+ * If PEBS hits kernel space, need to re-sample extended
+ * registers for user space.
+ */
+ if (user_mode(regs))
+ user_mask &= ~ignore_mask;
+
+ if (user_mask && test_thread_flag(TIF_NEED_FPU_LOAD)) {
+ perf_regs = container_of(data->regs_user.regs,
+ struct x86_perf_regs, regs);
+ fpu = x86_task_fpu(current);
+ /*
+ * If __task_fpstate is set, it holds the right pointer,
+ * otherwise fpstate will.
+ */
+ fps = READ_ONCE(fpu->__task_fpstate);
+ if (!fps)
+ fps = fpu->fpstate;
+ xsave = &fps->regs.xsave;
+
+ update_perf_regs(perf_regs, xsave, user_mask);
+ return 0;
+ }
+
+ guest_state = perf_guest_state();
+ /*
+ * Skip SIMD register sampling if a PMI hits while guest kernel
+ * state is still active.
+ */
+ if (user_mask && (guest_state & PERF_GUEST_ACTIVE) &&
+ !(guest_state & PERF_GUEST_USER))
+ return 0;
+
+ return user_mask;
+}
+
static void x86_pmu_sample_xregs(struct perf_event *event,
struct perf_sample_data *data,
+ struct pt_regs *regs,
u64 ignore_mask)
{
struct xregs_state *xsave = get_ext_regs_buf(smp_processor_id());
u64 sample_type = event->attr.sample_type;
struct x86_perf_regs *perf_regs;
+ u64 user_mask = 0;
u64 intr_mask = 0;
u64 mask = 0;
if (WARN_ON_ONCE(!xsave) || !in_nmi())
return;
- if ((sample_type & PERF_SAMPLE_REGS_INTR) &&
- (event->attr.sample_regs_intr & PERF_REG_EXTENDED_MASK))
+ if (event_has_extended_regs(event))
mask |= XFEATURE_MASK_SSE;
mask &= x86_pmu.ext_regs_mask;
+ if ((sample_type & PERF_SAMPLE_REGS_USER) && data->regs_user.regs) {
+ user_mask = x86_pmu_update_user_xregs(data, regs,
+ mask, ignore_mask);
+ }
if ((sample_type & PERF_SAMPLE_REGS_INTR) && data->regs_intr.regs)
intr_mask = mask & ~ignore_mask;
+ if (user_mask | intr_mask) {
+ xsave->header.xfeatures = 0;
+ xsaves_nmi(xsave, user_mask | intr_mask);
+ }
+
+ if (user_mask) {
+ perf_regs = container_of(data->regs_user.regs,
+ struct x86_perf_regs, regs);
+ update_perf_regs(perf_regs, xsave, user_mask);
+ }
+
if (intr_mask) {
perf_regs = container_of(data->regs_intr.regs,
struct x86_perf_regs, regs);
- xsave->header.xfeatures = 0;
- xsaves_nmi(xsave, mask);
update_perf_regs(perf_regs, xsave, intr_mask);
}
}
@@ -1850,20 +1976,21 @@ void x86_pmu_update_perf_regs(struct perf_event *event,
{
u64 sample_type = event->attr.sample_type;
- if (!((sample_type & PERF_SAMPLE_REGS_INTR) &&
- (event->attr.sample_regs_intr & PERF_REG_EXTENDED_MASK)))
+ if (!event_has_extended_regs(event))
return;
if (sample_type & PERF_SAMPLE_REGS_INTR) {
x86_pmu_update_regs_intr(event, data, regs,
event->attr.exclude_kernel);
}
+ if (sample_type & PERF_SAMPLE_REGS_USER)
+ x86_pmu_update_regs_user(event, data, regs);
/*
* ignore_mask indicates the PEBS sampled extended regs
* which are unnecessary to sample again.
*/
- x86_pmu_sample_xregs(event, data, ignore_mask);
+ x86_pmu_sample_xregs(event, data, regs, ignore_mask);
}
int x86_pmu_handle_irq(struct pt_regs *regs)
diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c
index 93ac6591cb3e..69294bc57225 100644
--- a/arch/x86/events/intel/core.c
+++ b/arch/x86/events/intel/core.c
@@ -4697,15 +4697,15 @@ static void intel_pebs_aliases_skl(struct perf_event *event)
static unsigned long intel_pmu_large_pebs_flags(struct perf_event *event)
{
unsigned long flags = x86_pmu.large_pebs_flags;
+ u64 gprs_mask = PEBS_GP_REGS | PERF_REG_EXTENDED_MASK;
if (event->attr.use_clockid)
flags &= ~PERF_SAMPLE_TIME;
if (!event->attr.exclude_kernel)
flags &= ~PERF_SAMPLE_REGS_USER;
- if (event->attr.sample_regs_user & ~PEBS_GP_REGS)
+ if (event->attr.sample_regs_user & ~gprs_mask)
flags &= ~PERF_SAMPLE_REGS_USER;
- if (event->attr.sample_regs_intr &
- ~(PEBS_GP_REGS | PERF_REG_EXTENDED_MASK))
+ if (event->attr.sample_regs_intr & ~gprs_mask)
flags &= ~PERF_SAMPLE_REGS_INTR;
return flags;
}
diff --git a/arch/x86/events/intel/ds.c b/arch/x86/events/intel/ds.c
index e2bb53f138ee..2f5e732d2c95 100644
--- a/arch/x86/events/intel/ds.c
+++ b/arch/x86/events/intel/ds.c
@@ -1733,8 +1733,7 @@ static u64 pebs_update_adaptive_cfg(struct perf_event *event)
if (gprs || (attr->precise_ip < 2) || tsx_weight)
pebs_data_cfg |= PEBS_DATACFG_GP;
- if ((sample_type & PERF_SAMPLE_REGS_INTR) &&
- (attr->sample_regs_intr & PERF_REG_EXTENDED_MASK))
+ if (event_has_extended_regs(event))
pebs_data_cfg |= PEBS_DATACFG_XMMS;
if (sample_type & PERF_SAMPLE_BRANCH_STACK) {
@@ -2941,6 +2940,8 @@ __intel_pmu_pebs_events(struct perf_event *event,
void *at = get_next_pebs_record_by_bit(base, top, bit);
int cnt = count;
+ x86_pmu_clear_perf_regs(regs);
+
if (!iregs)
iregs = &dummy_iregs;
--
2.34.1
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [Patch v9 13/24] perf: Add sampling support for SIMD registers
2026-07-06 1:54 [Patch v9 00/24] Support SIMD/eGPRs/SSP registers sampling for perf Dapeng Mi
` (11 preceding siblings ...)
2026-07-06 1:54 ` [Patch v9 12/24] perf/x86: Enable XMM register sampling for REGS_USER case Dapeng Mi
@ 2026-07-06 1:54 ` Dapeng Mi
2026-07-06 2:34 ` sashiko-bot
2026-07-06 1:54 ` [Patch v9 14/24] perf/x86: Support XMM sampling using sample_simd_vec_reg_* fields Dapeng Mi
` (10 subsequent siblings)
23 siblings, 1 reply; 42+ messages in thread
From: Dapeng Mi @ 2026-07-06 1:54 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Thomas Gleixner, Dave Hansen, Ian Rogers,
Adrian Hunter, Jiri Olsa, Alexander Shishkin, Andi Kleen,
Eranian Stephane
Cc: Mark Rutland, broonie, Ravi Bangoria, linux-kernel,
linux-perf-users, Zide Chen, Falcon Thomas, Dapeng Mi, Xudong Hao,
Kan Liang, Dapeng Mi
From: Kan Liang <kan.liang@linux.intel.com>
Users may be interested in sampling SIMD registers during profiling.
The current sample_regs_* structure does not have sufficient space
for all SIMD registers.
To address this, new attribute fields sample_simd_{pred,vec}_reg_* are
added to struct perf_event_attr to represent the SIMD registers that are
expected to be sampled.
Currently, the perf/x86 code supports XMM registers in sample_regs_*.
To unify the configuration of SIMD registers and ensure a consistent
method for configuring XMM and other SIMD registers, a new event
attribute field, sample_simd_regs_enabled, is introduced. When
sample_simd_regs_enabled is set, it indicates that all SIMD registers,
including XMM, will be represented by the newly introduced
sample_simd_{pred|vec}_reg_* fields. The original XMM space in
sample_regs_* is reserved for future uses.
Since SIMD registers are wider than 64 bits, a new output format is
introduced. The number and width of SIMD registers are dumped first,
followed by the register values. The number and width are based on the
user's configuration.
A new ABI, PERF_SAMPLE_REGS_ABI_SIMD, is added to indicate the new format.
The enum perf_sample_regs_abi is now a bitmap. This change should not
impact existing tools, as the version and bitmap remain the same for
values 1 and 2.
Additionally, two new __weak functions are introduced:
- perf_simd_reg_value(): Retrieves the value of the requested SIMD
register.
- perf_simd_reg_validate(): Validates the configuration of the SIMD
registers.
A new flag, PERF_PMU_CAP_SIMD_REGS, is added to indicate that the PMU
supports SIMD register dumping. An error is generated if
sample_simd_{pred|vec}_reg_* is mistakenly set for a PMU that does not
support this capability.
Suggested-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Signed-off-by: Kan Liang <kan.liang@linux.intel.com>
Co-developed-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
Signed-off-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
---
include/linux/perf_event.h | 12 +++
include/linux/perf_regs.h | 6 ++
include/uapi/linux/perf_event.h | 49 +++++++++-
kernel/events/core.c | 158 ++++++++++++++++++++++++++++++--
4 files changed, 211 insertions(+), 14 deletions(-)
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h
index fb38affa7352..c4e330c121d2 100644
--- a/include/linux/perf_event.h
+++ b/include/linux/perf_event.h
@@ -306,6 +306,7 @@ struct perf_event_pmu_context;
#define PERF_PMU_CAP_AUX_PAUSE 0x0200
#define PERF_PMU_CAP_AUX_PREFER_LARGE 0x0400
#define PERF_PMU_CAP_MEDIATED_VPMU 0x0800
+#define PERF_PMU_CAP_SIMD_REGS 0x1000
/**
* pmu::scope
@@ -1534,6 +1535,17 @@ perf_event__output_id_sample(struct perf_event *event,
extern void
perf_log_lost_samples(struct perf_event *event, u64 lost);
+static inline bool event_has_simd_regs(struct perf_event *event)
+{
+ struct perf_event_attr *attr = &event->attr;
+
+ if (!(event->attr.sample_type &
+ (PERF_SAMPLE_REGS_INTR | PERF_SAMPLE_REGS_USER)))
+ return false;
+
+ return attr->sample_simd_regs_enabled != 0;
+}
+
static inline bool event_has_extended_regs(struct perf_event *event)
{
struct perf_event_attr *attr = &event->attr;
diff --git a/include/linux/perf_regs.h b/include/linux/perf_regs.h
index 144bcc3ff19f..52eddbcdbf4e 100644
--- a/include/linux/perf_regs.h
+++ b/include/linux/perf_regs.h
@@ -14,6 +14,12 @@ int perf_reg_validate(u64 mask);
u64 perf_reg_abi(struct task_struct *task);
void perf_get_regs_user(struct perf_regs *regs_user,
struct pt_regs *regs);
+int perf_simd_reg_validate(u64 sample_type, u16 simd_enabled,
+ u16 vec_qwords, u64 vec_mask_intr,
+ u64 vec_mask_user, u16 pred_qwords,
+ u32 pred_mask_intr, u32 pred_mask_user);
+u64 perf_simd_reg_value(struct pt_regs *regs, int idx,
+ u16 qwords_idx, bool pred);
#ifdef CONFIG_HAVE_PERF_REGS
#include <asm/perf_regs.h>
diff --git a/include/uapi/linux/perf_event.h b/include/uapi/linux/perf_event.h
index fd10aa8d697f..c49fc76292f7 100644
--- a/include/uapi/linux/perf_event.h
+++ b/include/uapi/linux/perf_event.h
@@ -314,8 +314,9 @@ enum {
*/
enum perf_sample_regs_abi {
PERF_SAMPLE_REGS_ABI_NONE = 0,
- PERF_SAMPLE_REGS_ABI_32 = 1,
- PERF_SAMPLE_REGS_ABI_64 = 2,
+ PERF_SAMPLE_REGS_ABI_32 = (1 << 0),
+ PERF_SAMPLE_REGS_ABI_64 = (1 << 1),
+ PERF_SAMPLE_REGS_ABI_SIMD = (1 << 2),
};
/*
@@ -383,6 +384,7 @@ enum perf_event_read_format {
#define PERF_ATTR_SIZE_VER7 128 /* Add: sig_data */
#define PERF_ATTR_SIZE_VER8 136 /* Add: config3 */
#define PERF_ATTR_SIZE_VER9 144 /* add: config4 */
+#define PERF_ATTR_SIZE_VER10 176 /* Add: sample_simd_{vec|pred}_reg_* */
/*
* 'struct perf_event_attr' contains various attributes that define
@@ -547,6 +549,29 @@ struct perf_event_attr {
__u64 config3; /* extension of config2 */
__u64 config4; /* extension of config3 */
+
+ /*
+ * Defines the sampling SIMD/PRED(predicate) registers bitmap and
+ * qwords (8 bytes) length.
+ *
+ * sample_simd_regs_enabled != 0 indicates there are SIMD/PRED
+ * registers to be sampled, the SIMD/PRED registers bitmap and
+ * qwords length are represented in
+ * sample_simd_{vec|pred}_reg_{intr|user} and
+ * sample_simd_{vec|pred}_reg_qwords fields separately.
+ *
+ * sample_simd_regs_enabled == 0 indicates no SIMD/PRED registers
+ * are sampled.
+ */
+ __u16 sample_simd_regs_enabled;
+ __u16 sample_simd_pred_reg_qwords;
+ __u16 sample_simd_vec_reg_qwords;
+ __u16 __reserved_4;
+
+ __u32 sample_simd_pred_reg_intr;
+ __u32 sample_simd_pred_reg_user;
+ __u64 sample_simd_vec_reg_intr;
+ __u64 sample_simd_vec_reg_user;
};
/*
@@ -1020,7 +1045,15 @@ enum perf_event_type {
* } && PERF_SAMPLE_BRANCH_STACK
*
* { u64 abi; # enum perf_sample_regs_abi
- * u64 regs[weight(mask)]; } && PERF_SAMPLE_REGS_USER
+ * u64 regs[weight(mask)];
+ * struct {
+ * u64 nr_vectors; # 0 ... weight(sample_simd_vec_reg_user)
+ * u64 vector_qwords; # 0 ... sample_simd_vec_reg_qwords
+ * u64 nr_pred; # 0 ... weight(sample_simd_pred_reg_user)
+ * u64 pred_qwords; # 0 ... sample_simd_pred_reg_qwords
+ * u64 data[nr_vectors * vector_qwords + nr_pred * pred_qwords];
+ * } && (abi & PERF_SAMPLE_REGS_ABI_SIMD)
+ * } && PERF_SAMPLE_REGS_USER
*
* { u64 size;
* char data[size];
@@ -1047,7 +1080,15 @@ enum perf_event_type {
* { u64 data_src; } && PERF_SAMPLE_DATA_SRC
* { u64 transaction; } && PERF_SAMPLE_TRANSACTION
* { u64 abi; # enum perf_sample_regs_abi
- * u64 regs[weight(mask)]; } && PERF_SAMPLE_REGS_INTR
+ * u64 regs[weight(mask)];
+ * struct {
+ * u64 nr_vectors; # 0 ... weight(sample_simd_vec_reg_intr)
+ * u64 vector_qwords; # 0 ... sample_simd_vec_reg_qwords
+ * u64 nr_pred; # 0 ... weight(sample_simd_pred_reg_intr)
+ * u64 pred_qwords; # 0 ... sample_simd_pred_reg_qwords
+ * u64 data[nr_vectors * vector_qwords + nr_pred * pred_qwords];
+ * } && (abi & PERF_SAMPLE_REGS_ABI_SIMD)
+ * } && PERF_SAMPLE_REGS_INTR
* { u64 phys_addr;} && PERF_SAMPLE_PHYS_ADDR
* { u64 cgroup;} && PERF_SAMPLE_CGROUP
* { u64 data_page_size;} && PERF_SAMPLE_DATA_PAGE_SIZE
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 0239864029be..ce93c23b3a33 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -7829,22 +7829,60 @@ void __weak perf_get_regs_user(struct perf_regs *regs_user,
regs_user->abi = perf_reg_abi(current);
}
+#define word_for_each_set_bit(bit, val) \
+ for (unsigned long long __v = (val); \
+ __v && ((bit = __builtin_ctzll(__v)), 1); \
+ __v &= __v - 1)
+
static void
perf_output_sample_regs(struct perf_output_handle *handle,
struct pt_regs *regs, u64 mask)
{
int bit;
- DECLARE_BITMAP(_mask, 64);
-
- bitmap_from_u64(_mask, mask);
- for_each_set_bit(bit, _mask, sizeof(mask) * BITS_PER_BYTE) {
- u64 val;
- val = perf_reg_value(regs, bit);
+ word_for_each_set_bit(bit, mask) {
+ u64 val = perf_reg_value(regs, bit);
perf_output_put(handle, val);
}
}
+static void
+perf_output_sample_simd_regs(struct perf_output_handle *handle,
+ struct perf_event *event,
+ struct pt_regs *regs,
+ u64 mask, u32 pred_mask)
+{
+ u64 pred_qwords = event->attr.sample_simd_pred_reg_qwords;
+ u64 vec_qwords = event->attr.sample_simd_vec_reg_qwords;
+ u64 nr_vectors = hweight64(mask);
+ u64 nr_pred = hweight32(pred_mask);
+ int bit;
+
+ perf_output_put(handle, nr_vectors);
+ perf_output_put(handle, vec_qwords);
+ perf_output_put(handle, nr_pred);
+ perf_output_put(handle, pred_qwords);
+
+ if (nr_vectors) {
+ word_for_each_set_bit(bit, mask) {
+ for (int i = 0; i < vec_qwords; i++) {
+ u64 val = perf_simd_reg_value(regs, bit,
+ i, false);
+ perf_output_put(handle, val);
+ }
+ }
+ }
+ if (nr_pred) {
+ word_for_each_set_bit(bit, pred_mask) {
+ for (int i = 0; i < pred_qwords; i++) {
+ u64 val = perf_simd_reg_value(regs, bit,
+ i, true);
+ perf_output_put(handle, val);
+ }
+ }
+ }
+}
+
static void perf_sample_regs_user(struct perf_regs *regs_user,
struct pt_regs *regs)
{
@@ -7878,6 +7916,26 @@ static void perf_sample_regs_intr(struct perf_regs *regs_intr,
}
}
+int __weak perf_simd_reg_validate(u64 sample_type, u16 simd_enabled,
+ u16 vec_qwords, u64 vec_mask_intr,
+ u64 vec_mask_user, u16 pred_qwords,
+ u32 pred_mask_intr, u32 pred_mask_user)
+{
+ if (!(sample_type &
+ (PERF_SAMPLE_REGS_INTR | PERF_SAMPLE_REGS_USER)))
+ return 0;
+
+ if (!simd_enabled)
+ return 0;
+
+ return -EINVAL;
+}
+
+u64 __weak perf_simd_reg_value(struct pt_regs *regs, int idx,
+ u16 qwords_idx, bool pred)
+{
+ return 0;
+}
/*
* Get remaining task size from user stack pointer.
@@ -8408,10 +8466,17 @@ void perf_output_sample(struct perf_output_handle *handle,
perf_output_put(handle, abi);
if (abi) {
- u64 mask = event->attr.sample_regs_user;
+ struct perf_event_attr *attr = &event->attr;
+ u64 mask = attr->sample_regs_user;
perf_output_sample_regs(handle,
data->regs_user.regs,
mask);
+ if (abi & PERF_SAMPLE_REGS_ABI_SIMD) {
+ perf_output_sample_simd_regs(handle, event,
+ data->regs_user.regs,
+ attr->sample_simd_vec_reg_user,
+ attr->sample_simd_pred_reg_user);
+ }
}
}
@@ -8439,11 +8504,18 @@ void perf_output_sample(struct perf_output_handle *handle,
perf_output_put(handle, abi);
if (abi) {
- u64 mask = event->attr.sample_regs_intr;
+ struct perf_event_attr *attr = &event->attr;
+ u64 mask = attr->sample_regs_intr;
perf_output_sample_regs(handle,
data->regs_intr.regs,
mask);
+ if (abi & PERF_SAMPLE_REGS_ABI_SIMD) {
+ perf_output_sample_simd_regs(handle, event,
+ data->regs_intr.regs,
+ attr->sample_simd_vec_reg_intr,
+ attr->sample_simd_pred_reg_intr);
+ }
}
}
@@ -8646,6 +8718,33 @@ static __always_inline u64 __cond_set(u64 flags, u64 s, u64 d)
return d * !!(flags & s);
}
+static u64 perf_update_xregs_size(struct perf_event *event, bool intr)
+{
+ u16 pred_qwords = event->attr.sample_simd_pred_reg_qwords;
+ u16 vec_qwords = event->attr.sample_simd_vec_reg_qwords;
+ u64 pred_mask;
+ u64 mask;
+ int size;
+
+ if (intr) {
+ mask = event->attr.sample_simd_vec_reg_intr;
+ pred_mask = event->attr.sample_simd_pred_reg_intr;
+ } else {
+ mask = event->attr.sample_simd_vec_reg_user;
+ pred_mask = event->attr.sample_simd_pred_reg_user;
+ }
+
+ /* nr_vectors, vector_qwords, nr_pred, pred_qwords */
+ size = sizeof(u64) * 4;
+ size += (hweight64(mask) * vec_qwords +
+ hweight64(pred_mask) * pred_qwords) * sizeof(u64);
+
+ /* Warn if exceeding perf_event_header.size (u16). */
+ WARN_ON_ONCE(size > U16_MAX);
+
+ return size;
+}
+
void perf_prepare_sample(struct perf_sample_data *data,
struct perf_event *event,
struct pt_regs *regs)
@@ -8708,7 +8807,12 @@ void perf_prepare_sample(struct perf_sample_data *data,
if (data->regs_user.regs) {
u64 mask = event->attr.sample_regs_user;
+
size += hweight64(mask) * sizeof(u64);
+ if (event_has_simd_regs(event)) {
+ size += perf_update_xregs_size(event, false);
+ data->regs_user.abi |= PERF_SAMPLE_REGS_ABI_SIMD;
+ }
}
data->dyn_size += size;
@@ -8773,6 +8877,10 @@ void perf_prepare_sample(struct perf_sample_data *data,
u64 mask = event->attr.sample_regs_intr;
size += hweight64(mask) * sizeof(u64);
+ if (event_has_simd_regs(event)) {
+ size += perf_update_xregs_size(event, true);
+ data->regs_intr.abi |= PERF_SAMPLE_REGS_ABI_SIMD;
+ }
}
data->dyn_size += size;
@@ -13149,6 +13257,12 @@ static int perf_try_init_event(struct pmu *pmu, struct perf_event *event)
if (ret)
goto err_pmu;
+ if (!(pmu->capabilities & PERF_PMU_CAP_SIMD_REGS) &&
+ event_has_simd_regs(event)) {
+ ret = -EOPNOTSUPP;
+ goto err_destroy;
+ }
+
if (!(pmu->capabilities & PERF_PMU_CAP_EXTENDED_REGS) &&
event_has_extended_regs(event)) {
ret = -EOPNOTSUPP;
@@ -13645,7 +13759,8 @@ static int perf_copy_attr(struct perf_event_attr __user *uattr,
attr->size = size;
- if (attr->__reserved_1 || attr->__reserved_2 || attr->__reserved_3)
+ if (attr->__reserved_1 || attr->__reserved_2 ||
+ attr->__reserved_3 || attr->__reserved_4)
return -EINVAL;
if (attr->sample_type & ~(PERF_SAMPLE_MAX-1))
@@ -13694,6 +13809,16 @@ static int perf_copy_attr(struct perf_event_attr __user *uattr,
ret = perf_reg_validate(attr->sample_regs_user);
if (ret)
return ret;
+ ret = perf_simd_reg_validate(attr->sample_type,
+ attr->sample_simd_regs_enabled,
+ attr->sample_simd_vec_reg_qwords,
+ attr->sample_simd_vec_reg_intr,
+ attr->sample_simd_vec_reg_user,
+ attr->sample_simd_pred_reg_qwords,
+ attr->sample_simd_pred_reg_intr,
+ attr->sample_simd_pred_reg_user);
+ if (ret)
+ return ret;
}
if (attr->sample_type & PERF_SAMPLE_STACK_USER) {
@@ -13714,8 +13839,21 @@ static int perf_copy_attr(struct perf_event_attr __user *uattr,
if (!attr->sample_max_stack)
attr->sample_max_stack = sysctl_perf_event_max_stack;
- if (attr->sample_type & PERF_SAMPLE_REGS_INTR)
+ if (attr->sample_type & PERF_SAMPLE_REGS_INTR) {
ret = perf_reg_validate(attr->sample_regs_intr);
+ if (ret)
+ return ret;
+ ret = perf_simd_reg_validate(attr->sample_type,
+ attr->sample_simd_regs_enabled,
+ attr->sample_simd_vec_reg_qwords,
+ attr->sample_simd_vec_reg_intr,
+ attr->sample_simd_vec_reg_user,
+ attr->sample_simd_pred_reg_qwords,
+ attr->sample_simd_pred_reg_intr,
+ attr->sample_simd_pred_reg_user);
+ if (ret)
+ return ret;
+ }
#ifndef CONFIG_CGROUP_PERF
if (attr->sample_type & PERF_SAMPLE_CGROUP)
--
2.34.1
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [Patch v9 14/24] perf/x86: Support XMM sampling using sample_simd_vec_reg_* fields
2026-07-06 1:54 [Patch v9 00/24] Support SIMD/eGPRs/SSP registers sampling for perf Dapeng Mi
` (12 preceding siblings ...)
2026-07-06 1:54 ` [Patch v9 13/24] perf: Add sampling support for SIMD registers Dapeng Mi
@ 2026-07-06 1:54 ` Dapeng Mi
2026-07-06 6:45 ` sashiko-bot
2026-07-06 1:54 ` [Patch v9 15/24] perf/x86: Support YMM " Dapeng Mi
` (9 subsequent siblings)
23 siblings, 1 reply; 42+ messages in thread
From: Dapeng Mi @ 2026-07-06 1:54 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Thomas Gleixner, Dave Hansen, Ian Rogers,
Adrian Hunter, Jiri Olsa, Alexander Shishkin, Andi Kleen,
Eranian Stephane
Cc: Mark Rutland, broonie, Ravi Bangoria, linux-kernel,
linux-perf-users, Zide Chen, Falcon Thomas, Dapeng Mi, Xudong Hao,
Dapeng Mi, Kan Liang
Support sampling of XMM registers using the sample_simd_vec_reg_* fields.
When sample_simd_regs_enabled is set, the original XMM space in the
sample_regs_* field is treated as reserved. An INVAL error will be
reported to user space if any bit is set in the original XMM space while
sample_simd_regs_enabled is set.
The perf_reg_value function requires ABI information to understand the
layout of sample_regs. To accommodate this, a new abi field is introduced
in the struct x86_perf_regs to represent ABI information.
Additionally, the X86-specific perf_simd_reg_value function is implemented
to retrieve the XMM register values.
XMM sampling will be enabled in a subsequent patch that sets
PERF_PMU_CAP_SIMD_REGS.
Co-developed-by: Kan Liang <kan.liang@linux.intel.com>
Signed-off-by: Kan Liang <kan.liang@linux.intel.com>
Signed-off-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
---
arch/x86/events/core.c | 50 +++++++++++++++--
arch/x86/events/intel/ds.c | 2 +-
arch/x86/events/perf_event.h | 16 ++++++
arch/x86/include/asm/perf_event.h | 1 +
arch/x86/include/uapi/asm/perf_regs.h | 15 +++++
arch/x86/kernel/perf_regs.c | 80 ++++++++++++++++++++++++++-
include/linux/perf_event.h | 1 +
kernel/events/core.c | 2 +-
8 files changed, 158 insertions(+), 9 deletions(-)
diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c
index 0d42c51761f9..117d09fb9a05 100644
--- a/arch/x86/events/core.c
+++ b/arch/x86/events/core.c
@@ -715,6 +715,17 @@ int x86_pmu_hw_config(struct perf_event *event)
if (event_has_extended_regs(event)) {
if (!(event->pmu->capabilities & PERF_PMU_CAP_EXTENDED_REGS))
return -EINVAL;
+ if (event->attr.sample_simd_regs_enabled)
+ return -EINVAL;
+ }
+
+ if (event_has_simd_regs(event)) {
+ if (!(event->pmu->capabilities & PERF_PMU_CAP_SIMD_REGS))
+ return -EINVAL;
+ /* The vector registers set is not supported */
+ if (event_needs_xmm(event) &&
+ !(x86_pmu.ext_regs_mask & XFEATURE_MASK_SSE))
+ return -EINVAL;
}
}
@@ -1757,6 +1768,7 @@ void x86_pmu_clear_perf_regs(struct pt_regs *regs)
{
struct x86_perf_regs *perf_regs = container_of(regs, struct x86_perf_regs, regs);
+ perf_regs->abi = PERF_SAMPLE_REGS_ABI_NONE;
perf_regs->xmm_regs = NULL;
}
@@ -1777,14 +1789,15 @@ static void update_perf_regs(struct x86_perf_regs *perf_regs,
/*
* The x86 specific variant of perf_sample_regs_intr().
- * It would be extended to add more SIMD registers sampling support
- * in later patches.
+ * Update data->regs_intr fields for extended registers (e.g., SIMD).
*/
static void x86_pmu_update_regs_intr(struct perf_event *event,
struct perf_sample_data *data,
struct pt_regs *regs,
bool exclude_kernel)
{
+ struct x86_perf_regs *perf_regs;
+
if (exclude_kernel && !user_mode(regs)) {
data->regs_intr.regs = NULL;
data->regs_intr.abi = PERF_SAMPLE_REGS_ABI_NONE;
@@ -1797,6 +1810,16 @@ static void x86_pmu_update_regs_intr(struct perf_event *event,
if (data->regs_intr.regs) {
data->dyn_size += hweight64(event->attr.sample_regs_intr) *
sizeof(u64);
+ if (event_has_simd_regs(event)) {
+ data->dyn_size += perf_update_xregs_size(event, true);
+ data->regs_intr.abi |= PERF_SAMPLE_REGS_ABI_SIMD;
+ }
+ }
+
+ if (data->regs_intr.abi) {
+ perf_regs = container_of(data->regs_intr.regs,
+ struct x86_perf_regs, regs);
+ perf_regs->abi = data->regs_intr.abi;
}
/*
@@ -1839,6 +1862,7 @@ static void x86_pmu_update_regs_user(struct perf_event *event,
struct pt_regs *regs)
{
struct perf_event_attr *attr = &event->attr;
+ struct x86_perf_regs *perf_regs;
if (user_mode(regs)) {
data->regs_user.abi = perf_reg_abi(current);
@@ -1858,8 +1882,19 @@ static void x86_pmu_update_regs_user(struct perf_event *event,
}
data->dyn_size += sizeof(u64);
- if (data->regs_user.regs)
+ if (data->regs_user.regs) {
data->dyn_size += hweight64(attr->sample_regs_user) * sizeof(u64);
+ if (event_has_simd_regs(event)) {
+ data->dyn_size += perf_update_xregs_size(event, false);
+ data->regs_user.abi |= PERF_SAMPLE_REGS_ABI_SIMD;
+ }
+ }
+
+ if (data->regs_user.abi) {
+ perf_regs = container_of(data->regs_user.regs,
+ struct x86_perf_regs, regs);
+ perf_regs->abi = data->regs_user.abi;
+ }
/*
* Set PERF_SAMPLE_REGS_USER to bypass perf_sample_regs_user() call
@@ -1939,7 +1974,7 @@ static void x86_pmu_sample_xregs(struct perf_event *event,
if (WARN_ON_ONCE(!xsave) || !in_nmi())
return;
- if (event_has_extended_regs(event))
+ if (event_needs_xmm(event))
mask |= XFEATURE_MASK_SSE;
mask &= x86_pmu.ext_regs_mask;
@@ -1976,7 +2011,12 @@ void x86_pmu_update_perf_regs(struct perf_event *event,
{
u64 sample_type = event->attr.sample_type;
- if (!event_has_extended_regs(event))
+ if (!(sample_type &
+ (PERF_SAMPLE_REGS_INTR | PERF_SAMPLE_REGS_USER)))
+ return;
+
+ if (!event_needs_xmm(event) &&
+ !event_has_simd_regs(event))
return;
if (sample_type & PERF_SAMPLE_REGS_INTR) {
diff --git a/arch/x86/events/intel/ds.c b/arch/x86/events/intel/ds.c
index 2f5e732d2c95..24bfc3fb6060 100644
--- a/arch/x86/events/intel/ds.c
+++ b/arch/x86/events/intel/ds.c
@@ -1733,7 +1733,7 @@ static u64 pebs_update_adaptive_cfg(struct perf_event *event)
if (gprs || (attr->precise_ip < 2) || tsx_weight)
pebs_data_cfg |= PEBS_DATACFG_GP;
- if (event_has_extended_regs(event))
+ if (event_needs_xmm(event))
pebs_data_cfg |= PEBS_DATACFG_XMMS;
if (sample_type & PERF_SAMPLE_BRANCH_STACK) {
diff --git a/arch/x86/events/perf_event.h b/arch/x86/events/perf_event.h
index 70fa7ec21673..6b1b83c906eb 100644
--- a/arch/x86/events/perf_event.h
+++ b/arch/x86/events/perf_event.h
@@ -147,6 +147,22 @@ static inline bool is_acr_self_reload_event(struct perf_event *event)
return test_bit(hwc->idx, (unsigned long *)&hwc->config1);
}
+static inline bool event_needs_xmm(struct perf_event *event)
+{
+ if (!(event->attr.sample_type &
+ (PERF_SAMPLE_REGS_INTR | PERF_SAMPLE_REGS_USER)))
+ return false;
+
+ if (event->attr.sample_simd_regs_enabled &&
+ event->attr.sample_simd_vec_reg_qwords >= PERF_X86_XMM_QWORDS)
+ return true;
+
+ if (!event->attr.sample_simd_regs_enabled &&
+ event_has_extended_regs(event))
+ return true;
+ return false;
+}
+
struct amd_nb {
int nb_id; /* NorthBridge id */
int refcnt; /* reference count */
diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h
index 619e0ae915e1..a2b2123d008e 100644
--- a/arch/x86/include/asm/perf_event.h
+++ b/arch/x86/include/asm/perf_event.h
@@ -728,6 +728,7 @@ extern void perf_events_lapic_init(void);
struct pt_regs;
struct x86_perf_regs {
struct pt_regs regs;
+ u64 abi;
union {
u64 *xmm_regs;
u32 *xmm_space; /* for xsaves */
diff --git a/arch/x86/include/uapi/asm/perf_regs.h b/arch/x86/include/uapi/asm/perf_regs.h
index 7c9d2bb3833b..edb35408e4cc 100644
--- a/arch/x86/include/uapi/asm/perf_regs.h
+++ b/arch/x86/include/uapi/asm/perf_regs.h
@@ -2,6 +2,8 @@
#ifndef _ASM_X86_PERF_REGS_H
#define _ASM_X86_PERF_REGS_H
+#include <linux/bits.h>
+
enum perf_event_x86_regs {
PERF_REG_X86_AX,
PERF_REG_X86_BX,
@@ -55,4 +57,17 @@ enum perf_event_x86_regs {
#define PERF_REG_EXTENDED_MASK (~((1ULL << PERF_REG_X86_XMM0) - 1))
+enum {
+ PERF_X86_SIMD_XMM_REGS = 16,
+ PERF_X86_SIMD_VEC_REGS_MAX = PERF_X86_SIMD_XMM_REGS,
+};
+
+#define PERF_X86_SIMD_VEC_MASK __GENMASK_ULL(PERF_X86_SIMD_VEC_REGS_MAX - 1, 0)
+
+enum {
+ /* 1 qword = 8 bytes */
+ PERF_X86_XMM_QWORDS = 2,
+ PERF_X86_SIMD_QWORDS_MAX = PERF_X86_XMM_QWORDS,
+};
+
#endif /* _ASM_X86_PERF_REGS_H */
diff --git a/arch/x86/kernel/perf_regs.c b/arch/x86/kernel/perf_regs.c
index 81204cb7f723..8514baefb400 100644
--- a/arch/x86/kernel/perf_regs.c
+++ b/arch/x86/kernel/perf_regs.c
@@ -63,6 +63,9 @@ u64 perf_reg_value(struct pt_regs *regs, int idx)
if (idx >= PERF_REG_X86_XMM0 && idx < PERF_REG_X86_XMM_MAX) {
perf_regs = container_of(regs, struct x86_perf_regs, regs);
+ /* SIMD registers are moved to dedicated sample_simd_vec_reg */
+ if (perf_regs->abi & PERF_SAMPLE_REGS_ABI_SIMD)
+ return 0;
if (!perf_regs->xmm_regs)
return 0;
return perf_regs->xmm_regs[idx - PERF_REG_X86_XMM0];
@@ -74,6 +77,77 @@ u64 perf_reg_value(struct pt_regs *regs, int idx)
return regs_get_register(regs, pt_regs_offset[idx]);
}
+u64 perf_simd_reg_value(struct pt_regs *regs, int idx,
+ u16 qwords_idx, bool pred)
+{
+ struct x86_perf_regs *perf_regs =
+ container_of(regs, struct x86_perf_regs, regs);
+
+ if (!(perf_regs->abi & PERF_SAMPLE_REGS_ABI_SIMD))
+ return 0;
+
+ if (pred)
+ return 0;
+
+ if (WARN_ON_ONCE(idx >= PERF_X86_SIMD_VEC_REGS_MAX ||
+ qwords_idx >= PERF_X86_SIMD_QWORDS_MAX))
+ return 0;
+
+ if (qwords_idx < PERF_X86_XMM_QWORDS) {
+ if (!perf_regs->xmm_regs)
+ return 0;
+ return perf_regs->xmm_regs[idx * PERF_X86_XMM_QWORDS +
+ qwords_idx];
+ }
+
+ return 0;
+}
+
+int perf_simd_reg_validate(u64 sample_type, u16 simd_enabled,
+ u16 vec_qwords, u64 vec_mask_intr,
+ u64 vec_mask_user, u16 pred_qwords,
+ u32 pred_mask_intr, u32 pred_mask_user)
+{
+ u64 size = 0;
+
+ if (!(sample_type &
+ (PERF_SAMPLE_REGS_INTR | PERF_SAMPLE_REGS_USER)))
+ return 0;
+
+ if (!simd_enabled)
+ return 0;
+
+ if (vec_qwords) {
+ if (vec_qwords != PERF_X86_XMM_QWORDS)
+ return -EINVAL;
+ if (!vec_mask_intr && !vec_mask_user)
+ return -EINVAL;
+ if ((sample_type & PERF_SAMPLE_REGS_INTR) &&
+ (vec_mask_intr & ~PERF_X86_SIMD_VEC_MASK))
+ return -EINVAL;
+ if ((sample_type & PERF_SAMPLE_REGS_USER) &&
+ (vec_mask_user & ~PERF_X86_SIMD_VEC_MASK))
+ return -EINVAL;
+ }
+
+ if (pred_qwords || pred_mask_intr || pred_mask_user)
+ return -EINVAL;
+
+ if (sample_type & PERF_SAMPLE_REGS_INTR) {
+ size = (vec_qwords * hweight64(vec_mask_intr) +
+ pred_qwords * hweight32(pred_mask_intr)) * sizeof(u64);
+ }
+ if (sample_type & PERF_SAMPLE_REGS_USER) {
+ size += (vec_qwords * hweight64(vec_mask_user) +
+ pred_qwords * hweight32(pred_mask_user)) * sizeof(u64);
+ }
+
+ if (size > U16_MAX)
+ return -EINVAL;
+
+ return 0;
+}
+
#define PERF_REG_X86_RESERVED (((1ULL << PERF_REG_X86_XMM0) - 1) & \
~((1ULL << PERF_REG_X86_MAX) - 1))
@@ -89,7 +163,8 @@ u64 perf_reg_value(struct pt_regs *regs, int idx)
int perf_reg_validate(u64 mask)
{
- if (!mask || (mask & (REG_NOSUPPORT | PERF_REG_X86_RESERVED)))
+ /* The mask could be 0 if only the SIMD registers are interested */
+ if (mask & (REG_NOSUPPORT | PERF_REG_X86_RESERVED))
return -EINVAL;
return 0;
@@ -108,7 +183,8 @@ u64 perf_reg_abi(struct task_struct *task)
int perf_reg_validate(u64 mask)
{
- if (!mask || (mask & (REG_NOSUPPORT | PERF_REG_X86_RESERVED)))
+ /* The mask could be 0 if only the SIMD registers are interested */
+ if (mask & (REG_NOSUPPORT | PERF_REG_X86_RESERVED))
return -EINVAL;
return 0;
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h
index c4e330c121d2..e40d60ccab9e 100644
--- a/include/linux/perf_event.h
+++ b/include/linux/perf_event.h
@@ -1485,6 +1485,7 @@ static inline void perf_clear_branch_entry_bitfields(struct perf_branch_entry *b
br->reserved = 0;
}
+extern u64 perf_update_xregs_size(struct perf_event *event, bool intr);
extern void perf_output_sample(struct perf_output_handle *handle,
struct perf_event_header *header,
struct perf_sample_data *data,
diff --git a/kernel/events/core.c b/kernel/events/core.c
index ce93c23b3a33..2ce8c3cd6824 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -8718,7 +8718,7 @@ static __always_inline u64 __cond_set(u64 flags, u64 s, u64 d)
return d * !!(flags & s);
}
-static u64 perf_update_xregs_size(struct perf_event *event, bool intr)
+u64 perf_update_xregs_size(struct perf_event *event, bool intr)
{
u16 pred_qwords = event->attr.sample_simd_pred_reg_qwords;
u16 vec_qwords = event->attr.sample_simd_vec_reg_qwords;
--
2.34.1
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [Patch v9 15/24] perf/x86: Support YMM sampling using sample_simd_vec_reg_* fields
2026-07-06 1:54 [Patch v9 00/24] Support SIMD/eGPRs/SSP registers sampling for perf Dapeng Mi
` (13 preceding siblings ...)
2026-07-06 1:54 ` [Patch v9 14/24] perf/x86: Support XMM sampling using sample_simd_vec_reg_* fields Dapeng Mi
@ 2026-07-06 1:54 ` Dapeng Mi
2026-07-06 1:54 ` [Patch v9 16/24] perf/x86: Support ZMM " Dapeng Mi
` (8 subsequent siblings)
23 siblings, 0 replies; 42+ messages in thread
From: Dapeng Mi @ 2026-07-06 1:54 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Thomas Gleixner, Dave Hansen, Ian Rogers,
Adrian Hunter, Jiri Olsa, Alexander Shishkin, Andi Kleen,
Eranian Stephane
Cc: Mark Rutland, broonie, Ravi Bangoria, linux-kernel,
linux-perf-users, Zide Chen, Falcon Thomas, Dapeng Mi, Xudong Hao,
Dapeng Mi, Kan Liang
Support sampling of YMM registers via the sample_simd_vec_reg_* fields.
Each YMM register consists of 4 u64 words, assembled from two halves:
XMM (the lower 2 u64 words) and YMMH (the upper 2 u64 words). Although
both XMM and YMMH data can be retrieved with a single xsaves instruction,
they are stored in separate locations. The perf_simd_reg_value() function
is responsible for assembling these halves into a complete YMM register
for output to userspace.
Additionally, sample_simd_vec_reg_qwords should be set to 4 to indicate
YMM sampling.
YMM sampling will be enabled in a subsequent patch that sets
PERF_PMU_CAP_SIMD_REGS.
Co-developed-by: Kan Liang <kan.liang@linux.intel.com>
Signed-off-by: Kan Liang <kan.liang@linux.intel.com>
Signed-off-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
---
arch/x86/events/core.c | 8 ++++++++
arch/x86/events/perf_event.h | 13 +++++++++++++
arch/x86/include/asm/perf_event.h | 4 ++++
arch/x86/include/uapi/asm/perf_regs.h | 6 ++++--
arch/x86/kernel/perf_regs.c | 10 +++++++++-
5 files changed, 38 insertions(+), 3 deletions(-)
diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c
index 117d09fb9a05..10d90050def3 100644
--- a/arch/x86/events/core.c
+++ b/arch/x86/events/core.c
@@ -726,6 +726,9 @@ int x86_pmu_hw_config(struct perf_event *event)
if (event_needs_xmm(event) &&
!(x86_pmu.ext_regs_mask & XFEATURE_MASK_SSE))
return -EINVAL;
+ if (event_needs_ymm(event) &&
+ !(x86_pmu.ext_regs_mask & XFEATURE_MASK_YMM))
+ return -EINVAL;
}
}
@@ -1770,6 +1773,7 @@ void x86_pmu_clear_perf_regs(struct pt_regs *regs)
perf_regs->abi = PERF_SAMPLE_REGS_ABI_NONE;
perf_regs->xmm_regs = NULL;
+ perf_regs->ymmh_regs = NULL;
}
static void update_perf_regs(struct x86_perf_regs *perf_regs,
@@ -1785,6 +1789,8 @@ static void update_perf_regs(struct x86_perf_regs *perf_regs,
if (mask & XFEATURE_MASK_SSE)
perf_regs->xmm_space = xsave->i387.xmm_space;
+ if (mask & XFEATURE_MASK_YMM)
+ perf_regs->ymmh = get_xsave_addr(xsave, XFEATURE_YMM);
}
/*
@@ -1976,6 +1982,8 @@ static void x86_pmu_sample_xregs(struct perf_event *event,
if (event_needs_xmm(event))
mask |= XFEATURE_MASK_SSE;
+ if (event_needs_ymm(event))
+ mask |= XFEATURE_MASK_YMM;
mask &= x86_pmu.ext_regs_mask;
if ((sample_type & PERF_SAMPLE_REGS_USER) && data->regs_user.regs) {
diff --git a/arch/x86/events/perf_event.h b/arch/x86/events/perf_event.h
index 6b1b83c906eb..13f1f34ff8b0 100644
--- a/arch/x86/events/perf_event.h
+++ b/arch/x86/events/perf_event.h
@@ -163,6 +163,19 @@ static inline bool event_needs_xmm(struct perf_event *event)
return false;
}
+static inline bool event_needs_ymm(struct perf_event *event)
+{
+ if (!(event->attr.sample_type &
+ (PERF_SAMPLE_REGS_INTR | PERF_SAMPLE_REGS_USER)))
+ return false;
+
+ if (event->attr.sample_simd_regs_enabled &&
+ event->attr.sample_simd_vec_reg_qwords >= PERF_X86_YMM_QWORDS)
+ return true;
+
+ return false;
+}
+
struct amd_nb {
int nb_id; /* NorthBridge id */
int refcnt; /* reference count */
diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h
index a2b2123d008e..da77845e1f02 100644
--- a/arch/x86/include/asm/perf_event.h
+++ b/arch/x86/include/asm/perf_event.h
@@ -733,6 +733,10 @@ struct x86_perf_regs {
u64 *xmm_regs;
u32 *xmm_space; /* for xsaves */
};
+ union {
+ u64 *ymmh_regs;
+ struct ymmh_struct *ymmh;
+ };
};
extern unsigned long perf_arch_instruction_pointer(struct pt_regs *regs);
diff --git a/arch/x86/include/uapi/asm/perf_regs.h b/arch/x86/include/uapi/asm/perf_regs.h
index edb35408e4cc..d544f6d79871 100644
--- a/arch/x86/include/uapi/asm/perf_regs.h
+++ b/arch/x86/include/uapi/asm/perf_regs.h
@@ -59,7 +59,8 @@ enum perf_event_x86_regs {
enum {
PERF_X86_SIMD_XMM_REGS = 16,
- PERF_X86_SIMD_VEC_REGS_MAX = PERF_X86_SIMD_XMM_REGS,
+ PERF_X86_SIMD_YMM_REGS = 16,
+ PERF_X86_SIMD_VEC_REGS_MAX = PERF_X86_SIMD_YMM_REGS,
};
#define PERF_X86_SIMD_VEC_MASK __GENMASK_ULL(PERF_X86_SIMD_VEC_REGS_MAX - 1, 0)
@@ -67,7 +68,8 @@ enum {
enum {
/* 1 qword = 8 bytes */
PERF_X86_XMM_QWORDS = 2,
- PERF_X86_SIMD_QWORDS_MAX = PERF_X86_XMM_QWORDS,
+ PERF_X86_YMM_QWORDS = 4,
+ PERF_X86_SIMD_QWORDS_MAX = PERF_X86_YMM_QWORDS,
};
#endif /* _ASM_X86_PERF_REGS_H */
diff --git a/arch/x86/kernel/perf_regs.c b/arch/x86/kernel/perf_regs.c
index 8514baefb400..316d18c13c02 100644
--- a/arch/x86/kernel/perf_regs.c
+++ b/arch/x86/kernel/perf_regs.c
@@ -77,6 +77,8 @@ u64 perf_reg_value(struct pt_regs *regs, int idx)
return regs_get_register(regs, pt_regs_offset[idx]);
}
+#define PERF_X86_YMMH_QWORDS (PERF_X86_YMM_QWORDS / 2)
+
u64 perf_simd_reg_value(struct pt_regs *regs, int idx,
u16 qwords_idx, bool pred)
{
@@ -98,6 +100,11 @@ u64 perf_simd_reg_value(struct pt_regs *regs, int idx,
return 0;
return perf_regs->xmm_regs[idx * PERF_X86_XMM_QWORDS +
qwords_idx];
+ } else if (qwords_idx < PERF_X86_YMM_QWORDS) {
+ if (!perf_regs->ymmh_regs)
+ return 0;
+ return perf_regs->ymmh_regs[idx * PERF_X86_YMMH_QWORDS +
+ qwords_idx - PERF_X86_XMM_QWORDS];
}
return 0;
@@ -118,7 +125,8 @@ int perf_simd_reg_validate(u64 sample_type, u16 simd_enabled,
return 0;
if (vec_qwords) {
- if (vec_qwords != PERF_X86_XMM_QWORDS)
+ if (vec_qwords != PERF_X86_XMM_QWORDS &&
+ vec_qwords != PERF_X86_YMM_QWORDS)
return -EINVAL;
if (!vec_mask_intr && !vec_mask_user)
return -EINVAL;
--
2.34.1
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [Patch v9 16/24] perf/x86: Support ZMM sampling using sample_simd_vec_reg_* fields
2026-07-06 1:54 [Patch v9 00/24] Support SIMD/eGPRs/SSP registers sampling for perf Dapeng Mi
` (14 preceding siblings ...)
2026-07-06 1:54 ` [Patch v9 15/24] perf/x86: Support YMM " Dapeng Mi
@ 2026-07-06 1:54 ` Dapeng Mi
2026-07-06 1:54 ` [Patch v9 17/24] perf/x86: Support OPMASK sampling using sample_simd_pred_reg_* fields Dapeng Mi
` (7 subsequent siblings)
23 siblings, 0 replies; 42+ messages in thread
From: Dapeng Mi @ 2026-07-06 1:54 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Thomas Gleixner, Dave Hansen, Ian Rogers,
Adrian Hunter, Jiri Olsa, Alexander Shishkin, Andi Kleen,
Eranian Stephane
Cc: Mark Rutland, broonie, Ravi Bangoria, linux-kernel,
linux-perf-users, Zide Chen, Falcon Thomas, Dapeng Mi, Xudong Hao,
Dapeng Mi, Kan Liang
Support sampling of ZMM registers via the sample_simd_vec_reg_* fields.
Each ZMM register consists of 8 u64 words. Current x86 hardware supports
up to 32 ZMM registers. For ZMM registers from ZMM0 to ZMM15, they are
assembled from three parts: XMM (the lower 2 u64 words),
YMMH (the middle 2 u64 words), and ZMMH (the upper 4 u64 words). The
perf_simd_reg_value() function is responsible for assembling these three
parts into a complete ZMM register for output to userspace.
For ZMM registers ZMM16 to ZMM31, each register can be read as a whole
and directly outputted to userspace.
Additionally, sample_simd_vec_reg_qwords should be set to 8 to indicate
ZMM sampling.
ZMM sampling will be enabled in a subsequent patch that sets
PERF_PMU_CAP_SIMD_REGS.
Co-developed-by: Kan Liang <kan.liang@linux.intel.com>
Signed-off-by: Kan Liang <kan.liang@linux.intel.com>
Signed-off-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
---
arch/x86/events/core.c | 16 +++++++++++++
arch/x86/events/perf_event.h | 33 +++++++++++++++++++++++++++
arch/x86/include/asm/perf_event.h | 8 +++++++
arch/x86/include/uapi/asm/perf_regs.h | 8 +++++--
arch/x86/kernel/perf_regs.c | 16 ++++++++++++-
5 files changed, 78 insertions(+), 3 deletions(-)
diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c
index 10d90050def3..ef0e238a4678 100644
--- a/arch/x86/events/core.c
+++ b/arch/x86/events/core.c
@@ -729,6 +729,12 @@ int x86_pmu_hw_config(struct perf_event *event)
if (event_needs_ymm(event) &&
!(x86_pmu.ext_regs_mask & XFEATURE_MASK_YMM))
return -EINVAL;
+ if (event_needs_low16_zmm(event) &&
+ !(x86_pmu.ext_regs_mask & XFEATURE_MASK_ZMM_Hi256))
+ return -EINVAL;
+ if (event_needs_high16_zmm(event) &&
+ !(x86_pmu.ext_regs_mask & XFEATURE_MASK_Hi16_ZMM))
+ return -EINVAL;
}
}
@@ -1774,6 +1780,8 @@ void x86_pmu_clear_perf_regs(struct pt_regs *regs)
perf_regs->abi = PERF_SAMPLE_REGS_ABI_NONE;
perf_regs->xmm_regs = NULL;
perf_regs->ymmh_regs = NULL;
+ perf_regs->zmmh_regs = NULL;
+ perf_regs->h16zmm_regs = NULL;
}
static void update_perf_regs(struct x86_perf_regs *perf_regs,
@@ -1791,6 +1799,10 @@ static void update_perf_regs(struct x86_perf_regs *perf_regs,
perf_regs->xmm_space = xsave->i387.xmm_space;
if (mask & XFEATURE_MASK_YMM)
perf_regs->ymmh = get_xsave_addr(xsave, XFEATURE_YMM);
+ if (mask & XFEATURE_MASK_ZMM_Hi256)
+ perf_regs->zmmh = get_xsave_addr(xsave, XFEATURE_ZMM_Hi256);
+ if (mask & XFEATURE_MASK_Hi16_ZMM)
+ perf_regs->h16zmm = get_xsave_addr(xsave, XFEATURE_Hi16_ZMM);
}
/*
@@ -1984,6 +1996,10 @@ static void x86_pmu_sample_xregs(struct perf_event *event,
mask |= XFEATURE_MASK_SSE;
if (event_needs_ymm(event))
mask |= XFEATURE_MASK_YMM;
+ if (event_needs_low16_zmm(event))
+ mask |= XFEATURE_MASK_ZMM_Hi256;
+ if (event_needs_high16_zmm(event))
+ mask |= XFEATURE_MASK_Hi16_ZMM;
mask &= x86_pmu.ext_regs_mask;
if ((sample_type & PERF_SAMPLE_REGS_USER) && data->regs_user.regs) {
diff --git a/arch/x86/events/perf_event.h b/arch/x86/events/perf_event.h
index 13f1f34ff8b0..01414b3a88fd 100644
--- a/arch/x86/events/perf_event.h
+++ b/arch/x86/events/perf_event.h
@@ -176,6 +176,39 @@ static inline bool event_needs_ymm(struct perf_event *event)
return false;
}
+static inline bool event_needs_low16_zmm(struct perf_event *event)
+{
+ if (!(event->attr.sample_type &
+ (PERF_SAMPLE_REGS_INTR | PERF_SAMPLE_REGS_USER)))
+ return false;
+
+ if (event->attr.sample_simd_regs_enabled &&
+ event->attr.sample_simd_vec_reg_qwords >= PERF_X86_ZMM_QWORDS)
+ return true;
+
+ return false;
+}
+
+static inline bool event_needs_high16_zmm(struct perf_event *event)
+{
+ if (!(event->attr.sample_type &
+ (PERF_SAMPLE_REGS_INTR | PERF_SAMPLE_REGS_USER)))
+ return false;
+
+ if (!event->attr.sample_simd_regs_enabled)
+ return false;
+
+ if ((event->attr.sample_type & PERF_SAMPLE_REGS_INTR) &&
+ (fls64(event->attr.sample_simd_vec_reg_intr) > PERF_X86_H16ZMM_BASE))
+ return true;
+
+ if ((event->attr.sample_type & PERF_SAMPLE_REGS_USER) &&
+ (fls64(event->attr.sample_simd_vec_reg_user) > PERF_X86_H16ZMM_BASE))
+ return true;
+
+ return false;
+}
+
struct amd_nb {
int nb_id; /* NorthBridge id */
int refcnt; /* reference count */
diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h
index da77845e1f02..75394c4e8bc3 100644
--- a/arch/x86/include/asm/perf_event.h
+++ b/arch/x86/include/asm/perf_event.h
@@ -737,6 +737,14 @@ struct x86_perf_regs {
u64 *ymmh_regs;
struct ymmh_struct *ymmh;
};
+ union {
+ u64 *zmmh_regs;
+ struct avx_512_zmm_uppers_state *zmmh;
+ };
+ union {
+ u64 *h16zmm_regs;
+ struct avx_512_hi16_state *h16zmm;
+ };
};
extern unsigned long perf_arch_instruction_pointer(struct pt_regs *regs);
diff --git a/arch/x86/include/uapi/asm/perf_regs.h b/arch/x86/include/uapi/asm/perf_regs.h
index d544f6d79871..b88d0b6822fd 100644
--- a/arch/x86/include/uapi/asm/perf_regs.h
+++ b/arch/x86/include/uapi/asm/perf_regs.h
@@ -60,16 +60,20 @@ enum perf_event_x86_regs {
enum {
PERF_X86_SIMD_XMM_REGS = 16,
PERF_X86_SIMD_YMM_REGS = 16,
- PERF_X86_SIMD_VEC_REGS_MAX = PERF_X86_SIMD_YMM_REGS,
+ PERF_X86_SIMD_ZMM_REGS = 32,
+ PERF_X86_SIMD_VEC_REGS_MAX = PERF_X86_SIMD_ZMM_REGS,
};
#define PERF_X86_SIMD_VEC_MASK __GENMASK_ULL(PERF_X86_SIMD_VEC_REGS_MAX - 1, 0)
+#define PERF_X86_H16ZMM_BASE 16
+
enum {
/* 1 qword = 8 bytes */
PERF_X86_XMM_QWORDS = 2,
PERF_X86_YMM_QWORDS = 4,
- PERF_X86_SIMD_QWORDS_MAX = PERF_X86_YMM_QWORDS,
+ PERF_X86_ZMM_QWORDS = 8,
+ PERF_X86_SIMD_QWORDS_MAX = PERF_X86_ZMM_QWORDS,
};
#endif /* _ASM_X86_PERF_REGS_H */
diff --git a/arch/x86/kernel/perf_regs.c b/arch/x86/kernel/perf_regs.c
index 316d18c13c02..b4a584057fe4 100644
--- a/arch/x86/kernel/perf_regs.c
+++ b/arch/x86/kernel/perf_regs.c
@@ -78,6 +78,7 @@ u64 perf_reg_value(struct pt_regs *regs, int idx)
}
#define PERF_X86_YMMH_QWORDS (PERF_X86_YMM_QWORDS / 2)
+#define PERF_X86_ZMMH_QWORDS (PERF_X86_ZMM_QWORDS / 2)
u64 perf_simd_reg_value(struct pt_regs *regs, int idx,
u16 qwords_idx, bool pred)
@@ -95,6 +96,13 @@ u64 perf_simd_reg_value(struct pt_regs *regs, int idx,
qwords_idx >= PERF_X86_SIMD_QWORDS_MAX))
return 0;
+ if (idx >= PERF_X86_H16ZMM_BASE) {
+ if (!perf_regs->h16zmm_regs)
+ return 0;
+ return perf_regs->h16zmm_regs[(idx - PERF_X86_H16ZMM_BASE) *
+ PERF_X86_ZMM_QWORDS + qwords_idx];
+ }
+
if (qwords_idx < PERF_X86_XMM_QWORDS) {
if (!perf_regs->xmm_regs)
return 0;
@@ -105,6 +113,11 @@ u64 perf_simd_reg_value(struct pt_regs *regs, int idx,
return 0;
return perf_regs->ymmh_regs[idx * PERF_X86_YMMH_QWORDS +
qwords_idx - PERF_X86_XMM_QWORDS];
+ } else if (qwords_idx < PERF_X86_ZMM_QWORDS) {
+ if (!perf_regs->zmmh_regs)
+ return 0;
+ return perf_regs->zmmh_regs[idx * PERF_X86_ZMMH_QWORDS +
+ qwords_idx - PERF_X86_YMM_QWORDS];
}
return 0;
@@ -126,7 +139,8 @@ int perf_simd_reg_validate(u64 sample_type, u16 simd_enabled,
if (vec_qwords) {
if (vec_qwords != PERF_X86_XMM_QWORDS &&
- vec_qwords != PERF_X86_YMM_QWORDS)
+ vec_qwords != PERF_X86_YMM_QWORDS &&
+ vec_qwords != PERF_X86_ZMM_QWORDS)
return -EINVAL;
if (!vec_mask_intr && !vec_mask_user)
return -EINVAL;
--
2.34.1
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [Patch v9 17/24] perf/x86: Support OPMASK sampling using sample_simd_pred_reg_* fields
2026-07-06 1:54 [Patch v9 00/24] Support SIMD/eGPRs/SSP registers sampling for perf Dapeng Mi
` (15 preceding siblings ...)
2026-07-06 1:54 ` [Patch v9 16/24] perf/x86: Support ZMM " Dapeng Mi
@ 2026-07-06 1:54 ` Dapeng Mi
2026-07-06 1:54 ` [Patch v9 18/24] perf: Enhance perf_reg_validate() with simd_enabled argument Dapeng Mi
` (6 subsequent siblings)
23 siblings, 0 replies; 42+ messages in thread
From: Dapeng Mi @ 2026-07-06 1:54 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Thomas Gleixner, Dave Hansen, Ian Rogers,
Adrian Hunter, Jiri Olsa, Alexander Shishkin, Andi Kleen,
Eranian Stephane
Cc: Mark Rutland, broonie, Ravi Bangoria, linux-kernel,
linux-perf-users, Zide Chen, Falcon Thomas, Dapeng Mi, Xudong Hao,
Dapeng Mi, Kan Liang
Support sampling of OPMASK registers via the sample_simd_pred_reg_*
fields.
Each OPMASK register consists of 1 u64 word. Current x86 hardware
supports 8 OPMASK registers. The perf_simd_reg_value() function is
responsible for outputting OPMASK value to userspace.
Additionally, sample_simd_pred_reg_qwords should be set to 1 to indicate
OPMASK sampling.
OPMASK sampling will be enabled in a subsequent patch that sets
PERF_PMU_CAP_SIMD_REGS.
Co-developed-by: Kan Liang <kan.liang@linux.intel.com>
Signed-off-by: Kan Liang <kan.liang@linux.intel.com>
Signed-off-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
---
arch/x86/events/core.c | 8 ++++++++
arch/x86/events/perf_event.h | 13 +++++++++++++
arch/x86/include/asm/perf_event.h | 4 ++++
arch/x86/include/uapi/asm/perf_regs.h | 5 +++++
arch/x86/kernel/perf_regs.c | 24 ++++++++++++++++++++----
5 files changed, 50 insertions(+), 4 deletions(-)
diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c
index ef0e238a4678..de07747e939e 100644
--- a/arch/x86/events/core.c
+++ b/arch/x86/events/core.c
@@ -735,6 +735,9 @@ int x86_pmu_hw_config(struct perf_event *event)
if (event_needs_high16_zmm(event) &&
!(x86_pmu.ext_regs_mask & XFEATURE_MASK_Hi16_ZMM))
return -EINVAL;
+ if (event_needs_opmask(event) &&
+ !(x86_pmu.ext_regs_mask & XFEATURE_MASK_OPMASK))
+ return -EINVAL;
}
}
@@ -1782,6 +1785,7 @@ void x86_pmu_clear_perf_regs(struct pt_regs *regs)
perf_regs->ymmh_regs = NULL;
perf_regs->zmmh_regs = NULL;
perf_regs->h16zmm_regs = NULL;
+ perf_regs->opmask_regs = NULL;
}
static void update_perf_regs(struct x86_perf_regs *perf_regs,
@@ -1803,6 +1807,8 @@ static void update_perf_regs(struct x86_perf_regs *perf_regs,
perf_regs->zmmh = get_xsave_addr(xsave, XFEATURE_ZMM_Hi256);
if (mask & XFEATURE_MASK_Hi16_ZMM)
perf_regs->h16zmm = get_xsave_addr(xsave, XFEATURE_Hi16_ZMM);
+ if (mask & XFEATURE_MASK_OPMASK)
+ perf_regs->opmask = get_xsave_addr(xsave, XFEATURE_OPMASK);
}
/*
@@ -2000,6 +2006,8 @@ static void x86_pmu_sample_xregs(struct perf_event *event,
mask |= XFEATURE_MASK_ZMM_Hi256;
if (event_needs_high16_zmm(event))
mask |= XFEATURE_MASK_Hi16_ZMM;
+ if (event_needs_opmask(event))
+ mask |= XFEATURE_MASK_OPMASK;
mask &= x86_pmu.ext_regs_mask;
if ((sample_type & PERF_SAMPLE_REGS_USER) && data->regs_user.regs) {
diff --git a/arch/x86/events/perf_event.h b/arch/x86/events/perf_event.h
index 01414b3a88fd..f15dc414c57a 100644
--- a/arch/x86/events/perf_event.h
+++ b/arch/x86/events/perf_event.h
@@ -209,6 +209,19 @@ static inline bool event_needs_high16_zmm(struct perf_event *event)
return false;
}
+static inline bool event_needs_opmask(struct perf_event *event)
+{
+ if (!(event->attr.sample_type &
+ (PERF_SAMPLE_REGS_INTR | PERF_SAMPLE_REGS_USER)))
+ return false;
+
+ if (event->attr.sample_simd_regs_enabled &&
+ event->attr.sample_simd_pred_reg_qwords == PERF_X86_OPMASK_QWORDS)
+ return true;
+
+ return false;
+}
+
struct amd_nb {
int nb_id; /* NorthBridge id */
int refcnt; /* reference count */
diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h
index 75394c4e8bc3..49112e097e99 100644
--- a/arch/x86/include/asm/perf_event.h
+++ b/arch/x86/include/asm/perf_event.h
@@ -745,6 +745,10 @@ struct x86_perf_regs {
u64 *h16zmm_regs;
struct avx_512_hi16_state *h16zmm;
};
+ union {
+ u64 *opmask_regs;
+ struct avx_512_opmask_state *opmask;
+ };
};
extern unsigned long perf_arch_instruction_pointer(struct pt_regs *regs);
diff --git a/arch/x86/include/uapi/asm/perf_regs.h b/arch/x86/include/uapi/asm/perf_regs.h
index b88d0b6822fd..61aec60623f1 100644
--- a/arch/x86/include/uapi/asm/perf_regs.h
+++ b/arch/x86/include/uapi/asm/perf_regs.h
@@ -62,14 +62,19 @@ enum {
PERF_X86_SIMD_YMM_REGS = 16,
PERF_X86_SIMD_ZMM_REGS = 32,
PERF_X86_SIMD_VEC_REGS_MAX = PERF_X86_SIMD_ZMM_REGS,
+
+ PERF_X86_SIMD_OPMASK_REGS = 8,
+ PERF_X86_SIMD_PRED_REGS_MAX = PERF_X86_SIMD_OPMASK_REGS,
};
+#define PERF_X86_SIMD_PRED_MASK __GENMASK(PERF_X86_SIMD_PRED_REGS_MAX - 1, 0)
#define PERF_X86_SIMD_VEC_MASK __GENMASK_ULL(PERF_X86_SIMD_VEC_REGS_MAX - 1, 0)
#define PERF_X86_H16ZMM_BASE 16
enum {
/* 1 qword = 8 bytes */
+ PERF_X86_OPMASK_QWORDS = 1,
PERF_X86_XMM_QWORDS = 2,
PERF_X86_YMM_QWORDS = 4,
PERF_X86_ZMM_QWORDS = 8,
diff --git a/arch/x86/kernel/perf_regs.c b/arch/x86/kernel/perf_regs.c
index b4a584057fe4..83e22f63cef4 100644
--- a/arch/x86/kernel/perf_regs.c
+++ b/arch/x86/kernel/perf_regs.c
@@ -89,8 +89,14 @@ u64 perf_simd_reg_value(struct pt_regs *regs, int idx,
if (!(perf_regs->abi & PERF_SAMPLE_REGS_ABI_SIMD))
return 0;
- if (pred)
- return 0;
+ if (pred) {
+ if (WARN_ON_ONCE(idx >= PERF_X86_SIMD_PRED_REGS_MAX ||
+ qwords_idx >= PERF_X86_OPMASK_QWORDS))
+ return 0;
+ if (!perf_regs->opmask_regs)
+ return 0;
+ return perf_regs->opmask_regs[idx];
+ }
if (WARN_ON_ONCE(idx >= PERF_X86_SIMD_VEC_REGS_MAX ||
qwords_idx >= PERF_X86_SIMD_QWORDS_MAX))
@@ -152,8 +158,18 @@ int perf_simd_reg_validate(u64 sample_type, u16 simd_enabled,
return -EINVAL;
}
- if (pred_qwords || pred_mask_intr || pred_mask_user)
- return -EINVAL;
+ if (pred_qwords) {
+ if (pred_qwords != PERF_X86_OPMASK_QWORDS)
+ return -EINVAL;
+ if (!pred_mask_intr && !pred_mask_user)
+ return -EINVAL;
+ if ((sample_type & PERF_SAMPLE_REGS_INTR) &&
+ (pred_mask_intr & ~PERF_X86_SIMD_PRED_MASK))
+ return -EINVAL;
+ if ((sample_type & PERF_SAMPLE_REGS_USER) &&
+ (pred_mask_user & ~PERF_X86_SIMD_PRED_MASK))
+ return -EINVAL;
+ }
if (sample_type & PERF_SAMPLE_REGS_INTR) {
size = (vec_qwords * hweight64(vec_mask_intr) +
--
2.34.1
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [Patch v9 18/24] perf: Enhance perf_reg_validate() with simd_enabled argument
2026-07-06 1:54 [Patch v9 00/24] Support SIMD/eGPRs/SSP registers sampling for perf Dapeng Mi
` (16 preceding siblings ...)
2026-07-06 1:54 ` [Patch v9 17/24] perf/x86: Support OPMASK sampling using sample_simd_pred_reg_* fields Dapeng Mi
@ 2026-07-06 1:54 ` Dapeng Mi
2026-07-06 1:54 ` [Patch v9 19/24] perf/x86: Support eGPRs sampling using sample_regs_* fields Dapeng Mi
` (5 subsequent siblings)
23 siblings, 0 replies; 42+ messages in thread
From: Dapeng Mi @ 2026-07-06 1:54 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Thomas Gleixner, Dave Hansen, Ian Rogers,
Adrian Hunter, Jiri Olsa, Alexander Shishkin, Andi Kleen,
Eranian Stephane
Cc: Mark Rutland, broonie, Ravi Bangoria, linux-kernel,
linux-perf-users, Zide Chen, Falcon Thomas, Dapeng Mi, Xudong Hao,
Dapeng Mi
The upcoming patch will support x86 APX eGPRs sampling by using the
reclaimed XMM register space to represent eGPRs in sample_regs_* fields.
To differentiate between XMM and eGPRs in sample_regs_* fields, an
additional argument, simd_enabled, is introduced to the
perf_reg_validate() helper. If simd_enabled is set to 1, it indicates
that eGPRs are represented in sample_regs_* fields for the x86 platform;
otherwise, XMM registers are represented.
Signed-off-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
---
arch/arm/kernel/perf_regs.c | 2 +-
arch/arm64/kernel/perf_regs.c | 2 +-
arch/csky/kernel/perf_regs.c | 2 +-
arch/loongarch/kernel/perf_regs.c | 2 +-
arch/mips/kernel/perf_regs.c | 2 +-
arch/parisc/kernel/perf_regs.c | 2 +-
arch/powerpc/perf/perf_regs.c | 2 +-
arch/riscv/kernel/perf_regs.c | 2 +-
arch/s390/kernel/perf_regs.c | 2 +-
arch/x86/kernel/perf_regs.c | 4 ++--
include/linux/perf_regs.h | 2 +-
kernel/events/core.c | 8 +++++---
12 files changed, 17 insertions(+), 15 deletions(-)
diff --git a/arch/arm/kernel/perf_regs.c b/arch/arm/kernel/perf_regs.c
index d575a4c3ca56..838d701adf4d 100644
--- a/arch/arm/kernel/perf_regs.c
+++ b/arch/arm/kernel/perf_regs.c
@@ -18,7 +18,7 @@ u64 perf_reg_value(struct pt_regs *regs, int idx)
#define REG_RESERVED (~((1ULL << PERF_REG_ARM_MAX) - 1))
-int perf_reg_validate(u64 mask)
+int perf_reg_validate(u64 mask, bool simd_enabled)
{
if (!mask || mask & REG_RESERVED)
return -EINVAL;
diff --git a/arch/arm64/kernel/perf_regs.c b/arch/arm64/kernel/perf_regs.c
index 70e2f13f587f..71a3e0238de4 100644
--- a/arch/arm64/kernel/perf_regs.c
+++ b/arch/arm64/kernel/perf_regs.c
@@ -77,7 +77,7 @@ u64 perf_reg_value(struct pt_regs *regs, int idx)
#define REG_RESERVED (~((1ULL << PERF_REG_ARM64_MAX) - 1))
-int perf_reg_validate(u64 mask)
+int perf_reg_validate(u64 mask, bool simd_enabled)
{
u64 reserved_mask = REG_RESERVED;
diff --git a/arch/csky/kernel/perf_regs.c b/arch/csky/kernel/perf_regs.c
index 94601f37b596..c932a96afc56 100644
--- a/arch/csky/kernel/perf_regs.c
+++ b/arch/csky/kernel/perf_regs.c
@@ -18,7 +18,7 @@ u64 perf_reg_value(struct pt_regs *regs, int idx)
#define REG_RESERVED (~((1ULL << PERF_REG_CSKY_MAX) - 1))
-int perf_reg_validate(u64 mask)
+int perf_reg_validate(u64 mask, bool simd_enabled)
{
if (!mask || mask & REG_RESERVED)
return -EINVAL;
diff --git a/arch/loongarch/kernel/perf_regs.c b/arch/loongarch/kernel/perf_regs.c
index 8dd604f01745..164514f40ae0 100644
--- a/arch/loongarch/kernel/perf_regs.c
+++ b/arch/loongarch/kernel/perf_regs.c
@@ -25,7 +25,7 @@ u64 perf_reg_abi(struct task_struct *tsk)
}
#endif /* CONFIG_32BIT */
-int perf_reg_validate(u64 mask)
+int perf_reg_validate(u64 mask, bool simd_enabled)
{
if (!mask)
return -EINVAL;
diff --git a/arch/mips/kernel/perf_regs.c b/arch/mips/kernel/perf_regs.c
index 7736d3c5ebd2..00a5201dbd5d 100644
--- a/arch/mips/kernel/perf_regs.c
+++ b/arch/mips/kernel/perf_regs.c
@@ -28,7 +28,7 @@ u64 perf_reg_abi(struct task_struct *tsk)
}
#endif /* CONFIG_32BIT */
-int perf_reg_validate(u64 mask)
+int perf_reg_validate(u64 mask, bool simd_enabled)
{
if (!mask)
return -EINVAL;
diff --git a/arch/parisc/kernel/perf_regs.c b/arch/parisc/kernel/perf_regs.c
index b9fe1f2fcb9b..4f21aab5405c 100644
--- a/arch/parisc/kernel/perf_regs.c
+++ b/arch/parisc/kernel/perf_regs.c
@@ -34,7 +34,7 @@ u64 perf_reg_value(struct pt_regs *regs, int idx)
#define REG_RESERVED (~((1ULL << PERF_REG_PARISC_MAX) - 1))
-int perf_reg_validate(u64 mask)
+int perf_reg_validate(u64 mask, bool simd_enabled)
{
if (!mask || mask & REG_RESERVED)
return -EINVAL;
diff --git a/arch/powerpc/perf/perf_regs.c b/arch/powerpc/perf/perf_regs.c
index 350dccb0143c..a01d8a903640 100644
--- a/arch/powerpc/perf/perf_regs.c
+++ b/arch/powerpc/perf/perf_regs.c
@@ -125,7 +125,7 @@ u64 perf_reg_value(struct pt_regs *regs, int idx)
return regs_get_register(regs, pt_regs_offset[idx]);
}
-int perf_reg_validate(u64 mask)
+int perf_reg_validate(u64 mask, bool simd_enabled)
{
if (!mask || mask & REG_RESERVED)
return -EINVAL;
diff --git a/arch/riscv/kernel/perf_regs.c b/arch/riscv/kernel/perf_regs.c
index 3bba8deababb..1ecc8760b88b 100644
--- a/arch/riscv/kernel/perf_regs.c
+++ b/arch/riscv/kernel/perf_regs.c
@@ -18,7 +18,7 @@ u64 perf_reg_value(struct pt_regs *regs, int idx)
#define REG_RESERVED (~((1ULL << PERF_REG_RISCV_MAX) - 1))
-int perf_reg_validate(u64 mask)
+int perf_reg_validate(u64 mask, bool simd_enabled)
{
if (!mask || mask & REG_RESERVED)
return -EINVAL;
diff --git a/arch/s390/kernel/perf_regs.c b/arch/s390/kernel/perf_regs.c
index 7b305f1456f8..6496fd23c540 100644
--- a/arch/s390/kernel/perf_regs.c
+++ b/arch/s390/kernel/perf_regs.c
@@ -34,7 +34,7 @@ u64 perf_reg_value(struct pt_regs *regs, int idx)
#define REG_RESERVED (~((1UL << PERF_REG_S390_MAX) - 1))
-int perf_reg_validate(u64 mask)
+int perf_reg_validate(u64 mask, bool simd_enabled)
{
if (!mask || mask & REG_RESERVED)
return -EINVAL;
diff --git a/arch/x86/kernel/perf_regs.c b/arch/x86/kernel/perf_regs.c
index 83e22f63cef4..9576e4e9cbcb 100644
--- a/arch/x86/kernel/perf_regs.c
+++ b/arch/x86/kernel/perf_regs.c
@@ -199,7 +199,7 @@ int perf_simd_reg_validate(u64 sample_type, u16 simd_enabled,
(1ULL << PERF_REG_X86_R14) | \
(1ULL << PERF_REG_X86_R15))
-int perf_reg_validate(u64 mask)
+int perf_reg_validate(u64 mask, bool simd_enabled)
{
/* The mask could be 0 if only the SIMD registers are interested */
if (mask & (REG_NOSUPPORT | PERF_REG_X86_RESERVED))
@@ -219,7 +219,7 @@ u64 perf_reg_abi(struct task_struct *task)
(1ULL << PERF_REG_X86_FS) | \
(1ULL << PERF_REG_X86_GS))
-int perf_reg_validate(u64 mask)
+int perf_reg_validate(u64 mask, bool simd_enabled)
{
/* The mask could be 0 if only the SIMD registers are interested */
if (mask & (REG_NOSUPPORT | PERF_REG_X86_RESERVED))
diff --git a/include/linux/perf_regs.h b/include/linux/perf_regs.h
index 52eddbcdbf4e..1a7571c697a3 100644
--- a/include/linux/perf_regs.h
+++ b/include/linux/perf_regs.h
@@ -10,7 +10,7 @@ struct perf_regs {
};
u64 perf_reg_value(struct pt_regs *regs, int idx);
-int perf_reg_validate(u64 mask);
+int perf_reg_validate(u64 mask, bool simd_enabled);
u64 perf_reg_abi(struct task_struct *task);
void perf_get_regs_user(struct perf_regs *regs_user,
struct pt_regs *regs);
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 2ce8c3cd6824..2832da42a669 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -7812,7 +7812,7 @@ u64 __weak perf_reg_value(struct pt_regs *regs, int idx)
return 0;
}
-int __weak perf_reg_validate(u64 mask)
+int __weak perf_reg_validate(u64 mask, bool simd_enabled)
{
return mask ? -ENOSYS : 0;
}
@@ -13806,7 +13806,8 @@ static int perf_copy_attr(struct perf_event_attr __user *uattr,
}
if (attr->sample_type & PERF_SAMPLE_REGS_USER) {
- ret = perf_reg_validate(attr->sample_regs_user);
+ ret = perf_reg_validate(attr->sample_regs_user,
+ attr->sample_simd_regs_enabled);
if (ret)
return ret;
ret = perf_simd_reg_validate(attr->sample_type,
@@ -13840,7 +13841,8 @@ static int perf_copy_attr(struct perf_event_attr __user *uattr,
attr->sample_max_stack = sysctl_perf_event_max_stack;
if (attr->sample_type & PERF_SAMPLE_REGS_INTR) {
- ret = perf_reg_validate(attr->sample_regs_intr);
+ ret = perf_reg_validate(attr->sample_regs_intr,
+ attr->sample_simd_regs_enabled);
if (ret)
return ret;
ret = perf_simd_reg_validate(attr->sample_type,
--
2.34.1
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [Patch v9 19/24] perf/x86: Support eGPRs sampling using sample_regs_* fields
2026-07-06 1:54 [Patch v9 00/24] Support SIMD/eGPRs/SSP registers sampling for perf Dapeng Mi
` (17 preceding siblings ...)
2026-07-06 1:54 ` [Patch v9 18/24] perf: Enhance perf_reg_validate() with simd_enabled argument Dapeng Mi
@ 2026-07-06 1:54 ` Dapeng Mi
2026-07-06 1:54 ` [Patch v9 20/24] perf/x86: Support SSP " Dapeng Mi
` (4 subsequent siblings)
23 siblings, 0 replies; 42+ messages in thread
From: Dapeng Mi @ 2026-07-06 1:54 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Thomas Gleixner, Dave Hansen, Ian Rogers,
Adrian Hunter, Jiri Olsa, Alexander Shishkin, Andi Kleen,
Eranian Stephane
Cc: Mark Rutland, broonie, Ravi Bangoria, linux-kernel,
linux-perf-users, Zide Chen, Falcon Thomas, Dapeng Mi, Xudong Hao,
Dapeng Mi, Kan Liang
Support sampling of APX eGPRs (R16 ~ R31) via the sample_regs_* fields.
To sample eGPRs, the sample_simd_regs_enabled field must be set. This
allows the spare space (reclaimed from the original XMM space) in the
sample_regs_* fields to be used for representing eGPRs.
The perf_reg_value() function needs to check if the
PERF_SAMPLE_REGS_ABI_SIMD flag is set first, and then determine whether
to output eGPRs or legacy XMM registers to userspace.
The perf_reg_validate() function first checks the simd_enabled argument
to determine if the eGPRs bitmap is represented in sample_regs_* fields.
It then validates the eGPRs bitmap accordingly.
Currently, eGPRs sampling is only supported on the x86_64 architecture, as
APX is only available on x86_64 platforms.
APX eGPRs sampling will be enabled in a subsequent patch that sets
PERF_PMU_CAP_SIMD_REGS.
Suggested-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Co-developed-by: Kan Liang <kan.liang@linux.intel.com>
Signed-off-by: Kan Liang <kan.liang@linux.intel.com>
Signed-off-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
---
arch/x86/events/core.c | 39 +++++++++++++++------
arch/x86/events/intel/core.c | 4 ++-
arch/x86/events/perf_event.h | 16 +++++++++
arch/x86/include/asm/perf_event.h | 4 +++
arch/x86/include/uapi/asm/perf_regs.h | 26 ++++++++++++++
arch/x86/kernel/perf_regs.c | 50 +++++++++++++++++----------
6 files changed, 109 insertions(+), 30 deletions(-)
diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c
index de07747e939e..f4f1f80ed6f4 100644
--- a/arch/x86/events/core.c
+++ b/arch/x86/events/core.c
@@ -708,20 +708,23 @@ int x86_pmu_hw_config(struct perf_event *event)
}
if (event->attr.sample_type & (PERF_SAMPLE_REGS_INTR | PERF_SAMPLE_REGS_USER)) {
- /*
- * Besides the general purpose registers, XMM registers may
- * be collected as well.
- */
- if (event_has_extended_regs(event)) {
- if (!(event->pmu->capabilities & PERF_PMU_CAP_EXTENDED_REGS))
- return -EINVAL;
- if (event->attr.sample_simd_regs_enabled)
- return -EINVAL;
- }
-
if (event_has_simd_regs(event)) {
+ u64 reserved = ~GENMASK_ULL(PERF_REG_MISC_MAX - 1, 0);
+
if (!(event->pmu->capabilities & PERF_PMU_CAP_SIMD_REGS))
return -EINVAL;
+ /*
+ * The XMM space in the perf_event_x86_regs is reclaimed
+ * for eGPRs and other general registers.
+ */
+ if (((event->attr.sample_type & PERF_SAMPLE_REGS_INTR) &&
+ (event->attr.sample_regs_intr & reserved)) ||
+ ((event->attr.sample_type & PERF_SAMPLE_REGS_USER) &&
+ (event->attr.sample_regs_user & reserved)))
+ return -EINVAL;
+ if (event_needs_egprs(event) &&
+ !(x86_pmu.ext_regs_mask & XFEATURE_MASK_APX))
+ return -EINVAL;
/* The vector registers set is not supported */
if (event_needs_xmm(event) &&
!(x86_pmu.ext_regs_mask & XFEATURE_MASK_SSE))
@@ -738,6 +741,15 @@ int x86_pmu_hw_config(struct perf_event *event)
if (event_needs_opmask(event) &&
!(x86_pmu.ext_regs_mask & XFEATURE_MASK_OPMASK))
return -EINVAL;
+ } else {
+ /*
+ * Besides the general purpose registers, XMM registers may
+ * be collected as well.
+ */
+ if (event_has_extended_regs(event)) {
+ if (!(event->pmu->capabilities & PERF_PMU_CAP_EXTENDED_REGS))
+ return -EINVAL;
+ }
}
}
@@ -1786,6 +1798,7 @@ void x86_pmu_clear_perf_regs(struct pt_regs *regs)
perf_regs->zmmh_regs = NULL;
perf_regs->h16zmm_regs = NULL;
perf_regs->opmask_regs = NULL;
+ perf_regs->egpr_regs = NULL;
}
static void update_perf_regs(struct x86_perf_regs *perf_regs,
@@ -1809,6 +1822,8 @@ static void update_perf_regs(struct x86_perf_regs *perf_regs,
perf_regs->h16zmm = get_xsave_addr(xsave, XFEATURE_Hi16_ZMM);
if (mask & XFEATURE_MASK_OPMASK)
perf_regs->opmask = get_xsave_addr(xsave, XFEATURE_OPMASK);
+ if (mask & XFEATURE_MASK_APX)
+ perf_regs->egpr = get_xsave_addr(xsave, XFEATURE_APX);
}
/*
@@ -2008,6 +2023,8 @@ static void x86_pmu_sample_xregs(struct perf_event *event,
mask |= XFEATURE_MASK_Hi16_ZMM;
if (event_needs_opmask(event))
mask |= XFEATURE_MASK_OPMASK;
+ if (event_needs_egprs(event))
+ mask |= XFEATURE_MASK_APX;
mask &= x86_pmu.ext_regs_mask;
if ((sample_type & PERF_SAMPLE_REGS_USER) && data->regs_user.regs) {
diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c
index 69294bc57225..cfe5478aa5a4 100644
--- a/arch/x86/events/intel/core.c
+++ b/arch/x86/events/intel/core.c
@@ -4697,7 +4697,9 @@ static void intel_pebs_aliases_skl(struct perf_event *event)
static unsigned long intel_pmu_large_pebs_flags(struct perf_event *event)
{
unsigned long flags = x86_pmu.large_pebs_flags;
- u64 gprs_mask = PEBS_GP_REGS | PERF_REG_EXTENDED_MASK;
+ u64 gprs_mask = event->attr.sample_simd_regs_enabled ?
+ PEBS_GP_REGS :
+ PEBS_GP_REGS | PERF_REG_EXTENDED_MASK;
if (event->attr.use_clockid)
flags &= ~PERF_SAMPLE_TIME;
diff --git a/arch/x86/events/perf_event.h b/arch/x86/events/perf_event.h
index f15dc414c57a..840ef8a44b52 100644
--- a/arch/x86/events/perf_event.h
+++ b/arch/x86/events/perf_event.h
@@ -222,6 +222,22 @@ static inline bool event_needs_opmask(struct perf_event *event)
return false;
}
+static inline bool event_needs_egprs(struct perf_event *event)
+{
+ if (!event->attr.sample_simd_regs_enabled)
+ return false;
+
+ if ((event->attr.sample_type & PERF_SAMPLE_REGS_USER) &&
+ (event->attr.sample_regs_user & PERF_X86_EGPRS_MASK))
+ return true;
+
+ if ((event->attr.sample_type & PERF_SAMPLE_REGS_INTR) &&
+ (event->attr.sample_regs_intr & PERF_X86_EGPRS_MASK))
+ return true;
+
+ return false;
+}
+
struct amd_nb {
int nb_id; /* NorthBridge id */
int refcnt; /* reference count */
diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h
index 49112e097e99..bc05f8c17464 100644
--- a/arch/x86/include/asm/perf_event.h
+++ b/arch/x86/include/asm/perf_event.h
@@ -749,6 +749,10 @@ struct x86_perf_regs {
u64 *opmask_regs;
struct avx_512_opmask_state *opmask;
};
+ union {
+ u64 *egpr_regs;
+ struct apx_state *egpr;
+ };
};
extern unsigned long perf_arch_instruction_pointer(struct pt_regs *regs);
diff --git a/arch/x86/include/uapi/asm/perf_regs.h b/arch/x86/include/uapi/asm/perf_regs.h
index 61aec60623f1..977831bd7a9d 100644
--- a/arch/x86/include/uapi/asm/perf_regs.h
+++ b/arch/x86/include/uapi/asm/perf_regs.h
@@ -29,9 +29,34 @@ enum perf_event_x86_regs {
PERF_REG_X86_R13,
PERF_REG_X86_R14,
PERF_REG_X86_R15,
+ /*
+ * The eGPRs and XMM have overlaps. Only one can be used
+ * at a time. The ABI PERF_SAMPLE_REGS_ABI_SIMD is used to
+ * distinguish which one is used. If PERF_SAMPLE_REGS_ABI_SIMD
+ * is set, then eGPRs is used, otherwise, XMM is used.
+ *
+ * Extended GPRs (eGPRs)
+ */
+ PERF_REG_X86_R16,
+ PERF_REG_X86_R17,
+ PERF_REG_X86_R18,
+ PERF_REG_X86_R19,
+ PERF_REG_X86_R20,
+ PERF_REG_X86_R21,
+ PERF_REG_X86_R22,
+ PERF_REG_X86_R23,
+ PERF_REG_X86_R24,
+ PERF_REG_X86_R25,
+ PERF_REG_X86_R26,
+ PERF_REG_X86_R27,
+ PERF_REG_X86_R28,
+ PERF_REG_X86_R29,
+ PERF_REG_X86_R30,
+ PERF_REG_X86_R31,
/* These are the limits for the GPRs. */
PERF_REG_X86_32_MAX = PERF_REG_X86_GS + 1,
PERF_REG_X86_64_MAX = PERF_REG_X86_R15 + 1,
+ PERF_REG_MISC_MAX = PERF_REG_X86_R31 + 1,
/* These all need two bits set because they are 128bit */
PERF_REG_X86_XMM0 = 32,
@@ -56,6 +81,7 @@ enum perf_event_x86_regs {
};
#define PERF_REG_EXTENDED_MASK (~((1ULL << PERF_REG_X86_XMM0) - 1))
+#define PERF_X86_EGPRS_MASK __GENMASK_ULL(PERF_REG_X86_R31, PERF_REG_X86_R16)
enum {
PERF_X86_SIMD_XMM_REGS = 16,
diff --git a/arch/x86/kernel/perf_regs.c b/arch/x86/kernel/perf_regs.c
index 9576e4e9cbcb..b6f75196da02 100644
--- a/arch/x86/kernel/perf_regs.c
+++ b/arch/x86/kernel/perf_regs.c
@@ -61,14 +61,24 @@ 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) {
+ if (idx > PERF_REG_X86_R15) {
perf_regs = container_of(regs, struct x86_perf_regs, regs);
- /* SIMD registers are moved to dedicated sample_simd_vec_reg */
- if (perf_regs->abi & PERF_SAMPLE_REGS_ABI_SIMD)
+ if (perf_regs->abi == PERF_SAMPLE_REGS_ABI_NONE)
return 0;
- if (!perf_regs->xmm_regs)
- return 0;
- return perf_regs->xmm_regs[idx - PERF_REG_X86_XMM0];
+
+ if (perf_regs->abi & PERF_SAMPLE_REGS_ABI_SIMD) {
+ if (idx <= PERF_REG_X86_R31) {
+ if (!perf_regs->egpr_regs)
+ return 0;
+ return perf_regs->egpr_regs[idx - PERF_REG_X86_R16];
+ }
+ } else {
+ if (idx >= PERF_REG_X86_XMM0 && idx < PERF_REG_X86_XMM_MAX) {
+ 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)))
@@ -186,23 +196,22 @@ int perf_simd_reg_validate(u64 sample_type, u16 simd_enabled,
return 0;
}
-#define PERF_REG_X86_RESERVED (((1ULL << PERF_REG_X86_XMM0) - 1) & \
- ~((1ULL << PERF_REG_X86_MAX) - 1))
+#define PERF_REG_X86_RESERVED (GENMASK_ULL(PERF_REG_X86_XMM0 - 1, PERF_REG_X86_AX) & \
+ ~GENMASK_ULL(PERF_REG_X86_R15, PERF_REG_X86_AX))
+#define PERF_REG_X86_EXT_RESERVED (~GENMASK_ULL(PERF_REG_MISC_MAX - 1, PERF_REG_X86_AX))
#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))
+#define REG_NOSUPPORT GENMASK_ULL(PERF_REG_X86_R15, PERF_REG_X86_R8)
int perf_reg_validate(u64 mask, bool simd_enabled)
{
+ if (!simd_enabled &&
+ (!mask || (mask & (REG_NOSUPPORT | PERF_REG_X86_RESERVED))))
+ return -EINVAL;
+
/* The mask could be 0 if only the SIMD registers are interested */
- if (mask & (REG_NOSUPPORT | PERF_REG_X86_RESERVED))
+ if (simd_enabled &&
+ (mask & ~GENMASK_ULL(PERF_REG_X86_GS, PERF_REG_X86_AX)))
return -EINVAL;
return 0;
@@ -221,8 +230,13 @@ u64 perf_reg_abi(struct task_struct *task)
int perf_reg_validate(u64 mask, bool simd_enabled)
{
+ if (!simd_enabled &&
+ (!mask || (mask & (REG_NOSUPPORT | PERF_REG_X86_RESERVED))))
+ return -EINVAL;
+
/* The mask could be 0 if only the SIMD registers are interested */
- if (mask & (REG_NOSUPPORT | PERF_REG_X86_RESERVED))
+ if (simd_enabled &&
+ (mask & (REG_NOSUPPORT | PERF_REG_X86_EXT_RESERVED)))
return -EINVAL;
return 0;
--
2.34.1
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [Patch v9 20/24] perf/x86: Support SSP sampling using sample_regs_* fields
2026-07-06 1:54 [Patch v9 00/24] Support SIMD/eGPRs/SSP registers sampling for perf Dapeng Mi
` (18 preceding siblings ...)
2026-07-06 1:54 ` [Patch v9 19/24] perf/x86: Support eGPRs sampling using sample_regs_* fields Dapeng Mi
@ 2026-07-06 1:54 ` Dapeng Mi
2026-07-06 1:54 ` [Patch v9 21/24] perf/x86/intel: Support arch-PEBS based SIMD/eGPRs/SSP sampling Dapeng Mi
` (3 subsequent siblings)
23 siblings, 0 replies; 42+ messages in thread
From: Dapeng Mi @ 2026-07-06 1:54 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Thomas Gleixner, Dave Hansen, Ian Rogers,
Adrian Hunter, Jiri Olsa, Alexander Shishkin, Andi Kleen,
Eranian Stephane
Cc: Mark Rutland, broonie, Ravi Bangoria, linux-kernel,
linux-perf-users, Zide Chen, Falcon Thomas, Dapeng Mi, Xudong Hao,
Dapeng Mi, Kan Liang
Support sampling of CET SSP register via the sample_regs_* fields.
To sample SSP, the sample_simd_regs_enabled field must be set. This
allows the spare space (reclaimed from the original XMM space) in the
sample_regs_* fields to be used for representing SSP.
Similar to eGPRs sampling, the perf_reg_value() function needs to
check if the PERF_SAMPLE_REGS_ABI_SIMD flag is set first, and then
determine whether to output SSP or legacy XMM registers to userspace.
Additionally, arch-PEBS supports sampling SSP, which is placed into the
GPRs group. Also enables arch-PEBS-based SSP sampling in this patch.
Currently, SSP sampling is only supported on the x86_64 architecture, as
CET is only available on x86_64 platforms.
SSP sampling will be enabled in a subsequent patch that sets
PERF_PMU_CAP_SIMD_REGS.
Co-developed-by: Kan Liang <kan.liang@linux.intel.com>
Signed-off-by: Kan Liang <kan.liang@linux.intel.com>
Signed-off-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
---
arch/x86/events/core.c | 11 +++++++++++
arch/x86/events/intel/ds.c | 15 +++++++++++++--
arch/x86/events/perf_event.h | 16 ++++++++++++++++
arch/x86/include/asm/perf_event.h | 1 +
arch/x86/include/uapi/asm/perf_regs.h | 7 ++++---
arch/x86/kernel/perf_regs.c | 5 +++++
6 files changed, 50 insertions(+), 5 deletions(-)
diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c
index f4f1f80ed6f4..323be08778d6 100644
--- a/arch/x86/events/core.c
+++ b/arch/x86/events/core.c
@@ -725,6 +725,9 @@ int x86_pmu_hw_config(struct perf_event *event)
if (event_needs_egprs(event) &&
!(x86_pmu.ext_regs_mask & XFEATURE_MASK_APX))
return -EINVAL;
+ if (event_needs_ssp(event) &&
+ !(x86_pmu.ext_regs_mask & XFEATURE_MASK_CET_USER))
+ return -EINVAL;
/* The vector registers set is not supported */
if (event_needs_xmm(event) &&
!(x86_pmu.ext_regs_mask & XFEATURE_MASK_SSE))
@@ -1799,11 +1802,13 @@ void x86_pmu_clear_perf_regs(struct pt_regs *regs)
perf_regs->h16zmm_regs = NULL;
perf_regs->opmask_regs = NULL;
perf_regs->egpr_regs = NULL;
+ perf_regs->ssp = NULL;
}
static void update_perf_regs(struct x86_perf_regs *perf_regs,
struct xregs_state *xsave, u64 bitmap)
{
+ struct cet_user_state *cet;
u64 mask;
if (!xsave)
@@ -1824,6 +1829,10 @@ static void update_perf_regs(struct x86_perf_regs *perf_regs,
perf_regs->opmask = get_xsave_addr(xsave, XFEATURE_OPMASK);
if (mask & XFEATURE_MASK_APX)
perf_regs->egpr = get_xsave_addr(xsave, XFEATURE_APX);
+ if (mask & XFEATURE_MASK_CET_USER) {
+ cet = get_xsave_addr(xsave, XFEATURE_CET_USER);
+ perf_regs->ssp = cet ? &cet->user_ssp : NULL;
+ }
}
/*
@@ -2025,6 +2034,8 @@ static void x86_pmu_sample_xregs(struct perf_event *event,
mask |= XFEATURE_MASK_OPMASK;
if (event_needs_egprs(event))
mask |= XFEATURE_MASK_APX;
+ if (event_needs_ssp(event))
+ mask |= XFEATURE_MASK_CET_USER;
mask &= x86_pmu.ext_regs_mask;
if ((sample_type & PERF_SAMPLE_REGS_USER) && data->regs_user.regs) {
diff --git a/arch/x86/events/intel/ds.c b/arch/x86/events/intel/ds.c
index 24bfc3fb6060..54e6f73ffde4 100644
--- a/arch/x86/events/intel/ds.c
+++ b/arch/x86/events/intel/ds.c
@@ -1707,6 +1707,7 @@ static u64 pebs_update_adaptive_cfg(struct perf_event *event)
u64 sample_type = attr->sample_type;
u64 pebs_data_cfg = 0;
bool gprs, tsx_weight;
+ u64 xgprs_mask;
if (!(sample_type & ~(PERF_SAMPLE_IP|PERF_SAMPLE_TIME)) &&
attr->precise_ip > 1)
@@ -1721,10 +1722,13 @@ static u64 pebs_update_adaptive_cfg(struct perf_event *event)
* + precise_ip < 2 for the non event IP
* + For RTM TSX weight we need GPRs for the abort code.
*/
+ xgprs_mask = event->attr.sample_simd_regs_enabled ?
+ PEBS_GP_REGS | BIT_ULL(PERF_REG_X86_SSP) :
+ PEBS_GP_REGS;
gprs = ((sample_type & PERF_SAMPLE_REGS_INTR) &&
- (attr->sample_regs_intr & PEBS_GP_REGS)) ||
+ (attr->sample_regs_intr & xgprs_mask)) ||
((sample_type & PERF_SAMPLE_REGS_USER) &&
- (attr->sample_regs_user & PEBS_GP_REGS));
+ (attr->sample_regs_user & xgprs_mask));
tsx_weight = (sample_type & PERF_SAMPLE_WEIGHT_TYPE) &&
((attr->config & INTEL_ARCH_EVENT_MASK) ==
@@ -2674,6 +2678,13 @@ static void setup_arch_pebs_sample_data(struct perf_event *event,
__setup_pebs_gpr_group(event, regs,
(struct pebs_gprs *)gprs,
sample_type);
+
+ /* Currently only user space mode enables SSP. */
+ if (user_mode(regs) && (sample_type &
+ (PERF_SAMPLE_REGS_INTR | PERF_SAMPLE_REGS_USER))) {
+ perf_regs->ssp = &gprs->ssp;
+ ignore_mask |= XFEATURE_MASK_CET_USER;
+ }
}
if (header->aux) {
diff --git a/arch/x86/events/perf_event.h b/arch/x86/events/perf_event.h
index 840ef8a44b52..b1f9d17dddb6 100644
--- a/arch/x86/events/perf_event.h
+++ b/arch/x86/events/perf_event.h
@@ -238,6 +238,22 @@ static inline bool event_needs_egprs(struct perf_event *event)
return false;
}
+static inline bool event_needs_ssp(struct perf_event *event)
+{
+ if (!event->attr.sample_simd_regs_enabled)
+ return false;
+
+ if ((event->attr.sample_type & PERF_SAMPLE_REGS_USER) &&
+ (event->attr.sample_regs_user & BIT_ULL(PERF_REG_X86_SSP)))
+ return true;
+
+ if ((event->attr.sample_type & PERF_SAMPLE_REGS_INTR) &&
+ (event->attr.sample_regs_intr & BIT_ULL(PERF_REG_X86_SSP)))
+ return true;
+
+ return false;
+}
+
struct amd_nb {
int nb_id; /* NorthBridge id */
int refcnt; /* reference count */
diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h
index bc05f8c17464..4302ef39c42e 100644
--- a/arch/x86/include/asm/perf_event.h
+++ b/arch/x86/include/asm/perf_event.h
@@ -753,6 +753,7 @@ struct x86_perf_regs {
u64 *egpr_regs;
struct apx_state *egpr;
};
+ u64 *ssp;
};
extern unsigned long perf_arch_instruction_pointer(struct pt_regs *regs);
diff --git a/arch/x86/include/uapi/asm/perf_regs.h b/arch/x86/include/uapi/asm/perf_regs.h
index 977831bd7a9d..faaa82df688d 100644
--- a/arch/x86/include/uapi/asm/perf_regs.h
+++ b/arch/x86/include/uapi/asm/perf_regs.h
@@ -30,10 +30,10 @@ enum perf_event_x86_regs {
PERF_REG_X86_R14,
PERF_REG_X86_R15,
/*
- * The eGPRs and XMM have overlaps. Only one can be used
+ * The eGPRs/SSP and XMM have overlaps. Only one can be used
* at a time. The ABI PERF_SAMPLE_REGS_ABI_SIMD is used to
* distinguish which one is used. If PERF_SAMPLE_REGS_ABI_SIMD
- * is set, then eGPRs is used, otherwise, XMM is used.
+ * is set, then eGPRs/SSP is used, otherwise, XMM is used.
*
* Extended GPRs (eGPRs)
*/
@@ -53,10 +53,11 @@ enum perf_event_x86_regs {
PERF_REG_X86_R29,
PERF_REG_X86_R30,
PERF_REG_X86_R31,
+ PERF_REG_X86_SSP,
/* These are the limits for the GPRs. */
PERF_REG_X86_32_MAX = PERF_REG_X86_GS + 1,
PERF_REG_X86_64_MAX = PERF_REG_X86_R15 + 1,
- PERF_REG_MISC_MAX = PERF_REG_X86_R31 + 1,
+ PERF_REG_MISC_MAX = PERF_REG_X86_SSP + 1,
/* These all need two bits set because they are 128bit */
PERF_REG_X86_XMM0 = 32,
diff --git a/arch/x86/kernel/perf_regs.c b/arch/x86/kernel/perf_regs.c
index b6f75196da02..7a0607b81846 100644
--- a/arch/x86/kernel/perf_regs.c
+++ b/arch/x86/kernel/perf_regs.c
@@ -72,6 +72,11 @@ u64 perf_reg_value(struct pt_regs *regs, int idx)
return 0;
return perf_regs->egpr_regs[idx - PERF_REG_X86_R16];
}
+ if (idx == PERF_REG_X86_SSP) {
+ if (!perf_regs->ssp)
+ return 0;
+ return *perf_regs->ssp;
+ }
} else {
if (idx >= PERF_REG_X86_XMM0 && idx < PERF_REG_X86_XMM_MAX) {
if (!perf_regs->xmm_regs)
--
2.34.1
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [Patch v9 21/24] perf/x86/intel: Support arch-PEBS based SIMD/eGPRs/SSP sampling
2026-07-06 1:54 [Patch v9 00/24] Support SIMD/eGPRs/SSP registers sampling for perf Dapeng Mi
` (19 preceding siblings ...)
2026-07-06 1:54 ` [Patch v9 20/24] perf/x86: Support SSP " Dapeng Mi
@ 2026-07-06 1:54 ` Dapeng Mi
2026-07-06 1:54 ` [Patch v9 22/24] perf/x86/intel: Enable PERF_PMU_CAP_SIMD_REGS capability Dapeng Mi
` (2 subsequent siblings)
23 siblings, 0 replies; 42+ messages in thread
From: Dapeng Mi @ 2026-07-06 1:54 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Thomas Gleixner, Dave Hansen, Ian Rogers,
Adrian Hunter, Jiri Olsa, Alexander Shishkin, Andi Kleen,
Eranian Stephane
Cc: Mark Rutland, broonie, Ravi Bangoria, linux-kernel,
linux-perf-users, Zide Chen, Falcon Thomas, Dapeng Mi, Xudong Hao,
Dapeng Mi
Support arch-PEBS based SIMD/eGPRs/SSP registers sampling.
Arch-PEBS supports sampling of these registers, with all except SSP
placed into the XSAVE-Enabled Registers (XER) group with the layout
described below.
Field Name Registers Used Size
XSTATE_BV XINUSE for groups 8 B
Reserved Reserved 8 B
SSER XMM0-XMM15 16 regs * 16 B = 256 B
YMMHIR Upper 128 bits of YMM0-YMM15 16 regs * 16 B = 256 B
EGPR R16-R31 16 regs * 8 B = 128 B
OPMASKR K0-K7 8 regs * 8 B = 64 B
ZMMHIR Upper 256 bits of ZMM0-ZMM15 16 regs * 32 B = 512 B
Hi16ZMMR ZMM16-ZMM31 16 regs * 64 B = 1024 B
Memory space in the output buffer is allocated for these sub-groups as
long as the corresponding Format.XER[55:49] bits in the PEBS record
header are set. However, the arch-PEBS hardware engine does not write
the sub-group if it is not used (in INIT state). In such cases, the
corresponding bit in the XSTATE_BV bitmap is set to 0. Therefore, the
XSTATE_BV field is checked to determine if the register data is actually
written for each PEBS record. If not, the register data is not outputted
to userspace.
The SSP register is sampled and placed into the GPRs group by arch-PEBS.
Additionally, the MSRs IA32_PMC_{GPn|FXm}_CFG_C.[55:49] bits are used to
manage which types of these registers need to be sampled.
Arch-PEBS based SIMD/eGPRs/SSP sampling will be enabled in a subsequent
patch that sets PERF_PMU_CAP_SIMD_REGS.
Signed-off-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
---
arch/x86/events/intel/core.c | 126 ++++++++++++++++++++++++++++--
arch/x86/events/intel/ds.c | 77 ++++++++++++++++--
arch/x86/include/asm/msr-index.h | 7 ++
arch/x86/include/asm/perf_event.h | 8 +-
4 files changed, 204 insertions(+), 14 deletions(-)
diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c
index cfe5478aa5a4..15962a3457ee 100644
--- a/arch/x86/events/intel/core.c
+++ b/arch/x86/events/intel/core.c
@@ -3474,6 +3474,21 @@ static void intel_pmu_enable_event_ext(struct perf_event *event)
if (pebs_data_cfg & PEBS_DATACFG_XMMS)
ext |= ARCH_PEBS_VECR_XMM & cap.caps;
+ if (pebs_data_cfg & PEBS_DATACFG_YMMHS)
+ ext |= ARCH_PEBS_VECR_YMMH & cap.caps;
+
+ if (pebs_data_cfg & PEBS_DATACFG_EGPRS)
+ ext |= ARCH_PEBS_VECR_EGPRS & cap.caps;
+
+ if (pebs_data_cfg & PEBS_DATACFG_OPMASKS)
+ ext |= ARCH_PEBS_VECR_OPMASK & cap.caps;
+
+ if (pebs_data_cfg & PEBS_DATACFG_ZMMHS)
+ ext |= ARCH_PEBS_VECR_ZMMH & cap.caps;
+
+ if (pebs_data_cfg & PEBS_DATACFG_H16ZMMS)
+ ext |= ARCH_PEBS_VECR_H16ZMM & cap.caps;
+
if (pebs_data_cfg & PEBS_DATACFG_LBRS)
ext |= ARCH_PEBS_LBR & cap.caps;
@@ -4694,21 +4709,118 @@ static void intel_pebs_aliases_skl(struct perf_event *event)
return intel_pebs_aliases_precdist(event);
}
+static inline bool intel_pebs_support_regs(struct perf_event *event, u64 regs)
+{
+ struct arch_pebs_cap cap = hybrid(event->pmu, arch_pebs_cap);
+ int pebs_format = x86_pmu.intel_cap.pebs_format;
+ bool supported = true;
+
+ if (regs & PEBS_DATACFG_GP) {
+ /* Legacy PEBS always supports GPRs sampling. */
+ supported &= x86_pmu.arch_pebs ?
+ !!(ARCH_PEBS_GPR & cap.caps) : true;
+ }
+ if (regs & PEBS_DATACFG_XMMS) {
+ supported &= x86_pmu.arch_pebs ?
+ !!(ARCH_PEBS_VECR_XMM & cap.caps) :
+ pebs_format > 3 && x86_pmu.intel_cap.pebs_baseline;
+ }
+ /* Legacy PEBS doesn't support OPMASK/YMM+ and eGPRs sampling. */
+ if (regs & PEBS_DATACFG_YMMHS)
+ supported &= x86_pmu.arch_pebs && (ARCH_PEBS_VECR_YMMH & cap.caps);
+ if (regs & PEBS_DATACFG_EGPRS)
+ supported &= x86_pmu.arch_pebs && (ARCH_PEBS_VECR_EGPRS & cap.caps);
+ if (regs & PEBS_DATACFG_OPMASKS)
+ supported &= x86_pmu.arch_pebs && (ARCH_PEBS_VECR_OPMASK & cap.caps);
+ if (regs & PEBS_DATACFG_ZMMHS)
+ supported &= x86_pmu.arch_pebs && (ARCH_PEBS_VECR_ZMMH & cap.caps);
+ if (regs & PEBS_DATACFG_H16ZMMS)
+ supported &= x86_pmu.arch_pebs && (ARCH_PEBS_VECR_H16ZMM & cap.caps);
+
+ return supported;
+}
+
+static bool __regs_support_large_pebs(struct perf_event *event, bool intr)
+{
+ u64 regs = intr ? event->attr.sample_regs_intr :
+ event->attr.sample_regs_user;
+ u64 vec_regs = intr ? event->attr.sample_simd_vec_reg_intr :
+ event->attr.sample_simd_vec_reg_user;
+ u64 pred_regs = intr ? event->attr.sample_simd_pred_reg_intr :
+ event->attr.sample_simd_pred_reg_user;
+ u64 xregs_mask = PEBS_GP_REGS | PERF_X86_EGPRS_MASK |
+ BIT_ULL(PERF_REG_X86_SSP);
+
+ if (regs & ~xregs_mask)
+ return false;
+
+ if ((regs & PEBS_GP_REGS) &&
+ !intel_pebs_support_regs(event, PEBS_DATACFG_GP))
+ return false;
+
+ if ((regs & PERF_X86_EGPRS_MASK) &&
+ !intel_pebs_support_regs(event, PEBS_DATACFG_EGPRS))
+ return false;
+
+ if ((regs & BIT_ULL(PERF_REG_X86_SSP)) &&
+ (!x86_pmu.arch_pebs ||
+ !intel_pebs_support_regs(event, PEBS_DATACFG_GP)))
+ return false;
+
+ if (event_needs_opmask(event) && pred_regs &&
+ !intel_pebs_support_regs(event, PEBS_DATACFG_OPMASKS))
+ return false;
+
+ if (event_needs_xmm(event) && vec_regs &&
+ !intel_pebs_support_regs(event, PEBS_DATACFG_XMMS))
+ return false;
+
+ if (event_needs_ymm(event) && vec_regs &&
+ !intel_pebs_support_regs(event, PEBS_DATACFG_YMMHS))
+ return false;
+
+ if (event_needs_low16_zmm(event) && vec_regs &&
+ !intel_pebs_support_regs(event, PEBS_DATACFG_ZMMHS))
+ return false;
+
+ if (event_needs_high16_zmm(event) && vec_regs &&
+ !intel_pebs_support_regs(event, PEBS_DATACFG_H16ZMMS))
+ return false;
+
+ return true;
+}
+
+static inline bool intr_regs_support_large_pebs(struct perf_event *event)
+{
+ return __regs_support_large_pebs(event, true);
+}
+
+static inline bool user_regs_support_large_pebs(struct perf_event *event)
+{
+ return __regs_support_large_pebs(event, false);
+}
+
static unsigned long intel_pmu_large_pebs_flags(struct perf_event *event)
{
unsigned long flags = x86_pmu.large_pebs_flags;
- u64 gprs_mask = event->attr.sample_simd_regs_enabled ?
- PEBS_GP_REGS :
- PEBS_GP_REGS | PERF_REG_EXTENDED_MASK;
if (event->attr.use_clockid)
flags &= ~PERF_SAMPLE_TIME;
if (!event->attr.exclude_kernel)
flags &= ~PERF_SAMPLE_REGS_USER;
- if (event->attr.sample_regs_user & ~gprs_mask)
- flags &= ~PERF_SAMPLE_REGS_USER;
- if (event->attr.sample_regs_intr & ~gprs_mask)
- flags &= ~PERF_SAMPLE_REGS_INTR;
+ if (event->attr.sample_simd_regs_enabled) {
+ if (!user_regs_support_large_pebs(event))
+ flags &= ~PERF_SAMPLE_REGS_USER;
+ if (!intr_regs_support_large_pebs(event))
+ flags &= ~PERF_SAMPLE_REGS_INTR;
+ } else {
+ u64 gprs_mask = PEBS_GP_REGS | PERF_REG_EXTENDED_MASK;
+
+ if (event->attr.sample_regs_user & ~gprs_mask)
+ flags &= ~PERF_SAMPLE_REGS_USER;
+ if (event->attr.sample_regs_intr & ~gprs_mask)
+ flags &= ~PERF_SAMPLE_REGS_INTR;
+ }
return flags;
}
diff --git a/arch/x86/events/intel/ds.c b/arch/x86/events/intel/ds.c
index 54e6f73ffde4..c42c6f575a21 100644
--- a/arch/x86/events/intel/ds.c
+++ b/arch/x86/events/intel/ds.c
@@ -1734,11 +1734,22 @@ static u64 pebs_update_adaptive_cfg(struct perf_event *event)
((attr->config & INTEL_ARCH_EVENT_MASK) ==
x86_pmu.rtm_abort_event);
- if (gprs || (attr->precise_ip < 2) || tsx_weight)
+ if (gprs || (attr->precise_ip < 2) ||
+ tsx_weight || event_needs_ssp(event))
pebs_data_cfg |= PEBS_DATACFG_GP;
if (event_needs_xmm(event))
pebs_data_cfg |= PEBS_DATACFG_XMMS;
+ if (x86_pmu.arch_pebs && event_needs_ymm(event))
+ pebs_data_cfg |= PEBS_DATACFG_YMMHS;
+ if (x86_pmu.arch_pebs && event_needs_low16_zmm(event))
+ pebs_data_cfg |= PEBS_DATACFG_ZMMHS;
+ if (x86_pmu.arch_pebs && event_needs_high16_zmm(event))
+ pebs_data_cfg |= PEBS_DATACFG_H16ZMMS;
+ if (x86_pmu.arch_pebs && event_needs_opmask(event))
+ pebs_data_cfg |= PEBS_DATACFG_OPMASKS;
+ if (x86_pmu.arch_pebs && event_needs_egprs(event))
+ pebs_data_cfg |= PEBS_DATACFG_EGPRS;
if (sample_type & PERF_SAMPLE_BRANCH_STACK) {
/*
@@ -2697,15 +2708,69 @@ static void setup_arch_pebs_sample_data(struct perf_event *event,
meminfo->tsx_tuning, ax);
}
- if (header->xmm) {
+ if (header->xmm || header->ymmh || header->egpr ||
+ header->opmask || header->zmmh || header->h16zmm) {
+ struct arch_pebs_xer_header *xer_header = next_record;
struct pebs_xmm *xmm;
+ struct ymmh_struct *ymmh;
+ struct avx_512_zmm_uppers_state *zmmh;
+ struct avx_512_hi16_state *h16zmm;
+ struct avx_512_opmask_state *opmask;
+ struct apx_state *egpr;
next_record += sizeof(struct arch_pebs_xer_header);
- ignore_mask |= XFEATURE_MASK_SSE;
- xmm = next_record;
- perf_regs->xmm_regs = xmm->xmm;
- next_record = xmm + 1;
+ if (header->xmm) {
+ ignore_mask |= XFEATURE_MASK_SSE;
+ xmm = next_record;
+ /*
+ * Only output XMM regs to user space when arch-PEBS
+ * really writes data into xstate area.
+ */
+ if (xer_header->xstate & XFEATURE_MASK_SSE)
+ perf_regs->xmm_regs = xmm->xmm;
+ next_record = xmm + 1;
+ }
+
+ if (header->ymmh) {
+ ignore_mask |= XFEATURE_MASK_YMM;
+ ymmh = next_record;
+ if (xer_header->xstate & XFEATURE_MASK_YMM)
+ perf_regs->ymmh = ymmh;
+ next_record = ymmh + 1;
+ }
+
+ if (header->egpr) {
+ ignore_mask |= XFEATURE_MASK_APX;
+ egpr = next_record;
+ if (xer_header->xstate & XFEATURE_MASK_APX)
+ perf_regs->egpr = egpr;
+ next_record = egpr + 1;
+ }
+
+ if (header->opmask) {
+ ignore_mask |= XFEATURE_MASK_OPMASK;
+ opmask = next_record;
+ if (xer_header->xstate & XFEATURE_MASK_OPMASK)
+ perf_regs->opmask = opmask;
+ next_record = opmask + 1;
+ }
+
+ if (header->zmmh) {
+ ignore_mask |= XFEATURE_MASK_ZMM_Hi256;
+ zmmh = next_record;
+ if (xer_header->xstate & XFEATURE_MASK_ZMM_Hi256)
+ perf_regs->zmmh = zmmh;
+ next_record = zmmh + 1;
+ }
+
+ if (header->h16zmm) {
+ ignore_mask |= XFEATURE_MASK_Hi16_ZMM;
+ h16zmm = next_record;
+ if (xer_header->xstate & XFEATURE_MASK_Hi16_ZMM)
+ perf_regs->h16zmm = h16zmm;
+ next_record = h16zmm + 1;
+ }
}
if (header->lbr) {
diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h
index 18c4be75e927..9c26d4075c5f 100644
--- a/arch/x86/include/asm/msr-index.h
+++ b/arch/x86/include/asm/msr-index.h
@@ -350,6 +350,13 @@
#define ARCH_PEBS_LBR_SHIFT 40
#define ARCH_PEBS_LBR (0x3ull << ARCH_PEBS_LBR_SHIFT)
#define ARCH_PEBS_VECR_XMM BIT_ULL(49)
+#define ARCH_PEBS_VECR_YMMH BIT_ULL(50)
+#define ARCH_PEBS_VECR_EGPRS BIT_ULL(51)
+#define ARCH_PEBS_VECR_OPMASK BIT_ULL(53)
+#define ARCH_PEBS_VECR_ZMMH BIT_ULL(54)
+#define ARCH_PEBS_VECR_H16ZMM BIT_ULL(55)
+#define ARCH_PEBS_VECR_EXT_SHIFT 49
+#define ARCH_PEBS_VECR_EXT (0x7full << ARCH_PEBS_VECR_EXT_SHIFT)
#define ARCH_PEBS_GPR BIT_ULL(61)
#define ARCH_PEBS_AUX BIT_ULL(62)
#define ARCH_PEBS_EN BIT_ULL(63)
diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h
index 4302ef39c42e..12f7db8c57b4 100644
--- a/arch/x86/include/asm/perf_event.h
+++ b/arch/x86/include/asm/perf_event.h
@@ -150,6 +150,11 @@
#define PEBS_DATACFG_LBRS BIT_ULL(3)
#define PEBS_DATACFG_CNTR BIT_ULL(4)
#define PEBS_DATACFG_METRICS BIT_ULL(5)
+#define PEBS_DATACFG_YMMHS BIT_ULL(6)
+#define PEBS_DATACFG_OPMASKS BIT_ULL(7)
+#define PEBS_DATACFG_ZMMHS BIT_ULL(8)
+#define PEBS_DATACFG_H16ZMMS BIT_ULL(9)
+#define PEBS_DATACFG_EGPRS BIT_ULL(10)
#define PEBS_DATACFG_LBR_SHIFT 24
#define PEBS_DATACFG_CNTR_SHIFT 32
#define PEBS_DATACFG_CNTR_MASK GENMASK_ULL(15, 0)
@@ -547,7 +552,8 @@ struct arch_pebs_header {
rsvd3:7,
xmm:1,
ymmh:1,
- rsvd4:2,
+ egpr:1,
+ rsvd4:1,
opmask:1,
zmmh:1,
h16zmm:1,
--
2.34.1
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [Patch v9 22/24] perf/x86/intel: Enable PERF_PMU_CAP_SIMD_REGS capability
2026-07-06 1:54 [Patch v9 00/24] Support SIMD/eGPRs/SSP registers sampling for perf Dapeng Mi
` (20 preceding siblings ...)
2026-07-06 1:54 ` [Patch v9 21/24] perf/x86/intel: Support arch-PEBS based SIMD/eGPRs/SSP sampling Dapeng Mi
@ 2026-07-06 1:54 ` Dapeng Mi
2026-07-06 2:57 ` sashiko-bot
2026-07-06 1:54 ` [Patch v9 23/24] perf/x86: Activate back-to-back NMI detection for arch-PEBS induced NMIs Dapeng Mi
2026-07-06 1:54 ` [Patch v9 24/24] perf/x86/intel: Add sanity check for PEBS fragment size Dapeng Mi
23 siblings, 1 reply; 42+ messages in thread
From: Dapeng Mi @ 2026-07-06 1:54 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Thomas Gleixner, Dave Hansen, Ian Rogers,
Adrian Hunter, Jiri Olsa, Alexander Shishkin, Andi Kleen,
Eranian Stephane
Cc: Mark Rutland, broonie, Ravi Bangoria, linux-kernel,
linux-perf-users, Zide Chen, Falcon Thomas, Dapeng Mi, Xudong Hao,
Dapeng Mi, Kan Liang
Enable the PERF_PMU_CAP_SIMD_REGS capability when XSAVES support is
available for extended registers (YMM, ZMM, OPMASK, eGPRs, or SSP).
To simplify the validation logic and maintain consistency, enable
PERF_PMU_CAP_SIMD_REGS capability only when both XSAVES and
architectural PEBS are supported. In environments where PEBS is
unavailable (such as a guest), enable the capability if XSAVES
supports extended register states beyond basic XMM.
Co-developed-by: Kan Liang <kan.liang@linux.intel.com>
Signed-off-by: Kan Liang <kan.liang@linux.intel.com>
Signed-off-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
---
arch/x86/events/intel/core.c | 29 ++++++++++++++++++++++++++---
1 file changed, 26 insertions(+), 3 deletions(-)
diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c
index 15962a3457ee..56997731dc83 100644
--- a/arch/x86/events/intel/core.c
+++ b/arch/x86/events/intel/core.c
@@ -6303,14 +6303,37 @@ static inline void __intel_update_pmu_xregs_caps(struct pmu *pmu)
*/
x86_pmu.ext_regs_mask |= XFEATURE_MASK_SSE;
+ if (boot_cpu_has(X86_FEATURE_AVX) &&
+ cpu_has_xfeatures(XFEATURE_MASK_YMM, NULL))
+ x86_pmu.ext_regs_mask |= XFEATURE_MASK_YMM;
+ if (boot_cpu_has(X86_FEATURE_APX) &&
+ cpu_has_xfeatures(XFEATURE_MASK_APX, NULL))
+ x86_pmu.ext_regs_mask |= XFEATURE_MASK_APX;
+ if (boot_cpu_has(X86_FEATURE_AVX512F)) {
+ if (cpu_has_xfeatures(XFEATURE_MASK_OPMASK, NULL))
+ x86_pmu.ext_regs_mask |= XFEATURE_MASK_OPMASK;
+ if (cpu_has_xfeatures(XFEATURE_MASK_ZMM_Hi256, NULL))
+ x86_pmu.ext_regs_mask |= XFEATURE_MASK_ZMM_Hi256;
+ if (cpu_has_xfeatures(XFEATURE_MASK_Hi16_ZMM, NULL))
+ x86_pmu.ext_regs_mask |= XFEATURE_MASK_Hi16_ZMM;
+ }
+ if (cpu_feature_enabled(X86_FEATURE_USER_SHSTK))
+ x86_pmu.ext_regs_mask |= XFEATURE_MASK_CET_USER;
+
/* PEBS supported case */
- if ((x86_pmu.arch_pebs && (caps & ARCH_PEBS_VECR_XMM)) ||
- (x86_pmu.intel_cap.pebs_format >= 4 && x86_pmu.intel_cap.pebs_baseline))
+ if (x86_pmu.intel_cap.pebs_format >= 4 && x86_pmu.intel_cap.pebs_baseline)
dest_pmu->capabilities |= PERF_PMU_CAP_EXTENDED_REGS;
+ if (x86_pmu.arch_pebs && (caps & ARCH_PEBS_VECR_EXT)) {
+ dest_pmu->capabilities |= PERF_PMU_CAP_EXTENDED_REGS |
+ PERF_PMU_CAP_SIMD_REGS;
+ }
/* PEBS unsupported case (e.g., guest) */
- if (!x86_pmu.intel_cap.pebs_format)
+ if (!x86_pmu.intel_cap.pebs_format) {
dest_pmu->capabilities |= PERF_PMU_CAP_EXTENDED_REGS;
+ if (x86_pmu.ext_regs_mask > XFEATURE_MASK_SSE)
+ dest_pmu->capabilities |= PERF_PMU_CAP_SIMD_REGS;
+ }
}
static inline void __intel_update_large_pebs_flags(struct pmu *pmu)
--
2.34.1
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [Patch v9 23/24] perf/x86: Activate back-to-back NMI detection for arch-PEBS induced NMIs
2026-07-06 1:54 [Patch v9 00/24] Support SIMD/eGPRs/SSP registers sampling for perf Dapeng Mi
` (21 preceding siblings ...)
2026-07-06 1:54 ` [Patch v9 22/24] perf/x86/intel: Enable PERF_PMU_CAP_SIMD_REGS capability Dapeng Mi
@ 2026-07-06 1:54 ` Dapeng Mi
2026-07-06 1:54 ` [Patch v9 24/24] perf/x86/intel: Add sanity check for PEBS fragment size Dapeng Mi
23 siblings, 0 replies; 42+ messages in thread
From: Dapeng Mi @ 2026-07-06 1:54 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Thomas Gleixner, Dave Hansen, Ian Rogers,
Adrian Hunter, Jiri Olsa, Alexander Shishkin, Andi Kleen,
Eranian Stephane
Cc: Mark Rutland, broonie, Ravi Bangoria, linux-kernel,
linux-perf-users, Zide Chen, Falcon Thomas, Dapeng Mi, Xudong Hao,
Dapeng Mi
When two or more identical PEBS events with the same sampling period are
programmed on a mix of PDIST and non-PDIST counters, multiple
back-to-back NMIs can be triggered.
The Linux PMI handler processes the first NMI and clears the
GLOBAL_STATUS MSR. If a second NMI is triggered immediately after
the first, it is recognized as a "suspicious NMI" because no bits are set
in the GLOBAL_STATUS MSR (cleared by the first NMI).
This issue does not lead to PEBS data corruption or data loss, but it
does result in an annoying warning message.
The current NMI handler supports back-to-back NMI detection, but it
requires the PMI handler to return the count of actually processed events,
which the PEBS handler does not currently do.
Thus, modify the PEBS handlers to return the count of actually processed
events, thereby activating back-to-back NMI detection and avoiding the
"suspicious NMI" warning.
Suggested-by: Andi Kleen <ak@linux.intel.com>
Signed-off-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
---
arch/x86/events/intel/core.c | 31 +++++++++++++++++---------
arch/x86/events/intel/ds.c | 43 ++++++++++++++++++++++++------------
arch/x86/events/perf_event.h | 2 +-
3 files changed, 50 insertions(+), 26 deletions(-)
diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c
index 56997731dc83..a2d08f405a57 100644
--- a/arch/x86/events/intel/core.c
+++ b/arch/x86/events/intel/core.c
@@ -3762,8 +3762,8 @@ static void intel_pmu_reset(void)
*
* The contents and other behavior of the guest event do not matter.
*/
-static void x86_pmu_handle_guest_pebs(struct pt_regs *regs,
- struct perf_sample_data *data)
+static int x86_pmu_handle_guest_pebs(struct pt_regs *regs,
+ struct perf_sample_data *data)
{
struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
u64 guest_pebs_idxs = cpuc->pebs_enabled & ~cpuc->intel_ctrl_host_mask;
@@ -3771,11 +3771,11 @@ static void x86_pmu_handle_guest_pebs(struct pt_regs *regs,
int bit;
if (!unlikely(perf_guest_state()))
- return;
+ return 0;
if (!x86_pmu.pebs_ept || !x86_pmu.pebs_active ||
!guest_pebs_idxs)
- return;
+ return 0;
for_each_set_bit(bit, (unsigned long *)&guest_pebs_idxs, X86_PMC_IDX_MAX) {
event = cpuc->events[bit];
@@ -3785,9 +3785,14 @@ static void x86_pmu_handle_guest_pebs(struct pt_regs *regs,
perf_sample_data_init(data, 0, event->hw.last_period);
perf_event_overflow(event, data, regs);
- /* Inject one fake event is enough. */
- break;
+ /*
+ * Inject one fake event is enough.
+ * Returning 1 to inform PMI is handled.
+ */
+ return 1;
}
+
+ return 0;
}
static int handle_pmi_common(struct pt_regs *regs, u64 status)
@@ -3836,9 +3841,11 @@ static int handle_pmi_common(struct pt_regs *regs, u64 status)
if (__test_and_clear_bit(GLOBAL_STATUS_BUFFER_OVF_BIT, (unsigned long *)&status)) {
u64 pebs_enabled = cpuc->pebs_enabled;
- handled++;
- x86_pmu_handle_guest_pebs(regs, &data);
- static_call(x86_pmu_drain_pebs)(regs, &data);
+ handled += x86_pmu_handle_guest_pebs(regs, &data);
+ handled += static_call(x86_pmu_drain_pebs)(regs, &data);
+ /* Ensure no "suspicious NMI" warning for empty PEBS buffer. */
+ if (!handled)
+ handled++;
/*
* PMI throttle may be triggered, which stops the PEBS event.
@@ -3865,8 +3872,10 @@ static int handle_pmi_common(struct pt_regs *regs, u64 status)
*/
if (__test_and_clear_bit(GLOBAL_STATUS_ARCH_PEBS_THRESHOLD_BIT,
(unsigned long *)&status)) {
- handled++;
- static_call(x86_pmu_drain_pebs)(regs, &data);
+ handled += static_call(x86_pmu_drain_pebs)(regs, &data);
+ /* Ensure no "suspicious NMI" warning for empty PEBS buffer. */
+ if (!handled)
+ handled++;
if (cpuc->events[INTEL_PMC_IDX_FIXED_SLOTS] &&
is_pebs_counter_event_group(cpuc->events[INTEL_PMC_IDX_FIXED_SLOTS]))
diff --git a/arch/x86/events/intel/ds.c b/arch/x86/events/intel/ds.c
index c42c6f575a21..b38aed4f62b4 100644
--- a/arch/x86/events/intel/ds.c
+++ b/arch/x86/events/intel/ds.c
@@ -3031,7 +3031,7 @@ __intel_pmu_pebs_events(struct perf_event *event,
__intel_pmu_pebs_last_event(event, iregs, regs, data, at, count, setup_sample);
}
-static void intel_pmu_drain_pebs_core(struct pt_regs *iregs, struct perf_sample_data *data)
+static int intel_pmu_drain_pebs_core(struct pt_regs *iregs, struct perf_sample_data *data)
{
struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
struct debug_store *ds = cpuc->ds;
@@ -3040,7 +3040,7 @@ static void intel_pmu_drain_pebs_core(struct pt_regs *iregs, struct perf_sample_
int n;
if (!x86_pmu.pebs_active)
- return;
+ return 0;
at = (struct pebs_record_core *)(unsigned long)ds->pebs_buffer_base;
top = (struct pebs_record_core *)(unsigned long)ds->pebs_index;
@@ -3051,22 +3051,25 @@ static void intel_pmu_drain_pebs_core(struct pt_regs *iregs, struct perf_sample_
ds->pebs_index = ds->pebs_buffer_base;
if (!test_bit(0, cpuc->active_mask))
- return;
+ return 0;
WARN_ON_ONCE(!event);
if (!event->attr.precise_ip)
- return;
+ return 0;
n = top - at;
if (n <= 0) {
if (event->hw.flags & PERF_X86_EVENT_AUTO_RELOAD)
intel_pmu_save_and_restart_reload(event, 0);
- return;
+ return 0;
}
__intel_pmu_pebs_events(event, iregs, data, at, top, 0, n,
setup_pebs_fixed_sample_data);
+
+ /* PMC0 only */
+ return 1;
}
static void intel_pmu_pebs_event_update_no_drain(struct cpu_hw_events *cpuc, u64 mask)
@@ -3089,7 +3092,7 @@ static void intel_pmu_pebs_event_update_no_drain(struct cpu_hw_events *cpuc, u64
}
}
-static void intel_pmu_drain_pebs_nhm(struct pt_regs *iregs, struct perf_sample_data *data)
+static int intel_pmu_drain_pebs_nhm(struct pt_regs *iregs, struct perf_sample_data *data)
{
struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
struct debug_store *ds = cpuc->ds;
@@ -3098,11 +3101,12 @@ static void intel_pmu_drain_pebs_nhm(struct pt_regs *iregs, struct perf_sample_d
short counts[INTEL_PMC_IDX_FIXED + MAX_FIXED_PEBS_EVENTS] = {};
short error[INTEL_PMC_IDX_FIXED + MAX_FIXED_PEBS_EVENTS] = {};
int max_pebs_events = intel_pmu_max_num_pebs(NULL);
+ u64 events_bitmap = 0;
int bit, i, size;
u64 mask;
if (!x86_pmu.pebs_active)
- return;
+ return 0;
base = (struct pebs_record_nhm *)(unsigned long)ds->pebs_buffer_base;
top = (struct pebs_record_nhm *)(unsigned long)ds->pebs_index;
@@ -3118,7 +3122,7 @@ static void intel_pmu_drain_pebs_nhm(struct pt_regs *iregs, struct perf_sample_d
if (unlikely(base >= top)) {
intel_pmu_pebs_event_update_no_drain(cpuc, mask);
- return;
+ return 0;
}
for (at = base; at < top; at += x86_pmu.pebs_record_size) {
@@ -3182,6 +3186,7 @@ static void intel_pmu_drain_pebs_nhm(struct pt_regs *iregs, struct perf_sample_d
if ((counts[bit] == 0) && (error[bit] == 0))
continue;
+ events_bitmap |= BIT_ULL(bit);
event = cpuc->events[bit];
if (WARN_ON_ONCE(!event))
continue;
@@ -3203,6 +3208,8 @@ static void intel_pmu_drain_pebs_nhm(struct pt_regs *iregs, struct perf_sample_d
setup_pebs_fixed_sample_data);
}
}
+
+ return hweight64(events_bitmap);
}
static __always_inline void
@@ -3256,7 +3263,7 @@ __intel_pmu_handle_last_pebs_record(struct pt_regs *iregs,
}
-static void intel_pmu_drain_pebs_icl(struct pt_regs *iregs, struct perf_sample_data *data)
+static int intel_pmu_drain_pebs_icl(struct pt_regs *iregs, struct perf_sample_data *data)
{
short counts[INTEL_PMC_IDX_FIXED + MAX_FIXED_PEBS_EVENTS] = {};
void *last[INTEL_PMC_IDX_FIXED + MAX_FIXED_PEBS_EVENTS];
@@ -3266,10 +3273,11 @@ static void intel_pmu_drain_pebs_icl(struct pt_regs *iregs, struct perf_sample_d
struct pt_regs *regs = &perf_regs->regs;
struct pebs_basic *basic;
void *base, *at, *top;
+ u64 events_bitmap = 0;
u64 mask;
if (!x86_pmu.pebs_active)
- return;
+ return 0;
base = (struct pebs_basic *)(unsigned long)ds->pebs_buffer_base;
top = (struct pebs_basic *)(unsigned long)ds->pebs_index;
@@ -3282,7 +3290,7 @@ static void intel_pmu_drain_pebs_icl(struct pt_regs *iregs, struct perf_sample_d
if (unlikely(base >= top)) {
intel_pmu_pebs_event_update_no_drain(cpuc, mask);
- return;
+ return 0;
}
if (!iregs)
@@ -3297,6 +3305,7 @@ static void intel_pmu_drain_pebs_icl(struct pt_regs *iregs, struct perf_sample_d
continue;
pebs_status = mask & basic->applicable_counters;
+ events_bitmap |= pebs_status;
__intel_pmu_handle_pebs_record(iregs, regs, data, at,
pebs_status, counts, last,
setup_pebs_adaptive_sample_data);
@@ -3304,10 +3313,12 @@ static void intel_pmu_drain_pebs_icl(struct pt_regs *iregs, struct perf_sample_d
__intel_pmu_handle_last_pebs_record(iregs, regs, data, mask, counts, last,
setup_pebs_adaptive_sample_data);
+
+ return hweight64(events_bitmap);
}
-static void intel_pmu_drain_arch_pebs(struct pt_regs *iregs,
- struct perf_sample_data *data)
+static int intel_pmu_drain_arch_pebs(struct pt_regs *iregs,
+ struct perf_sample_data *data)
{
short counts[INTEL_PMC_IDX_FIXED + MAX_FIXED_PEBS_EVENTS] = {};
void *last[INTEL_PMC_IDX_FIXED + MAX_FIXED_PEBS_EVENTS];
@@ -3316,13 +3327,14 @@ static void intel_pmu_drain_arch_pebs(struct pt_regs *iregs,
struct x86_perf_regs *perf_regs = this_cpu_ptr(&x86_pebs_regs);
struct pt_regs *regs = &perf_regs->regs;
void *base, *at, *top;
+ u64 events_bitmap = 0;
u64 mask;
rdmsrq(MSR_IA32_PEBS_INDEX, index.whole);
if (unlikely(!index.wr)) {
intel_pmu_pebs_event_update_no_drain(cpuc, X86_PMC_IDX_MAX);
- return;
+ return 0;
}
base = cpuc->pebs_vaddr;
@@ -3361,6 +3373,7 @@ static void intel_pmu_drain_arch_pebs(struct pt_regs *iregs,
basic = at + sizeof(struct arch_pebs_header);
pebs_status = mask & basic->applicable_counters;
+ events_bitmap |= pebs_status;
__intel_pmu_handle_pebs_record(iregs, regs, data, at,
pebs_status, counts, last,
setup_arch_pebs_sample_data);
@@ -3380,6 +3393,8 @@ static void intel_pmu_drain_arch_pebs(struct pt_regs *iregs,
__intel_pmu_handle_last_pebs_record(iregs, regs, data, mask,
counts, last,
setup_arch_pebs_sample_data);
+
+ return hweight64(events_bitmap);
}
static void __init intel_arch_pebs_init(void)
diff --git a/arch/x86/events/perf_event.h b/arch/x86/events/perf_event.h
index b1f9d17dddb6..1b22be540fde 100644
--- a/arch/x86/events/perf_event.h
+++ b/arch/x86/events/perf_event.h
@@ -1059,7 +1059,7 @@ struct x86_pmu {
int pebs_record_size;
int pebs_buffer_size;
u64 pebs_events_mask;
- void (*drain_pebs)(struct pt_regs *regs, struct perf_sample_data *data);
+ int (*drain_pebs)(struct pt_regs *regs, struct perf_sample_data *data);
struct event_constraint *pebs_constraints;
void (*pebs_aliases)(struct perf_event *event);
u64 (*pebs_latency_data)(struct perf_event *event, u64 status);
--
2.34.1
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [Patch v9 24/24] perf/x86/intel: Add sanity check for PEBS fragment size
2026-07-06 1:54 [Patch v9 00/24] Support SIMD/eGPRs/SSP registers sampling for perf Dapeng Mi
` (22 preceding siblings ...)
2026-07-06 1:54 ` [Patch v9 23/24] perf/x86: Activate back-to-back NMI detection for arch-PEBS induced NMIs Dapeng Mi
@ 2026-07-06 1:54 ` Dapeng Mi
2026-07-06 5:04 ` sashiko-bot
23 siblings, 1 reply; 42+ messages in thread
From: Dapeng Mi @ 2026-07-06 1:54 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Thomas Gleixner, Dave Hansen, Ian Rogers,
Adrian Hunter, Jiri Olsa, Alexander Shishkin, Andi Kleen,
Eranian Stephane
Cc: Mark Rutland, broonie, Ravi Bangoria, linux-kernel,
linux-perf-users, Zide Chen, Falcon Thomas, Dapeng Mi, Xudong Hao,
Dapeng Mi
Prevent potential infinite loops by adding a sanity check for the
corrupted PEBS fragment sizes which could happen in theory.
If a corrupted PEBS fragment is detected, the entire PEBS record
including the corrupted fragment and all subsequent records will be
dropped and a NULL PEBS record is reported to user space. This ensures
the integrity of PEBS data and prevents infinite loops in
setup_arch_pebs_sample_data() again.
Please note software has no way to figure out which events are impacted
by the corrupted record, so the last record of each event would be
discarded for all events if corrupted record is detected even though
it may be a well-formed record for some events.
Signed-off-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
---
arch/x86/events/intel/core.c | 2 +-
arch/x86/events/intel/ds.c | 70 ++++++++++++++++++++++++++----------
2 files changed, 52 insertions(+), 20 deletions(-)
diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c
index a2d08f405a57..7fb0b53ed8b4 100644
--- a/arch/x86/events/intel/core.c
+++ b/arch/x86/events/intel/core.c
@@ -3797,7 +3797,7 @@ static int x86_pmu_handle_guest_pebs(struct pt_regs *regs,
static int handle_pmi_common(struct pt_regs *regs, u64 status)
{
- struct perf_sample_data data;
+ struct perf_sample_data data = {};
struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
int bit;
int handled = 0;
diff --git a/arch/x86/events/intel/ds.c b/arch/x86/events/intel/ds.c
index b38aed4f62b4..7a8e61905539 100644
--- a/arch/x86/events/intel/ds.c
+++ b/arch/x86/events/intel/ds.c
@@ -1242,7 +1242,7 @@ int intel_pmu_drain_bts_buffer(void)
void intel_pmu_drain_pebs_buffer(void)
{
- struct perf_sample_data data;
+ struct perf_sample_data data = {};
static_call(x86_pmu_drain_pebs)(NULL, &data);
}
@@ -2659,6 +2659,9 @@ static void setup_arch_pebs_sample_data(struct perf_event *event,
again:
header = at;
+ if (!header->size)
+ return;
+
next_record = at + sizeof(struct arch_pebs_header);
if (header->basic) {
struct arch_pebs_basic *basic = next_record;
@@ -2940,13 +2943,21 @@ __intel_pmu_pebs_last_event(struct perf_event *event,
struct pt_regs *iregs,
struct pt_regs *regs,
struct perf_sample_data *data,
- void *at,
- int count,
+ void *at, int count, bool corrupted,
setup_fn setup_sample)
{
struct hw_perf_event *hwc = &event->hw;
- setup_sample(event, iregs, at, data, regs);
+ /* Skip parsing corrupted PEBS record. */
+ if (corrupted) {
+ /* Clear stale register states in previous records. */
+ memset(regs, 0, sizeof(*regs));
+ x86_pmu_clear_perf_regs(regs);
+ perf_sample_data_init(data, 0, event->hw.last_period);
+ } else {
+ setup_sample(event, iregs, at, data, regs);
+ }
+
if (iregs == &dummy_iregs) {
/*
* The PEBS records may be drained in the non-overflow context,
@@ -2964,12 +2975,16 @@ __intel_pmu_pebs_last_event(struct perf_event *event,
}
if (hwc->flags & PERF_X86_EVENT_AUTO_RELOAD) {
- if ((is_pebs_counter_event_group(event))) {
- /*
- * The value of each sample has been updated when setup
- * the corresponding sample data.
- */
- perf_event_update_userpage(event);
+ if (is_pebs_counter_event_group(event)) {
+ if (corrupted) {
+ intel_pmu_save_and_restart_reload(event, 1);
+ } else {
+ /*
+ * The value of each sample has been updated
+ * when setup the corresponding sample data.
+ */
+ perf_event_update_userpage(event);
+ }
} else {
/*
* Now, auto-reload is only enabled in fixed period mode.
@@ -2993,7 +3008,7 @@ __intel_pmu_pebs_last_event(struct perf_event *event,
* counters-snapshotting record, only needs to set the new
* period for the counter.
*/
- if (is_pebs_counter_event_group(event))
+ if (is_pebs_counter_event_group(event) && !corrupted)
static_call(x86_pmu_set_period)(event);
else
intel_pmu_save_and_restart(event);
@@ -3022,13 +3037,15 @@ __intel_pmu_pebs_events(struct perf_event *event,
iregs = &dummy_iregs;
while (cnt > 1) {
- __intel_pmu_pebs_event(event, iregs, regs, data, at, setup_sample);
+ __intel_pmu_pebs_event(event, iregs, regs, data,
+ at, setup_sample);
at += cpuc->pebs_record_size;
at = get_next_pebs_record_by_bit(at, top, bit);
cnt--;
}
- __intel_pmu_pebs_last_event(event, iregs, regs, data, at, count, setup_sample);
+ __intel_pmu_pebs_last_event(event, iregs, regs, data, at,
+ count, false, setup_sample);
}
static int intel_pmu_drain_pebs_core(struct pt_regs *iregs, struct perf_sample_data *data)
@@ -3244,7 +3261,8 @@ static __always_inline void
__intel_pmu_handle_last_pebs_record(struct pt_regs *iregs,
struct pt_regs *regs,
struct perf_sample_data *data,
- u64 mask, short *counts, void **last,
+ u64 mask, short *counts,
+ void **last, bool corrupted,
setup_fn setup_sample)
{
struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
@@ -3258,7 +3276,7 @@ __intel_pmu_handle_last_pebs_record(struct pt_regs *iregs,
event = cpuc->events[bit];
__intel_pmu_pebs_last_event(event, iregs, regs, data, last[bit],
- counts[bit], setup_sample);
+ counts[bit], corrupted, setup_sample);
}
}
@@ -3301,6 +3319,8 @@ static int intel_pmu_drain_pebs_icl(struct pt_regs *iregs, struct perf_sample_da
u64 pebs_status;
basic = at;
+ if (WARN_ON_ONCE(!basic->format_size))
+ break;
if (basic->format_size != cpuc->pebs_record_size)
continue;
@@ -3312,7 +3332,7 @@ static int intel_pmu_drain_pebs_icl(struct pt_regs *iregs, struct perf_sample_da
}
__intel_pmu_handle_last_pebs_record(iregs, regs, data, mask, counts, last,
- setup_pebs_adaptive_sample_data);
+ false, setup_pebs_adaptive_sample_data);
return hweight64(events_bitmap);
}
@@ -3328,6 +3348,7 @@ static int intel_pmu_drain_arch_pebs(struct pt_regs *iregs,
struct pt_regs *regs = &perf_regs->regs;
void *base, *at, *top;
u64 events_bitmap = 0;
+ bool corrupted = false;
u64 mask;
rdmsrq(MSR_IA32_PEBS_INDEX, index.whole);
@@ -3362,8 +3383,10 @@ static int intel_pmu_drain_arch_pebs(struct pt_regs *iregs,
header = at;
- if (WARN_ON_ONCE(!header->size))
- break;
+ if (WARN_ON_ONCE(!header->size)) {
+ corrupted = true;
+ goto done;
+ }
/* 1st fragment or single record must have basic group */
if (!header->basic) {
@@ -3383,15 +3406,24 @@ static int intel_pmu_drain_arch_pebs(struct pt_regs *iregs,
if (!header->size)
break;
at += header->size;
+ if (WARN_ON_ONCE(at >= top)) {
+ corrupted = true;
+ goto done;
+ }
header = at;
}
/* Skip last fragment or the single record */
at += header->size;
+ if (WARN_ON_ONCE(at > top)) {
+ corrupted = true;
+ goto done;
+ }
}
+done:
__intel_pmu_handle_last_pebs_record(iregs, regs, data, mask,
- counts, last,
+ counts, last, corrupted,
setup_arch_pebs_sample_data);
return hweight64(events_bitmap);
--
2.34.1
^ permalink raw reply related [flat|nested] 42+ messages in thread
* Re: [Patch v9 07/24] x86/fpu/xstate: Add xsaves_nmi() helper
2026-07-06 1:54 ` [Patch v9 07/24] x86/fpu/xstate: Add xsaves_nmi() helper Dapeng Mi
@ 2026-07-06 2:18 ` sashiko-bot
2026-07-06 9:09 ` Mi, Dapeng
0 siblings, 1 reply; 42+ messages in thread
From: sashiko-bot @ 2026-07-06 2:18 UTC (permalink / raw)
To: Dapeng Mi; +Cc: linux-perf-users
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] The use of the XSAVES instruction in xsaves_nmi() breaks FPU snapshotting to independent buffers due to the hardware's 'modified optimization', leading to a cross-process information leak.
--
commit e8d6f39a5d4512be90e9976febc30b8f46c0ea64
Author: Kan Liang <kan.liang@linux.intel.com>
x86/fpu/xstate: Add xsaves_nmi() helper
This commit adds a new xsaves_nmi() helper to safely save supported xsave
states from within an NMI handler. The helper is intended for the perf
subsystem to snapshot SIMD and APX eGPRs registers.
> diff --git a/arch/x86/kernel/fpu/xstate.c b/arch/x86/kernel/fpu/xstate.c
> index a7b6524a9dea2..d39c01546dee7 100644
> --- a/arch/x86/kernel/fpu/xstate.c
> +++ b/arch/x86/kernel/fpu/xstate.c
[ ... ]
> +void xsaves_nmi(struct xregs_state *xstate, u64 mask)
> +{
> + int err;
> +
> + if (!in_nmi())
> + return;
> +
> + XSTATE_OP(XSAVES, xstate, (u32)mask, (u32)(mask >> 32), err);
[Severity: High]
Does this code leak stale data into the perf samples?
The XSAVES instruction uses a hardware modified optimization that tracks
whether a state component was modified since the last XRSTORS. If a
component is unmodified, XSAVES skips writing it to memory while still
setting its presence bit in the XSTATE_BV header.
When the perf subsystem calls xsaves_nmi() in x86_pmu_sample_xregs()
with an independent per-CPU buffer like ext_regs_buf, skipping the memory
write leaves whatever stale data was previously in that buffer.
Because the bit in XSTATE_BV is still set, won't the perf subsystem
read this stale data (potentially from other tasks) and leak it to
user-space?
> + WARN_ON_ONCE(err);
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260706015439.3040804-1-dapeng1.mi@linux.intel.com?part=7
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [Patch v9 02/24] perf/x86: Move hybrid PMU initialization before x86_pmu_starting_cpu()
2026-07-06 1:54 ` [Patch v9 02/24] perf/x86: Move hybrid PMU initialization before x86_pmu_starting_cpu() Dapeng Mi
@ 2026-07-06 2:18 ` sashiko-bot
2026-07-06 8:33 ` Mi, Dapeng
0 siblings, 1 reply; 42+ messages in thread
From: sashiko-bot @ 2026-07-06 2:18 UTC (permalink / raw)
To: Dapeng Mi; +Cc: linux-perf-users
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Preexisting out-of-bounds read in `check_hw_exists()` on hybrid platforms due to an invalid `container_of()` cast on the global static `pmu` struct via the `hybrid()` macro.
--
commit 8216e613cee4854e16daaa11279146d17bc7f313
Author: Dapeng Mi <dapeng1.mi@linux.intel.com>
perf/x86: Move hybrid PMU initialization before x86_pmu_starting_cpu()
The current approach initializes hybrid PMU structures immediately before
registering them. This is risky as it can lead to key fields being
inadvertently overwritten. This patch mitigates this by moving the
default hybrid structure initialization before calling
x86_pmu_starting_cpu().
> diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c
> index 872d07a5fa80d..0888d3b0923e1 100644
> --- a/arch/x86/events/core.c
> +++ b/arch/x86/events/core.c
> @@ -2195,8 +2195,20 @@ static int __init init_hw_perf_events(void)
>
> pmu.attr_update = x86_pmu.attr_update;
>
> - if (!is_hybrid())
> + if (!is_hybrid()) {
> x86_pmu_show_pmu_cap(NULL);
> + } else {
> + int i;
> +
> + /*
> + * Init default ops.
> + * Must be called before registering x86_pmu_starting_cpu(),
> + * otherwise some key PMU fields, e.g., capabilities
> + * initialized in x86_pmu_starting_cpu(), would be overwritten.
> + */
> + for (i = 0; i < x86_pmu.num_hybrid_pmus; i++)
> + x86_pmu.hybrid_pmu[i].pmu = pmu;
> + }
[Severity: High]
This is a pre-existing issue, but does passing the global pmu struct to
check_hw_exists() earlier in this function cause an out-of-bounds read on
hybrid platforms?
In init_hw_perf_events(), check_hw_exists(&pmu, ...) is called after
perf_is_hybrid is enabled. Inside check_hw_exists(), it iterates over
fixed counters and calls fixed_counter_disabled():
arch/x86/events/perf_event.h:fixed_counter_disabled() {
...
u64 intel_ctrl = hybrid(pmu, intel_ctrl);
...
}
The hybrid() macro expands to a container_of() cast:
container_of(_pmu, struct x86_hybrid_pmu, pmu)->_field
Because the pmu passed in is the standalone global struct pmu (not embedded
within a struct x86_hybrid_pmu), this cast yields an invalid pointer.
Could this read garbage data past the end of the global pmu struct when
accessing intel_ctrl, potentially causing the BIOS hardware check to act on
garbage data or trigger a kernel panic if it crosses an unmapped page
boundary?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260706015439.3040804-1-dapeng1.mi@linux.intel.com?part=2
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [Patch v9 01/24] perf/x86: Fix two error-path and hybrid PMU guard issues
2026-07-06 1:54 ` [Patch v9 01/24] perf/x86: Fix two error-path and hybrid PMU guard issues Dapeng Mi
@ 2026-07-06 2:21 ` sashiko-bot
2026-07-06 8:05 ` Mi, Dapeng
0 siblings, 1 reply; 42+ messages in thread
From: sashiko-bot @ 2026-07-06 2:21 UTC (permalink / raw)
To: Dapeng Mi; +Cc: linux-perf-users
Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
Pre-existing issues:
- [High] The patch adds a check for `x86_pmu.num_hybrid_pmus` to prevent memory corruption in `intel_pmu_cpu_dead()` on error paths, but it is incomplete and still allows an out-of-bounds write during CPU hotplug rollback if the `CPUHP_AP_PERF_X86_STARTING` state setup fails.
- [High] If `alloc_arch_pebs_buf_on_cpu()` fails in `intel_pmu_cpu_prepare()`, resources allocated by `intel_cpuc_prepare()` are leaked.
- [High] Dangling `cpuc->pmu` pointer leads to Use-After-Free in `perf_event_print_debug()` when hybrid PMU registration fails.
- [High] Out-of-bounds read on the global `.bss` section in `perf_event_print_debug()` due to an invalid struct cast when `init_hw_perf_events()` fails early.
- [Medium] The dynamically allocated `x86_pmu.hybrid_pmu` array is permanently leaked on multiple error paths in `init_hw_perf_events()`.
--
commit e4457672386c09fea418faeba61fc0c2418ad7e5
Author: Dapeng Mi <dapeng1.mi@linux.intel.com>
perf/x86: Fix two error-path and hybrid PMU guard issues
Fix an NMI handler leak in init_hw_perf_events(). When PMU
initialization fails after register_nmi_handler(), the error path
exits without calling unregister_nmi_handler(), leaving a stale
NMI_LOCAL "PMI" handler registered. Add the missing call before
clearing x86_pmu state.
Also guard the hybrid PMU cpumask update in intel_pmu_cpu_dead()
with a check on x86_pmu.num_hybrid_pmus. Without this, hybrid_pmu()
may be called when the hybrid PMU array has not been allocated,
leading to an out-of-bounds access.
> diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c
> index af0b67ffb43d8..872d07a5fa80d 100644
> --- a/arch/x86/events/core.c
> +++ b/arch/x86/events/core.c
[ ... ]
> @@ -2273,6 +2273,8 @@ static int __init init_hw_perf_events(void)
> cpuhp_remove_state(CPUHP_AP_PERF_X86_STARTING);
> out:
> cpuhp_remove_state(CPUHP_PERF_X86_PREPARE);
> +pmi_unregister:
> + unregister_nmi_handler(NMI_LOCAL, "PMI");
> out_bad_pmu:
> memset(&x86_pmu, 0, sizeof(x86_pmu));
[Severity: Medium]
This isn't a bug introduced by this patch, but does this memset cause a
memory leak?
If cpuhp_setup_state() or check_hw_exists() fails during initialization,
the code jumps to the out_bad_pmu label and executes the memset without
freeing x86_pmu.hybrid_pmu. Could this permanently leak the dynamically
allocated hybrid PMU array?
[Severity: High]
This is a pre-existing issue, but could this error path lead to an
out-of-bounds read in perf_event_print_debug()?
If check_hw_exists() fails early during initialization, out_bad_pmu is
reached, but the perf_is_hybrid static key is never disabled. cpuc->pmu
defaults to the global &pmu.
If a privileged user triggers sysrq-p, perf_event_print_debug() uses
hybrid(cpuc->pmu, cntr_mask). Because is_hybrid() evaluates to true, it
casts the global &pmu to the larger struct x86_hybrid_pmu and attempts to
read cntr_mask, reading memory well beyond the object bounds in the .bss
section.
[Severity: High]
This is a pre-existing issue, but is there a use-after-free risk here if
hybrid PMU registration fails?
If perf_pmu_register() fails for a hybrid PMU inside init_hw_perf_events(),
the x86_pmu.hybrid_pmu array is freed via kfree. However, cpuc->pmu was
already assigned to elements of this array during intel_pmu_cpu_starting().
Since the teardown callbacks do not clear the cpuc->pmu pointers, they
remain dangling. A privileged user triggering sysrq-p will invoke
perf_event_print_debug(), which dereferences cpuc->pmu, potentially causing
a use-after-free.
> return err;
> }
> diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c
> index b39c6ce0efb5c..b8a6382dbb825 100644
> --- a/arch/x86/events/intel/core.c
> +++ b/arch/x86/events/intel/core.c
[ ... ]
> @@ -6479,7 +6479,7 @@ static void intel_pmu_cpu_dead(int cpu)
> release_arch_pebs_buf_on_cpu(cpu);
> intel_cpuc_finish(cpuc);
>
> - if (is_hybrid() && cpuc->pmu)
> + if (is_hybrid() && x86_pmu.num_hybrid_pmus && cpuc->pmu)
[Severity: High]
This is a pre-existing issue, but does this incomplete guard still allow an
out-of-bounds write during CPU hotplug rollback?
If CPUHP_AP_PERF_X86_STARTING hotplug setup fails, it rolls back the already
successful CPUHP_PERF_X86_PREPARE state by invoking intel_pmu_cpu_dead().
Because cpuc->pmu defaults to the statically allocated &pmu, the condition
evaluates to true. hybrid_pmu() casts the global pmu to the much larger
struct x86_hybrid_pmu, which could result in an out-of-bounds write when
modifying supported_cpus.
> cpumask_clear_cpu(cpu, &hybrid_pmu(cpuc->pmu)->supported_cpus);
> }
[Severity: High]
This is a pre-existing issue, but can intel_pmu_cpu_prepare() leak memory
allocated by intel_cpuc_prepare() if alloc_arch_pebs_buf_on_cpu() fails?
When the CPU hotplug core attempts to online a CPU, it runs the prepare
callback. If alloc_arch_pebs_buf_on_cpu() fails, the state transitions are
aborted without calling the teardown callback intel_pmu_cpu_dead() for this
specific CPU.
Under memory pressure, could repeated CPU hotplug operations leak the
cpuc->shared_regs, cpuc->constraint_list, and cpuc->excl_cntrs structures?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260706015439.3040804-1-dapeng1.mi@linux.intel.com?part=1
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [Patch v9 08/24] x86/fpu: Add update_fpu_state_and_flag() helper
2026-07-06 1:54 ` [Patch v9 08/24] x86/fpu: Add update_fpu_state_and_flag() helper Dapeng Mi
@ 2026-07-06 2:22 ` sashiko-bot
2026-07-06 9:15 ` Mi, Dapeng
0 siblings, 1 reply; 42+ messages in thread
From: sashiko-bot @ 2026-07-06 2:22 UTC (permalink / raw)
To: Dapeng Mi; +Cc: linux-perf-users
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Guest FPU state leaks to host perf samples because the NMI handler incorrectly bypasses the `__task_fpstate` check when `TIF_NEED_FPU_LOAD` is 0.
--
commit a7b6e478b8260eaf627518f9d19e511212779fab
Author: Dapeng Mi <dapeng1.mi@linux.intel.com>
x86/fpu: Add update_fpu_state_and_flag() helper
This commit introduces the update_fpu_state_and_flag() helper to save user
FPU state and set TIF_NEED_FPU_LOAD. It also adds memory barriers in the
host/guest FPU state switch path to ensure fpu->__task_fpstate is observed
correctly when non-NULL.
Link: https://lore.kernel.org/all/20251204154721.GB2619703@noisy.programming.kicks-ass.net/
> diff --git a/arch/x86/kernel/fpu/core.c b/arch/x86/kernel/fpu/core.c
> index 584fb9913be42..fb78e0ecd5fa8 100644
> --- a/arch/x86/kernel/fpu/core.c
> +++ b/arch/x86/kernel/fpu/core.c
[ ... ]
> @@ -383,13 +396,15 @@ int fpu_swap_kvm_fpstate(struct fpu_guest *guest_fpu, bool enter_guest)
>
> /* Swap fpstate */
> if (enter_guest) {
> - fpu->__task_fpstate = cur_fps;
> + WRITE_ONCE(fpu->__task_fpstate, cur_fps);
> + barrier();
> fpu->fpstate = guest_fps;
> guest_fps->in_use = true;
> } else {
> guest_fps->in_use = false;
> fpu->fpstate = fpu->__task_fpstate;
> - fpu->__task_fpstate = NULL;
> + barrier();
> + WRITE_ONCE(fpu->__task_fpstate, NULL);
[Severity: High]
Can guest FPU state leak into host perf samples during this window?
The commit message mentions that subsequent patches will use this guarantee
in NMI context by checking TIF_NEED_FPU_LOAD before retrieving user FPU
state from the saved task FPU state.
However, during KVM guest execution and within the VM-exit path here,
TIF_NEED_FPU_LOAD is 0 because it was cleared on guest entry and is not set
by KVM.
If an NMI hits here before restore_fpregs_from_fpstate() restores the host
state, would the NMI handler bypass the fpu->__task_fpstate check because
TIF_NEED_FPU_LOAD is 0?
arch/x86/events/core.c:x86_pmu_update_user_xregs() {
...
if (user_mask && test_thread_flag(TIF_NEED_FPU_LOAD)) {
perf_regs = container_of(data->regs_user.regs,
struct x86_perf_regs, regs);
fpu = x86_task_fpu(current);
/*
* If __task_fpstate is set, it holds the right pointer,
* otherwise fpstate will.
*/
fps = READ_ONCE(fpu->__task_fpstate);
...
}
If this memory check is skipped, wouldn't the NMI handler fall back to
reading directly from the hardware registers, which still contain the guest
FPU state at this point in fpu_swap_kvm_fpstate()?
> }
>
> cur_fps = fpu->fpstate;
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260706015439.3040804-1-dapeng1.mi@linux.intel.com?part=8
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [Patch v9 06/24] perf/x86: Use x86_perf_regs in NMI handlers
2026-07-06 1:54 ` [Patch v9 06/24] perf/x86: Use x86_perf_regs in NMI handlers Dapeng Mi
@ 2026-07-06 2:31 ` sashiko-bot
2026-07-06 8:43 ` Mi, Dapeng
0 siblings, 1 reply; 42+ messages in thread
From: sashiko-bot @ 2026-07-06 2:31 UTC (permalink / raw)
To: Dapeng Mi; +Cc: linux-perf-users
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Concurrent use of `x86_pebs_regs` and `x86_user_regs` per-CPU variables between NMI and IRQ-disabled contexts corrupts PEBS samples.
--
commit a90fb4d272f2182569ad5a2105f6b3f7c429263d
Author: Dapeng Mi <dapeng1.mi@linux.intel.com>
perf/x86: Use x86_perf_regs in NMI handlers
Support for sampling additional register state in NMI context requires an
x86-specific register container. This switches x86 NMI handlers to use
the x86_perf_regs structure.
> diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c
> index 0888d3b0923e1..d83ea02e24577 100644
> --- a/arch/x86/events/core.c
> +++ b/arch/x86/events/core.c
> @@ -1788,9 +1788,11 @@ void perf_put_guest_lvtpc(void)
> EXPORT_SYMBOL_FOR_KVM(perf_put_guest_lvtpc);
> #endif /* CONFIG_PERF_GUEST_MEDIATED_PMU */
>
> +static DEFINE_PER_CPU(struct x86_perf_regs, x86_intr_regs);
> static int
> perf_event_nmi_handler(unsigned int cmd, struct pt_regs *regs)
> {
> + struct x86_perf_regs *x86_regs = this_cpu_ptr(&x86_intr_regs);
> u64 start_clock;
> u64 finish_clock;
> int ret;
> @@ -1814,7 +1816,8 @@ perf_event_nmi_handler(unsigned int cmd, struct pt_regs *regs)
> return NMI_DONE;
>
> start_clock = sched_clock();
> - ret = static_call(x86_pmu_handle_irq)(regs);
> + x86_regs->regs = *regs;
> + ret = static_call(x86_pmu_handle_irq)(&x86_regs->regs);
[Severity: High]
Is this call path safe from NMI reentrancy when accessing the per-CPU
variables introduced earlier in this series?
If intel_pmu_drain_pebs_buffer() is called from a non-NMI context (e.g.
intel_pmu_read_event() via the perf_event_read syscall) with local IRQs
disabled, NMIs can still fire.
If a hardware PMI fires during the execution of __intel_pmu_pebs_events(),
the CPU will jump here to perf_event_nmi_handler().
The NMI handler calls x86_pmu_handle_irq() -> handle_pmi_common(), which
also calls x86_pmu_drain_pebs(). Since the earlier patch in this series
"perf/x86/intel: Convert x86_perf_regs to per-cpu variables" replaced stack
allocations with shared per-CPU variables:
arch/x86/events/intel/ds.c:__intel_pmu_pebs_events() {
...
struct x86_perf_regs *perf_regs = this_cpu_ptr(&x86_pebs_regs);
struct pt_regs *regs = &perf_regs->regs;
...
}
And similarly for user regs:
arch/x86/events/core.c:x86_pmu_get_regs_user() {
...
struct x86_perf_regs *x86_regs_user = this_cpu_ptr(&x86_user_regs);
...
}
Will the NMI handler overwrite the same per-CPU structures the interrupted
non-NMI context was actively using, leading to corrupted PEBS samples when
the original context resumes?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260706015439.3040804-1-dapeng1.mi@linux.intel.com?part=6
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [Patch v9 11/24] perf/x86: Enable XMM register sampling for non-PEBS events
2026-07-06 1:54 ` [Patch v9 11/24] perf/x86: Enable XMM register sampling for non-PEBS events Dapeng Mi
@ 2026-07-06 2:34 ` sashiko-bot
2026-07-06 9:47 ` Mi, Dapeng
0 siblings, 1 reply; 42+ messages in thread
From: sashiko-bot @ 2026-07-06 2:34 UTC (permalink / raw)
To: Dapeng Mi; +Cc: linux-perf-users
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] reserve_ext_regs_buffers() fails to propagate memory allocation errors and improperly uses WARN_ON_ONCE, leading to a silent feature failure and possible kernel panics under memory pressure.
--
commit 8262cdbae2d650c85cf4924888ecf8b4f85b98de
Author: Dapeng Mi <dapeng1.mi@linux.intel.com>
perf/x86: Enable XMM register sampling for non-PEBS events
This commit extends XMM register sampling to non-PEBS events using the xsaves
instruction. It allocates a per-CPU ext_regs_buf to store SIMD and other
registers. For PEBS events, data is retrieved directly from PEBS records.
> diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c
> index d83ea02e24577..3e52610510cdc 100644
> --- a/arch/x86/events/core.c
> +++ b/arch/x86/events/core.c
[ ... ]
> @@ -410,6 +410,56 @@ set_ext_hw_attr(struct hw_perf_event *hwc, struct perf_event *event)
> +static void reserve_ext_regs_buffers(void)
> +{
> + bool compacted = cpu_feature_enabled(X86_FEATURE_XCOMPACTED);
> + unsigned int size;
> + int cpu;
> +
> + if (!x86_pmu.ext_regs_mask)
> + return;
> +
> + /* +64 bytes for the 64 bytes alignment request of xsave area. */
> + size = xstate_calculate_size(x86_pmu.ext_regs_mask, compacted) + 64;
> +
> + for_each_possible_cpu(cpu) {
> + per_cpu(ext_regs_buf, cpu) = kzalloc_node(size, GFP_KERNEL,
> + cpu_to_node(cpu));
> + if (WARN_ON_ONCE(!per_cpu(ext_regs_buf, cpu)))
> + goto err;
> + }
> +
> + return;
> +
> +err:
> + release_ext_regs_buffers();
> +}
[ ... ]
> int x86_reserve_hardware(void)
> {
> int err = 0;
>
> if (!atomic_inc_not_zero(&pmc_refcount)) {
> mutex_lock(&pmc_reserve_mutex);
> if (atomic_read(&pmc_refcount) == 0) {
> if (!reserve_pmc_hardware()) {
> err = -EBUSY;
> } else {
> reserve_ds_buffers();
> reserve_lbr_buffers();
> + reserve_ext_regs_buffers();
> }
> }
> if (!err)
[Severity: High]
Does this code swallow the memory allocation failure?
Since reserve_ext_regs_buffers() returns void, x86_reserve_hardware()
appears unaware if kzalloc_node() fails under memory pressure. The hardware
reservation will succeed anyway, and the perf event creation is permitted
even though the backing buffers do not exist.
[ ... ]
> +static void x86_pmu_sample_xregs(struct perf_event *event,
> + struct perf_sample_data *data,
> + u64 ignore_mask)
> +{
> + struct xregs_state *xsave = get_ext_regs_buf(smp_processor_id());
> + u64 sample_type = event->attr.sample_type;
> + struct x86_perf_regs *perf_regs;
> + u64 intr_mask = 0;
> + u64 mask = 0;
> +
> + if (WARN_ON_ONCE(!xsave) || !in_nmi())
> + return;
[Severity: High]
Can this path be reached with a NULL xsave buffer during a PMI if the earlier
allocation failed?
If an unprivileged user creates an event requesting extended registers during
memory pressure, and the allocation in reserve_ext_regs_buffers() fails,
the event is successfully created. When the PMI hits, this WARN_ON_ONCE in
NMI context will trigger.
This could panic the system if panic_on_warn is set, or otherwise silently
drop the requested register data. Should reserve_ext_regs_buffers() return
an error code so x86_reserve_hardware() can fail the event creation instead?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260706015439.3040804-1-dapeng1.mi@linux.intel.com?part=11
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [Patch v9 13/24] perf: Add sampling support for SIMD registers
2026-07-06 1:54 ` [Patch v9 13/24] perf: Add sampling support for SIMD registers Dapeng Mi
@ 2026-07-06 2:34 ` sashiko-bot
0 siblings, 0 replies; 42+ messages in thread
From: sashiko-bot @ 2026-07-06 2:34 UTC (permalink / raw)
To: Dapeng Mi; +Cc: linux-perf-users
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
New issues:
- [Medium] Uninitialized, flag-gated syscall parameters are unconditionally passed to and evaluated by the validation function `perf_simd_reg_validate()`, breaking trust boundaries.
Pre-existing issues:
- [High] The dynamic size added by PERF_SAMPLE_REGS_INTR (and exacerbated by SIMD registers) bypasses the size capping applied during PERF_SAMPLE_STACK_USER, leading to a U16_MAX integer overflow and ring buffer corruption.
--
commit 8a8cf6dc3b640f823c8a7990b8c46e78cd38719e
Author: Kan Liang <kan.liang@linux.intel.com>
perf: Add sampling support for SIMD registers
This commit introduces support for sampling SIMD registers during profiling
by adding new attribute fields to the perf_event_attr structure. It provides
a new output format and the PERF_SAMPLE_REGS_ABI_SIMD ABI to represent the
number and width of these registers based on user configuration.
> diff --git a/kernel/events/core.c b/kernel/events/core.c
> index 0239864029bec..ce93c23b3a33f 100644
> --- a/kernel/events/core.c
> +++ b/kernel/events/core.c
> @@ -8773,6 +8877,10 @@ void perf_prepare_sample(struct perf_sample_data *data,
> u64 mask = event->attr.sample_regs_intr;
>
> size += hweight64(mask) * sizeof(u64);
> + if (event_has_simd_regs(event)) {
> + size += perf_update_xregs_size(event, true);
> + data->regs_intr.abi |= PERF_SAMPLE_REGS_ABI_SIMD;
> + }
> }
>
> data->dyn_size += size;
[Severity: High]
This is a pre-existing issue, but does adding this new SIMD register size
cause a buffer overrun by overflowing data->dyn_size past USHRT_MAX?
Earlier in perf_prepare_sample(), when PERF_SAMPLE_STACK_USER is processed,
perf_sample_ustack_size() caps the stack dump size so that the total dynamic
size precisely reaches USHRT_MAX.
Since PERF_SAMPLE_REGS_INTR is unconditionally processed after
PERF_SAMPLE_STACK_USER, adding its size (which can now be significantly
larger with SIMD registers) to data->dyn_size will exceed 65535 bytes.
Could this truncate the 16-bit header size and cause perf_output_sample()
to write far more data than was allocated in the ring buffer?
> [ ... ]
> @@ -13694,6 +13809,16 @@ static int perf_copy_attr(struct perf_event_attr __user *uattr,
> ret = perf_reg_validate(attr->sample_regs_user);
> if (ret)
> return ret;
> + ret = perf_simd_reg_validate(attr->sample_type,
> + attr->sample_simd_regs_enabled,
> + attr->sample_simd_vec_reg_qwords,
> + attr->sample_simd_vec_reg_intr,
> + attr->sample_simd_vec_reg_user,
> + attr->sample_simd_pred_reg_qwords,
> + attr->sample_simd_pred_reg_intr,
> + attr->sample_simd_pred_reg_user);
> + if (ret)
> + return ret;
> }
[Severity: Medium]
Could this pass uninitialized syscall parameters to the validation logic?
When PERF_SAMPLE_REGS_USER is set but PERF_SAMPLE_REGS_INTR is not, the user
is not required to initialize the sample_simd_vec_reg_intr or
sample_simd_pred_reg_intr fields.
If perf_simd_reg_validate() evaluates these uninitialized fields:
arch/x86/kernel/perf_regs.c:perf_simd_reg_validate() {
if (!vec_mask_intr && !vec_mask_user)
return -EINVAL;
}
Does this allow an invalid configuration where vec_mask_user is zero to
bypass validation because vec_mask_intr contains non-zero garbage data?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260706015439.3040804-1-dapeng1.mi@linux.intel.com?part=13
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [Patch v9 12/24] perf/x86: Enable XMM register sampling for REGS_USER case
2026-07-06 1:54 ` [Patch v9 12/24] perf/x86: Enable XMM register sampling for REGS_USER case Dapeng Mi
@ 2026-07-06 2:35 ` sashiko-bot
0 siblings, 0 replies; 42+ messages in thread
From: sashiko-bot @ 2026-07-06 2:35 UTC (permalink / raw)
To: Dapeng Mi; +Cc: linux-perf-users
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] For PEBS events, a delayed PMI NMI hitting in kernel mode causes REGS_INTR to overwrite REGS_USER's XMM registers with the current hardware FPU state, leaking kernel FPU data.
- [High] PERF_SAMPLE_REGS_USER incorrectly samples Guest XMM registers paired with Host GPRs when a PMI hits during KVM guest execution, leaking guest state to the host.
--
commit 74494b0320a832fddee80ea2906067a8bc2b32e0
Author: Dapeng Mi <dapeng1.mi@linux.intel.com>
perf/x86: Enable XMM register sampling for REGS_USER case
This commit introduces support for XMM register sampling in the REGS_USER
case. It adds a per-CPU x86_user_regs to store REGS_USER-specific XMM
registers and a helper x86_pmu_update_user_xregs() to retrieve them either
from cached task FPU state or hardware registers.
> diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c
> index 3e52610510cdc..0d42c51761f9d 100644
> --- a/arch/x86/events/core.c
> +++ b/arch/x86/events/core.c
[ ... ]
> @@ -1812,33 +1806,165 @@ static void x86_pmu_update_regs_intr(struct perf_event *event,
[ ... ]
> +static inline u64 x86_pmu_update_user_xregs(struct perf_sample_data *data,
> + struct pt_regs *regs,
> + u64 mask, u64 ignore_mask)
> +{
> + struct x86_perf_regs *perf_regs;
> + struct xregs_state *xsave;
> + unsigned int guest_state;
> + struct fpu *fpu;
> + struct fpstate *fps;
> + u64 user_mask = mask;
> +
> + if (data->regs_user.abi == PERF_SAMPLE_REGS_ABI_NONE)
> + return 0;
> +
> + /*
> + * If PEBS hits kernel space, need to re-sample extended
> + * registers for user space.
> + */
> + if (user_mode(regs))
> + user_mask &= ~ignore_mask;
> +
> + if (user_mask && test_thread_flag(TIF_NEED_FPU_LOAD)) {
[Severity: High]
Can this incorrectly sample Guest XMM registers paired with Host GPRs when
a PMI hits during KVM guest execution?
Since TIF_NEED_FPU_LOAD is cleared when KVM actively runs a guest, this
condition evaluates to false, bypassing the fpu->__task_fpstate memory
load.
If the guest is running in user mode, perf_guest_state() & PERF_GUEST_USER
will be true, causing this function to return user_mask. Consequently,
xsaves_nmi() will sample the live hardware FPU containing the guest's
registers instead of QEMU's.
> + perf_regs = container_of(data->regs_user.regs,
> + struct x86_perf_regs, regs);
> + fpu = x86_task_fpu(current);
> + /*
> + * If __task_fpstate is set, it holds the right pointer,
> + * otherwise fpstate will.
> + */
> + fps = READ_ONCE(fpu->__task_fpstate);
[ ... ]
> static void x86_pmu_sample_xregs(struct perf_event *event,
> struct perf_sample_data *data,
> + struct pt_regs *regs,
> u64 ignore_mask)
> {
[ ... ]
> + if (user_mask) {
> + perf_regs = container_of(data->regs_user.regs,
> + struct x86_perf_regs, regs);
> + update_perf_regs(perf_regs, xsave, user_mask);
> + }
> +
> if (intr_mask) {
> perf_regs = container_of(data->regs_intr.regs,
> struct x86_perf_regs, regs);
> - xsave->header.xfeatures = 0;
> - xsaves_nmi(xsave, mask);
> update_perf_regs(perf_regs, xsave, intr_mask);
[Severity: High]
For PEBS events, if a delayed PMI NMI hits in kernel mode, can REGS_INTR
overwrite REGS_USER's XMM registers and leak kernel FPU data?
If a PEBS event requests both PERF_SAMPLE_REGS_USER and
PERF_SAMPLE_REGS_INTR, and a PEBS record is generated in user space but
the PMI is delivered while the CPU is in kernel mode (after the kernel has
set TIF_NEED_FPU_LOAD):
x86_pmu_update_regs_user() sets:
data->regs_user.regs = regs;
x86_pmu_update_regs_intr() sets:
data->regs_intr.regs = regs;
Both now share the same x86_perf_regs container. In x86_pmu_sample_xregs(),
the container is first updated with the user's FPU state, but then it is
unconditionally overwritten with the kernel's live FPU state from
xsaves_nmi() here because intr_mask is non-zero.
> }
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260706015439.3040804-1-dapeng1.mi@linux.intel.com?part=12
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [Patch v9 22/24] perf/x86/intel: Enable PERF_PMU_CAP_SIMD_REGS capability
2026-07-06 1:54 ` [Patch v9 22/24] perf/x86/intel: Enable PERF_PMU_CAP_SIMD_REGS capability Dapeng Mi
@ 2026-07-06 2:57 ` sashiko-bot
0 siblings, 0 replies; 42+ messages in thread
From: sashiko-bot @ 2026-07-06 2:57 UTC (permalink / raw)
To: Dapeng Mi; +Cc: linux-perf-users
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
New issues:
- [Critical] The `XSAVES` instruction writes PMU extended register state out-of-bounds due to incorrect buffer sizing in `reserve_ext_regs_buffers()`.
- [Low] Fragile bitmask comparison using the strictly greater-than operator (`>`).
Pre-existing issues:
- [High] Pre-existing type confusion in `perf_reg_value` allowing out-of-bounds stack memory read/dereference via software PMUs.
--
commit 9750372940712500ad29f87f143032cba6ffbe36
Author: Dapeng Mi <dapeng1.mi@linux.intel.com>
perf/x86/intel: Enable PERF_PMU_CAP_SIMD_REGS capability
This commit enables the PERF_PMU_CAP_SIMD_REGS capability when XSAVES support
is available for extended registers. It simplifies validation logic by tying
the capability to XSAVES and architectural PEBS support.
> diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c
> index 15962a3457eeb..56997731dc83a 100644
> --- a/arch/x86/events/intel/core.c
> +++ b/arch/x86/events/intel/core.c
[ ... ]
> @@ -6303,14 +6303,37 @@ static inline void __intel_update_pmu_xregs_caps(struct pmu *pmu)
> */
> x86_pmu.ext_regs_mask |= XFEATURE_MASK_SSE;
>
> + if (boot_cpu_has(X86_FEATURE_AVX) &&
> + cpu_has_xfeatures(XFEATURE_MASK_YMM, NULL))
> + x86_pmu.ext_regs_mask |= XFEATURE_MASK_YMM;
> + if (boot_cpu_has(X86_FEATURE_APX) &&
> + cpu_has_xfeatures(XFEATURE_MASK_APX, NULL))
> + x86_pmu.ext_regs_mask |= XFEATURE_MASK_APX;
[Severity: Critical]
Can modifying this mask cause an out-of-bounds write during an NMI?
When extended registers like APX are requested, the PMU allocates ext_regs_buf
using xstate_calculate_size(x86_pmu.ext_regs_mask, compacted) in
arch/x86/events/core.c:reserve_ext_regs_buffers(). This calculation omits the
sizes of non-PMU active states like AMX.
However, when xsaves_nmi() executes the XSAVES instruction in
x86_pmu_sample_xregs(), the hardware determines compacted offsets based on ALL
active CPU features (XCR0 | IA32_XSS). For a CPU with AMX enabled, XSAVES will
write the APX state at an offset exceeding the allocated buffer size.
Does this mismatch lead to a heap buffer overflow when XSAVES executes?
> + if (boot_cpu_has(X86_FEATURE_AVX512F)) {
> + if (cpu_has_xfeatures(XFEATURE_MASK_OPMASK, NULL))
> + x86_pmu.ext_regs_mask |= XFEATURE_MASK_OPMASK;
> + if (cpu_has_xfeatures(XFEATURE_MASK_ZMM_Hi256, NULL))
> + x86_pmu.ext_regs_mask |= XFEATURE_MASK_ZMM_Hi256;
> + if (cpu_has_xfeatures(XFEATURE_MASK_Hi16_ZMM, NULL))
> + x86_pmu.ext_regs_mask |= XFEATURE_MASK_Hi16_ZMM;
> + }
> + if (cpu_feature_enabled(X86_FEATURE_USER_SHSTK))
> + x86_pmu.ext_regs_mask |= XFEATURE_MASK_CET_USER;
> +
> /* PEBS supported case */
> - if ((x86_pmu.arch_pebs && (caps & ARCH_PEBS_VECR_XMM)) ||
> - (x86_pmu.intel_cap.pebs_format >= 4 && x86_pmu.intel_cap.pebs_baseline))
> + if (x86_pmu.intel_cap.pebs_format >= 4 && x86_pmu.intel_cap.pebs_baseline)
> dest_pmu->capabilities |= PERF_PMU_CAP_EXTENDED_REGS;
> + if (x86_pmu.arch_pebs && (caps & ARCH_PEBS_VECR_EXT)) {
> + dest_pmu->capabilities |= PERF_PMU_CAP_EXTENDED_REGS |
> + PERF_PMU_CAP_SIMD_REGS;
> + }
[Severity: High]
This is a pre-existing issue, but does enabling these capabilities expose a
type confusion bug in software PMUs?
In arch/x86/kernel/perf_regs.c:perf_reg_validate(), the PERF_REG_X86_RESERVED
mask only covers bits 16-31. When simd_enabled is false, this allows a user
requesting extended register bits (>= 32) to bypass validation.
When a tracepoint fires, perf_output_sample_regs() passes a raw
stack-allocated pt_regs pointer to perf_reg_value(). The perf_reg_value()
function incorrectly casts it to a larger struct x86_perf_regs using
container_of(), which reads the abi and xmm_regs fields from uninitialized
adjacent stack memory and dereferences them.
Can a user trigger this out-of-bounds stack read by creating a tracepoint or
software PMU event with PERF_SAMPLE_REGS_INTR and requesting extended bits?
>
> /* PEBS unsupported case (e.g., guest) */
> - if (!x86_pmu.intel_cap.pebs_format)
> + if (!x86_pmu.intel_cap.pebs_format) {
> dest_pmu->capabilities |= PERF_PMU_CAP_EXTENDED_REGS;
> + if (x86_pmu.ext_regs_mask > XFEATURE_MASK_SSE)
[Severity: Low]
Is it safe to use a strictly greater-than operator here?
If a new feature using bit 0 is ever added to ext_regs_mask, the mask
value would become 3. The condition 3 > 2 evaluates to true, incorrectly
enabling PERF_PMU_CAP_SIMD_REGS even if no SIMD extended features are
supported.
Could this use a bitwise comparison like (mask & ~XFEATURE_MASK_SSE) != 0
instead?
> + dest_pmu->capabilities |= PERF_PMU_CAP_SIMD_REGS;
> + }
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260706015439.3040804-1-dapeng1.mi@linux.intel.com?part=22
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [Patch v9 24/24] perf/x86/intel: Add sanity check for PEBS fragment size
2026-07-06 1:54 ` [Patch v9 24/24] perf/x86/intel: Add sanity check for PEBS fragment size Dapeng Mi
@ 2026-07-06 5:04 ` sashiko-bot
0 siblings, 0 replies; 42+ messages in thread
From: sashiko-bot @ 2026-07-06 5:04 UTC (permalink / raw)
To: Dapeng Mi; +Cc: linux-perf-users
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Events without preceding valid records in a corrupted PEBS buffer are permanently stalled.
--
commit e2dd3df4b73a7bfac8a1332ea38906750253ef38
Author: Dapeng Mi <dapeng1.mi@linux.intel.com>
perf/x86/intel: Add sanity check for PEBS fragment size
This patch adds a sanity check for corrupted PEBS fragment sizes to prevent
potential infinite loops. If a corrupted fragment is detected, the entire
PEBS record and all subsequent records are dropped to ensure data integrity.
> diff --git a/arch/x86/events/intel/ds.c b/arch/x86/events/intel/ds.c
> index b38aed4f62b4c..7a8e619055393 100644
> --- a/arch/x86/events/intel/ds.c
> +++ b/arch/x86/events/intel/ds.c
[ ... ]
> @@ -3244,7 +3261,8 @@ static __always_inline void
> __intel_pmu_handle_last_pebs_record(struct pt_regs *iregs,
> struct pt_regs *regs,
> struct perf_sample_data *data,
> - u64 mask, short *counts, void **last,
> + u64 mask, short *counts,
> + void **last, bool corrupted,
> setup_fn setup_sample)
> {
> struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
> @@ -3258,7 +3276,7 @@ __intel_pmu_handle_last_pebs_record(struct pt_regs *iregs,
> event = cpuc->events[bit];
>
> __intel_pmu_pebs_last_event(event, iregs, regs, data, last[bit],
> - counts[bit], setup_sample);
> + counts[bit], corrupted, setup_sample);
> }
>
> }
[Severity: High]
This is a pre-existing issue, but does this logic permanently stall PMU
profiling for events without preceding valid records in a corrupted buffer?
In arch/x86/events/intel/ds.c:__intel_pmu_handle_last_pebs_record(), we skip
processing if the count is zero:
for_each_set_bit(bit, (unsigned long *)&mask, X86_PMC_IDX_MAX) {
if (!counts[bit])
continue;
...
If a corrupted PEBS record is detected (e.g., via the newly added at >= top
bounds checks) and the code exits the drain loop early, events that had no
valid records parsed prior to the corruption will have a count of zero.
Because these events are skipped here, __intel_pmu_pebs_last_event() is never
called, preventing intel_pmu_save_and_restart() from re-arming their hardware
counters. Furthermore, since handle_pmi_common() explicitly masks PEBS events
out of the global status, the generic NMI handler will also ignore them.
Could this lead to the hardware counters remaining disabled, stopping
profiling until the session is recreated?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260706015439.3040804-1-dapeng1.mi@linux.intel.com?part=24
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [Patch v9 14/24] perf/x86: Support XMM sampling using sample_simd_vec_reg_* fields
2026-07-06 1:54 ` [Patch v9 14/24] perf/x86: Support XMM sampling using sample_simd_vec_reg_* fields Dapeng Mi
@ 2026-07-06 6:45 ` sashiko-bot
0 siblings, 0 replies; 42+ messages in thread
From: sashiko-bot @ 2026-07-06 6:45 UTC (permalink / raw)
To: Dapeng Mi; +Cc: linux-perf-users
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
New issues:
- [Medium] Validation logic unconditionally evaluates flag-gated syscall parameters (`vec_mask_user`, `pred_mask_user`) without checking their respective flag gates, leading to validation bypasses due to uninitialized garbage.
Pre-existing issues:
- [Critical] The functions `perf_reg_value` and `perf_simd_reg_value` unsafely assume the provided `pt_regs *` is always embedded within an `x86_perf_regs` structure, leading to out-of-bounds reads and arbitrary pointer dereferences for software events and `PERF_SAMPLE_REGS_USER`.
--
commit 9c74aceec4401218b750679a5b08ba8009aa85a3
Author: Dapeng Mi <dapeng1.mi@linux.intel.com>
perf/x86: Support XMM sampling using sample_simd_vec_reg_* fields
This commit adds support for sampling XMM registers by introducing a new ABI
field and extracting XMM values through the sample_simd_vec_reg_* fields. It
adds a new perf_simd_reg_value function to retrieve the values and updates
the SIMD registration validation logic.
> diff --git a/arch/x86/kernel/perf_regs.c b/arch/x86/kernel/perf_regs.c
> index 81204cb7f7239..8514baefb400e 100644
> --- a/arch/x86/kernel/perf_regs.c
> +++ b/arch/x86/kernel/perf_regs.c
[ ... ]
> @@ -63,6 +63,9 @@ u64 perf_reg_value(struct pt_regs *regs, int idx)
>
> if (idx >= PERF_REG_X86_XMM0 && idx < PERF_REG_X86_XMM_MAX) {
> perf_regs = container_of(regs, struct x86_perf_regs, regs);
> + /* SIMD registers are moved to dedicated sample_simd_vec_reg */
> + if (perf_regs->abi & PERF_SAMPLE_REGS_ABI_SIMD)
[Severity: Critical]
This isn't a bug introduced by this patch, but evaluating perf_regs->abi
here relies on an unsafe cast.
For tracepoints and PERF_SAMPLE_REGS_USER, regs points to a bare pt_regs
instance rather than an embedded x86_perf_regs struct. The container_of()
cast accesses memory outside the bounds of the pt_regs struct.
Could this allow an attacker to control the out-of-bounds pointers (like
xmm_regs) and read arbitrary kernel memory into the perf ring buffer?
> + return 0;
> if (!perf_regs->xmm_regs)
> return 0;
> return perf_regs->xmm_regs[idx - PERF_REG_X86_XMM0];
[ ... ]
> @@ -74,6 +77,77 @@ u64 perf_reg_value(struct pt_regs *regs, int idx)
> return regs_get_register(regs, pt_regs_offset[idx]);
> }
>
> +u64 perf_simd_reg_value(struct pt_regs *regs, int idx,
> + u16 qwords_idx, bool pred)
> +{
> + struct x86_perf_regs *perf_regs =
> + container_of(regs, struct x86_perf_regs, regs);
> +
> + if (!(perf_regs->abi & PERF_SAMPLE_REGS_ABI_SIMD))
[Severity: Critical]
This is a pre-existing issue, but this new code copies the same unsafe
container_of() cast pattern.
Since regs is not actually embedded in an x86_perf_regs struct when
called for PERF_SAMPLE_REGS_USER, reading perf_regs->abi reads
out-of-bounds memory.
> + return 0;
> +
> + if (pred)
> + return 0;
[ ... ]
> +int perf_simd_reg_validate(u64 sample_type, u16 simd_enabled,
> + u16 vec_qwords, u64 vec_mask_intr,
> + u64 vec_mask_user, u16 pred_qwords,
> + u32 pred_mask_intr, u32 pred_mask_user)
> +{
> + u64 size = 0;
> +
> + if (!(sample_type &
> + (PERF_SAMPLE_REGS_INTR | PERF_SAMPLE_REGS_USER)))
> + return 0;
> +
> + if (!simd_enabled)
> + return 0;
> +
> + if (vec_qwords) {
> + if (vec_qwords != PERF_X86_XMM_QWORDS)
> + return -EINVAL;
> + if (!vec_mask_intr && !vec_mask_user)
> + return -EINVAL;
[Severity: Medium]
Does this logic evaluate flag-gated parameters without checking their
respective flag gates?
PERF_SAMPLE_REGS_INTR and PERF_SAMPLE_REGS_USER are independent flags. If a
user sets INTR but not USER, vec_mask_user is outside its flag gate and
can contain arbitrary garbage from userspace.
If vec_mask_intr is 0 (an invalid configuration), but the garbage in
vec_mask_user is non-zero, this check passes and the validation is bypassed.
Should these masks be validated against their respective sample_type flags
before checking if they are both empty?
> + if ((sample_type & PERF_SAMPLE_REGS_INTR) &&
> + (vec_mask_intr & ~PERF_X86_SIMD_VEC_MASK))
> + return -EINVAL;
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260706015439.3040804-1-dapeng1.mi@linux.intel.com?part=14
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [Patch v9 01/24] perf/x86: Fix two error-path and hybrid PMU guard issues
2026-07-06 2:21 ` sashiko-bot
@ 2026-07-06 8:05 ` Mi, Dapeng
0 siblings, 0 replies; 42+ messages in thread
From: Mi, Dapeng @ 2026-07-06 8:05 UTC (permalink / raw)
To: sashiko-reviews; +Cc: linux-perf-users
On 7/6/2026 10:21 AM, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
>
> Pre-existing issues:
> - [High] The patch adds a check for `x86_pmu.num_hybrid_pmus` to prevent memory corruption in `intel_pmu_cpu_dead()` on error paths, but it is incomplete and still allows an out-of-bounds write during CPU hotplug rollback if the `CPUHP_AP_PERF_X86_STARTING` state setup fails.
> - [High] If `alloc_arch_pebs_buf_on_cpu()` fails in `intel_pmu_cpu_prepare()`, resources allocated by `intel_cpuc_prepare()` are leaked.
> - [High] Dangling `cpuc->pmu` pointer leads to Use-After-Free in `perf_event_print_debug()` when hybrid PMU registration fails.
> - [High] Out-of-bounds read on the global `.bss` section in `perf_event_print_debug()` due to an invalid struct cast when `init_hw_perf_events()` fails early.
> - [Medium] The dynamically allocated `x86_pmu.hybrid_pmu` array is permanently leaked on multiple error paths in `init_hw_perf_events()`.
> --
>
> commit e4457672386c09fea418faeba61fc0c2418ad7e5
> Author: Dapeng Mi <dapeng1.mi@linux.intel.com>
>
> perf/x86: Fix two error-path and hybrid PMU guard issues
>
> Fix an NMI handler leak in init_hw_perf_events(). When PMU
> initialization fails after register_nmi_handler(), the error path
> exits without calling unregister_nmi_handler(), leaving a stale
> NMI_LOCAL "PMI" handler registered. Add the missing call before
> clearing x86_pmu state.
>
> Also guard the hybrid PMU cpumask update in intel_pmu_cpu_dead()
> with a check on x86_pmu.num_hybrid_pmus. Without this, hybrid_pmu()
> may be called when the hybrid PMU array has not been allocated,
> leading to an out-of-bounds access.
>
>> diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c
>> index af0b67ffb43d8..872d07a5fa80d 100644
>> --- a/arch/x86/events/core.c
>> +++ b/arch/x86/events/core.c
> [ ... ]
>
>> @@ -2273,6 +2273,8 @@ static int __init init_hw_perf_events(void)
>> cpuhp_remove_state(CPUHP_AP_PERF_X86_STARTING);
>> out:
>> cpuhp_remove_state(CPUHP_PERF_X86_PREPARE);
>> +pmi_unregister:
>> + unregister_nmi_handler(NMI_LOCAL, "PMI");
>> out_bad_pmu:
>> memset(&x86_pmu, 0, sizeof(x86_pmu));
> [Severity: Medium]
> This isn't a bug introduced by this patch, but does this memset cause a
> memory leak?
>
> If cpuhp_setup_state() or check_hw_exists() fails during initialization,
> the code jumps to the out_bad_pmu label and executes the memset without
> freeing x86_pmu.hybrid_pmu. Could this permanently leak the dynamically
> allocated hybrid PMU array?
Yeah, this is a true issue, need to free the x86_pmu.hybrid_pmu after the
out_bad_pmu tag. Considering this patchset is quite large and close to
mature, would fix it in a separate patch.
>
>
> [Severity: High]
> This is a pre-existing issue, but could this error path lead to an
> out-of-bounds read in perf_event_print_debug()?
>
> If check_hw_exists() fails early during initialization, out_bad_pmu is
> reached, but the perf_is_hybrid static key is never disabled. cpuc->pmu
> defaults to the global &pmu.
>
> If a privileged user triggers sysrq-p, perf_event_print_debug() uses
> hybrid(cpuc->pmu, cntr_mask). Because is_hybrid() evaluates to true, it
> casts the global &pmu to the larger struct x86_hybrid_pmu and attempts to
> read cntr_mask, reading memory well beyond the object bounds in the .bss
> section.
Yeah, it's real issue too. Need to call
"static_branch_disable(&perf_is_hybrid);" to disable the hybrid PMU
after the out_bad_pmu tag,
>
>
> [Severity: High]
> This is a pre-existing issue, but is there a use-after-free risk here if
> hybrid PMU registration fails?
>
> If perf_pmu_register() fails for a hybrid PMU inside init_hw_perf_events(),
> the x86_pmu.hybrid_pmu array is freed via kfree. However, cpuc->pmu was
> already assigned to elements of this array during intel_pmu_cpu_starting().
>
> Since the teardown callbacks do not clear the cpuc->pmu pointers, they
> remain dangling. A privileged user triggering sysrq-p will invoke
> perf_event_print_debug(), which dereferences cpuc->pmu, potentially causing
> a use-after-free.
Yes, real issue too. Need to reset the cpuc->pmu to static pmu in
intel_pmu_cpu_dead().
>
>> return err;
>> }
>> diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c
>> index b39c6ce0efb5c..b8a6382dbb825 100644
>> --- a/arch/x86/events/intel/core.c
>> +++ b/arch/x86/events/intel/core.c
> [ ... ]
>
>> @@ -6479,7 +6479,7 @@ static void intel_pmu_cpu_dead(int cpu)
>> release_arch_pebs_buf_on_cpu(cpu);
>> intel_cpuc_finish(cpuc);
>>
>> - if (is_hybrid() && cpuc->pmu)
>> + if (is_hybrid() && x86_pmu.num_hybrid_pmus && cpuc->pmu)
> [Severity: High]
> This is a pre-existing issue, but does this incomplete guard still allow an
> out-of-bounds write during CPU hotplug rollback?
>
> If CPUHP_AP_PERF_X86_STARTING hotplug setup fails, it rolls back the already
> successful CPUHP_PERF_X86_PREPARE state by invoking intel_pmu_cpu_dead().
>
> Because cpuc->pmu defaults to the statically allocated &pmu, the condition
> evaluates to true. hybrid_pmu() casts the global pmu to the much larger
> struct x86_hybrid_pmu, which could result in an out-of-bounds write when
> modifying supported_cpus.
Yes, real issue. Need to check "cpuc->pmu != pmu" before accessing
hybrid_pmu(cpuc->pmu).
>
>> cpumask_clear_cpu(cpu, &hybrid_pmu(cpuc->pmu)->supported_cpus);
>> }
>
> [Severity: High]
> This is a pre-existing issue, but can intel_pmu_cpu_prepare() leak memory
> allocated by intel_cpuc_prepare() if alloc_arch_pebs_buf_on_cpu() fails?
>
> When the CPU hotplug core attempts to online a CPU, it runs the prepare
> callback. If alloc_arch_pebs_buf_on_cpu() fails, the state transitions are
> aborted without calling the teardown callback intel_pmu_cpu_dead() for this
> specific CPU.
>
> Under memory pressure, could repeated CPU hotplug operations leak the
> cpuc->shared_regs, cpuc->constraint_list, and cpuc->excl_cntrs structures?
Yes, need to call intel_cpuc_finish() once alloc_arch_pebs_buf_on_cpu()
fails in intel_pmu_cpu_prepare().
Thanks.
>
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [Patch v9 02/24] perf/x86: Move hybrid PMU initialization before x86_pmu_starting_cpu()
2026-07-06 2:18 ` sashiko-bot
@ 2026-07-06 8:33 ` Mi, Dapeng
0 siblings, 0 replies; 42+ messages in thread
From: Mi, Dapeng @ 2026-07-06 8:33 UTC (permalink / raw)
To: sashiko-reviews; +Cc: linux-perf-users
On 7/6/2026 10:18 AM, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
>
> Pre-existing issues:
> - [High] Preexisting out-of-bounds read in `check_hw_exists()` on hybrid platforms due to an invalid `container_of()` cast on the global static `pmu` struct via the `hybrid()` macro.
> --
>
> commit 8216e613cee4854e16daaa11279146d17bc7f313
> Author: Dapeng Mi <dapeng1.mi@linux.intel.com>
>
> perf/x86: Move hybrid PMU initialization before x86_pmu_starting_cpu()
>
> The current approach initializes hybrid PMU structures immediately before
> registering them. This is risky as it can lead to key fields being
> inadvertently overwritten. This patch mitigates this by moving the
> default hybrid structure initialization before calling
> x86_pmu_starting_cpu().
>
>> diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c
>> index 872d07a5fa80d..0888d3b0923e1 100644
>> --- a/arch/x86/events/core.c
>> +++ b/arch/x86/events/core.c
>> @@ -2195,8 +2195,20 @@ static int __init init_hw_perf_events(void)
>>
>> pmu.attr_update = x86_pmu.attr_update;
>>
>> - if (!is_hybrid())
>> + if (!is_hybrid()) {
>> x86_pmu_show_pmu_cap(NULL);
>> + } else {
>> + int i;
>> +
>> + /*
>> + * Init default ops.
>> + * Must be called before registering x86_pmu_starting_cpu(),
>> + * otherwise some key PMU fields, e.g., capabilities
>> + * initialized in x86_pmu_starting_cpu(), would be overwritten.
>> + */
>> + for (i = 0; i < x86_pmu.num_hybrid_pmus; i++)
>> + x86_pmu.hybrid_pmu[i].pmu = pmu;
>> + }
> [Severity: High]
> This is a pre-existing issue, but does passing the global pmu struct to
> check_hw_exists() earlier in this function cause an out-of-bounds read on
> hybrid platforms?
>
> In init_hw_perf_events(), check_hw_exists(&pmu, ...) is called after
> perf_is_hybrid is enabled. Inside check_hw_exists(), it iterates over
> fixed counters and calls fixed_counter_disabled():
>
> arch/x86/events/perf_event.h:fixed_counter_disabled() {
> ...
> u64 intel_ctrl = hybrid(pmu, intel_ctrl);
> ...
> }
>
> The hybrid() macro expands to a container_of() cast:
>
> container_of(_pmu, struct x86_hybrid_pmu, pmu)->_field
>
> Because the pmu passed in is the standalone global struct pmu (not embedded
> within a struct x86_hybrid_pmu), this cast yields an invalid pointer.
>
> Could this read garbage data past the end of the global pmu struct when
> accessing intel_ctrl, potentially causing the BIOS hardware check to act on
> garbage data or trigger a kernel panic if it crosses an unmapped page
> boundary?
Yes, a real issue. It looks unnecessary to run fixed_counter_disabled()
check since intel_ctrl always comes from the the bitwised-or between
cntr_mask64 and fixed_cntr_mask64. Would drop fixed_counter_disabled() and
all calling in a separate patch.
Thanks.
>
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [Patch v9 06/24] perf/x86: Use x86_perf_regs in NMI handlers
2026-07-06 2:31 ` sashiko-bot
@ 2026-07-06 8:43 ` Mi, Dapeng
0 siblings, 0 replies; 42+ messages in thread
From: Mi, Dapeng @ 2026-07-06 8:43 UTC (permalink / raw)
To: sashiko-reviews; +Cc: linux-perf-users
On 7/6/2026 10:31 AM, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> - [High] Concurrent use of `x86_pebs_regs` and `x86_user_regs` per-CPU variables between NMI and IRQ-disabled contexts corrupts PEBS samples.
> --
>
> commit a90fb4d272f2182569ad5a2105f6b3f7c429263d
> Author: Dapeng Mi <dapeng1.mi@linux.intel.com>
>
> perf/x86: Use x86_perf_regs in NMI handlers
>
> Support for sampling additional register state in NMI context requires an
> x86-specific register container. This switches x86 NMI handlers to use
> the x86_perf_regs structure.
>
>> diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c
>> index 0888d3b0923e1..d83ea02e24577 100644
>> --- a/arch/x86/events/core.c
>> +++ b/arch/x86/events/core.c
>> @@ -1788,9 +1788,11 @@ void perf_put_guest_lvtpc(void)
>> EXPORT_SYMBOL_FOR_KVM(perf_put_guest_lvtpc);
>> #endif /* CONFIG_PERF_GUEST_MEDIATED_PMU */
>>
>> +static DEFINE_PER_CPU(struct x86_perf_regs, x86_intr_regs);
>> static int
>> perf_event_nmi_handler(unsigned int cmd, struct pt_regs *regs)
>> {
>> + struct x86_perf_regs *x86_regs = this_cpu_ptr(&x86_intr_regs);
>> u64 start_clock;
>> u64 finish_clock;
>> int ret;
>> @@ -1814,7 +1816,8 @@ perf_event_nmi_handler(unsigned int cmd, struct pt_regs *regs)
>> return NMI_DONE;
>>
>> start_clock = sched_clock();
>> - ret = static_call(x86_pmu_handle_irq)(regs);
>> + x86_regs->regs = *regs;
>> + ret = static_call(x86_pmu_handle_irq)(&x86_regs->regs);
> [Severity: High]
> Is this call path safe from NMI reentrancy when accessing the per-CPU
> variables introduced earlier in this series?
>
> If intel_pmu_drain_pebs_buffer() is called from a non-NMI context (e.g.
> intel_pmu_read_event() via the perf_event_read syscall) with local IRQs
> disabled, NMIs can still fire.
>
> If a hardware PMI fires during the execution of __intel_pmu_pebs_events(),
> the CPU will jump here to perf_event_nmi_handler().
>
> The NMI handler calls x86_pmu_handle_irq() -> handle_pmi_common(), which
> also calls x86_pmu_drain_pebs(). Since the earlier patch in this series
> "perf/x86/intel: Convert x86_perf_regs to per-cpu variables" replaced stack
> allocations with shared per-CPU variables:
>
> arch/x86/events/intel/ds.c:__intel_pmu_pebs_events() {
> ...
> struct x86_perf_regs *perf_regs = this_cpu_ptr(&x86_pebs_regs);
> struct pt_regs *regs = &perf_regs->regs;
> ...
> }
>
> And similarly for user regs:
>
> arch/x86/events/core.c:x86_pmu_get_regs_user() {
> ...
> struct x86_perf_regs *x86_regs_user = this_cpu_ptr(&x86_user_regs);
> ...
> }
>
> Will the NMI handler overwrite the same per-CPU structures the interrupted
> non-NMI context was actively using, leading to corrupted PEBS samples when
> the original context resumes?
That should never happen. When intel_pmu_drain_pebs_buffer() is called from
non-NMI context, the whole PMU hardware has been disabled, so NMI can't be
triggered and interrupt current running PEBS handler.
Thanks.
>
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [Patch v9 07/24] x86/fpu/xstate: Add xsaves_nmi() helper
2026-07-06 2:18 ` sashiko-bot
@ 2026-07-06 9:09 ` Mi, Dapeng
0 siblings, 0 replies; 42+ messages in thread
From: Mi, Dapeng @ 2026-07-06 9:09 UTC (permalink / raw)
To: sashiko-reviews; +Cc: linux-perf-users
On 7/6/2026 10:18 AM, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> - [High] The use of the XSAVES instruction in xsaves_nmi() breaks FPU snapshotting to independent buffers due to the hardware's 'modified optimization', leading to a cross-process information leak.
> --
>
> commit e8d6f39a5d4512be90e9976febc30b8f46c0ea64
> Author: Kan Liang <kan.liang@linux.intel.com>
>
> x86/fpu/xstate: Add xsaves_nmi() helper
>
> This commit adds a new xsaves_nmi() helper to safely save supported xsave
> states from within an NMI handler. The helper is intended for the perf
> subsystem to snapshot SIMD and APX eGPRs registers.
>
>> diff --git a/arch/x86/kernel/fpu/xstate.c b/arch/x86/kernel/fpu/xstate.c
>> index a7b6524a9dea2..d39c01546dee7 100644
>> --- a/arch/x86/kernel/fpu/xstate.c
>> +++ b/arch/x86/kernel/fpu/xstate.c
> [ ... ]
>
>> +void xsaves_nmi(struct xregs_state *xstate, u64 mask)
>> +{
>> + int err;
>> +
>> + if (!in_nmi())
>> + return;
>> +
>> + XSTATE_OP(XSAVES, xstate, (u32)mask, (u32)(mask >> 32), err);
> [Severity: High]
> Does this code leak stale data into the perf samples?
>
> The XSAVES instruction uses a hardware modified optimization that tracks
> whether a state component was modified since the last XRSTORS. If a
> component is unmodified, XSAVES skips writing it to memory while still
> setting its presence bit in the XSTATE_BV header.
>
> When the perf subsystem calls xsaves_nmi() in x86_pmu_sample_xregs()
> with an independent per-CPU buffer like ext_regs_buf, skipping the memory
> write leaves whatever stale data was previously in that buffer.
>
> Because the bit in XSTATE_BV is still set, won't the perf subsystem
> read this stale data (potentially from other tasks) and leak it to
> user-space?
No, not exactly. The modified optimization only works when xrstors is
called for the same xstate area.
Copy the description about modified optimization from section 13.6 in SDM
vol. 1.
"
A processor that implements the modified optimization saves information
about the most recent execution of
XRSTOR or XRSTORS in a quantity called XRSTOR_INFO, a 4-tuple containing
the following: (1) the CPL;
(2) whether the logical processor was in VMX non-root operation; (3) *the
linear address of the XSAVE area; *and
(4) the XCOMP_BV field in the XSAVE area. An execution of XSAVEOPT or
XSAVES uses the modified optimization
only if that execution corresponds to XRSTOR_INFO on these four parameters.
"
Since xsaves_nmi() uses a dedicated xstate area and never call xrstors
instruction for this dedicated xstate area, the modified optimization won't
really happen.
Thanks.
>
>> + WARN_ON_ONCE(err);
>> +}
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [Patch v9 08/24] x86/fpu: Add update_fpu_state_and_flag() helper
2026-07-06 2:22 ` sashiko-bot
@ 2026-07-06 9:15 ` Mi, Dapeng
0 siblings, 0 replies; 42+ messages in thread
From: Mi, Dapeng @ 2026-07-06 9:15 UTC (permalink / raw)
To: sashiko-reviews; +Cc: linux-perf-users
On 7/6/2026 10:22 AM, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> - [High] Guest FPU state leaks to host perf samples because the NMI handler incorrectly bypasses the `__task_fpstate` check when `TIF_NEED_FPU_LOAD` is 0.
> --
>
> commit a7b6e478b8260eaf627518f9d19e511212779fab
> Author: Dapeng Mi <dapeng1.mi@linux.intel.com>
>
> x86/fpu: Add update_fpu_state_and_flag() helper
>
> This commit introduces the update_fpu_state_and_flag() helper to save user
> FPU state and set TIF_NEED_FPU_LOAD. It also adds memory barriers in the
> host/guest FPU state switch path to ensure fpu->__task_fpstate is observed
> correctly when non-NULL.
>
> Link: https://lore.kernel.org/all/20251204154721.GB2619703@noisy.programming.kicks-ass.net/
>
>> diff --git a/arch/x86/kernel/fpu/core.c b/arch/x86/kernel/fpu/core.c
>> index 584fb9913be42..fb78e0ecd5fa8 100644
>> --- a/arch/x86/kernel/fpu/core.c
>> +++ b/arch/x86/kernel/fpu/core.c
> [ ... ]
>> @@ -383,13 +396,15 @@ int fpu_swap_kvm_fpstate(struct fpu_guest *guest_fpu, bool enter_guest)
>>
>> /* Swap fpstate */
>> if (enter_guest) {
>> - fpu->__task_fpstate = cur_fps;
>> + WRITE_ONCE(fpu->__task_fpstate, cur_fps);
>> + barrier();
>> fpu->fpstate = guest_fps;
>> guest_fps->in_use = true;
>> } else {
>> guest_fps->in_use = false;
>> fpu->fpstate = fpu->__task_fpstate;
>> - fpu->__task_fpstate = NULL;
>> + barrier();
>> + WRITE_ONCE(fpu->__task_fpstate, NULL);
> [Severity: High]
> Can guest FPU state leak into host perf samples during this window?
>
> The commit message mentions that subsequent patches will use this guarantee
> in NMI context by checking TIF_NEED_FPU_LOAD before retrieving user FPU
> state from the saved task FPU state.
>
> However, during KVM guest execution and within the VM-exit path here,
> TIF_NEED_FPU_LOAD is 0 because it was cleared on guest entry and is not set
> by KVM.
>
> If an NMI hits here before restore_fpregs_from_fpstate() restores the host
> state, would the NMI handler bypass the fpu->__task_fpstate check because
> TIF_NEED_FPU_LOAD is 0?
>
> arch/x86/events/core.c:x86_pmu_update_user_xregs() {
> ...
> if (user_mask && test_thread_flag(TIF_NEED_FPU_LOAD)) {
> perf_regs = container_of(data->regs_user.regs,
> struct x86_perf_regs, regs);
> fpu = x86_task_fpu(current);
> /*
> * If __task_fpstate is set, it holds the right pointer,
> * otherwise fpstate will.
> */
> fps = READ_ONCE(fpu->__task_fpstate);
> ...
> }
>
> If this memory check is skipped, wouldn't the NMI handler fall back to
> reading directly from the hardware registers, which still contain the guest
> FPU state at this point in fpu_swap_kvm_fpstate()?
Yes, this is intended. Currently host perf has been granted the right to
sample the guest except exclude_guest attribute is set.
Thanks.
>
>> }
>>
>> cur_fps = fpu->fpstate;
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [Patch v9 11/24] perf/x86: Enable XMM register sampling for non-PEBS events
2026-07-06 2:34 ` sashiko-bot
@ 2026-07-06 9:47 ` Mi, Dapeng
0 siblings, 0 replies; 42+ messages in thread
From: Mi, Dapeng @ 2026-07-06 9:47 UTC (permalink / raw)
To: sashiko-reviews; +Cc: linux-perf-users
On 7/6/2026 10:34 AM, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> - [High] reserve_ext_regs_buffers() fails to propagate memory allocation errors and improperly uses WARN_ON_ONCE, leading to a silent feature failure and possible kernel panics under memory pressure.
> --
>
> commit 8262cdbae2d650c85cf4924888ecf8b4f85b98de
> Author: Dapeng Mi <dapeng1.mi@linux.intel.com>
>
> perf/x86: Enable XMM register sampling for non-PEBS events
>
> This commit extends XMM register sampling to non-PEBS events using the xsaves
> instruction. It allocates a per-CPU ext_regs_buf to store SIMD and other
> registers. For PEBS events, data is retrieved directly from PEBS records.
>
>> diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c
>> index d83ea02e24577..3e52610510cdc 100644
>> --- a/arch/x86/events/core.c
>> +++ b/arch/x86/events/core.c
> [ ... ]
>> @@ -410,6 +410,56 @@ set_ext_hw_attr(struct hw_perf_event *hwc, struct perf_event *event)
>> +static void reserve_ext_regs_buffers(void)
>> +{
>> + bool compacted = cpu_feature_enabled(X86_FEATURE_XCOMPACTED);
>> + unsigned int size;
>> + int cpu;
>> +
>> + if (!x86_pmu.ext_regs_mask)
>> + return;
>> +
>> + /* +64 bytes for the 64 bytes alignment request of xsave area. */
>> + size = xstate_calculate_size(x86_pmu.ext_regs_mask, compacted) + 64;
>> +
>> + for_each_possible_cpu(cpu) {
>> + per_cpu(ext_regs_buf, cpu) = kzalloc_node(size, GFP_KERNEL,
>> + cpu_to_node(cpu));
>> + if (WARN_ON_ONCE(!per_cpu(ext_regs_buf, cpu)))
>> + goto err;
>> + }
>> +
>> + return;
>> +
>> +err:
>> + release_ext_regs_buffers();
>> +}
> [ ... ]
>> int x86_reserve_hardware(void)
>> {
>> int err = 0;
>>
>> if (!atomic_inc_not_zero(&pmc_refcount)) {
>> mutex_lock(&pmc_reserve_mutex);
>> if (atomic_read(&pmc_refcount) == 0) {
>> if (!reserve_pmc_hardware()) {
>> err = -EBUSY;
>> } else {
>> reserve_ds_buffers();
>> reserve_lbr_buffers();
>> + reserve_ext_regs_buffers();
>> }
>> }
>> if (!err)
> [Severity: High]
> Does this code swallow the memory allocation failure?
>
> Since reserve_ext_regs_buffers() returns void, x86_reserve_hardware()
> appears unaware if kzalloc_node() fails under memory pressure. The hardware
> reservation will succeed anyway, and the perf event creation is permitted
> even though the backing buffers do not exist.
>
> [ ... ]
>> +static void x86_pmu_sample_xregs(struct perf_event *event,
>> + struct perf_sample_data *data,
>> + u64 ignore_mask)
>> +{
>> + struct xregs_state *xsave = get_ext_regs_buf(smp_processor_id());
>> + u64 sample_type = event->attr.sample_type;
>> + struct x86_perf_regs *perf_regs;
>> + u64 intr_mask = 0;
>> + u64 mask = 0;
>> +
>> + if (WARN_ON_ONCE(!xsave) || !in_nmi())
>> + return;
> [Severity: High]
> Can this path be reached with a NULL xsave buffer during a PMI if the earlier
> allocation failed?
>
> If an unprivileged user creates an event requesting extended registers during
> memory pressure, and the allocation in reserve_ext_regs_buffers() fails,
> the event is successfully created. When the PMI hits, this WARN_ON_ONCE in
> NMI context will trigger.
>
> This could panic the system if panic_on_warn is set, or otherwise silently
> drop the requested register data. Should reserve_ext_regs_buffers() return
> an error code so x86_reserve_hardware() can fail the event creation instead?
IMO, it's good enough to use WARN_ON_ONCE() to warn the allocation error.
Propagating the allocation error would need to add much complicated
fallback code since there are lots of allocations, it seems unworthy to
make the code be so complicated.
Thanks.
>
^ permalink raw reply [flat|nested] 42+ messages in thread
end of thread, other threads:[~2026-07-06 9:47 UTC | newest]
Thread overview: 42+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-06 1:54 [Patch v9 00/24] Support SIMD/eGPRs/SSP registers sampling for perf Dapeng Mi
2026-07-06 1:54 ` [Patch v9 01/24] perf/x86: Fix two error-path and hybrid PMU guard issues Dapeng Mi
2026-07-06 2:21 ` sashiko-bot
2026-07-06 8:05 ` Mi, Dapeng
2026-07-06 1:54 ` [Patch v9 02/24] perf/x86: Move hybrid PMU initialization before x86_pmu_starting_cpu() Dapeng Mi
2026-07-06 2:18 ` sashiko-bot
2026-07-06 8:33 ` Mi, Dapeng
2026-07-06 1:54 ` [Patch v9 03/24] perf/x86/intel: Enable large PEBS sampling for XMMs Dapeng Mi
2026-07-06 1:54 ` [Patch v9 04/24] perf/x86/intel: Convert x86_perf_regs to per-cpu variables Dapeng Mi
2026-07-06 1:54 ` [Patch v9 05/24] perf: Eliminate duplicate arch-specific function definitions Dapeng Mi
2026-07-06 1:54 ` [Patch v9 06/24] perf/x86: Use x86_perf_regs in NMI handlers Dapeng Mi
2026-07-06 2:31 ` sashiko-bot
2026-07-06 8:43 ` Mi, Dapeng
2026-07-06 1:54 ` [Patch v9 07/24] x86/fpu/xstate: Add xsaves_nmi() helper Dapeng Mi
2026-07-06 2:18 ` sashiko-bot
2026-07-06 9:09 ` Mi, Dapeng
2026-07-06 1:54 ` [Patch v9 08/24] x86/fpu: Add update_fpu_state_and_flag() helper Dapeng Mi
2026-07-06 2:22 ` sashiko-bot
2026-07-06 9:15 ` Mi, Dapeng
2026-07-06 1:54 ` [Patch v9 09/24] perf: Move and enhance has_extended_regs() for arch-specific use Dapeng Mi
2026-07-06 1:54 ` [Patch v9 10/24] perf/x86/intel: Consolidate PMU capability updates Dapeng Mi
2026-07-06 1:54 ` [Patch v9 11/24] perf/x86: Enable XMM register sampling for non-PEBS events Dapeng Mi
2026-07-06 2:34 ` sashiko-bot
2026-07-06 9:47 ` Mi, Dapeng
2026-07-06 1:54 ` [Patch v9 12/24] perf/x86: Enable XMM register sampling for REGS_USER case Dapeng Mi
2026-07-06 2:35 ` sashiko-bot
2026-07-06 1:54 ` [Patch v9 13/24] perf: Add sampling support for SIMD registers Dapeng Mi
2026-07-06 2:34 ` sashiko-bot
2026-07-06 1:54 ` [Patch v9 14/24] perf/x86: Support XMM sampling using sample_simd_vec_reg_* fields Dapeng Mi
2026-07-06 6:45 ` sashiko-bot
2026-07-06 1:54 ` [Patch v9 15/24] perf/x86: Support YMM " Dapeng Mi
2026-07-06 1:54 ` [Patch v9 16/24] perf/x86: Support ZMM " Dapeng Mi
2026-07-06 1:54 ` [Patch v9 17/24] perf/x86: Support OPMASK sampling using sample_simd_pred_reg_* fields Dapeng Mi
2026-07-06 1:54 ` [Patch v9 18/24] perf: Enhance perf_reg_validate() with simd_enabled argument Dapeng Mi
2026-07-06 1:54 ` [Patch v9 19/24] perf/x86: Support eGPRs sampling using sample_regs_* fields Dapeng Mi
2026-07-06 1:54 ` [Patch v9 20/24] perf/x86: Support SSP " Dapeng Mi
2026-07-06 1:54 ` [Patch v9 21/24] perf/x86/intel: Support arch-PEBS based SIMD/eGPRs/SSP sampling Dapeng Mi
2026-07-06 1:54 ` [Patch v9 22/24] perf/x86/intel: Enable PERF_PMU_CAP_SIMD_REGS capability Dapeng Mi
2026-07-06 2:57 ` sashiko-bot
2026-07-06 1:54 ` [Patch v9 23/24] perf/x86: Activate back-to-back NMI detection for arch-PEBS induced NMIs Dapeng Mi
2026-07-06 1:54 ` [Patch v9 24/24] perf/x86/intel: Add sanity check for PEBS fragment size Dapeng Mi
2026-07-06 5:04 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox