Linux Perf Users
 help / color / mirror / Atom feed
* [PATCH 0/7] perf/x86: Miscellaneous PMU bug fixes and optimizations
@ 2026-07-10  6:51 Dapeng Mi
  2026-07-10  6:51 ` [PATCH 1/7] perf/x86: Unregister PMI handler on PMU init failure Dapeng Mi
                   ` (6 more replies)
  0 siblings, 7 replies; 11+ messages in thread
From: Dapeng Mi @ 2026-07-10  6:51 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

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 last patch 7/7 optimizes ACR handling in match_prev_assignment() and
mitigate the performance overhead.

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

Dapeng Mi (7):
  perf/x86: Unregister PMI handler on PMU init failure
  perf/x86: Free hybrid state on PMU init failure
  perf/x86/intel: Clear cpuc->pmu on hybrid PMU init failure
  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()

 arch/x86/events/core.c       | 40 ++++++++++++++++++-------
 arch/x86/events/intel/core.c | 58 ++++++++++++++++++++++++++----------
 arch/x86/events/perf_event.h | 11 ++-----
 3 files changed, 73 insertions(+), 36 deletions(-)


base-commit: edda9051e267b7390c7ce24b1b71434414ad156e
-- 
2.34.1


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH 1/7] perf/x86: Unregister PMI handler on PMU init failure
  2026-07-10  6:51 [PATCH 0/7] perf/x86: Miscellaneous PMU bug fixes and optimizations Dapeng Mi
@ 2026-07-10  6:51 ` Dapeng Mi
  2026-07-10  6:51 ` [PATCH 2/7] perf/x86: Free hybrid state " Dapeng Mi
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 11+ messages in thread
From: Dapeng Mi @ 2026-07-10  6:51 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>
---
 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] 11+ messages in thread

* [PATCH 2/7] perf/x86: Free hybrid state on PMU init failure
  2026-07-10  6:51 [PATCH 0/7] perf/x86: Miscellaneous PMU bug fixes and optimizations Dapeng Mi
  2026-07-10  6:51 ` [PATCH 1/7] perf/x86: Unregister PMI handler on PMU init failure Dapeng Mi
@ 2026-07-10  6:51 ` Dapeng Mi
  2026-07-10  6:51 ` [PATCH 3/7] perf/x86/intel: Clear cpuc->pmu on hybrid " Dapeng Mi
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 11+ messages in thread
From: Dapeng Mi @ 2026-07-10  6:51 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>
---
 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] 11+ messages in thread

* [PATCH 3/7] perf/x86/intel: Clear cpuc->pmu on hybrid PMU init failure
  2026-07-10  6:51 [PATCH 0/7] perf/x86: Miscellaneous PMU bug fixes and optimizations Dapeng Mi
  2026-07-10  6:51 ` [PATCH 1/7] perf/x86: Unregister PMI handler on PMU init failure Dapeng Mi
  2026-07-10  6:51 ` [PATCH 2/7] perf/x86: Free hybrid state " Dapeng Mi
@ 2026-07-10  6:51 ` Dapeng Mi
  2026-07-10  7:26   ` sashiko-bot
  2026-07-10  8:20   ` Mi, Dapeng
  2026-07-10  6:51 ` [PATCH 4/7] perf/x86/intel: Unwind cpuc state if PEBS buffer setup fails Dapeng Mi
                   ` (3 subsequent siblings)
  6 siblings, 2 replies; 11+ messages in thread
From: Dapeng Mi @ 2026-07-10  6:51 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

When init_hybrid_pmu() fails at check_hw_exists(), cpuc->pmu may still
point to the default static PMU.

The CPU hotplug rollback then runs intel_pmu_cpu_dead(). On hybrid
systems, that path may call hybrid_pmu(cpuc->pmu), which is not valid
for the static PMU pointer and can result in incorrect hybrid state
access.

Fix this by resetting cpuc->pmu to NULL on hybrid PMU init failure.

Signed-off-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
---
 arch/x86/events/intel/core.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c
