From: Dapeng Mi <dapeng1.mi@linux.intel.com>
To: Peter Zijlstra <peterz@infradead.org>,
Ingo Molnar <mingo@redhat.com>,
Arnaldo Carvalho de Melo <acme@kernel.org>,
Namhyung Kim <namhyung@kernel.org>,
Thomas Gleixner <tglx@linutronix.de>,
Dave Hansen <dave.hansen@linux.intel.com>,
Ian Rogers <irogers@google.com>,
Adrian Hunter <adrian.hunter@intel.com>,
Jiri Olsa <jolsa@kernel.org>,
Alexander Shishkin <alexander.shishkin@linux.intel.com>,
Andi Kleen <ak@linux.intel.com>,
Eranian Stephane <eranian@google.com>
Cc: Mark Rutland <mark.rutland@arm.com>,
broonie@kernel.org, Ravi Bangoria <ravi.bangoria@amd.com>,
linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org,
Zide Chen <zide.chen@intel.com>,
Falcon Thomas <thomas.falcon@intel.com>,
Dapeng Mi <dapeng1.mi@intel.com>,
Xudong Hao <xudong.hao@intel.com>,
Dapeng Mi <dapeng1.mi@linux.intel.com>,
Kan Liang <kan.liang@linux.intel.com>
Subject: [Patch v10 11/23] perf/x86: Enable XMM register sampling for REGS_USER case
Date: Tue, 21 Jul 2026 14:24:54 +0800 [thread overview]
Message-ID: <20260721062506.3745816-12-dapeng1.mi@linux.intel.com> (raw)
In-Reply-To: <20260721062506.3745816-1-dapeng1.mi@linux.intel.com>
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 user-space FPU state has been
cached into task FPU state. 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 state or guest state. Otherwise, the data could 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 | 154 ++++++++++++++++++++++++++++++++---
arch/x86/events/intel/core.c | 6 +-
arch/x86/events/intel/ds.c | 5 +-
3 files changed, 147 insertions(+), 18 deletions(-)
diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c
index 1c3e2bfa2673..20140643e21c 100644
--- a/arch/x86/events/core.c
+++ b/arch/x86/events/core.c
@@ -702,12 +702,12 @@ 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;
@@ -724,12 +724,6 @@ int x86_pmu_hw_config(struct perf_event *event)
}
}
- 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);
}
@@ -1832,14 +1826,129 @@ static void x86_pmu_update_regs_intr(struct perf_event *event,
data->sample_flags |= PERF_SAMPLE_REGS_INTR;
}
+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 x86_perf_regs *x86_regs_user = this_cpu_ptr(&x86_user_regs);
+ struct perf_event_attr *attr = &event->attr;
+ struct x86_perf_regs *perf_regs;
+
+ /*
+ * PERF_SAMPLE_REGS_INTR and PERF_SAMPLE_REGS_USER can both be
+ * requested for one event. Keep user regs in a separate x86_perf_regs
+ * instance, so intr-reg collection does not overwrite user-reg data.
+ */
+ if (user_mode(regs)) {
+ x86_pmu_clear_perf_regs(&x86_regs_user->regs);
+ perf_regs = container_of(regs, struct x86_perf_regs, regs);
+ /* Copy all sampled regs data to x86_regs_user. */
+ *x86_regs_user = *perf_regs;
+ data->regs_user.regs = &x86_regs_user->regs;
+ data->regs_user.abi = perf_reg_abi(current);
+ } else if (is_user_task(current)) {
+ 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 or PMI hits into guest, 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, bool from_pebs)
+{
+ struct x86_perf_regs *perf_regs;
+ struct xregs_state *xsave;
+ struct fpu *fpu;
+ struct fpstate *fps;
+ u64 user_mask = mask;
+
+ if (!is_user_task(current))
+ return 0;
+
+ 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 = from_pebs ? 0 : mask;
+
+ fpu = x86_task_fpu(current);
+ fps = READ_ONCE(fpu->__task_fpstate);
+ /*
+ * If fpu->__task_fpstate is set, it points to the cached user
+ * FPU state (e.g. with KVM guest-state swapping). Otherwise,
+ * when TIF_NEED_FPU_LOAD is set, fpu->fpstate holds the cached
+ * user state. If neither is true, the user state is live in hardware.
+ */
+ if (user_mask && (test_thread_flag(TIF_NEED_FPU_LOAD) || fps)) {
+ perf_regs = container_of(data->regs_user.regs,
+ struct x86_perf_regs, regs);
+ if (!fps)
+ fps = fpu->fpstate;
+ xsave = &fps->regs.xsave;
+
+ update_perf_regs(perf_regs, xsave, user_mask);
+ return 0;
+ }
+
+ return user_mask;
+}
+
static void x86_pmu_sample_xregs(struct perf_event *event,
struct perf_sample_data *data,
+ struct pt_regs *regs,
bool from_pebs)
{
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 user_mask = 0;
if (WARN_ON_ONCE(!xsave) || !in_nmi())
return;
@@ -1852,13 +1961,31 @@ static void x86_pmu_sample_xregs(struct perf_event *event,
intr_mask = from_pebs ? 0 : intr_mask;
}
+ if ((sample_type & PERF_SAMPLE_REGS_USER) && data->regs_user.regs) {
+ if (event->attr.sample_regs_user & PERF_REG_EXTENDED_MASK)
+ user_mask |= XFEATURE_MASK_SSE;
+
+ user_mask &= x86_pmu.ext_regs_mask;
+ user_mask = x86_pmu_update_user_xregs(data, regs,
+ user_mask, from_pebs);
+ }
+
+ if (user_mask | intr_mask) {
+ xsave->header.xfeatures = 0;
+ xsaves_nmi(xsave, user_mask | intr_mask);
+ }
+
if (intr_mask) {
perf_regs = container_of(data->regs_intr.regs,
struct x86_perf_regs, regs);
- xsave->header.xfeatures = 0;
- xsaves_nmi(xsave, intr_mask);
update_perf_regs(perf_regs, xsave, 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);
+ }
}
void x86_pmu_update_perf_regs(struct perf_event *event,
@@ -1868,16 +1995,17 @@ 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);
- x86_pmu_sample_xregs(event, data, from_pebs);
+ x86_pmu_sample_xregs(event, data, regs, from_pebs);
}
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 24f3f49f3953..dbf143eb3547 100644
--- a/arch/x86/events/intel/core.c
+++ b/arch/x86/events/intel/core.c
@@ -4719,15 +4719,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 208584b32878..841b5a2ed89b 100644
--- a/arch/x86/events/intel/ds.c
+++ b/arch/x86/events/intel/ds.c
@@ -1726,8 +1726,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) {
@@ -2930,6 +2929,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
next prev parent reply other threads:[~2026-07-21 6:33 UTC|newest]
Thread overview: 25+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-21 6:24 [Patch v10 00/23] Support SIMD/eGPRs/SSP registers sampling for perf Dapeng Mi
2026-07-21 6:24 ` [Patch v10 01/23] perf/x86: Move hybrid PMU initialization before x86_pmu_starting_cpu() Dapeng Mi
2026-07-21 6:24 ` [Patch v10 02/23] perf/x86/intel: Enable large PEBS sampling for XMMs Dapeng Mi
2026-07-21 6:24 ` [Patch v10 03/23] perf/x86/intel: Convert x86_perf_regs to per-cpu variables Dapeng Mi
2026-07-21 6:24 ` [Patch v10 04/23] perf: Eliminate duplicate arch-specific function definitions Dapeng Mi
2026-07-21 6:24 ` [Patch v10 05/23] perf/x86: Use x86_perf_regs in NMI handlers Dapeng Mi
2026-07-21 6:24 ` [Patch v10 06/23] x86/fpu/xstate: Add xsaves_nmi() helper Dapeng Mi
2026-07-21 6:24 ` [Patch v10 07/23] x86/fpu: Add update_fpu_state_and_flag() helper Dapeng Mi
2026-07-21 6:24 ` [Patch v10 08/23] perf: Move and enhance has_extended_regs() for arch-specific use Dapeng Mi
2026-07-21 6:24 ` [Patch v10 09/23] perf/x86/intel: Centralize PERF_PMU_CAP_EXTENDED_REGS updates Dapeng Mi
2026-07-21 6:24 ` [Patch v10 10/23] perf/x86: Enable XMM register sampling for non-PEBS events Dapeng Mi
2026-07-21 6:24 ` Dapeng Mi [this message]
2026-07-21 6:24 ` [Patch v10 12/23] perf: Add sampling support for SIMD registers Dapeng Mi
2026-07-21 6:24 ` [Patch v10 13/23] perf/x86: Support XMM sampling using sample_simd_vec_reg_* fields Dapeng Mi
2026-07-21 6:24 ` [Patch v10 14/23] perf/x86: Support YMM " Dapeng Mi
2026-07-21 6:24 ` [Patch v10 15/23] perf/x86: Support ZMM " Dapeng Mi
2026-07-21 6:24 ` [Patch v10 16/23] perf/x86: Support OPMASK sampling using sample_simd_pred_reg_* fields Dapeng Mi
2026-07-21 6:25 ` [Patch v10 17/23] perf: Enhance perf_reg_validate() with simd_enabled argument Dapeng Mi
2026-07-21 6:25 ` [Patch v10 18/23] perf/x86: Support eGPRs sampling using sample_regs_* fields Dapeng Mi
2026-07-21 6:25 ` [Patch v10 19/23] perf/x86: Support SSP " Dapeng Mi
2026-07-21 6:25 ` [Patch v10 20/23] perf/x86/intel: Support arch-PEBS based SIMD/eGPRs sampling Dapeng Mi
2026-07-21 6:25 ` [Patch v10 21/23] perf/x86/intel: Advertise PERF_PMU_CAP_SIMD_REGS capability Dapeng Mi
2026-07-21 6:25 ` [Patch v10 22/23] perf/x86: Activate back-to-back NMI detection for arch-PEBS induced NMIs Dapeng Mi
2026-07-21 6:25 ` [Patch v10 23/23] perf/x86/intel: Add sanity check for PEBS record/fragment size Dapeng Mi
2026-07-21 17:42 ` [Patch v10 00/23] Support SIMD/eGPRs/SSP registers sampling for perf Ian Rogers
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260721062506.3745816-12-dapeng1.mi@linux.intel.com \
--to=dapeng1.mi@linux.intel.com \
--cc=acme@kernel.org \
--cc=adrian.hunter@intel.com \
--cc=ak@linux.intel.com \
--cc=alexander.shishkin@linux.intel.com \
--cc=broonie@kernel.org \
--cc=dapeng1.mi@intel.com \
--cc=dave.hansen@linux.intel.com \
--cc=eranian@google.com \
--cc=irogers@google.com \
--cc=jolsa@kernel.org \
--cc=kan.liang@linux.intel.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-perf-users@vger.kernel.org \
--cc=mark.rutland@arm.com \
--cc=mingo@redhat.com \
--cc=namhyung@kernel.org \
--cc=peterz@infradead.org \
--cc=ravi.bangoria@amd.com \
--cc=tglx@linutronix.de \
--cc=thomas.falcon@intel.com \
--cc=xudong.hao@intel.com \
--cc=zide.chen@intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox