* [Patch v3 1/8] perf/x86: Unregister PMI handler on PMU init failure
2026-07-17 8:03 [Patch v3 0/8] perf/x86: Miscellaneous PMU bug fixes and optimizations Dapeng Mi
@ 2026-07-17 8:03 ` Dapeng Mi
2026-07-17 8:03 ` [Patch v3 2/8] perf/x86: Free hybrid state " Dapeng Mi
` (8 subsequent siblings)
9 siblings, 0 replies; 17+ messages in thread
From: Dapeng Mi @ 2026-07-17 8:03 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Ian Rogers, Adrian Hunter, Alexander Shishkin,
Andi Kleen, Eranian Stephane
Cc: linux-kernel, linux-perf-users, Dapeng Mi, Zide Chen,
Falcon Thomas, 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.
Signed-off-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
Reviewed-by: Thomas Falcon <thomas.falcon@intel.com>
---
arch/x86/events/core.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
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;
--
2.34.1
^ permalink raw reply related [flat|nested] 17+ messages in thread* [Patch v3 2/8] perf/x86: Free hybrid state on PMU init failure
2026-07-17 8:03 [Patch v3 0/8] perf/x86: Miscellaneous PMU bug fixes and optimizations Dapeng Mi
2026-07-17 8:03 ` [Patch v3 1/8] perf/x86: Unregister PMI handler on PMU init failure Dapeng Mi
@ 2026-07-17 8:03 ` Dapeng Mi
2026-07-17 8:03 ` [Patch v3 3/8] perf/x86: Guard intel_pmu_cpu_dead() against invalid hybrid PMU casts Dapeng Mi
` (7 subsequent siblings)
9 siblings, 0 replies; 17+ messages in thread
From: Dapeng Mi @ 2026-07-17 8:03 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Ian Rogers, Adrian Hunter, Alexander Shishkin,
Andi Kleen, Eranian Stephane
Cc: linux-kernel, linux-perf-users, Dapeng Mi, Zide Chen,
Falcon Thomas, Xudong Hao, Dapeng Mi
If PMU initialization fails, for example in check_hw_exists(), hybrid
state can be left partially initialized: x86_pmu.hybrid_pmu is not freed
and perf_is_hybrid remains set. This can leak memory and leave stale
hybrid state reachable after a failed init path.
Add x86_pmu_free_hybrid() and use it on PMU init failure paths so all
hybrid-related state is consistently reset.
Signed-off-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
Reviewed-by: Thomas Falcon <thomas.falcon@intel.com>
---
arch/x86/events/core.c | 15 ++++++++++++---
1 file changed, 12 insertions(+), 3 deletions(-)
diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c
index 872d07a5fa80..6c63b27e11e6 100644
--- a/arch/x86/events/core.c
+++ b/arch/x86/events/core.c
@@ -2130,6 +2130,17 @@ void x86_pmu_show_pmu_cap(struct pmu *pmu)
pr_info("... global_ctrl mask: %016llx\n", hybrid(pmu, intel_ctrl));
}
+static void x86_pmu_free_hybrid(void)
+{
+ if (!x86_pmu.hybrid_pmu)
+ return;
+
+ static_branch_disable(&perf_is_hybrid);
+ kfree(x86_pmu.hybrid_pmu);
+ x86_pmu.hybrid_pmu = NULL;
+ x86_pmu.num_hybrid_pmus = 0;
+}
+
static int __init init_hw_perf_events(void)
{
struct x86_pmu_quirk *quirk;
@@ -2258,9 +2269,6 @@ static int __init init_hw_perf_events(void)
for (j = 0; j < i; j++)
perf_pmu_unregister(&x86_pmu.hybrid_pmu[j].pmu);
pr_warn("Failed to register hybrid PMUs\n");
- kfree(x86_pmu.hybrid_pmu);
- x86_pmu.hybrid_pmu = NULL;
- x86_pmu.num_hybrid_pmus = 0;
goto out2;
}
}
@@ -2276,6 +2284,7 @@ static int __init init_hw_perf_events(void)
pmi_unregister:
unregister_nmi_handler(NMI_LOCAL, "PMI");
out_bad_pmu:
+ x86_pmu_free_hybrid();
memset(&x86_pmu, 0, sizeof(x86_pmu));
return err;
}
--
2.34.1
^ permalink raw reply related [flat|nested] 17+ messages in thread* [Patch v3 3/8] perf/x86: Guard intel_pmu_cpu_dead() against invalid hybrid PMU casts
2026-07-17 8:03 [Patch v3 0/8] perf/x86: Miscellaneous PMU bug fixes and optimizations Dapeng Mi
2026-07-17 8:03 ` [Patch v3 1/8] perf/x86: Unregister PMI handler on PMU init failure Dapeng Mi
2026-07-17 8:03 ` [Patch v3 2/8] perf/x86: Free hybrid state " Dapeng Mi
@ 2026-07-17 8:03 ` Dapeng Mi
2026-07-17 8:03 ` [Patch v3 4/8] perf/x86/intel: Unwind cpuc state if PEBS buffer setup fails Dapeng Mi
` (6 subsequent siblings)
9 siblings, 0 replies; 17+ messages in thread
From: Dapeng Mi @ 2026-07-17 8:03 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Ian Rogers, Adrian Hunter, Alexander Shishkin,
Andi Kleen, Eranian Stephane
Cc: linux-kernel, linux-perf-users, Dapeng Mi, Zide Chen,
Falcon Thomas, Xudong Hao, Dapeng Mi
In failure paths, cpuc->pmu can still point to the global static pmu
instead of an embedded x86_hybrid_pmu::pmu. Calling hybrid_pmu() on
that pointer causes an invalid container conversion and may lead to
out-of-bounds access.
This can happen in at least two cases:
- init_hybrid_pmu() fails check_hw_exists() and leaves cpuc->pmu as-is.
- CPU hotplug fails between CPUHP_PERF_X86_PREPARE and
CPUHP_AP_PERF_X86_STARTING, and rollback invokes intel_pmu_cpu_dead().
Fix both paths by:
- Clear cpuc->pmu to NULL when check_hw_exists() fails.
- Validat that cpuc->pmu is not the global static pmu before calling
hybrid_pmu() in intel_pmu_cpu_dead().
A new helper x86_get_static_pmu() is added to get the global static pmu.
Signed-off-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
Reviewed-by: Thomas Falcon <thomas.falcon@intel.com>
---
arch/x86/events/core.c | 5 +++++
arch/x86/events/intel/core.c | 7 +++++--
arch/x86/events/perf_event.h | 1 +
3 files changed, 11 insertions(+), 2 deletions(-)
diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c
index 6c63b27e11e6..a02f303a9151 100644
--- a/arch/x86/events/core.c
+++ b/arch/x86/events/core.c
@@ -790,6 +790,11 @@ int is_x86_event(struct perf_event *event)
return false;
}
+inline struct pmu *x86_get_static_pmu(void)
+{
+ return &pmu;
+}
+
struct pmu *x86_get_pmu(unsigned int cpu)
{
struct cpu_hw_events *cpuc = &per_cpu(cpu_hw_events, cpu);
diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c
index b39c6ce0efb5..a991fc4f1575 100644
--- a/arch/x86/events/intel/core.c
+++ b/arch/x86/events/intel/core.c
@@ -6329,8 +6329,10 @@ static bool init_hybrid_pmu(int cpu)
intel_pmu_check_hybrid_pmus(pmu);
- if (!check_hw_exists(&pmu->pmu, pmu->cntr_mask, pmu->fixed_cntr_mask))
+ if (!check_hw_exists(&pmu->pmu, pmu->cntr_mask, pmu->fixed_cntr_mask)) {
+ cpuc->pmu = NULL;
return false;
+ }
pr_info("%s PMU driver: ", pmu->name);
@@ -6475,11 +6477,12 @@ void intel_cpuc_finish(struct cpu_hw_events *cpuc)
static void intel_pmu_cpu_dead(int cpu)
{
struct cpu_hw_events *cpuc = &per_cpu(cpu_hw_events, cpu);
+ struct pmu *pmu = x86_get_static_pmu();
release_arch_pebs_buf_on_cpu(cpu);
intel_cpuc_finish(cpuc);
- if (is_hybrid() && cpuc->pmu)
+ if (is_hybrid() && cpuc->pmu && cpuc->pmu != pmu)
cpumask_clear_cpu(cpu, &hybrid_pmu(cpuc->pmu)->supported_cpus);
}
diff --git a/arch/x86/events/perf_event.h b/arch/x86/events/perf_event.h
index a8afea8d38f0..01ae287cde16 100644
--- a/arch/x86/events/perf_event.h
+++ b/arch/x86/events/perf_event.h
@@ -1161,6 +1161,7 @@ static struct perf_pmu_format_hybrid_attr format_attr_hybrid_##_name = {\
.pmu_type = _pmu, \
}
+struct pmu *x86_get_static_pmu(void);
struct pmu *x86_get_pmu(unsigned int cpu);
extern struct x86_pmu x86_pmu __read_mostly;
--
2.34.1
^ permalink raw reply related [flat|nested] 17+ messages in thread* [Patch v3 4/8] perf/x86/intel: Unwind cpuc state if PEBS buffer setup fails
2026-07-17 8:03 [Patch v3 0/8] perf/x86: Miscellaneous PMU bug fixes and optimizations Dapeng Mi
` (2 preceding siblings ...)
2026-07-17 8:03 ` [Patch v3 3/8] perf/x86: Guard intel_pmu_cpu_dead() against invalid hybrid PMU casts Dapeng Mi
@ 2026-07-17 8:03 ` Dapeng Mi
2026-07-17 8:03 ` [Patch v3 5/8] perf/x86: Remove stale fixed counter helper and fix hybrid PMU access Dapeng Mi
` (5 subsequent siblings)
9 siblings, 0 replies; 17+ messages in thread
From: Dapeng Mi @ 2026-07-17 8:03 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Ian Rogers, Adrian Hunter, Alexander Shishkin,
Andi Kleen, Eranian Stephane
Cc: linux-kernel, linux-perf-users, Dapeng Mi, Zide Chen,
Falcon Thomas, Xudong Hao, Dapeng Mi
intel_pmu_cpu_prepare() allocates per-CPU perf state first and then sets
up the arch PEBS buffer. If alloc_arch_pebs_buf_on_cpu() fails,
the previously allocated cpuc resources are left behind.
Make the failure path call intel_cpuc_finish(cpuc) to release the per-CPU
state allocated by intel_cpuc_prepare().
Signed-off-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
Reviewed-by: Thomas Falcon <thomas.falcon@intel.com>
---
arch/x86/events/intel/core.c | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c
index a991fc4f1575..b47d2f00ac13 100644
--- a/arch/x86/events/intel/core.c
+++ b/arch/x86/events/intel/core.c
@@ -5924,13 +5924,20 @@ int intel_cpuc_prepare(struct cpu_hw_events *cpuc, int cpu)
static int intel_pmu_cpu_prepare(int cpu)
{
+ struct cpu_hw_events *cpuc = &per_cpu(cpu_hw_events, cpu);
int ret;
- ret = intel_cpuc_prepare(&per_cpu(cpu_hw_events, cpu), cpu);
+ ret = intel_cpuc_prepare(cpuc, cpu);
if (ret)
return ret;
- return alloc_arch_pebs_buf_on_cpu(cpu);
+ ret = alloc_arch_pebs_buf_on_cpu(cpu);
+ if (ret) {
+ intel_cpuc_finish(cpuc);
+ return ret;
+ }
+
+ return 0;
}
static void flip_smm_bit(void *data)
--
2.34.1
^ permalink raw reply related [flat|nested] 17+ messages in thread* [Patch v3 5/8] perf/x86: Remove stale fixed counter helper and fix hybrid PMU access
2026-07-17 8:03 [Patch v3 0/8] perf/x86: Miscellaneous PMU bug fixes and optimizations Dapeng Mi
` (3 preceding siblings ...)
2026-07-17 8:03 ` [Patch v3 4/8] perf/x86/intel: Unwind cpuc state if PEBS buffer setup fails Dapeng Mi
@ 2026-07-17 8:03 ` Dapeng Mi
2026-07-17 8:03 ` [Patch v3 6/8] perf/x86/intel: Fix intel_cap handling on hybrid PMUs Dapeng Mi
` (4 subsequent siblings)
9 siblings, 0 replies; 17+ messages in thread
From: Dapeng Mi @ 2026-07-17 8:03 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Ian Rogers, Adrian Hunter, Alexander Shishkin,
Andi Kleen, Eranian Stephane
Cc: linux-kernel, linux-perf-users, Dapeng Mi, Zide Chen,
Falcon Thomas, Xudong Hao, Dapeng Mi
On hybrid systems, init_hw_perf_events() can call check_hw_exists() with
the global PMU pointer after perf_is_hybrid is set. In that case,
fixed_counter_disabled() uses hybrid() on a non-hybrid PMU object, so the
intel_ctrl access is taken from the wrong layout and can read out of
bounds.
fixed_counter_disabled() was added in commit 32451614da2a
("perf/x86/intel: Support CPUID 10.ECX to disable fixed counters"), when
fixed counters were tracked via num_fixed_counters. Today fixed counters
are represented by fixed_cntr_mask, so this helper is obsolete.
Remove fixed_counter_disabled() and its callers, and rely directly on the
fixed-counter bitmask. With the helper gone, check_hw_exists() no longer
needs a PMU argument, so drop that parameter as well. This removes the
invalid hybrid access and closes the out-of-bounds read risk.
Signed-off-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
Reviewed-by: Thomas Falcon <thomas.falcon@intel.com>
---
arch/x86/events/core.c | 8 ++------
arch/x86/events/intel/core.c | 4 +---
arch/x86/events/perf_event.h | 9 +--------
3 files changed, 4 insertions(+), 17 deletions(-)
diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c
index a02f303a9151..143a6e735d9e 100644
--- a/arch/x86/events/core.c
+++ b/arch/x86/events/core.c
@@ -265,7 +265,7 @@ static void release_pmc_hardware(void) {}
#endif
-bool check_hw_exists(struct pmu *pmu, unsigned long *cntr_mask,
+bool check_hw_exists(unsigned long *cntr_mask,
unsigned long *fixed_cntr_mask)
{
u64 val, val_fail = -1, val_new= ~0;
@@ -297,8 +297,6 @@ bool check_hw_exists(struct pmu *pmu, unsigned long *cntr_mask,
if (ret)
goto msr_fail;
for_each_set_bit(i, fixed_cntr_mask, X86_PMC_IDX_MAX) {
- if (fixed_counter_disabled(i, pmu))
- continue;
if (val & (0x03ULL << i*4)) {
bios_fail = 1;
val_fail = val;
@@ -1618,8 +1616,6 @@ void perf_event_print_debug(void)
cpu, idx, prev_left);
}
for_each_set_bit(idx, fixed_cntr_mask, X86_PMC_IDX_MAX) {
- if (fixed_counter_disabled(idx, cpuc->pmu))
- continue;
rdmsrq(x86_pmu_fixed_ctr_addr(idx), pmc_count);
pr_info("CPU#%d: fixed-PMC%d count: %016llx\n",
@@ -2180,7 +2176,7 @@ static int __init init_hw_perf_events(void)
pmu_check_apic();
/* sanity check that the hardware exists or is emulated */
- if (!check_hw_exists(&pmu, x86_pmu.cntr_mask, x86_pmu.fixed_cntr_mask))
+ if (!check_hw_exists(x86_pmu.cntr_mask, x86_pmu.fixed_cntr_mask))
goto out_bad_pmu;
pr_cont("%s PMU driver.\n", x86_pmu.name);
diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c
index b47d2f00ac13..c418176065f6 100644
--- a/arch/x86/events/intel/core.c
+++ b/arch/x86/events/intel/core.c
@@ -3713,8 +3713,6 @@ static void intel_pmu_reset(void)
wrmsrq_safe(x86_pmu_event_addr(idx), 0ull);
}
for_each_set_bit(idx, fixed_cntr_mask, INTEL_PMC_MAX_FIXED) {
- if (fixed_counter_disabled(idx, cpuc->pmu))
- continue;
wrmsrq_safe(x86_pmu_fixed_ctr_addr(idx), 0ull);
}
@@ -6336,7 +6334,7 @@ static bool init_hybrid_pmu(int cpu)
intel_pmu_check_hybrid_pmus(pmu);
- if (!check_hw_exists(&pmu->pmu, pmu->cntr_mask, pmu->fixed_cntr_mask)) {
+ if (!check_hw_exists(pmu->cntr_mask, pmu->fixed_cntr_mask)) {
cpuc->pmu = NULL;
return false;
}
diff --git a/arch/x86/events/perf_event.h b/arch/x86/events/perf_event.h
index 01ae287cde16..cc9cfaae4f01 100644
--- a/arch/x86/events/perf_event.h
+++ b/arch/x86/events/perf_event.h
@@ -1243,7 +1243,7 @@ static inline int x86_pmu_rdpmc_index(int index)
return x86_pmu.rdpmc_index ? x86_pmu.rdpmc_index(index) : index;
}
-bool check_hw_exists(struct pmu *pmu, unsigned long *cntr_mask,
+bool check_hw_exists(unsigned long *cntr_mask,
unsigned long *fixed_cntr_mask);
int x86_add_exclusive(unsigned int what);
@@ -1456,13 +1456,6 @@ ssize_t events_hybrid_sysfs_show(struct device *dev,
struct device_attribute *attr,
char *page);
-static inline bool fixed_counter_disabled(int i, struct pmu *pmu)
-{
- u64 intel_ctrl = hybrid(pmu, intel_ctrl);
-
- return !(intel_ctrl >> (i + INTEL_PMC_IDX_FIXED));
-}
-
#ifdef CONFIG_CPU_SUP_AMD
int amd_pmu_init(void);
--
2.34.1
^ permalink raw reply related [flat|nested] 17+ messages in thread* [Patch v3 6/8] perf/x86/intel: Fix intel_cap handling on hybrid PMUs
2026-07-17 8:03 [Patch v3 0/8] perf/x86: Miscellaneous PMU bug fixes and optimizations Dapeng Mi
` (4 preceding siblings ...)
2026-07-17 8:03 ` [Patch v3 5/8] perf/x86: Remove stale fixed counter helper and fix hybrid PMU access Dapeng Mi
@ 2026-07-17 8:03 ` Dapeng Mi
2026-08-10 12:18 ` Peter Zijlstra
2026-07-17 8:03 ` [Patch v3 7/8] perf/x86: Optimize ACR handling in match_prev_assignment() Dapeng Mi
` (3 subsequent siblings)
9 siblings, 1 reply; 17+ messages in thread
From: Dapeng Mi @ 2026-07-17 8:03 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Ian Rogers, Adrian Hunter, Alexander Shishkin,
Andi Kleen, Eranian Stephane
Cc: linux-kernel, linux-perf-users, Dapeng Mi, Zide Chen,
Falcon Thomas, Xudong Hao, Dapeng Mi
intel_cap (IA32_PERF_CAPABILITIES) updates are currently tied to
X86_FEATURE_ARCH_PERFMON_EXT, but these are independent feature paths.
As a result, hybrid PMU capability state can be updated under the wrong
condition.
Also, intel_pmu_broken_perf_cap() is too narrow. Per RPL018, the missing
PERF_METRICS_AVAILABLE bit affects both Raptor Lake and Meteor Lake
parts, not only the currently covered subset.
Move intel_cap updates out of the ARCH_PERFMON_EXT-gated path, extend
intel_pmu_broken_perf_cap() coverage to both RPL and MTL families, and
introduce intel_update_pmu_caps() to centralize PMU capability updates.
Signed-off-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
Reviewed-by: Thomas Falcon <thomas.falcon@intel.com>
---
arch/x86/events/intel/core.c | 41 ++++++++++++++++++++++++++----------
1 file changed, 30 insertions(+), 11 deletions(-)
diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c
index c418176065f6..361f8e0ab36c 100644
--- a/arch/x86/events/intel/core.c
+++ b/arch/x86/events/intel/core.c
@@ -6147,8 +6147,15 @@ 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
+ * RPL and MTL. Details can be found in RPL018 erratum.
+ * https://edc.intel.com/content/www/us/en/design/products/platforms/details/raptor-lake-s/13th-generation-core-processor-specification-update/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;
@@ -6183,7 +6190,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;
@@ -6241,10 +6248,24 @@ static void update_pmu_cap(struct pmu *pmu)
WARN_ON(x86_pmu.arch_pebs == 1);
x86_pmu.arch_pebs = 0;
}
+}
+
+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 (!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);
+ 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;
}
}
@@ -6329,9 +6350,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);
if (!check_hw_exists(pmu->cntr_mask, pmu->fixed_cntr_mask)) {
@@ -8828,8 +8847,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,
--
2.34.1
^ permalink raw reply related [flat|nested] 17+ messages in thread* Re: [Patch v3 6/8] perf/x86/intel: Fix intel_cap handling on hybrid PMUs
2026-07-17 8:03 ` [Patch v3 6/8] perf/x86/intel: Fix intel_cap handling on hybrid PMUs Dapeng Mi
@ 2026-08-10 12:18 ` Peter Zijlstra
2026-08-10 12:25 ` Mi, Dapeng
0 siblings, 1 reply; 17+ messages in thread
From: Peter Zijlstra @ 2026-08-10 12:18 UTC (permalink / raw)
To: Dapeng Mi
Cc: Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim, Ian Rogers,
Adrian Hunter, Alexander Shishkin, Andi Kleen, Eranian Stephane,
linux-kernel, linux-perf-users, Dapeng Mi, Zide Chen,
Falcon Thomas, Xudong Hao
On Fri, Jul 17, 2026 at 04:03:40PM +0800, Dapeng Mi wrote:
> + /*
> + * The Perf Metric (Bit 15) is always cleared on P-core of
> + * RPL and MTL. Details can be found in RPL018 erratum.
> + * https://edc.intel.com/content/www/us/en/design/products/platforms/details/raptor-lake-s/13th-generation-core-processor-specification-update/errata-details/
> + */
Typically intel.com URLs are unstable and not worth the effort of
including anywhere. I'll strip this out, its probably easy enough to
google the erratum number.
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [Patch v3 6/8] perf/x86/intel: Fix intel_cap handling on hybrid PMUs
2026-08-10 12:18 ` Peter Zijlstra
@ 2026-08-10 12:25 ` Mi, Dapeng
0 siblings, 0 replies; 17+ messages in thread
From: Mi, Dapeng @ 2026-08-10 12:25 UTC (permalink / raw)
To: Peter Zijlstra
Cc: Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim, Ian Rogers,
Adrian Hunter, Alexander Shishkin, Andi Kleen, Eranian Stephane,
linux-kernel, linux-perf-users, Dapeng Mi, Zide Chen,
Falcon Thomas, Xudong Hao
On 8/10/2026 8:18 PM, Peter Zijlstra wrote:
> On Fri, Jul 17, 2026 at 04:03:40PM +0800, Dapeng Mi wrote:
>> + /*
>> + * The Perf Metric (Bit 15) is always cleared on P-core of
>> + * RPL and MTL. Details can be found in RPL018 erratum.
>> + * https://edc.intel.com/content/www/us/en/design/products/platforms/details/raptor-lake-s/13th-generation-core-processor-specification-update/errata-details/
>> + */
> Typically intel.com URLs are unstable and not worth the effort of
> including anywhere. I'll strip this out, its probably easy enough to
> google the erratum number.
Sure. Thanks.
^ permalink raw reply [flat|nested] 17+ messages in thread
* [Patch v3 7/8] perf/x86: Optimize ACR handling in match_prev_assignment()
2026-07-17 8:03 [Patch v3 0/8] perf/x86: Miscellaneous PMU bug fixes and optimizations Dapeng Mi
` (5 preceding siblings ...)
2026-07-17 8:03 ` [Patch v3 6/8] perf/x86/intel: Fix intel_cap handling on hybrid PMUs Dapeng Mi
@ 2026-07-17 8:03 ` Dapeng Mi
2026-07-17 8:03 ` [Patch v3 8/8] perf/x86/intel: Prevent drain_pebs() reentry Dapeng Mi
` (2 subsequent siblings)
9 siblings, 0 replies; 17+ messages in thread
From: Dapeng Mi @ 2026-07-17 8:03 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Ian Rogers, Adrian Hunter, Alexander Shishkin,
Andi Kleen, Eranian Stephane
Cc: linux-kernel, linux-perf-users, Dapeng Mi, Zide Chen,
Falcon Thomas, Xudong Hao, Dapeng Mi
match_prev_assignment() currently forces a mismatch for ACR events, so
ACR counter indices are reprogrammed on every scheduling pass. That
causes avoidable overhead because disable and enable paths must touch
multiple MSRs.
The previous ACR assignment is already cached in acr_cfg_b[]. Use that
state to compare the newly computed ACR counter indices in hwc->config1
against the cached value in acr_cfg_b[hwc->idx]. If they match, skip
unnecessary disable and enable work.
Also tighten is_acr_self_reload_event() so it first verifies the event
is an ACR event before testing for the self-reload case.
Signed-off-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
Reviewed-by: Thomas Falcon <thomas.falcon@intel.com>
---
arch/x86/events/core.c | 13 ++++++++++++-
arch/x86/events/perf_event.h | 2 +-
2 files changed, 13 insertions(+), 2 deletions(-)
diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c
index 143a6e735d9e..8b3ea0adb965 100644
--- a/arch/x86/events/core.c
+++ b/arch/x86/events/core.c
@@ -1297,6 +1297,17 @@ int x86_perf_rdpmc_index(struct perf_event *event)
return event->hw.event_base_rdpmc;
}
+static inline bool acr_match_prev_indices(struct perf_event *event,
+ struct cpu_hw_events *cpuc)
+{
+ struct hw_perf_event *hwc = &event->hw;
+
+ if (!is_acr_event_group(event))
+ return true;
+ /* ACR counter indices don't change. */
+ return hwc->config1 == cpuc->acr_cfg_b[hwc->idx];
+}
+
static inline int match_prev_assignment(struct perf_event *event,
struct cpu_hw_events *cpuc,
int i)
@@ -1306,7 +1317,7 @@ static inline int match_prev_assignment(struct perf_event *event,
return hwc->idx == cpuc->assign[i] &&
hwc->last_cpu == smp_processor_id() &&
hwc->last_tag == cpuc->tags[i] &&
- !is_acr_event_group(event);
+ acr_match_prev_indices(event, cpuc);
}
static void x86_pmu_start(struct perf_event *event, int flags);
diff --git a/arch/x86/events/perf_event.h b/arch/x86/events/perf_event.h
index cc9cfaae4f01..fa381110f7a7 100644
--- a/arch/x86/events/perf_event.h
+++ b/arch/x86/events/perf_event.h
@@ -141,7 +141,7 @@ static inline bool is_acr_self_reload_event(struct perf_event *event)
{
struct hw_perf_event *hwc = &event->hw;
- if (hwc->idx < 0)
+ if (hwc->idx < 0 || !is_acr_event_group(event))
return false;
return test_bit(hwc->idx, (unsigned long *)&hwc->config1);
--
2.34.1
^ permalink raw reply related [flat|nested] 17+ messages in thread* [Patch v3 8/8] perf/x86/intel: Prevent drain_pebs() reentry
2026-07-17 8:03 [Patch v3 0/8] perf/x86: Miscellaneous PMU bug fixes and optimizations Dapeng Mi
` (6 preceding siblings ...)
2026-07-17 8:03 ` [Patch v3 7/8] perf/x86: Optimize ACR handling in match_prev_assignment() Dapeng Mi
@ 2026-07-17 8:03 ` Dapeng Mi
2026-08-10 13:01 ` Peter Zijlstra
2026-07-21 14:58 ` [Patch v3 0/8] perf/x86: Miscellaneous PMU bug fixes and optimizations Chen, Zide
2026-08-10 9:06 ` Mi, Dapeng
9 siblings, 1 reply; 17+ messages in thread
From: Dapeng Mi @ 2026-07-17 8:03 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Ian Rogers, Adrian Hunter, Alexander Shishkin,
Andi Kleen, Eranian Stephane
Cc: linux-kernel, linux-perf-users, Dapeng Mi, Zide Chen,
Falcon Thomas, Xudong Hao, Dapeng Mi
The PEBS buffer is shared by all events on a CPU, so drain_pebs() must
not run concurrently. If it is reentered, one instance may observe stale
buffer state and potentially access out-of-bound memory.
Most invocations happen in NMI context, which naturally prevents reentry.
However, drain_pebs() is also reachable from process context via
intel_pmu_drain_pebs_buffer().
In those paths, the PMU is often already disabled, but not guaranteed.
For example, __intel_pmu_pebs_disable() only disables the target counter,
so other active counters can still raise a PMI and interrupt an in-flight
drain_pebs().
Introduce __intel_pmu_quiesce() and __intel_pmu_resume() helpers and
use them in intel_pmu_drain_pebs_buffer() to disable the full PMU
around the drain_pebs() call, preventing reentry.
Signed-off-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
---
arch/x86/events/intel/core.c | 40 ++++++++++++++++++++++++++++--------
arch/x86/events/intel/ds.c | 7 -------
2 files changed, 32 insertions(+), 15 deletions(-)
diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c
index 361f8e0ab36c..726b39b9bba9 100644
--- a/arch/x86/events/intel/core.c
+++ b/arch/x86/events/intel/core.c
@@ -3125,6 +3125,36 @@ static void intel_pmu_del_event(struct perf_event *event)
this_cpu_ptr(&cpu_hw_events)->n_late_setup--;
}
+static inline void __intel_pmu_quiesce(bool pmu_enabled)
+{
+ struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
+
+ cpuc->enabled = 0;
+ if (pmu_enabled)
+ intel_pmu_disable_all();
+}
+
+static inline void __intel_pmu_resume(bool pmu_enabled)
+{
+ struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
+
+ cpuc->enabled = pmu_enabled;
+ if (pmu_enabled)
+ intel_pmu_enable_all(0);
+}
+
+void intel_pmu_drain_pebs_buffer(void)
+{
+ struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
+ bool pmu_enabled = cpuc->enabled;
+ struct perf_sample_data data;
+
+ /* Disable PMU so no new PMI can interrupt and re-enter drain_pebs(). */
+ __intel_pmu_quiesce(pmu_enabled);
+ static_call(x86_pmu_drain_pebs)(NULL, &data);
+ __intel_pmu_resume(pmu_enabled);
+}
+
static int icl_set_topdown_event_period(struct perf_event *event)
{
struct hw_perf_event *hwc = &event->hw;
@@ -3322,10 +3352,7 @@ static void intel_pmu_read_event(struct perf_event *event)
if (is_metric_event(event) && (cpuc->txn_flags & PERF_PMU_TXN_READ))
return;
- cpuc->enabled = 0;
- if (pmu_enabled)
- intel_pmu_disable_all();
-
+ __intel_pmu_quiesce(pmu_enabled);
/*
* If the PEBS counters snapshotting is enabled,
* the topdown event is available in PEBS records.
@@ -3334,10 +3361,7 @@ static void intel_pmu_read_event(struct perf_event *event)
static_call(intel_pmu_update_topdown_event)(event, NULL);
else
intel_pmu_drain_pebs_buffer();
-
- cpuc->enabled = pmu_enabled;
- if (pmu_enabled)
- intel_pmu_enable_all(0);
+ __intel_pmu_resume(pmu_enabled);
return;
}
diff --git a/arch/x86/events/intel/ds.c b/arch/x86/events/intel/ds.c
index e86e4ba91e1b..7f8b98d1837d 100644
--- a/arch/x86/events/intel/ds.c
+++ b/arch/x86/events/intel/ds.c
@@ -1240,13 +1240,6 @@ int intel_pmu_drain_bts_buffer(void)
return 1;
}
-void intel_pmu_drain_pebs_buffer(void)
-{
- struct perf_sample_data data;
-
- static_call(x86_pmu_drain_pebs)(NULL, &data);
-}
-
/*
* PEBS
*/
--
2.34.1
^ permalink raw reply related [flat|nested] 17+ messages in thread* Re: [Patch v3 8/8] perf/x86/intel: Prevent drain_pebs() reentry
2026-07-17 8:03 ` [Patch v3 8/8] perf/x86/intel: Prevent drain_pebs() reentry Dapeng Mi
@ 2026-08-10 13:01 ` Peter Zijlstra
2026-08-11 1:39 ` Mi, Dapeng
0 siblings, 1 reply; 17+ messages in thread
From: Peter Zijlstra @ 2026-08-10 13:01 UTC (permalink / raw)
To: Dapeng Mi
Cc: Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim, Ian Rogers,
Adrian Hunter, Alexander Shishkin, Andi Kleen, Eranian Stephane,
linux-kernel, linux-perf-users, Dapeng Mi, Zide Chen,
Falcon Thomas, Xudong Hao
On Fri, Jul 17, 2026 at 04:03:42PM +0800, Dapeng Mi wrote:
> The PEBS buffer is shared by all events on a CPU, so drain_pebs() must
> not run concurrently. If it is reentered, one instance may observe stale
> buffer state and potentially access out-of-bound memory.
>
> Most invocations happen in NMI context, which naturally prevents reentry.
> However, drain_pebs() is also reachable from process context via
> intel_pmu_drain_pebs_buffer().
>
> In those paths, the PMU is often already disabled, but not guaranteed.
> For example, __intel_pmu_pebs_disable() only disables the target counter,
> so other active counters can still raise a PMI and interrupt an in-flight
> drain_pebs().
>
> Introduce __intel_pmu_quiesce() and __intel_pmu_resume() helpers and
> use them in intel_pmu_drain_pebs_buffer() to disable the full PMU
> around the drain_pebs() call, preventing reentry.
>
It is not at all clear to me where the exact recursion happens. (The
word you're looking for was recursion, not concurrent).
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [Patch v3 8/8] perf/x86/intel: Prevent drain_pebs() reentry
2026-08-10 13:01 ` Peter Zijlstra
@ 2026-08-11 1:39 ` Mi, Dapeng
2026-08-11 8:22 ` Peter Zijlstra
0 siblings, 1 reply; 17+ messages in thread
From: Mi, Dapeng @ 2026-08-11 1:39 UTC (permalink / raw)
To: Peter Zijlstra
Cc: Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim, Ian Rogers,
Adrian Hunter, Alexander Shishkin, Andi Kleen, Eranian Stephane,
linux-kernel, linux-perf-users, Dapeng Mi, Zide Chen,
Falcon Thomas, Xudong Hao
On 8/10/2026 9:01 PM, Peter Zijlstra wrote:
> On Fri, Jul 17, 2026 at 04:03:42PM +0800, Dapeng Mi wrote:
>> The PEBS buffer is shared by all events on a CPU, so drain_pebs() must
>> not run concurrently. If it is reentered, one instance may observe stale
>> buffer state and potentially access out-of-bound memory.
>>
>> Most invocations happen in NMI context, which naturally prevents reentry.
>> However, drain_pebs() is also reachable from process context via
>> intel_pmu_drain_pebs_buffer().
>>
>> In those paths, the PMU is often already disabled, but not guaranteed.
>> For example, __intel_pmu_pebs_disable() only disables the target counter,
>> so other active counters can still raise a PMI and interrupt an in-flight
>> drain_pebs().
>>
>> Introduce __intel_pmu_quiesce() and __intel_pmu_resume() helpers and
>> use them in intel_pmu_drain_pebs_buffer() to disable the full PMU
>> around the drain_pebs() call, preventing reentry.
>>
> It is not at all clear to me where the exact recursion happens. (The
> word you're looking for was recursion, not concurrent).
Yes, the word "concurrently" is not accurate, reentry is the more accurate
word.
Currently drain_pebs() would be called in two places, one is the in the PMI
handler, like handle_pmi_common(). The other place is
intel_pmu_drain_pebs_buffer() which is from process context.
So when intel_pmu_drain_pebs_buffer() is calling drain_pebs(), if there is
an active PEBS event triggering PMI, it would interrupt current in-flight
drain_pebs() and lead to drain_pebs() reentry.
The good news is the global pmu has been disabled in most places before
calling intel_pmu_drain_pebs_buffer(), so no new PMI can be triggered to
interrupt current running drain_pebs() helper, but not all places does so,
like __intel_pmu_pebs_disable() where only the target counter has been
disabled instead of the whole PMU. So it's still possible tjat another
active PEBS event triggers PMI and interrupts current running drain_pebs().
Take the intel_pmu_drain_arch_pebs() as an example,
base = cpuc->pebs_vaddr;
top = cpuc->pebs_vaddr + (index.wr << ARCH_PEBS_INDEX_WR_SHIFT);
------> interrupted here ...
index.wr = 0;
index.full = 0;
index.en = 1;
if (cpuc->n_pebs == cpuc->n_large_pebs)
index.thresh = ARCH_PEBS_THRESH_MULTI;
else
index.thresh = ARCH_PEBS_THRESH_SINGLE;
wrmsrq(MSR_IA32_PEBS_INDEX, index.whole);
Assume the drain_pebs() is interrupted just after reading the top value by
a new PEBS PMI and the PMI handler would drain all the PEBS buffer. When
the PMI returns and the original drian_pebs() continues to execute but it
doesn't know the PEBS buffer has been cleared and may access some stale
data and lead to some unexpected errors.
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [Patch v3 8/8] perf/x86/intel: Prevent drain_pebs() reentry
2026-08-11 1:39 ` Mi, Dapeng
@ 2026-08-11 8:22 ` Peter Zijlstra
2026-08-11 10:00 ` Mi, Dapeng
0 siblings, 1 reply; 17+ messages in thread
From: Peter Zijlstra @ 2026-08-11 8:22 UTC (permalink / raw)
To: Mi, Dapeng
Cc: Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim, Ian Rogers,
Adrian Hunter, Alexander Shishkin, Andi Kleen, Eranian Stephane,
linux-kernel, linux-perf-users, Dapeng Mi, Zide Chen,
Falcon Thomas, Xudong Hao
On Tue, Aug 11, 2026 at 09:39:23AM +0800, Mi, Dapeng wrote:
>
> On 8/10/2026 9:01 PM, Peter Zijlstra wrote:
> > On Fri, Jul 17, 2026 at 04:03:42PM +0800, Dapeng Mi wrote:
> >> The PEBS buffer is shared by all events on a CPU, so drain_pebs() must
> >> not run concurrently. If it is reentered, one instance may observe stale
> >> buffer state and potentially access out-of-bound memory.
> >>
> >> Most invocations happen in NMI context, which naturally prevents reentry.
> >> However, drain_pebs() is also reachable from process context via
> >> intel_pmu_drain_pebs_buffer().
> >>
> >> In those paths, the PMU is often already disabled, but not guaranteed.
> >> For example, __intel_pmu_pebs_disable() only disables the target counter,
> >> so other active counters can still raise a PMI and interrupt an in-flight
> >> drain_pebs().
> >>
> >> Introduce __intel_pmu_quiesce() and __intel_pmu_resume() helpers and
> >> use them in intel_pmu_drain_pebs_buffer() to disable the full PMU
> >> around the drain_pebs() call, preventing reentry.
> >>
> > It is not at all clear to me where the exact recursion happens. (The
> > word you're looking for was recursion, not concurrent).
>
> Yes, the word "concurrently" is not accurate, reentry is the more accurate
> word.
>
> Currently drain_pebs() would be called in two places, one is the in the PMI
> handler, like handle_pmi_common(). The other place is
> intel_pmu_drain_pebs_buffer() which is from process context.
>
> So when intel_pmu_drain_pebs_buffer() is calling drain_pebs(), if there is
> an active PEBS event triggering PMI, it would interrupt current in-flight
> drain_pebs() and lead to drain_pebs() reentry.
What is the actual callchain here?
Because the one I'm thinking off is:
perf_ctx_disable();
perf_ctx_sched_task_cb()
x86_pmu_sched_task()
intel_pmu_sched_task()
intel_pmu_pebs_sched_task()
intel_pmu_drain_pebs_buffer();
perf_ctx_enable();
And that one has the full pmu disabled, because of context switch etc.
And then there is one in perf_read():
pmu->read()
intel_pmu_read_event()
pmu_enabled = cpuc->enabled;
cpuc->enabled = 0;
if (pmu_enabled)
intel_pmu_disable_all();
intel_pmu_drain_pebs_buffer();
cpuc->enabled = pmu_enabled;
if (pmu_enabled)
intel_pmu_enable_all();
So what specific callchain is going sideways?
^ permalink raw reply [flat|nested] 17+ messages in thread* Re: [Patch v3 8/8] perf/x86/intel: Prevent drain_pebs() reentry
2026-08-11 8:22 ` Peter Zijlstra
@ 2026-08-11 10:00 ` Mi, Dapeng
0 siblings, 0 replies; 17+ messages in thread
From: Mi, Dapeng @ 2026-08-11 10:00 UTC (permalink / raw)
To: Peter Zijlstra
Cc: Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim, Ian Rogers,
Adrian Hunter, Alexander Shishkin, Andi Kleen, Eranian Stephane,
linux-kernel, linux-perf-users, Dapeng Mi, Zide Chen,
Falcon Thomas, Xudong Hao
On 8/11/2026 4:22 PM, Peter Zijlstra wrote:
> On Tue, Aug 11, 2026 at 09:39:23AM +0800, Mi, Dapeng wrote:
>> On 8/10/2026 9:01 PM, Peter Zijlstra wrote:
>>> On Fri, Jul 17, 2026 at 04:03:42PM +0800, Dapeng Mi wrote:
>>>> The PEBS buffer is shared by all events on a CPU, so drain_pebs() must
>>>> not run concurrently. If it is reentered, one instance may observe stale
>>>> buffer state and potentially access out-of-bound memory.
>>>>
>>>> Most invocations happen in NMI context, which naturally prevents reentry.
>>>> However, drain_pebs() is also reachable from process context via
>>>> intel_pmu_drain_pebs_buffer().
>>>>
>>>> In those paths, the PMU is often already disabled, but not guaranteed.
>>>> For example, __intel_pmu_pebs_disable() only disables the target counter,
>>>> so other active counters can still raise a PMI and interrupt an in-flight
>>>> drain_pebs().
>>>>
>>>> Introduce __intel_pmu_quiesce() and __intel_pmu_resume() helpers and
>>>> use them in intel_pmu_drain_pebs_buffer() to disable the full PMU
>>>> around the drain_pebs() call, preventing reentry.
>>>>
>>> It is not at all clear to me where the exact recursion happens. (The
>>> word you're looking for was recursion, not concurrent).
>> Yes, the word "concurrently" is not accurate, reentry is the more accurate
>> word.
>>
>> Currently drain_pebs() would be called in two places, one is the in the PMI
>> handler, like handle_pmi_common(). The other place is
>> intel_pmu_drain_pebs_buffer() which is from process context.
>>
>> So when intel_pmu_drain_pebs_buffer() is calling drain_pebs(), if there is
>> an active PEBS event triggering PMI, it would interrupt current in-flight
>> drain_pebs() and lead to drain_pebs() reentry.
> What is the actual callchain here?
>
> Because the one I'm thinking off is:
>
> perf_ctx_disable();
> perf_ctx_sched_task_cb()
> x86_pmu_sched_task()
> intel_pmu_sched_task()
> intel_pmu_pebs_sched_task()
> intel_pmu_drain_pebs_buffer();
> perf_ctx_enable();
>
> And that one has the full pmu disabled, because of context switch etc.
> And then there is one in perf_read():
>
> pmu->read()
> intel_pmu_read_event()
> pmu_enabled = cpuc->enabled;
> cpuc->enabled = 0;
> if (pmu_enabled)
> intel_pmu_disable_all();
> intel_pmu_drain_pebs_buffer();
> cpuc->enabled = pmu_enabled;
> if (pmu_enabled)
> intel_pmu_enable_all();
>
> So what specific callchain is going sideways?
Yes, the whole PMU would be disabled for these 2 call-chains.
But for the helper __intel_pmu_pebs_disable(), it seems the whole PMU is
not disabled, only the target counter has been stopped. Here is one call-chain.
__perf_addr_filters_adjust()
perf_event_stop()
__perf_event_stop()
x86_pmu_stop() (event->pmu->stop)
intel_pmu_disable_event()
intel_pmu_pebs_disable()
__intel_pmu_pebs_disable()
intel_pmu_drain_large_pebs()
intel_pmu_drain_pebs_buffer()
>
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [Patch v3 0/8] perf/x86: Miscellaneous PMU bug fixes and optimizations
2026-07-17 8:03 [Patch v3 0/8] perf/x86: Miscellaneous PMU bug fixes and optimizations Dapeng Mi
` (7 preceding siblings ...)
2026-07-17 8:03 ` [Patch v3 8/8] perf/x86/intel: Prevent drain_pebs() reentry Dapeng Mi
@ 2026-07-21 14:58 ` Chen, Zide
2026-08-10 9:06 ` Mi, Dapeng
9 siblings, 0 replies; 17+ messages in thread
From: Chen, Zide @ 2026-07-21 14:58 UTC (permalink / raw)
To: Dapeng Mi, Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Ian Rogers, Adrian Hunter, Alexander Shishkin,
Andi Kleen, Eranian Stephane
Cc: linux-kernel, linux-perf-users, Dapeng Mi, Falcon Thomas,
Xudong Hao
For the whole series:
Reviewed-by: Zide Chen <zide.chen@intel.com>
On 7/17/2026 3:03 AM, Dapeng Mi wrote:
> Changes since v2:
> - Patch 6/8: Fix the typo in comments (Thomas).
> - Patch 8/8: New patch which prevents drain_pebs() reentry.
>
> Changes since v1:
> - Patch 3/7: Ensure cpuc->pmu is not the static pmu before calling
> hybrid_pmu(cpuc->pmu) in intel_pmu_cpu_dead() (Sashiko).
>
> This series fixes recently found x86 core PMU bugs. Most of bugs are
> found by Sashiko in reviewing the "Support SIMD/eGPRs/SSP registers
> sampling for perf" patch-set, e.g.,
> https://lore.kernel.org/all/20260706022123.480411F000E9@smtp.kernel.org/
> https://lore.kernel.org/all/20260706021852.DE2ED1F000E9@smtp.kernel.org/
>
> The patch 7/8 optimizes ACR handling in match_prev_assignment() and
> mitigate the performance overhead.
>
> The patch 8/8 enhances intel_pmu_drain_pebs_buffer() to prevent
> drain_pebs() reentry.
>
> Tests:
> Below test cases are run on Diamonds and Novalake. No issues are found.
> - Perf stat test
> $ perf test 119
> - Perf record tests
> $ perf test 155
> - Perf record LBR tests
> $ perf test 156
>
> History:
> v2: https://lore.kernel.org/all/20260713082734.3162099-1-dapeng1.mi@linux.intel.com/
> v1: https://lore.kernel.org/all/20260710065128.1799838-1-dapeng1.mi@linux.intel.com/
>
> Dapeng Mi (8):
> perf/x86: Unregister PMI handler on PMU init failure
> perf/x86: Free hybrid state on PMU init failure
> perf/x86: Guard intel_pmu_cpu_dead() against invalid hybrid PMU casts
> perf/x86/intel: Unwind cpuc state if PEBS buffer setup fails
> perf/x86: Remove stale fixed counter helper and fix hybrid PMU access
> perf/x86/intel: Fix intel_cap handling on hybrid PMUs
> perf/x86: Optimize ACR handling in match_prev_assignment()
> perf/x86/intel: Prevent drain_pebs() reentry
>
> arch/x86/events/core.c | 45 ++++++++++++----
> arch/x86/events/intel/core.c | 101 ++++++++++++++++++++++++++---------
> arch/x86/events/intel/ds.c | 7 ---
> arch/x86/events/perf_event.h | 12 ++---
> 4 files changed, 113 insertions(+), 52 deletions(-)
>
>
> base-commit: edda9051e267b7390c7ce24b1b71434414ad156e
^ permalink raw reply [flat|nested] 17+ messages in thread* Re: [Patch v3 0/8] perf/x86: Miscellaneous PMU bug fixes and optimizations
2026-07-17 8:03 [Patch v3 0/8] perf/x86: Miscellaneous PMU bug fixes and optimizations Dapeng Mi
` (8 preceding siblings ...)
2026-07-21 14:58 ` [Patch v3 0/8] perf/x86: Miscellaneous PMU bug fixes and optimizations Chen, Zide
@ 2026-08-10 9:06 ` Mi, Dapeng
9 siblings, 0 replies; 17+ messages in thread
From: Mi, Dapeng @ 2026-08-10 9:06 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Ian Rogers, Adrian Hunter, Alexander Shishkin,
Andi Kleen, Eranian Stephane
Cc: linux-kernel, linux-perf-users, Dapeng Mi, Zide Chen,
Falcon Thomas, Xudong Hao
Peter,
Kindly ping. Could you please review this bug fixing patch series? The
changes in this series suppose to be low risky.
This series is a preceding patchset of the "Support SIMD/eGPRs/SSP
registers sampling for perf" patch series.
https://lore.kernel.org/all/20260721062506.3745816-1-dapeng1.mi@linux.intel.com/
Thanks.
On 7/17/2026 4:03 PM, Dapeng Mi wrote:
> Changes since v2:
> - Patch 6/8: Fix the typo in comments (Thomas).
> - Patch 8/8: New patch which prevents drain_pebs() reentry.
>
> Changes since v1:
> - Patch 3/7: Ensure cpuc->pmu is not the static pmu before calling
> hybrid_pmu(cpuc->pmu) in intel_pmu_cpu_dead() (Sashiko).
>
> This series fixes recently found x86 core PMU bugs. Most of bugs are
> found by Sashiko in reviewing the "Support SIMD/eGPRs/SSP registers
> sampling for perf" patch-set, e.g.,
> https://lore.kernel.org/all/20260706022123.480411F000E9@smtp.kernel.org/
> https://lore.kernel.org/all/20260706021852.DE2ED1F000E9@smtp.kernel.org/
>
> The patch 7/8 optimizes ACR handling in match_prev_assignment() and
> mitigate the performance overhead.
>
> The patch 8/8 enhances intel_pmu_drain_pebs_buffer() to prevent
> drain_pebs() reentry.
>
> Tests:
> Below test cases are run on Diamonds and Novalake. No issues are found.
> - Perf stat test
> $ perf test 119
> - Perf record tests
> $ perf test 155
> - Perf record LBR tests
> $ perf test 156
>
> History:
> v2: https://lore.kernel.org/all/20260713082734.3162099-1-dapeng1.mi@linux.intel.com/
> v1: https://lore.kernel.org/all/20260710065128.1799838-1-dapeng1.mi@linux.intel.com/
>
> Dapeng Mi (8):
> perf/x86: Unregister PMI handler on PMU init failure
> perf/x86: Free hybrid state on PMU init failure
> perf/x86: Guard intel_pmu_cpu_dead() against invalid hybrid PMU casts
> perf/x86/intel: Unwind cpuc state if PEBS buffer setup fails
> perf/x86: Remove stale fixed counter helper and fix hybrid PMU access
> perf/x86/intel: Fix intel_cap handling on hybrid PMUs
> perf/x86: Optimize ACR handling in match_prev_assignment()
> perf/x86/intel: Prevent drain_pebs() reentry
>
> arch/x86/events/core.c | 45 ++++++++++++----
> arch/x86/events/intel/core.c | 101 ++++++++++++++++++++++++++---------
> arch/x86/events/intel/ds.c | 7 ---
> arch/x86/events/perf_event.h | 12 ++---
> 4 files changed, 113 insertions(+), 52 deletions(-)
>
>
> base-commit: edda9051e267b7390c7ce24b1b71434414ad156e
^ permalink raw reply [flat|nested] 17+ messages in thread