index b39c6ce0efb5..9d4774278b50 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);
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 4/7] perf/x86/intel: Unwind cpuc state if PEBS buffer setup fails
  2026-07-10  6:51 [PATCH 0/7] perf/x86: Miscellaneous PMU bug fixes and optimizations Dapeng Mi
                   ` (2 preceding siblings ...)
  2026-07-10  6:51 ` [PATCH 3/7] perf/x86/intel: Clear cpuc->pmu on hybrid " Dapeng Mi
@ 2026-07-10  6:51 ` Dapeng Mi
  2026-07-10  6:51 ` [PATCH 5/7] perf/x86: Remove stale fixed counter helper and fix hybrid PMU access Dapeng Mi
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 11+ messages in thread
From: Dapeng Mi @ 2026-07-10  6:51 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>
---
 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 9d4774278b50..737c5a070379 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] 11+ messages in thread

* [PATCH 5/7] perf/x86: Remove stale fixed counter helper and fix hybrid PMU access
  2026-07-10  6:51 [PATCH 0/7] perf/x86: Miscellaneous PMU bug fixes and optimizations Dapeng Mi
                   ` (3 preceding siblings ...)
  2026-07-10  6:51 ` [PATCH 4/7] perf/x86/intel: Unwind cpuc state if PEBS buffer setup fails Dapeng Mi
@ 2026-07-10  6:51 ` Dapeng Mi
  2026-07-10  6:51 ` [PATCH 6/7] perf/x86/intel: Fix intel_cap handling on hybrid PMUs Dapeng Mi
  2026-07-10  6:51 ` [PATCH 7/7] perf/x86: Optimize ACR handling in match_prev_assignment() Dapeng Mi
  6 siblings, 0 replies; 11+ messages in thread
From: Dapeng Mi @ 2026-07-10  6:51 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>
---
 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 6c63b27e11e6..0bd3798b6e33 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;
@@ -1613,8 +1611,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",
@@ -2175,7 +2171,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 737c5a070379..83c60ad00085 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 a8afea8d38f0..088f7ce715df 100644
--- a/arch/x86/events/perf_event.h
+++ b/arch/x86/events/perf_event.h
@@ -1242,7 +1242,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);
@@ -1455,13 +1455,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] 11+ messages in thread

* [PATCH 6/7] perf/x86/intel: Fix intel_cap handling on hybrid PMUs
  2026-07-10  6:51 [PATCH 0/7] perf/x86: Miscellaneous PMU bug fixes and optimizations Dapeng Mi
                   ` (4 preceding siblings ...)
  2026-07-10  6:51 ` [PATCH 5/7] perf/x86: Remove stale fixed counter helper and fix hybrid PMU access Dapeng Mi
@ 2026-07-10  6:51 ` Dapeng Mi
  2026-07-10  6:51 ` [PATCH 7/7] perf/x86: Optimize ACR handling in match_prev_assignment() Dapeng Mi
  6 siblings, 0 replies; 11+ messages in thread
From: Dapeng Mi @ 2026-07-10  6:51 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>
---
 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 83c60ad00085..36de21484ada 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
+	 * PRL and MTL. Details can be found in RPL018 Errata Details.
+	 * 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)) {
@@ -8827,8 +8846,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] 11+ messages in thread

* [PATCH 7/7] perf/x86: Optimize ACR handling in match_prev_assignment()
  2026-07-10  6:51 [PATCH 0/7] perf/x86: Miscellaneous PMU bug fixes and optimizations Dapeng Mi
                   ` (5 preceding siblings ...)
  2026-07-10  6:51 ` [PATCH 6/7] perf/x86/intel: Fix intel_cap handling on hybrid PMUs Dapeng Mi
@ 2026-07-10  6:51 ` Dapeng Mi
  6 siblings, 0 replies; 11+ messages in thread
From: Dapeng Mi @ 2026-07-10  6:51 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>
---
 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 0bd3798b6e33..6a7502d2ae6e 100644
--- a/arch/x86/events/core.c
+++ b/arch/x86/events/core.c
@@ -1292,6 +1292,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)
@@ -1301,7 +1312,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 088f7ce715df..d382e5ed72a8 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] 11+ messages in thread

* Re: [PATCH 3/7] perf/x86/intel: Clear cpuc->pmu on hybrid PMU init failure
  2026-07-10  6:51 ` [PATCH 3/7] perf/x86/intel: Clear cpuc->pmu on hybrid " Dapeng Mi
@ 2026-07-10  7:26   ` sashiko-bot
  2026-07-10  8:01     ` Mi, Dapeng
  2026-07-10  8:20   ` Mi, Dapeng
  1 sibling, 1 reply; 11+ messages in thread
From: sashiko-bot @ 2026-07-10  7:26 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:
- [High] Lockless double-read of `cpuc->pmu` in `x86_get_pmu()` leads to a TOCTOU race when racing with `cpuc->pmu = NULL`, causing a NULL pointer dereference.

Pre-existing issues:
- [High] The patch leaves a critical memory corruption vulnerability unhandled if a CPU hotplug rollback occurs before the hybrid PMU initialization runs.
- [High] An unhandled #GP fault occurs in `intel_pmu_handle_irq` if a spurious NMI/PMI is received on a CPU where PMU initialization failed.
--

--- Patch [3]: [PATCH 3/7] perf/x86/intel: Clear cpuc->pmu on hybrid PMU init failure ---
commit 0c199c356195e47c5b429eac88846944835a4589
Author: Dapeng Mi <dapeng1.mi@linux.intel.com>

perf/x86/intel: Clear cpuc->pmu on hybrid PMU init failure

When init_hybrid_pmu() fails at check_hw_exists(), cpuc->pmu may still
point to the default static PMU. The CPU hotplug rollback then runs
intel_pmu_cpu_dead(). On hybrid systems, that path may call
hybrid_pmu(cpuc->pmu), which is not valid for the static PMU pointer and
can result in incorrect hybrid state access.

Fix this by resetting cpuc->pmu to NULL on hybrid PMU init failure.

> diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c
> index b39c6ce0efb5c..9d4774278b508 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;

[Severity: High]
Can this assignment race with concurrent readers?  Looking at x86_get_pmu()
in arch/x86/events/core.c, there is a lockless double-read of cpuc->pmu:

x86_get_pmu(int cpu)
{
    ...
    if (WARN_ON_ONCE(!cpuc->pmu))
        return &pmu;

    return cpuc->pmu;
}

If sys_perf_event_open() executes concurrently and calls x86_get_pmu()
via reserve_lbr_buffers(), could the compiler emit two independent loads?

If cpuc->pmu is non-NULL during the first load but is set to NULL by this
patch before the second load, x86_get_pmu() would return NULL.  Does this
lead to a NULL pointer dereference in reserve_lbr_buffers()?

> +		return false;
> +	}

[Severity: High]
This isn't a bug introduced by this patch, but does this fully resolve the
rollback vulnerability?

If a CPU hotplug operation fails between the CPUHP_PERF_X86_PREPARE and
CPUHP_AP_PERF_X86_STARTING states, the hotplug core rolls back and invokes
intel_pmu_cpu_dead().

Because init_hybrid_pmu() is bypassed entirely, cpuc->pmu remains pointing
to the static default pmu struct.

intel_pmu_cpu_dead() then executes:

    if (is_hybrid() && cpuc->pmu)
        cpumask_clear_cpu(cpu, &hybrid_pmu(cpuc->pmu)->supported_cpus);

Since cpuc->pmu is non-NULL, hybrid_pmu() calculates a negative offset from
the static pmu struct, and cpumask_clear_cpu() performs an out-of-bounds
memory write. Is there a way to safely handle rollbacks that bypass
initialization entirely?

[Severity: High]
This is also a pre-existing issue, but what happens if a spurious NMI is
received on a CPU where check_hw_exists() failed?

If init_hybrid_pmu() returns false here, x86_pmu_starting_cpu() ignores this
failure and returns 0, allowing the CPU to come online with a disabled PMU.
If another CPU has active perf events, active_events is greater than 0.

When an NMI broadcast hits this broken CPU, perf_event_nmi_handler() sees
active_events > 0 and calls intel_pmu_handle_irq(), which executes:

    __intel_pmu_disable_all(true);

This unconditionally executes wrmsrq(MSR_CORE_PERF_GLOBAL_CTRL, 0).
Since the PMU MSRs are unsupported on this CPU, will this unchecked wrmsrq
trigger a fatal general protection fault?

>  	pr_info("%s PMU driver: ", pmu->name);
>

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260710065128.1799838-1-dapeng1.mi@linux.intel.com?part=3

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 3/7] perf/x86/intel: Clear cpuc->pmu on hybrid PMU init failure
  2026-07-10  7:26   ` sashiko-bot
@ 2026-07-10  8:01     ` Mi, Dapeng
  0 siblings, 0 replies; 11+ messages in thread
From: Mi, Dapeng @ 2026-07-10  8:01 UTC (permalink / raw)
  To: sashiko-reviews; +Cc: linux-perf-users


On 7/10/2026 3:26 PM, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
>
> New issues:
> - [High] Lockless double-read of `cpuc->pmu` in `x86_get_pmu()` leads to a TOCTOU race when racing with `cpuc->pmu = NULL`, causing a NULL pointer dereference.
>
> Pre-existing issues:
> - [High] The patch leaves a critical memory corruption vulnerability unhandled if a CPU hotplug rollback occurs before the hybrid PMU initialization runs.
> - [High] An unhandled #GP fault occurs in `intel_pmu_handle_irq` if a spurious NMI/PMI is received on a CPU where PMU initialization failed.
> --
>
> --- Patch [3]: [PATCH 3/7] perf/x86/intel: Clear cpuc->pmu on hybrid PMU init failure ---
> commit 0c199c356195e47c5b429eac88846944835a4589
> Author: Dapeng Mi <dapeng1.mi@linux.intel.com>
>
> perf/x86/intel: Clear cpuc->pmu on hybrid PMU init failure
>
> When init_hybrid_pmu() fails at check_hw_exists(), cpuc->pmu may still
> point to the default static PMU. The CPU hotplug rollback then runs
> intel_pmu_cpu_dead(). On hybrid systems, that path may call
> hybrid_pmu(cpuc->pmu), which is not valid for the static PMU pointer and
> can result in incorrect hybrid state access.
>
> Fix this by resetting cpuc->pmu to NULL on hybrid PMU init failure.
>
>> diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c
>> index b39c6ce0efb5c..9d4774278b508 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;
> [Severity: High]
> Can this assignment race with concurrent readers?  Looking at x86_get_pmu()
> in arch/x86/events/core.c, there is a lockless double-read of cpuc->pmu:
>
> x86_get_pmu(int cpu)
> {
>     ...
>     if (WARN_ON_ONCE(!cpuc->pmu))
>         return &pmu;
>
>     return cpuc->pmu;
> }
>
> If sys_perf_event_open() executes concurrently and calls x86_get_pmu()
> via reserve_lbr_buffers(), could the compiler emit two independent loads?
>
> If cpuc->pmu is non-NULL during the first load but is set to NULL by this
> patch before the second load, x86_get_pmu() would return NULL.  Does this
> lead to a NULL pointer dereference in reserve_lbr_buffers()?

Doubt if the issue could really happen. init_hybrid_pmu() is called on cpu
online phase. When it's called, there should no any perf event is created
and enabled. As the 3rd comment says, currently "If init_hybrid_pmu()
returns false here, x86_pmu_starting_cpu() ignores this
failure and returns 0, allowing the CPU to come online with a disabled
PMU." But If we enhance x86_pmu_starting_cpu() to propagate the error, then
the CPU won't be really online and no events can be run this cpu.


>
>> +		return false;
>> +	}
> [Severity: High]
> This isn't a bug introduced by this patch, but does this fully resolve the
> rollback vulnerability?
>
> If a CPU hotplug operation fails between the CPUHP_PERF_X86_PREPARE and
> CPUHP_AP_PERF_X86_STARTING states, the hotplug core rolls back and invokes
> intel_pmu_cpu_dead().
>
> Because init_hybrid_pmu() is bypassed entirely, cpuc->pmu remains pointing
> to the static default pmu struct.
>
> intel_pmu_cpu_dead() then executes:
>
>     if (is_hybrid() && cpuc->pmu)
>         cpumask_clear_cpu(cpu, &hybrid_pmu(cpuc->pmu)->supported_cpus);
>
> Since cpuc->pmu is non-NULL, hybrid_pmu() calculates a negative offset from
> the static pmu struct, and cpumask_clear_cpu() performs an out-of-bounds
> memory write. Is there a way to safely handle rollbacks that bypass
> initialization entirely?

Hmm, yes. Need to check if cpuc->pmu still points to the static pmu before
calling hybrid_pmu().


>
> [Severity: High]
> This is also a pre-existing issue, but what happens if a spurious NMI is
> received on a CPU where check_hw_exists() failed?
>
> If init_hybrid_pmu() returns false here, x86_pmu_starting_cpu() ignores this
> failure and returns 0, allowing the CPU to come online with a disabled PMU.
> If another CPU has active perf events, active_events is greater than 0.
>
> When an NMI broadcast hits this broken CPU, perf_event_nmi_handler() sees
> active_events > 0 and calls intel_pmu_handle_irq(), which executes:
>
>     __intel_pmu_disable_all(true);
>
> This unconditionally executes wrmsrq(MSR_CORE_PERF_GLOBAL_CTRL, 0).
> Since the PMU MSRs are unsupported on this CPU, will this unchecked wrmsrq
> trigger a fatal general protection fault?

Yes, need to propagate the error for x86_pmu_starting_cpu().

would fix this issue in v2. Thanks.



>
>>  	pr_info("%s PMU driver: ", pmu->name);
>>

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 3/7] perf/x86/intel: Clear cpuc->pmu on hybrid PMU init failure
  2026-07-10  6:51 ` [PATCH 3/7] perf/x86/intel: Clear cpuc->pmu on hybrid " Dapeng Mi
  2026-07-10  7:26   ` sashiko-bot
@ 2026-07-10  8:20   ` Mi, Dapeng
  1 sibling, 0 replies; 11+ messages in thread
From: Mi, Dapeng @ 2026-07-10  8:20 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

Sashiko raises comments about this patch.
https://lore.kernel.org/all/20260710072619.5E3011F000E9@smtp.kernel.org/

The latter 2 comments are valid. Would send v2 to address these 2 comments.
Thanks.


On 7/10/2026 2:51 PM, Dapeng Mi wrote:
> When init_hybrid_pmu() fails at check_hw_exists(), cpuc->pmu may still
> point to the default static PMU.
>
> The CPU hotplug rollback then runs intel_pmu_cpu_dead(). On hybrid
> systems, that path may call hybrid_pmu(cpuc->pmu), which is not valid
> for the static PMU pointer and can result in incorrect hybrid state
> access.
>
> Fix this by resetting cpuc->pmu to NULL on hybrid PMU init failure.
>
> Signed-off-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
> ---
>  arch/x86/events/intel/core.c | 4 +++-
>  1 file changed, 3 insertions(+), 1 deletion(-)
>
> diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c
> index b39c6ce0efb5..9d4774278b50 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);
>  

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2026-07-10  8:21 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-10  6:51 [PATCH 0/7] perf/x86: Miscellaneous PMU bug fixes and optimizations Dapeng Mi
2026-07-10  6:51 ` [PATCH 1/7] perf/x86: Unregister PMI handler on PMU init failure Dapeng Mi
2026-07-10  6:51 ` [PATCH 2/7] perf/x86: Free hybrid state " Dapeng Mi
2026-07-10  6:51 ` [PATCH 3/7] perf/x86/intel: Clear cpuc->pmu on hybrid " Dapeng Mi
2026-07-10  7:26   ` sashiko-bot
2026-07-10  8:01     ` Mi, Dapeng
2026-07-10  8:20   ` Mi, Dapeng
2026-07-10  6:51 ` [PATCH 4/7] perf/x86/intel: Unwind cpuc state if PEBS buffer setup fails Dapeng Mi
2026-07-10  6:51 ` [PATCH 5/7] perf/x86: Remove stale fixed counter helper and fix hybrid PMU access Dapeng Mi
2026-07-10  6:51 ` [PATCH 6/7] perf/x86/intel: Fix intel_cap handling on hybrid PMUs Dapeng Mi
2026-07-10  6:51 ` [PATCH 7/7] perf/x86: Optimize ACR handling in match_prev_assignment() Dapeng Mi

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox