mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v9 00/12] KVM: x86/pmu: Add hardware Topdown metrics support
@ 2026-09-18 19:39 Zide Chen
  2026-09-18 19:39 ` [PATCH v9 01/12] KVM: x86/pmu: Do not map fixed counters >= 3 to generic perf events Zide Chen
                   ` (11 more replies)
  0 siblings, 12 replies; 20+ messages in thread
From: Zide Chen @ 2026-09-18 19:39 UTC (permalink / raw)
  To: Sean Christopherson, Paolo Bonzini, Peter Zijlstra
  Cc: kvm, Jim Mattson, Andi Kleen, Stephane Eranian, linux-kernel,
	Mingwei Zhang, Zide Chen, Das Sandipan, Shukla Manali, Dapeng Mi,
	Xudong Hao

Previous revisions were missing two things that are now addressed:

- A previous comment incorrectly claimed that MSR_PERF_METRICS bits
  [63:32] are ignored on hardware that only implements level-1 metrics;
  patch 10/12 fixes this by rejecting such writes.

- Patch 11/12 adds support for RDPMC Metrics Clear Mode. Without
  this feature, for user programs using RDPMC directly, the counter
  has to be reset explicitly using ioctl:

  ioctl(perf_fd, PERF_EVENT_IOC_RESET, 0);

Tests: besides the tests mentioned on v8 cover letter, tested RDPMC
Metrics Clear on a DMR machine, and tested Topdown metrics on nested
setup on top of the vPMU support series [1].

perf tool misidentifies "cpu/slots,metrics_clear=1/" as not being the
slots event, so it inserts an additional slots event into the group;
event scheduling then fails because the group needs fixed counter 3
(SLOTS) twice:

  $ perf stat -e "{cpu/slots,metrics_clear=1/,cpu/topdown-retiring/}" -- sleep 1
    WARNING: events were regrouped to match PMUs
           <not counted>      slots
         <not supported>      cpu/slots,metrics_clear=1/
           <not counted>      cpu/topdown-retiring/

A fix for this perf tool bug is posted at [2].

New patches:
patch 8/12: Add INTEL_TD_METRIC_FIELD_BITS for code cleanup.
patch 9/12: perf/x86: Expose number of Topdown metric events to KVM.
patch 10/12: Reject writes to reserved MSR_PERF_METRICS bits.
patch 11/12: Support RDPMC Metrics Clear Mode.

Minor non-logical changes to existing patches:
patch 5/12: Remove the incorrect comment about writing to PERF_METRICS.
patch 7/12: Remove references to Metrics Clear from the changelog.

Patch 11/12 could be folded into patch 5/12, but I kept them separate
because patches 1-8 were already well reviewed, and keeping them
unchanged should make reviewing v9 easier.

Rebased to kvm-x86/next: 70c944caf570fda2d79baa71435589a8db39f048

v8: https://lore.kernel.org/kvm/20260817183143.226156-1-zide.chen@intel.com/
QEMU:  https://lore.kernel.org/qemu-devel/20260604025546.19378-7-zide.chen@intel.com/

[1] https://lore.kernel.org/kvm/20260827223755.143247-1-zide.chen@intel.com/
[2] https://lore.kernel.org/linux-perf-users/20260917182525.3DE801F000FF@smtp.kernel.org/T/#t

Dapeng Mi (2):
  KVM: x86/pmu: Support Intel fixed counter 3 on mediated vPMU
  KVM: x86/pmu: Support PERF_METRICS MSR in mediated vPMU

Mingwei Zhang (1):
  KVM: x86/pmu: Snapshot host IA32_PERF_CAPABILITIES in kvm_host

Zide Chen (9):
  KVM: x86/pmu: Do not map fixed counters >= 3 to generic perf events
  KVM: x86/pmu: Rename and move vcpu_get_perf_capabilities() to pmu.h
  KVM: x86/pmu: Move RDPMC emulation into per-vendor callbacks
  KVM: x86/pmu: Emulate RDPMC on performance metrics
  KVM: selftests: Add PERF_METRICS and fixed counter 3 tests
  perf/x86: Add INTEL_TD_METRIC_FIELD_{BITS,MASK} constants
  perf/x86: Expose number of Topdown metric events to KVM
  KVM: x86/pmu: Reject writes to reserved MSR_PERF_METRICS bits
  KVM: x86/pmu: Support RDPMC Metrics Clear Mode

 arch/x86/events/core.c                        |   1 +
 arch/x86/events/intel/core.c                  |   8 +-
 arch/x86/include/asm/kvm-x86-pmu-ops.h        |   2 +-
 arch/x86/include/asm/kvm_host.h               |   5 +-
 arch/x86/include/asm/msr-index.h              |   2 +
 arch/x86/include/asm/perf_event.h             |   5 +
 arch/x86/kvm/msrs.c                           |  10 +-
 arch/x86/kvm/pmu.c                            |  37 +++--
 arch/x86/kvm/pmu.h                            |  17 ++-
 arch/x86/kvm/svm/pmu.c                        |  13 +-
 arch/x86/kvm/vmx/nested.c                     |   2 +
 arch/x86/kvm/vmx/pmu_intel.c                  | 132 +++++++++++++++---
 arch/x86/kvm/vmx/pmu_intel.h                  |  12 +-
 arch/x86/kvm/vmx/vmx.c                        |  19 ++-
 arch/x86/kvm/x86.c                            |   4 +
 tools/arch/x86/include/asm/msr-index.h        |   1 +
 tools/testing/selftests/kvm/include/x86/pmu.h |   3 +
 .../selftests/kvm/x86/pmu_counters_test.c     |  97 ++++++++++++-
 18 files changed, 303 insertions(+), 67 deletions(-)

-- 
2.55.0


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

* [PATCH v9 01/12] KVM: x86/pmu: Do not map fixed counters >= 3 to generic perf events
  2026-09-18 19:39 [PATCH v9 00/12] KVM: x86/pmu: Add hardware Topdown metrics support Zide Chen
@ 2026-09-18 19:39 ` Zide Chen
  2026-09-18 19:39 ` [PATCH v9 02/12] KVM: x86/pmu: Support Intel fixed counter 3 on mediated vPMU Zide Chen
                   ` (10 subsequent siblings)
  11 siblings, 0 replies; 20+ messages in thread
From: Zide Chen @ 2026-09-18 19:39 UTC (permalink / raw)
  To: Sean Christopherson, Paolo Bonzini, Peter Zijlstra
  Cc: kvm, Jim Mattson, Andi Kleen, Stephane Eranian, linux-kernel,
	Mingwei Zhang, Zide Chen, Das Sandipan, Shukla Manali, Dapeng Mi,
	Xudong Hao

Only fixed counters 0..2 have matching generic cross-platform
hardware perf events (INSTRUCTIONS, CPU_CYCLES, REF_CPU_CYCLES).
Therefore, perf_get_hw_event_config() is only applicable to these
counters.

KVM does not intend to emulate fixed counters >= 3 on legacy
(non-mediated) vPMU, while for mediated vPMU, KVM does not care what
the fixed counter event mappings are.  Therefore, return 0 for their
eventsel.

The two BUILD_BUG_ON() checks are no longer needed, so drop them along
with __always_inline.

Signed-off-by: Zide Chen <zide.chen@intel.com>
Reviewed-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
Reviewed-by: Jim Mattson <jmattson@google.com>
---
v6:
- Re-arrange the code for early return. Clearer.
---
 arch/x86/kvm/vmx/pmu_intel.c | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c
index 70a8c4816135..11ad64d43e35 100644
--- a/arch/x86/kvm/vmx/pmu_intel.c
+++ b/arch/x86/kvm/vmx/pmu_intel.c
@@ -468,11 +468,8 @@ static int intel_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
  * different perf_event is already utilizing the requested counter, but the end
  * result is the same (ignoring the fact that using a general purpose counter
  * will likely exacerbate counter contention).
- *
- * Forcibly inlined to allow asserting on @index at build time, and there should
- * never be more than one user.
  */
-static __always_inline u64 intel_get_fixed_pmc_eventsel(unsigned int index)
+static u64 intel_get_fixed_pmc_eventsel(unsigned int index)
 {
 	const enum perf_hw_id fixed_pmc_perf_ids[] = {
 		[0] = PERF_COUNT_HW_INSTRUCTIONS,
@@ -481,8 +478,13 @@ static __always_inline u64 intel_get_fixed_pmc_eventsel(unsigned int index)
 	};
 	u64 eventsel;
 
-	BUILD_BUG_ON(ARRAY_SIZE(fixed_pmc_perf_ids) != KVM_MAX_NR_INTEL_FIXED_COUNTERS);
-	BUILD_BUG_ON(index >= KVM_MAX_NR_INTEL_FIXED_COUNTERS);
+	/*
+	 * Fixed counters 3 and above don't have a corresponding generic
+	 * hardware perf event, and KVM does not intend to emulate them on
+	 * non-mediated vPMU.
+	 */
+	if (index >= ARRAY_SIZE(fixed_pmc_perf_ids))
+		return 0;
 
 	/*
 	 * Yell if perf reports support for a fixed counter but perf doesn't
-- 
2.55.0


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

* [PATCH v9 02/12] KVM: x86/pmu: Support Intel fixed counter 3 on mediated vPMU
  2026-09-18 19:39 [PATCH v9 00/12] KVM: x86/pmu: Add hardware Topdown metrics support Zide Chen
  2026-09-18 19:39 ` [PATCH v9 01/12] KVM: x86/pmu: Do not map fixed counters >= 3 to generic perf events Zide Chen
@ 2026-09-18 19:39 ` Zide Chen
  2026-09-18 19:39 ` [PATCH v9 03/12] KVM: x86/pmu: Rename and move vcpu_get_perf_capabilities() to pmu.h Zide Chen
                   ` (9 subsequent siblings)
  11 siblings, 0 replies; 20+ messages in thread
From: Zide Chen @ 2026-09-18 19:39 UTC (permalink / raw)
  To: Sean Christopherson, Paolo Bonzini, Peter Zijlstra
  Cc: kvm, Jim Mattson, Andi Kleen, Stephane Eranian, linux-kernel,
	Mingwei Zhang, Zide Chen, Das Sandipan, Shukla Manali, Dapeng Mi,
	Xudong Hao

From: Dapeng Mi <dapeng1.mi@linux.intel.com>

Starting with Ice Lake, Intel introduced fixed counter 3, which counts
TOPDOWN.SLOTS - the number of available slots for an unhalted logical
processor.  It serves as the denominator for top-level metrics in the
Top-down Microarchitecture Analysis method.

Emulating this counter on legacy vPMU would require introducing a new
generic perf encoding for the Intel-specific TOPDOWN.SLOTS event in
order to call perf_get_hw_event_config().  This is undesirable as it
would pollute the generic perf event encoding.

Moreover, KVM does not intend to emulate IA32_PERF_METRICS in the
legacy vPMU model, and without IA32_PERF_METRICS, emulating this
counter has little practical value.  Therefore, expose fixed counter
3 to guests only when mediated vPMU is enabled.

Signed-off-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
Co-developed-by: Zide Chen <zide.chen@intel.com>
Signed-off-by: Zide Chen <zide.chen@intel.com>
Reviewed-by: Jim Mattson <jmattson@google.com>
---
v6:
- Update comments to replace 2 with KVM_MAX_NR_INTEL_FIXED_COUNTERS - 1.
---
 arch/x86/include/asm/kvm_host.h |  2 +-
 arch/x86/kvm/msrs.c             |  4 ++--
 arch/x86/kvm/pmu.c              | 19 ++++++++++++++++++-
 3 files changed, 21 insertions(+), 4 deletions(-)

diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 30ffaa65f589..00584fcffa33 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -563,7 +563,7 @@ struct kvm_pmc {
 #define KVM_MAX_NR_GP_COUNTERS		KVM_MAX(KVM_MAX_NR_INTEL_GP_COUNTERS, \
 						KVM_MAX_NR_AMD_GP_COUNTERS)
 
-#define KVM_MAX_NR_INTEL_FIXED_COUNTERS	3
+#define KVM_MAX_NR_INTEL_FIXED_COUNTERS	4
 #define KVM_MAX_NR_AMD_FIXED_COUNTERS	0
 #define KVM_MAX_NR_FIXED_COUNTERS	KVM_MAX(KVM_MAX_NR_INTEL_FIXED_COUNTERS, \
 						KVM_MAX_NR_AMD_FIXED_COUNTERS)
diff --git a/arch/x86/kvm/msrs.c b/arch/x86/kvm/msrs.c
index 37d5bed3529f..45e4c8a32436 100644
--- a/arch/x86/kvm/msrs.c
+++ b/arch/x86/kvm/msrs.c
@@ -217,7 +217,7 @@ static const u32 msrs_to_save_base[] = {
 
 static const u32 msrs_to_save_pmu[] = {
 	MSR_ARCH_PERFMON_FIXED_CTR0, MSR_ARCH_PERFMON_FIXED_CTR1,
-	MSR_ARCH_PERFMON_FIXED_CTR0 + 2,
+	MSR_ARCH_PERFMON_FIXED_CTR2, MSR_ARCH_PERFMON_FIXED_CTR3,
 	MSR_CORE_PERF_FIXED_CTR_CTRL, MSR_CORE_PERF_GLOBAL_STATUS,
 	MSR_CORE_PERF_GLOBAL_CTRL,
 	MSR_IA32_PEBS_ENABLE, MSR_IA32_DS_AREA, MSR_PEBS_DATA_CFG,
@@ -2672,7 +2672,7 @@ void kvm_init_msr_lists(void)
 {
 	unsigned i;
 
-	BUILD_BUG_ON_MSG(KVM_MAX_NR_FIXED_COUNTERS != 3,
+	BUILD_BUG_ON_MSG(KVM_MAX_NR_FIXED_COUNTERS != 4,
 			 "Please update the fixed PMCs in msrs_to_save_pmu[]");
 
 	num_msrs_to_save = 0;
diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c
index d2fd47ee5ec8..448044a2b121 100644
--- a/arch/x86/kvm/pmu.c
+++ b/arch/x86/kvm/pmu.c
@@ -19,6 +19,7 @@
 #include <linux/moduleparam.h>
 #include <asm/perf_event.h>
 #include <asm/cpu_device_id.h>
+#include <asm/cpuid/api.h>
 #include "x86.h"
 #include "cpuid.h"
 #include "lapic.h"
@@ -99,7 +100,8 @@ static const struct x86_cpu_id vmx_pebs_pdist_cpu[] = {
  *        all perf counters (both gp and fixed). The mapping relationship
  *        between pmc and perf counters is as the following:
  *        * Intel: [0 .. KVM_MAX_NR_INTEL_GP_COUNTERS-1] <=> gp counters
- *                 [KVM_FIXED_PMC_BASE_IDX .. KVM_FIXED_PMC_BASE_IDX + 2] <=> fixed
+ *                 KVM_MAX_NR_INTEL_FIXED_COUNTERS counters starting at
+ *                 KVM_FIXED_PMC_BASE_IDX <=> fixed
  *        * AMD:   [0 .. AMD64_NUM_COUNTERS-1] and, for families 15H
  *          and later, [0 .. AMD64_NUM_COUNTERS_CORE-1] <=> gp counters
  */
@@ -134,6 +136,8 @@ void kvm_init_pmu_capability(struct kvm_pmu_ops *pmu_ops)
 {
 	bool is_intel = boot_cpu_data.x86_vendor == X86_VENDOR_INTEL;
 	int min_nr_gp_ctrs = pmu_ops->MIN_NR_GP_COUNTERS;
+	union cpuid10_edx edx;
+	u32 eax, ebx, ecx;
 
 	/*
 	 * Hybrid PMUs don't play nice with virtualization without careful
@@ -181,6 +185,19 @@ void kvm_init_pmu_capability(struct kvm_pmu_ops *pmu_ops)
 	kvm_pmu_cap.num_counters_fixed = min(kvm_pmu_cap.num_counters_fixed,
 					     KVM_MAX_NR_FIXED_COUNTERS);
 
+	/*
+	 * Currently, KVM doesn't support non-contiguous fixed counters; make
+	 * sure only contiguous ones are retained in kvm_pmu_cap.
+	 */
+	if (kvm_host_pmu.version >= 5) {
+		cpuid(0xa, &eax, &ebx, &ecx, &edx.full);
+		if (kvm_pmu_cap.num_counters_fixed > edx.split.num_counters_fixed)
+			kvm_pmu_cap.num_counters_fixed = edx.split.num_counters_fixed;
+	}
+
+	if (!enable_mediated_pmu && kvm_pmu_cap.num_counters_fixed > 3)
+		kvm_pmu_cap.num_counters_fixed = 3;
+
 	kvm_pmu_eventsel.INSTRUCTIONS_RETIRED =
 		perf_get_hw_event_config(PERF_COUNT_HW_INSTRUCTIONS);
 	kvm_pmu_eventsel.BRANCH_INSTRUCTIONS_RETIRED =
-- 
2.55.0


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

* [PATCH v9 03/12] KVM: x86/pmu: Rename and move vcpu_get_perf_capabilities() to pmu.h
  2026-09-18 19:39 [PATCH v9 00/12] KVM: x86/pmu: Add hardware Topdown metrics support Zide Chen
  2026-09-18 19:39 ` [PATCH v9 01/12] KVM: x86/pmu: Do not map fixed counters >= 3 to generic perf events Zide Chen
  2026-09-18 19:39 ` [PATCH v9 02/12] KVM: x86/pmu: Support Intel fixed counter 3 on mediated vPMU Zide Chen
@ 2026-09-18 19:39 ` Zide Chen
  2026-09-18 19:39 ` [PATCH v9 04/12] KVM: x86/pmu: Snapshot host IA32_PERF_CAPABILITIES in kvm_host Zide Chen
                   ` (8 subsequent siblings)
  11 siblings, 0 replies; 20+ messages in thread
From: Zide Chen @ 2026-09-18 19:39 UTC (permalink / raw)
  To: Sean Christopherson, Paolo Bonzini, Peter Zijlstra
  Cc: kvm, Jim Mattson, Andi Kleen, Stephane Eranian, linux-kernel,
	Mingwei Zhang, Zide Chen, Das Sandipan, Shukla Manali, Dapeng Mi,
	Xudong Hao

This is in preparation for it to be called from common x86 code, for
example kvm_need_rdpmc_intercept(), to check the guest's PERF_METRICS
capability.

Rename it to kvm_vcpu_get_perf_caps() to indicate that it's part of
the common API, and shorten _capabilities to _caps.

No functional change intended.

Signed-off-by: Zide Chen <zide.chen@intel.com>
Reviewed-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
Reviewed-by: Jim Mattson <jmattson@google.com>
---
v8:
- remove include "cpuid.h" from pmu_intel.h.
---
 arch/x86/kvm/pmu.h           |  8 ++++++++
 arch/x86/kvm/vmx/pmu_intel.c |  6 +++---
 arch/x86/kvm/vmx/pmu_intel.h | 12 +-----------
 3 files changed, 12 insertions(+), 14 deletions(-)

diff --git a/arch/x86/kvm/pmu.h b/arch/x86/kvm/pmu.h
index 090c9bbb74f4..82f955e5a450 100644
--- a/arch/x86/kvm/pmu.h
+++ b/arch/x86/kvm/pmu.h
@@ -271,6 +271,14 @@ static inline bool kvm_pmu_is_fastpath_emulation_allowed(struct kvm_vcpu *vcpu)
 				  X86_PMC_IDX_MAX);
 }
 
+static inline u64 kvm_vcpu_get_perf_caps(struct kvm_vcpu *vcpu)
+{
+	if (!guest_cpu_cap_has(vcpu, X86_FEATURE_PDCM))
+		return 0;
+
+	return vcpu->arch.perf_capabilities;
+}
+
 void kvm_pmu_deliver_pmi(struct kvm_vcpu *vcpu);
 int kvm_pmu_rdpmc(struct kvm_vcpu *vcpu, unsigned pmc, u64 *data);
 int kvm_pmu_check_rdpmc_early(struct kvm_vcpu *vcpu, unsigned int idx);
diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c
index 11ad64d43e35..28c1542e5ba5 100644
--- a/arch/x86/kvm/vmx/pmu_intel.c
+++ b/arch/x86/kvm/vmx/pmu_intel.c
@@ -193,13 +193,13 @@ static bool intel_is_valid_msr(struct kvm_vcpu *vcpu, u32 msr)
 	case MSR_CORE_PERF_FIXED_CTR_CTRL:
 		return kvm_pmu_has_perf_global_ctrl(pmu);
 	case MSR_IA32_PEBS_ENABLE:
-		ret = vcpu_get_perf_capabilities(vcpu) & PERF_CAP_PEBS_FORMAT;
+		ret = kvm_vcpu_get_perf_caps(vcpu) & PERF_CAP_PEBS_FORMAT;
 		break;
 	case MSR_IA32_DS_AREA:
 		ret = guest_cpu_cap_has(vcpu, X86_FEATURE_DS);
 		break;
 	case MSR_PEBS_DATA_CFG:
-		perf_capabilities = vcpu_get_perf_capabilities(vcpu);
+		perf_capabilities = kvm_vcpu_get_perf_caps(vcpu);
 		ret = (perf_capabilities & PERF_CAP_PEBS_BASELINE) &&
 			((perf_capabilities & PERF_CAP_PEBS_FORMAT) > 3);
 		break;
@@ -554,7 +554,7 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu)
 		pmu->raw_event_mask |= (HSW_IN_TX|HSW_IN_TX_CHECKPOINTED);
 	}
 
-	perf_capabilities = vcpu_get_perf_capabilities(vcpu);
+	perf_capabilities = kvm_vcpu_get_perf_caps(vcpu);
 	if (intel_pmu_lbr_is_compatible(vcpu) &&
 	    (perf_capabilities & PERF_CAP_LBR_FMT))
 		memcpy(&lbr_desc->records, &vmx_lbr_caps, sizeof(vmx_lbr_caps));
diff --git a/arch/x86/kvm/vmx/pmu_intel.h b/arch/x86/kvm/vmx/pmu_intel.h
index 5d9357640aa1..dd447d8b6fdd 100644
--- a/arch/x86/kvm/vmx/pmu_intel.h
+++ b/arch/x86/kvm/vmx/pmu_intel.h
@@ -4,19 +4,9 @@
 
 #include <linux/kvm_host.h>
 
-#include "cpuid.h"
-
-static inline u64 vcpu_get_perf_capabilities(struct kvm_vcpu *vcpu)
-{
-	if (!guest_cpu_cap_has(vcpu, X86_FEATURE_PDCM))
-		return 0;
-
-	return vcpu->arch.perf_capabilities;
-}
-
 static inline bool fw_writes_is_enabled(struct kvm_vcpu *vcpu)
 {
-	return (vcpu_get_perf_capabilities(vcpu) & PERF_CAP_FW_WRITES) != 0;
+	return (kvm_vcpu_get_perf_caps(vcpu) & PERF_CAP_FW_WRITES) != 0;
 }
 
 bool intel_pmu_lbr_is_enabled(struct kvm_vcpu *vcpu);
-- 
2.55.0


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

* [PATCH v9 04/12] KVM: x86/pmu: Snapshot host IA32_PERF_CAPABILITIES in kvm_host
  2026-09-18 19:39 [PATCH v9 00/12] KVM: x86/pmu: Add hardware Topdown metrics support Zide Chen
                   ` (2 preceding siblings ...)
  2026-09-18 19:39 ` [PATCH v9 03/12] KVM: x86/pmu: Rename and move vcpu_get_perf_capabilities() to pmu.h Zide Chen
@ 2026-09-18 19:39 ` Zide Chen
  2026-09-18 19:39 ` [PATCH v9 05/12] KVM: x86/pmu: Support PERF_METRICS MSR in mediated vPMU Zide Chen
                   ` (7 subsequent siblings)
  11 siblings, 0 replies; 20+ messages in thread
From: Zide Chen @ 2026-09-18 19:39 UTC (permalink / raw)
  To: Sean Christopherson, Paolo Bonzini, Peter Zijlstra
  Cc: kvm, Jim Mattson, Andi Kleen, Stephane Eranian, linux-kernel,
	Mingwei Zhang, Zide Chen, Das Sandipan, Shukla Manali, Dapeng Mi,
	Xudong Hao

From: Mingwei Zhang <mizhang@google.com>

Cache the unadulterated snapshot of perf_capabilities so that KVM can
compare guest vPMU capabilities against raw hardware capabilities.

For example, if the host supports PERF_METRICS but it is not configured
for the guest, KVM can use it to determine that RDPMC accesses must be
intercepted.

Signed-off-by: Mingwei Zhang <mizhang@google.com>
Signed-off-by: Zide Chen <zide.chen@intel.com>
Reviewed-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
Reviewed-by: Jim Mattson <jmattson@google.com>
---
v5: new patch.
---
 arch/x86/include/asm/kvm_host.h | 1 +
 arch/x86/kvm/vmx/vmx.c          | 8 ++------
 arch/x86/kvm/x86.c              | 4 ++++
 3 files changed, 7 insertions(+), 6 deletions(-)

diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 00584fcffa33..d00c7cbcfa14 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -295,6 +295,7 @@ struct kvm_host_values {
 	u64 xss;
 	u64 s_cet;
 	u64 arch_capabilities;
+	u64 perf_capabilities;
 };
 extern struct kvm_host_values kvm_host;
 
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index 56b44e032395..715e44489e22 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -7949,14 +7949,10 @@ void vmx_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu)
 static __init u64 vmx_get_perf_capabilities(void)
 {
 	u64 perf_cap = PERF_CAP_FW_WRITES;
-	u64 host_perf_cap = 0;
 
 	if (!enable_pmu)
 		return 0;
 
-	if (boot_cpu_has(X86_FEATURE_PDCM))
-		rdmsrq(MSR_IA32_PERF_CAPABILITIES, host_perf_cap);
-
 	if (!cpu_feature_enabled(X86_FEATURE_ARCH_LBR) &&
 	    !enable_mediated_pmu) {
 		x86_perf_get_lbr(&vmx_lbr_caps);
@@ -7969,11 +7965,11 @@ static __init u64 vmx_get_perf_capabilities(void)
 		if (!vmx_lbr_caps.has_callstack)
 			memset(&vmx_lbr_caps, 0, sizeof(vmx_lbr_caps));
 		else if (vmx_lbr_caps.nr)
-			perf_cap |= host_perf_cap & PERF_CAP_LBR_FMT;
+			perf_cap |= kvm_host.perf_capabilities & PERF_CAP_LBR_FMT;
 	}
 
 	if (vmx_pebs_supported()) {
-		perf_cap |= host_perf_cap & PERF_CAP_PEBS_MASK;
+		perf_cap |= kvm_host.perf_capabilities & PERF_CAP_PEBS_MASK;
 
 		/*
 		 * Disallow adaptive PEBS as it is functionally broken, can be
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index af3ceee714c9..83106ce72d76 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -7037,6 +7037,10 @@ int kvm_x86_vendor_init(struct kvm_x86_init_ops *ops)
 	if (boot_cpu_has(X86_FEATURE_ARCH_CAPABILITIES))
 		rdmsrq(MSR_IA32_ARCH_CAPABILITIES, kvm_host.arch_capabilities);
 
+	if (boot_cpu_has(X86_FEATURE_PDCM))
+		rdmsrq_safe(MSR_IA32_PERF_CAPABILITIES,
+			    &kvm_host.perf_capabilities);
+
 	WARN_ON_ONCE(kvm_nr_uret_msrs);
 
 	r = ops->hardware_setup();
-- 
2.55.0


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

* [PATCH v9 05/12] KVM: x86/pmu: Support PERF_METRICS MSR in mediated vPMU
  2026-09-18 19:39 [PATCH v9 00/12] KVM: x86/pmu: Add hardware Topdown metrics support Zide Chen
                   ` (3 preceding siblings ...)
  2026-09-18 19:39 ` [PATCH v9 04/12] KVM: x86/pmu: Snapshot host IA32_PERF_CAPABILITIES in kvm_host Zide Chen
@ 2026-09-18 19:39 ` Zide Chen
  2026-10-23 18:27   ` Jim Mattson
  2026-09-18 19:39 ` [PATCH v9 06/12] KVM: x86/pmu: Move RDPMC emulation into per-vendor callbacks Zide Chen
                   ` (6 subsequent siblings)
  11 siblings, 1 reply; 20+ messages in thread
From: Zide Chen @ 2026-09-18 19:39 UTC (permalink / raw)
  To: Sean Christopherson, Paolo Bonzini, Peter Zijlstra
  Cc: kvm, Jim Mattson, Andi Kleen, Stephane Eranian, linux-kernel,
	Mingwei Zhang, Zide Chen, Das Sandipan, Shukla Manali, Dapeng Mi,
	Xudong Hao

From: Dapeng Mi <dapeng1.mi@linux.intel.com>

Bit 15 in IA32_PERF_CAPABILITIES indicates that the CPU provides
built-in support for Topdown Microarchitecture Analysis (TMA) L1
metrics via the IA32_PERF_METRICS MSR.

Expose this capability only when mediated vPMU is enabled, as emulating
IA32_PERF_METRICS in the legacy vPMU model is impractical.

Pass IA32_PERF_METRICS through to the guest only when mediated vPMU is
enabled and bit 15 is set in guest IA32_PERF_CAPABILITIES.  Allow
kvm_pmu_{get,set}_msr() to handle this MSR for host accesses.

Save and restore this MSR on host/guest PMU context switches so that
host PMU activity does not clobber the guest value, and guest state
is not leaked into the host.

If the host supports PERF_METRICS but it is not exposed to the guest,
MSR_CORE_PERF_GLOBAL_CTRL must be intercepted so the guest cannot
enable PERF_METRICS via GLOBAL_CTRL[48].

Signed-off-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
Co-developed-by: Zide Chen <zide.chen@intel.com>
Signed-off-by: Zide Chen <zide.chen@intel.com>
---
v9:
- Remove the incorrect comment about writing to MSR_PERF_METRICS.
v8:
- Intercept global ctrl MSR if host supports PerfMetrics but not
  enabled in the guest.
---
 arch/x86/include/asm/kvm_host.h  |  1 +
 arch/x86/include/asm/msr-index.h |  1 +
 arch/x86/kvm/msrs.c              |  6 +++++-
 arch/x86/kvm/pmu.c               |  7 +++++++
 arch/x86/kvm/pmu.h               |  5 +++++
 arch/x86/kvm/vmx/nested.c        |  2 ++
 arch/x86/kvm/vmx/pmu_intel.c     | 29 +++++++++++++++++++++++++++++
 arch/x86/kvm/vmx/vmx.c           |  7 +++++++
 8 files changed, 57 insertions(+), 1 deletion(-)

diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index d00c7cbcfa14..052bf377dedb 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -584,6 +584,7 @@ struct kvm_pmu {
 	u64 global_status_rsvd;
 	u64 reserved_bits;
 	u64 raw_event_mask;
+	u64 perf_metrics;
 	struct kvm_pmc gp_counters[KVM_MAX_NR_GP_COUNTERS];
 	struct kvm_pmc fixed_counters[KVM_MAX_NR_FIXED_COUNTERS];
 
diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h
index 3a8e51a0c9e8..8fad944197fb 100644
--- a/arch/x86/include/asm/msr-index.h
+++ b/arch/x86/include/asm/msr-index.h
@@ -331,6 +331,7 @@
 #define PERF_CAP_PEBS_FORMAT		0xf00
 #define PERF_CAP_FW_WRITES		BIT_ULL(13)
 #define PERF_CAP_PEBS_BASELINE		BIT_ULL(14)
+#define PERF_CAP_PERF_METRICS		BIT_ULL(15)
 #define PERF_CAP_PEBS_TIMING_INFO	BIT_ULL(17)
 #define PERF_CAP_PEBS_MASK		(PERF_CAP_PEBS_TRAP | PERF_CAP_ARCH_REG | \
 					 PERF_CAP_PEBS_FORMAT | PERF_CAP_PEBS_BASELINE | \
diff --git a/arch/x86/kvm/msrs.c b/arch/x86/kvm/msrs.c
index 45e4c8a32436..7b7b5db00f54 100644
--- a/arch/x86/kvm/msrs.c
+++ b/arch/x86/kvm/msrs.c
@@ -219,7 +219,7 @@ static const u32 msrs_to_save_pmu[] = {
 	MSR_ARCH_PERFMON_FIXED_CTR0, MSR_ARCH_PERFMON_FIXED_CTR1,
 	MSR_ARCH_PERFMON_FIXED_CTR2, MSR_ARCH_PERFMON_FIXED_CTR3,
 	MSR_CORE_PERF_FIXED_CTR_CTRL, MSR_CORE_PERF_GLOBAL_STATUS,
-	MSR_CORE_PERF_GLOBAL_CTRL,
+	MSR_CORE_PERF_GLOBAL_CTRL, MSR_PERF_METRICS,
 	MSR_IA32_PEBS_ENABLE, MSR_IA32_DS_AREA, MSR_PEBS_DATA_CFG,
 
 	/* This part of MSRs should match KVM_MAX_NR_INTEL_GP_COUNTERS. */
@@ -2609,6 +2609,10 @@ static void kvm_probe_msr_to_save(u32 msr_index)
 		     intel_pt_validate_hw_cap(PT_CAP_num_address_ranges) * 2))
 			return;
 		break;
+	case MSR_PERF_METRICS:
+		if (!(kvm_caps.supported_perf_cap & PERF_CAP_PERF_METRICS))
+			return;
+		break;
 	case MSR_ARCH_PERFMON_PERFCTR0 ...
 	     MSR_ARCH_PERFMON_PERFCTR0 + KVM_MAX_NR_GP_COUNTERS - 1:
 		if (msr_index - MSR_ARCH_PERFMON_PERFCTR0 >=
diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c
index 448044a2b121..f4d3801b97bc 100644
--- a/arch/x86/kvm/pmu.c
+++ b/arch/x86/kvm/pmu.c
@@ -806,9 +806,16 @@ static bool kvm_need_any_pmc_intercept(struct kvm_vcpu *vcpu)
 	       pmu->nr_arch_fixed_counters != kvm_host_pmu.num_counters_fixed;
 }
 
+static bool kvm_need_perf_metrics_intercept(struct kvm_vcpu *vcpu)
+{
+	return (kvm_host.perf_capabilities & PERF_CAP_PERF_METRICS) &&
+		!kvm_vcpu_has_perf_metrics(vcpu);
+}
+
 bool kvm_need_perf_global_ctrl_intercept(struct kvm_vcpu *vcpu)
 {
 	return kvm_need_any_pmc_intercept(vcpu) ||
+	       kvm_need_perf_metrics_intercept(vcpu) ||
 	       !kvm_pmu_has_perf_global_ctrl(vcpu_to_pmu(vcpu));
 }
 EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_need_perf_global_ctrl_intercept);
diff --git a/arch/x86/kvm/pmu.h b/arch/x86/kvm/pmu.h
index 82f955e5a450..be578013ed83 100644
--- a/arch/x86/kvm/pmu.h
+++ b/arch/x86/kvm/pmu.h
@@ -279,6 +279,11 @@ static inline u64 kvm_vcpu_get_perf_caps(struct kvm_vcpu *vcpu)
 	return vcpu->arch.perf_capabilities;
 }
 
+static inline bool kvm_vcpu_has_perf_metrics(struct kvm_vcpu *vcpu)
+{
+	return kvm_vcpu_get_perf_caps(vcpu) & PERF_CAP_PERF_METRICS;
+}
+
 void kvm_pmu_deliver_pmi(struct kvm_vcpu *vcpu);
 int kvm_pmu_rdpmc(struct kvm_vcpu *vcpu, unsigned pmc, u64 *data);
 int kvm_pmu_check_rdpmc_early(struct kvm_vcpu *vcpu, unsigned int idx);
diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c
index 0e5f4d7ce9eb..a1cf57c6bea1 100644
--- a/arch/x86/kvm/vmx/nested.c
+++ b/arch/x86/kvm/vmx/nested.c
@@ -724,6 +724,8 @@ static void nested_vmx_merge_pmu_msr_bitmaps(struct kvm_vcpu *vcpu,
 	nested_vmx_merge_msr_bitmaps_rw(MSR_CORE_PERF_GLOBAL_CTRL);
 	nested_vmx_merge_msr_bitmaps_read(MSR_CORE_PERF_GLOBAL_STATUS);
 	nested_vmx_merge_msr_bitmaps_write(MSR_CORE_PERF_GLOBAL_OVF_CTRL);
+
+	nested_vmx_merge_msr_bitmaps_rw(MSR_PERF_METRICS);
 }
 
 /*
diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c
index 28c1542e5ba5..fe66f03b006b 100644
--- a/arch/x86/kvm/vmx/pmu_intel.c
+++ b/arch/x86/kvm/vmx/pmu_intel.c
@@ -192,6 +192,8 @@ static bool intel_is_valid_msr(struct kvm_vcpu *vcpu, u32 msr)
 	case MSR_CORE_PERF_GLOBAL_OVF_CTRL:
 	case MSR_CORE_PERF_FIXED_CTR_CTRL:
 		return kvm_pmu_has_perf_global_ctrl(pmu);
+	case MSR_PERF_METRICS:
+		return kvm_vcpu_has_perf_metrics(vcpu);
 	case MSR_IA32_PEBS_ENABLE:
 		ret = kvm_vcpu_get_perf_caps(vcpu) & PERF_CAP_PEBS_FORMAT;
 		break;
@@ -349,6 +351,9 @@ static int intel_pmu_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
 	case MSR_CORE_PERF_FIXED_CTR_CTRL:
 		msr_info->data = pmu->fixed_ctr_ctrl;
 		break;
+	case MSR_PERF_METRICS:
+		msr_info->data = pmu->perf_metrics;
+		break;
 	case MSR_IA32_PEBS_ENABLE:
 		msr_info->data = pmu->pebs_enable;
 		break;
@@ -398,6 +403,9 @@ static int intel_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
 		if (pmu->fixed_ctr_ctrl != data)
 			reprogram_fixed_counters(pmu, data);
 		break;
+	case MSR_PERF_METRICS:
+		pmu->perf_metrics = data;
+		break;
 	case MSR_IA32_PEBS_ENABLE:
 		if (data & pmu->pebs_enable_rsvd)
 			return 1;
@@ -580,6 +588,8 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu)
 	counter_rsvd = ~((BIT_ULL(pmu->nr_arch_gp_counters) - 1) |
 			 ((BIT_ULL(pmu->nr_arch_fixed_counters) - 1) << KVM_FIXED_PMC_BASE_IDX));
 	pmu->global_ctrl_rsvd = counter_rsvd;
+	if (perf_capabilities & PERF_CAP_PERF_METRICS)
+		pmu->global_ctrl_rsvd &= ~GLOBAL_CTRL_EN_PERF_METRICS;
 
 	/*
 	 * GLOBAL_STATUS and GLOBAL_OVF_CONTROL (a.k.a. GLOBAL_STATUS_RESET)
@@ -636,6 +646,9 @@ static void intel_pmu_init(struct kvm_vcpu *vcpu)
 
 static void intel_pmu_reset(struct kvm_vcpu *vcpu)
 {
+	struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
+
+	pmu->perf_metrics = 0;
 	intel_pmu_release_guest_lbr_event(vcpu);
 }
 
@@ -806,6 +819,9 @@ static void intel_mediated_pmu_load(struct kvm_vcpu *vcpu)
 	struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
 	u64 global_status, toggle;
 
+	if (kvm_vcpu_has_perf_metrics(vcpu))
+		wrmsrq(MSR_PERF_METRICS, pmu->perf_metrics);
+
 	rdmsrq(MSR_CORE_PERF_GLOBAL_STATUS, global_status);
 	toggle = pmu->global_status ^ global_status;
 	if (global_status & toggle)
@@ -834,6 +850,19 @@ static void intel_mediated_pmu_put(struct kvm_vcpu *vcpu)
 	 */
 	if (pmu->fixed_ctr_ctrl_hw)
 		wrmsrq(MSR_CORE_PERF_FIXED_CTR_CTRL, 0);
+
+	if (kvm_vcpu_has_perf_metrics(vcpu)) {
+		pmu->perf_metrics = rdpmc(INTEL_PMC_FIXED_RDPMC_METRICS);
+		/*
+		 * The SDM requires restoring fixed counter 3 before
+		 * PERF_METRICS. However, this path writes 0 to PERF_METRICS
+		 * before fixed counter 3. For this all-zero case, the
+		 * resulting hardware state is therefore the same regardless
+		 * of write order.
+		 */
+		if (pmu->perf_metrics)
+			wrmsrq(MSR_PERF_METRICS, 0);
+	}
 }
 
 struct kvm_pmu_ops intel_pmu_ops __initdata = {
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index 715e44489e22..372de66365ab 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -4289,6 +4289,10 @@ static void vmx_recalc_pmu_msr_intercepts(struct kvm_vcpu *vcpu)
 				  MSR_TYPE_RW, intercept);
 	vmx_set_intercept_for_msr(vcpu, MSR_CORE_PERF_GLOBAL_OVF_CTRL,
 				  MSR_TYPE_RW, intercept);
+
+	intercept = !has_mediated_pmu || !kvm_vcpu_has_perf_metrics(vcpu);
+	vmx_set_intercept_for_msr(vcpu, MSR_PERF_METRICS,
+				  MSR_TYPE_RW, intercept);
 }
 
 static void vmx_recalc_msr_intercepts(struct kvm_vcpu *vcpu)
@@ -7994,6 +7998,9 @@ static __init u64 vmx_get_perf_capabilities(void)
 		perf_cap &= ~PERF_CAP_PEBS_BASELINE;
 	}
 
+	if (enable_mediated_pmu)
+		perf_cap |= kvm_host.perf_capabilities & PERF_CAP_PERF_METRICS;
+
 	return perf_cap;
 }
 
-- 
2.55.0


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

* [PATCH v9 06/12] KVM: x86/pmu: Move RDPMC emulation into per-vendor callbacks
  2026-09-18 19:39 [PATCH v9 00/12] KVM: x86/pmu: Add hardware Topdown metrics support Zide Chen
                   ` (4 preceding siblings ...)
  2026-09-18 19:39 ` [PATCH v9 05/12] KVM: x86/pmu: Support PERF_METRICS MSR in mediated vPMU Zide Chen
@ 2026-09-18 19:39 ` Zide Chen
  2026-09-18 19:39 ` [PATCH v9 07/12] KVM: x86/pmu: Emulate RDPMC on performance metrics Zide Chen
                   ` (5 subsequent siblings)
  11 siblings, 0 replies; 20+ messages in thread
From: Zide Chen @ 2026-09-18 19:39 UTC (permalink / raw)
  To: Sean Christopherson, Paolo Bonzini, Peter Zijlstra
  Cc: kvm, Jim Mattson, Andi Kleen, Stephane Eranian, linux-kernel,
	Mingwei Zhang, Zide Chen, Das Sandipan, Shukla Manali, Dapeng Mi,
	Xudong Hao

The current RDPMC emulation splits responsibility: rdpmc_ecx_to_pmc()
in each vendor returns a kvm_pmc, then common code calls
pmc_read_counter().

This design cannot support RDPMC reads that don't map to a counter,
such as PERF_METRICS on Intel platforms.

Replace rdpmc_ecx_to_pmc() with emulate_rdpmc(), which takes full
ownership of the emulation and writes the result directly into @data.

Opportunistically drop the redundant bitmask in intel_emulate_rdpmc()
since pmc_read_counter() already applies the counter's bit-width mask.

No functional change intended.

Signed-off-by: Zide Chen <zide.chen@intel.com>
Reviewed-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
Reviewed-by: Jim Mattson <jmattson@google.com>
Reviewed-by: Sandipan Das <sandipan.das@amd.com>
---
v6: new patch.
---
 arch/x86/include/asm/kvm-x86-pmu-ops.h |  2 +-
 arch/x86/kvm/pmu.c                     |  9 +--------
 arch/x86/kvm/pmu.h                     |  4 ++--
 arch/x86/kvm/svm/pmu.c                 | 13 +++++++++----
 arch/x86/kvm/vmx/pmu_intel.c           | 25 ++++++++++++-------------
 5 files changed, 25 insertions(+), 28 deletions(-)

diff --git a/arch/x86/include/asm/kvm-x86-pmu-ops.h b/arch/x86/include/asm/kvm-x86-pmu-ops.h
index 4a223c2793e3..4b50ed058aed 100644
--- a/arch/x86/include/asm/kvm-x86-pmu-ops.h
+++ b/arch/x86/include/asm/kvm-x86-pmu-ops.h
@@ -13,7 +13,7 @@
  * KVM_X86_PMU_OP_OPTIONAL() can be used for those functions that can have
  * a NULL definition.
  */
-KVM_X86_PMU_OP(rdpmc_ecx_to_pmc)
+KVM_X86_PMU_OP(emulate_rdpmc)
 KVM_X86_PMU_OP(msr_idx_to_pmc)
 KVM_X86_PMU_OP_OPTIONAL(check_rdpmc_early)
 KVM_X86_PMU_OP(is_valid_msr)
diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c
index f4d3801b97bc..897c1428b650 100644
--- a/arch/x86/kvm/pmu.c
+++ b/arch/x86/kvm/pmu.c
@@ -768,8 +768,6 @@ static int kvm_pmu_rdpmc_vmware(struct kvm_vcpu *vcpu, unsigned idx, u64 *data)
 int kvm_pmu_rdpmc(struct kvm_vcpu *vcpu, unsigned idx, u64 *data)
 {
 	struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
-	struct kvm_pmc *pmc;
-	u64 mask = ~0ull;
 
 	if (!pmu->version)
 		return 1;
@@ -777,17 +775,12 @@ int kvm_pmu_rdpmc(struct kvm_vcpu *vcpu, unsigned idx, u64 *data)
 	if (is_vmware_backdoor_pmc(idx))
 		return kvm_pmu_rdpmc_vmware(vcpu, idx, data);
 
-	pmc = kvm_pmu_call(rdpmc_ecx_to_pmc)(vcpu, idx, &mask);
-	if (!pmc)
-		return 1;
-
 	if (!kvm_is_cr4_bit_set(vcpu, X86_CR4_PCE) &&
 	    (kvm_x86_call(get_cpl)(vcpu) != 0) &&
 	    kvm_is_cr0_bit_set(vcpu, X86_CR0_PE))
 		return 1;
 
-	*data = pmc_read_counter(pmc) & mask;
-	return 0;
+	return kvm_pmu_call(emulate_rdpmc)(vcpu, idx, data);
 }
 
 static bool kvm_need_any_pmc_intercept(struct kvm_vcpu *vcpu)
diff --git a/arch/x86/kvm/pmu.h b/arch/x86/kvm/pmu.h
index be578013ed83..eb6bbc658f5b 100644
--- a/arch/x86/kvm/pmu.h
+++ b/arch/x86/kvm/pmu.h
@@ -24,8 +24,8 @@
 #define KVM_FIXED_PMC_BASE_IDX INTEL_PMC_IDX_FIXED
 
 struct kvm_pmu_ops {
-	struct kvm_pmc *(*rdpmc_ecx_to_pmc)(struct kvm_vcpu *vcpu,
-		unsigned int idx, u64 *mask);
+	int (*emulate_rdpmc)(struct kvm_vcpu *vcpu, unsigned int idx,
+			     u64 *data);
 	struct kvm_pmc *(*msr_idx_to_pmc)(struct kvm_vcpu *vcpu, u32 msr);
 	int (*check_rdpmc_early)(struct kvm_vcpu *vcpu, unsigned int idx);
 	bool (*is_valid_msr)(struct kvm_vcpu *vcpu, u32 msr);
diff --git a/arch/x86/kvm/svm/pmu.c b/arch/x86/kvm/svm/pmu.c
index c18286545a7a..0517fd4bbcd7 100644
--- a/arch/x86/kvm/svm/pmu.c
+++ b/arch/x86/kvm/svm/pmu.c
@@ -84,10 +84,15 @@ static int amd_check_rdpmc_early(struct kvm_vcpu *vcpu, unsigned int idx)
 }
 
 /* idx is the ECX register of RDPMC instruction */
-static struct kvm_pmc *amd_rdpmc_ecx_to_pmc(struct kvm_vcpu *vcpu,
-	unsigned int idx, u64 *mask)
+static int amd_emulate_rdpmc(struct kvm_vcpu *vcpu, unsigned int idx, u64 *data)
 {
-	return amd_pmu_get_pmc(vcpu_to_pmu(vcpu), idx);
+	struct kvm_pmc *pmc = amd_pmu_get_pmc(vcpu_to_pmu(vcpu), idx);
+
+	if (!pmc)
+		return 1;
+
+	*data = pmc_read_counter(pmc);
+	return 0;
 }
 
 static struct kvm_pmc *amd_msr_idx_to_pmc(struct kvm_vcpu *vcpu, u32 msr)
@@ -302,7 +307,7 @@ static bool amd_pmc_is_disabled_in_current_mode(struct kvm_pmc *pmc)
 }
 
 struct kvm_pmu_ops amd_pmu_ops __initdata = {
-	.rdpmc_ecx_to_pmc = amd_rdpmc_ecx_to_pmc,
+	.emulate_rdpmc = amd_emulate_rdpmc,
 	.msr_idx_to_pmc = amd_msr_idx_to_pmc,
 	.check_rdpmc_early = amd_check_rdpmc_early,
 	.is_valid_msr = amd_is_valid_msr,
diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c
index fe66f03b006b..2eb1bff75e60 100644
--- a/arch/x86/kvm/vmx/pmu_intel.c
+++ b/arch/x86/kvm/vmx/pmu_intel.c
@@ -85,14 +85,13 @@ static void reprogram_fixed_counters(struct kvm_pmu *pmu, u64 data)
 	}
 }
 
-static struct kvm_pmc *intel_rdpmc_ecx_to_pmc(struct kvm_vcpu *vcpu,
-					    unsigned int idx, u64 *mask)
+static int intel_emulate_rdpmc(struct kvm_vcpu *vcpu, unsigned int idx,
+			       u64 *data)
 {
 	unsigned int type = idx & INTEL_RDPMC_TYPE_MASK;
 	struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
-	struct kvm_pmc *counters;
+	struct kvm_pmc *counters, *pmc;
 	unsigned int num_counters;
-	u64 bitmask;
 
 	/*
 	 * The encoding of ECX for RDPMC is different for architectural versus
@@ -105,7 +104,9 @@ static struct kvm_pmc *intel_rdpmc_ecx_to_pmc(struct kvm_vcpu *vcpu,
 	 * as KVM doesn't support such PMUs.
 	 */
 	if (WARN_ON_ONCE(!pmu->version))
-		return NULL;
+		return 1;
+
+	idx &= INTEL_RDPMC_INDEX_MASK;
 
 	/*
 	 * General Purpose (GP) PMCs are supported on all PMUs, and fixed PMCs
@@ -119,23 +120,21 @@ static struct kvm_pmc *intel_rdpmc_ecx_to_pmc(struct kvm_vcpu *vcpu,
 	case INTEL_RDPMC_FIXED:
 		counters = pmu->fixed_counters;
 		num_counters = pmu->nr_arch_fixed_counters;
-		bitmask = pmu->counter_bitmask[KVM_PMC_FIXED];
 		break;
 	case INTEL_RDPMC_GP:
 		counters = pmu->gp_counters;
 		num_counters = pmu->nr_arch_gp_counters;
-		bitmask = pmu->counter_bitmask[KVM_PMC_GP];
 		break;
 	default:
-		return NULL;
+		return 1;
 	}
 
-	idx &= INTEL_RDPMC_INDEX_MASK;
 	if (idx >= num_counters)
-		return NULL;
+		return 1;
 
-	*mask &= bitmask;
-	return &counters[array_index_nospec(idx, num_counters)];
+	pmc = &counters[array_index_nospec(idx, num_counters)];
+	*data = pmc_read_counter(pmc);
+	return 0;
 }
 
 static inline struct kvm_pmc *get_fw_gp_pmc(struct kvm_pmu *pmu, u32 msr)
@@ -866,7 +865,7 @@ static void intel_mediated_pmu_put(struct kvm_vcpu *vcpu)
 }
 
 struct kvm_pmu_ops intel_pmu_ops __initdata = {
-	.rdpmc_ecx_to_pmc = intel_rdpmc_ecx_to_pmc,
+	.emulate_rdpmc = intel_emulate_rdpmc,
 	.msr_idx_to_pmc = intel_msr_idx_to_pmc,
 	.is_valid_msr = intel_is_valid_msr,
 	.get_msr = intel_pmu_get_msr,
-- 
2.55.0


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

* [PATCH v9 07/12] KVM: x86/pmu: Emulate RDPMC on performance metrics
  2026-09-18 19:39 [PATCH v9 00/12] KVM: x86/pmu: Add hardware Topdown metrics support Zide Chen
                   ` (5 preceding siblings ...)
  2026-09-18 19:39 ` [PATCH v9 06/12] KVM: x86/pmu: Move RDPMC emulation into per-vendor callbacks Zide Chen
@ 2026-09-18 19:39 ` Zide Chen
  2026-10-23 18:33   ` Jim Mattson
  2026-09-18 19:39 ` [PATCH v9 08/12] KVM: selftests: Add PERF_METRICS and fixed counter 3 tests Zide Chen
                   ` (4 subsequent siblings)
  11 siblings, 1 reply; 20+ messages in thread
From: Zide Chen @ 2026-09-18 19:39 UTC (permalink / raw)
  To: Sean Christopherson, Paolo Bonzini, Peter Zijlstra
  Cc: kvm, Jim Mattson, Andi Kleen, Stephane Eranian, linux-kernel,
	Mingwei Zhang, Zide Chen, Das Sandipan, Shukla Manali, Dapeng Mi,
	Xudong Hao

If the host has the PERF_METRICS capability but it's not present on
the guest, RDPMC interception must be enabled and KVM should inject
an #GP when the guest attempts a PERF_METRICS RDPMC.

If the guest has PERF_METRICS but RDPMC interception is enabled for
other reasons, KVM needs to emulate RDPMC with type 0x2000.

Signed-off-by: Zide Chen <zide.chen@intel.com>
Reviewed-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
---
v9:
- Remove references to Metrics Clear from the changelog.
v8:
- Move kvm_need_perf_metrics_intercept() to patch 5/9.
---
 arch/x86/kvm/pmu.c           |  1 +
 arch/x86/kvm/vmx/pmu_intel.c | 14 ++++++++++++++
 2 files changed, 15 insertions(+)

diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c
index 897c1428b650..0df283a169b6 100644
--- a/arch/x86/kvm/pmu.c
+++ b/arch/x86/kvm/pmu.c
@@ -825,6 +825,7 @@ bool kvm_need_rdpmc_intercept(struct kvm_vcpu *vcpu)
 		return true;
 
 	return kvm_need_any_pmc_intercept(vcpu) ||
+	       kvm_need_perf_metrics_intercept(vcpu) ||
 	       pmu->counter_bitmask[KVM_PMC_GP] != (BIT_ULL(kvm_host_pmu.bit_width_gp) - 1) ||
 	       pmu->counter_bitmask[KVM_PMC_FIXED] != (BIT_ULL(kvm_host_pmu.bit_width_fixed) - 1);
 }
diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c
index 2eb1bff75e60..fab0891ad46a 100644
--- a/arch/x86/kvm/vmx/pmu_intel.c
+++ b/arch/x86/kvm/vmx/pmu_intel.c
@@ -31,6 +31,7 @@
  */
 #define INTEL_RDPMC_GP		0
 #define INTEL_RDPMC_FIXED	INTEL_PMC_FIXED_RDPMC_BASE
+#define INTEL_RDPMC_METRICS	INTEL_PMC_FIXED_RDPMC_METRICS
 
 #define INTEL_RDPMC_TYPE_MASK	GENMASK(31, 16)
 #define INTEL_RDPMC_INDEX_MASK	GENMASK(15, 0)
@@ -125,6 +126,19 @@ static int intel_emulate_rdpmc(struct kvm_vcpu *vcpu, unsigned int idx,
 		counters = pmu->gp_counters;
 		num_counters = pmu->nr_arch_gp_counters;
 		break;
+	case INTEL_RDPMC_METRICS:
+		if (!kvm_vcpu_has_perf_metrics(vcpu))
+			return 1;
+
+		/*
+		 * The index in ECX[15:0] is implementation specific, but no
+		 * platform currently supports a non-zero index.
+		 */
+		if (idx)
+			return 1;
+
+		*data = pmu->perf_metrics;
+		return 0;
 	default:
 		return 1;
 	}
-- 
2.55.0


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

* [PATCH v9 08/12] KVM: selftests: Add PERF_METRICS and fixed counter 3 tests
  2026-09-18 19:39 [PATCH v9 00/12] KVM: x86/pmu: Add hardware Topdown metrics support Zide Chen
                   ` (6 preceding siblings ...)
  2026-09-18 19:39 ` [PATCH v9 07/12] KVM: x86/pmu: Emulate RDPMC on performance metrics Zide Chen
@ 2026-09-18 19:39 ` Zide Chen
  2026-09-18 19:39 ` [PATCH v9 09/12] perf/x86: Add INTEL_TD_METRIC_FIELD_{BITS,MASK} constants Zide Chen
                   ` (3 subsequent siblings)
  11 siblings, 0 replies; 20+ messages in thread
From: Zide Chen @ 2026-09-18 19:39 UTC (permalink / raw)
  To: Sean Christopherson, Paolo Bonzini, Peter Zijlstra
  Cc: kvm, Jim Mattson, Andi Kleen, Stephane Eranian, linux-kernel,
	Mingwei Zhang, Zide Chen, Das Sandipan, Shukla Manali, Dapeng Mi,
	Xudong Hao

Add a test case to exercise IA32_PERF_METRICS, i.e. architectural
support for Topdown (TMA) Level 1 metrics, enumerated by
IA32_PERF_CAPABILITIES[15].

Only check for non-zero metrics, as they are derived and depend on
the workload, CPU model, and host scheduling, making precise
expectations fragile.

Extend the PMU selftest to cover Intel fixed counter 3 by bumping
MAX_NR_FIXED_COUNTERS to 4 and validating basic functionality.

Signed-off-by: Zide Chen <zide.chen@intel.com>
Reviewed-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
---
v7:
- Use RDPMC instead of RDMSR in PERF_METRICS sanity test.
---
 tools/arch/x86/include/asm/msr-index.h        |  1 +
 tools/testing/selftests/kvm/include/x86/pmu.h |  3 +
 .../selftests/kvm/x86/pmu_counters_test.c     | 97 ++++++++++++++++++-
 3 files changed, 96 insertions(+), 5 deletions(-)

diff --git a/tools/arch/x86/include/asm/msr-index.h b/tools/arch/x86/include/asm/msr-index.h
index 18c4be75e927..fdcaeb6c8352 100644
--- a/tools/arch/x86/include/asm/msr-index.h
+++ b/tools/arch/x86/include/asm/msr-index.h
@@ -331,6 +331,7 @@
 #define PERF_CAP_PEBS_FORMAT		0xf00
 #define PERF_CAP_FW_WRITES		BIT_ULL(13)
 #define PERF_CAP_PEBS_BASELINE		BIT_ULL(14)
+#define PERF_CAP_PERF_METRICS		BIT_ULL(15)
 #define PERF_CAP_PEBS_TIMING_INFO	BIT_ULL(17)
 #define PERF_CAP_PEBS_MASK		(PERF_CAP_PEBS_TRAP | PERF_CAP_ARCH_REG | \
 					 PERF_CAP_PEBS_FORMAT | PERF_CAP_PEBS_BASELINE | \
diff --git a/tools/testing/selftests/kvm/include/x86/pmu.h b/tools/testing/selftests/kvm/include/x86/pmu.h
index 608ed83d7c6a..6c19503e0bb7 100644
--- a/tools/testing/selftests/kvm/include/x86/pmu.h
+++ b/tools/testing/selftests/kvm/include/x86/pmu.h
@@ -52,6 +52,9 @@
 /* Fixed PMC controls, Intel only. */
 #define FIXED_PMC_GLOBAL_CTRL_ENABLE(_idx)	BIT_ULL((32 + (_idx)))
 
+/* PERF_METRICS enable, Intel only. */
+#define PERF_METRICS_GLOBAL_CTRL_ENABLE		BIT_ULL(48)
+
 #define FIXED_PMC_KERNEL			BIT_ULL(0)
 #define FIXED_PMC_USER				BIT_ULL(1)
 #define FIXED_PMC_ANYTHREAD			BIT_ULL(2)
diff --git a/tools/testing/selftests/kvm/x86/pmu_counters_test.c b/tools/testing/selftests/kvm/x86/pmu_counters_test.c
index c3e784e16348..cf09b587a4ac 100644
--- a/tools/testing/selftests/kvm/x86/pmu_counters_test.c
+++ b/tools/testing/selftests/kvm/x86/pmu_counters_test.c
@@ -3,6 +3,7 @@
  * Copyright (C) 2023, Tencent, Inc.
  */
 #include <x86intrin.h>
+#include <linux/bitfield.h>
 
 #include "pmu.h"
 #include "processor.h"
@@ -254,17 +255,20 @@ do {										\
 	);									\
 } while (0)
 
-#define GUEST_TEST_EVENT(_idx, _pmc, _pmc_msr, _ctrl_msr, _value, FEP)		\
+#define GUEST_RUN_PAYLOAD(_ctrl_msr, _value, FEP)				\
 do {										\
-	wrmsr(_pmc_msr, 0);							\
-										\
 	if (this_cpu_has(X86_FEATURE_CLFLUSHOPT))				\
 		GUEST_MEASURE_EVENT(_ctrl_msr, _value, "clflushopt %[m]", FEP);	\
 	else if (this_cpu_has(X86_FEATURE_CLFLUSH))				\
 		GUEST_MEASURE_EVENT(_ctrl_msr, _value, "clflush  %[m]", FEP);	\
 	else									\
 		GUEST_MEASURE_EVENT(_ctrl_msr, _value, "nop", FEP);		\
-										\
+} while (0)
+
+#define GUEST_TEST_EVENT(_idx, _pmc, _pmc_msr, _ctrl_msr, _value, FEP)		\
+do {										\
+	wrmsr(_pmc_msr, 0);							\
+	GUEST_RUN_PAYLOAD(_ctrl_msr, _value, FEP);				\
 	guest_assert_event_count(_idx, _pmc, _pmc_msr);				\
 } while (0)
 
@@ -412,7 +416,7 @@ static void test_arch_events(u8 pmu_version, u64 perf_capabilities)
  * other than PMCs in the future.
  */
 #define MAX_NR_GP_COUNTERS	8
-#define MAX_NR_FIXED_COUNTERS	3
+#define MAX_NR_FIXED_COUNTERS	4
 
 #define GUEST_ASSERT_PMC_MSR_ACCESS(insn, msr, expect_gp, vector)		\
 __GUEST_ASSERT(expect_gp ? vector == GP_VECTOR : !vector,			\
@@ -653,8 +657,85 @@ static void test_fixed_counters(u8 pmu_version, u64 perf_capabilities)
 	pmu_vm_free(vm, vcpus);
 }
 
+static void __guest_test_perf_metrics(void)
+{
+	int retiring, bad_spec, fe_bound, be_bound, sum;
+	u64 global_ctrl, metrics;
+
+	if ((guest_get_pmu_version() < 2) ||	/* Does guest have GLOBAL_CTRL? */
+	    !this_cpu_has(X86_FEATURE_PDCM) ||
+	    !(rdmsr(MSR_IA32_PERF_CAPABILITIES) & PERF_CAP_PERF_METRICS))
+		return;
+
+	wrmsr(MSR_CORE_PERF_GLOBAL_CTRL, 0);
+	wrmsr(MSR_CORE_PERF_FIXED_CTR3, 0);
+	wrmsr(MSR_PERF_METRICS, 0);
+
+	/* Enable fixed ctr3 (TOPDOWN.SLOTS) and PERF_METRICS. */
+	wrmsr(MSR_CORE_PERF_FIXED_CTR_CTRL, FIXED_PMC_CTRL(3, FIXED_PMC_KERNEL));
+	global_ctrl = FIXED_PMC_GLOBAL_CTRL_ENABLE(3) |
+		      PERF_METRICS_GLOBAL_CTRL_ENABLE;
+
+	GUEST_RUN_PAYLOAD(MSR_CORE_PERF_GLOBAL_CTRL, global_ctrl, "");
+
+	/* Check test results. */
+	metrics = rdmsr(MSR_PERF_METRICS);
+	retiring = FIELD_GET(GENMASK_ULL(7, 0), metrics);
+	bad_spec = FIELD_GET(GENMASK_ULL(15, 8), metrics);
+	fe_bound = FIELD_GET(GENMASK_ULL(23, 16), metrics);
+	be_bound = FIELD_GET(GENMASK_ULL(31, 24), metrics);
+
+	/*
+	 * Be conservative: the measured payload definitely retires work, so
+	 * Retiring should be non-zero.
+	 */
+	GUEST_ASSERT_NE(metrics, 0);
+	GUEST_ASSERT_NE(retiring, 0);
+
+	/*
+	 * Each level-1 Topdown metric is an integer fraction of 0xff.
+	 * A +/-3 error margin is chosen for a loose sanity check.
+	 */
+	sum = retiring + bad_spec + fe_bound + be_bound;
+	GUEST_ASSERT(sum >= 0xfc && sum <= 0x102);
+
+	/*
+	 * Burn some cycles; MSR_CORE_PERF_GLOBAL_CTRL is left zeroed after
+	 * GUEST_RUN_PAYLOAD() returns, so PERF_METRICS is not counting.
+	 */
+	__asm__ __volatile__("loop ." : "+c"((int){NUM_LOOPS}));
+	/* Confirm PERF_METRICS did not advance while counting was disabled. */
+	GUEST_ASSERT_EQ(rdmsr(MSR_PERF_METRICS), metrics);
+	wrmsr(MSR_PERF_METRICS, 0xdeaddead);
+
+	guest_test_rdpmc(INTEL_RDPMC_METRICS, true, 0xdeaddead);
+}
+
+static void guest_test_perf_metrics(void)
+{
+	__guest_test_perf_metrics();
+	GUEST_DONE();
+}
+
+static void test_perf_metrics(u8 pmu_version, u64 perf_capabilities)
+{
+	struct kvm_vcpu **vcpus;
+	struct kvm_vm *vm;
+
+	pr_info("Testing Perf Metrics, PMU version %u, perf_caps = %lx\n",
+		pmu_version, perf_capabilities);
+
+	vm = pmu_vm_create_with_vcpus(1, guest_test_perf_metrics,
+				      pmu_version, perf_capabilities, &vcpus);
+
+	run_vcpu(vcpus[0]);
+
+	pmu_vm_free(vm, vcpus);
+}
+
 static void test_intel_counters(void)
 {
+	u64 advertised_perf_caps = kvm_get_feature_msr(MSR_IA32_PERF_CAPABILITIES);
 	u8 pmu_version = kvm_cpu_property(X86_PROPERTY_PMU_VERSION);
 	unsigned int i;
 	u8 v;
@@ -662,6 +743,7 @@ static void test_intel_counters(void)
 	const u64 perf_caps[] = {
 		0,
 		PMU_CAP_FW_WRITES,
+		PERF_CAP_PERF_METRICS,
 	};
 
 	/*
@@ -699,9 +781,14 @@ static void test_intel_counters(void)
 			if (!kvm_has_perf_caps && perf_caps[i])
 				continue;
 
+			/* Ignore unsupported features. */
+			if (perf_caps[i] & ~advertised_perf_caps)
+				continue;
+
 			test_arch_events(v, perf_caps[i]);
 			test_gp_counters(v, perf_caps[i]);
 			test_fixed_counters(v, perf_caps[i]);
+			test_perf_metrics(v, perf_caps[i]);
 		}
 	}
 }
-- 
2.55.0


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

* [PATCH v9 09/12] perf/x86: Add INTEL_TD_METRIC_FIELD_{BITS,MASK} constants
  2026-09-18 19:39 [PATCH v9 00/12] KVM: x86/pmu: Add hardware Topdown metrics support Zide Chen
                   ` (7 preceding siblings ...)
  2026-09-18 19:39 ` [PATCH v9 08/12] KVM: selftests: Add PERF_METRICS and fixed counter 3 tests Zide Chen
@ 2026-09-18 19:39 ` Zide Chen
  2026-09-21  7:01   ` Mi, Dapeng
  2026-09-18 19:39 ` [PATCH v9 10/12] perf/x86: Expose number of Topdown metric events to KVM Zide Chen
                   ` (2 subsequent siblings)
  11 siblings, 1 reply; 20+ messages in thread
From: Zide Chen @ 2026-09-18 19:39 UTC (permalink / raw)
  To: Sean Christopherson, Paolo Bonzini, Peter Zijlstra
  Cc: kvm, Jim Mattson, Andi Kleen, Stephane Eranian, linux-kernel,
	Mingwei Zhang, Zide Chen, Das Sandipan, Shukla Manali, Dapeng Mi,
	Xudong Hao

Replace the hard coded field width (8) and the equivalent 0xff mask
used for PERF_METRICS MSR field access with named constants
INTEL_TD_METRIC_FIELD_{BITS,MASK}.

INTEL_TD_METRIC_FIELD_BITS will be used by a subsequent KVM patch to
validat reserved bits in the PERF_METRICS MSR.

No functional change intended.

Signed-off-by: Zide Chen <zide.chen@intel.com>
Cc: Peter Zijlstra <peterz@infradead.org>
---
v9: new patch.
---
 arch/x86/events/intel/core.c      | 8 +++++---
 arch/x86/include/asm/perf_event.h | 4 ++++
 2 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c
index cc13164d948f..cbc9a179f2e5 100644
--- a/arch/x86/events/intel/core.c
+++ b/arch/x86/events/intel/core.c
@@ -3159,6 +3159,7 @@ DEFINE_STATIC_CALL(intel_pmu_set_topdown_event_period, x86_perf_event_set_period
 
 static inline u64 icl_get_metrics_event_value(u64 metric, u64 slots, int idx)
 {
+	int shift = (idx - INTEL_PMC_IDX_METRIC_BASE) * INTEL_TD_METRIC_FIELD_BITS;
 	u32 val;
 
 	/*
@@ -3166,8 +3167,8 @@ static inline u64 icl_get_metrics_event_value(u64 metric, u64 slots, int idx)
 	 * summing up to 0xff.
 	 * slots-in-metric = (Metric / 0xff) * slots
 	 */
-	val = (metric >> ((idx - INTEL_PMC_IDX_METRIC_BASE) * 8)) & 0xff;
-	return  mul_u64_u32_div(slots, val, 0xff);
+	val = (metric >> shift) & INTEL_TD_METRIC_FIELD_MASK;
+	return mul_u64_u32_div(slots, val, INTEL_TD_METRIC_FIELD_MASK);
 }
 
 static u64 icl_get_topdown_value(struct perf_event *event,
@@ -4742,7 +4743,8 @@ static int core_pmu_hw_config(struct perf_event *event)
 }
 
 #define INTEL_TD_METRIC_AVAILABLE_MAX	(INTEL_TD_METRIC_RETIRING + \
-					 ((x86_pmu.num_topdown_events - 1) << 8))
+					 ((x86_pmu.num_topdown_events - 1) << \
+					  INTEL_TD_METRIC_FIELD_BITS))
 
 static bool is_available_metric_event(struct perf_event *event)
 {
diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h
index 1eb13673e889..ceb6188e5217 100644
--- a/arch/x86/include/asm/perf_event.h
+++ b/arch/x86/include/asm/perf_event.h
@@ -418,6 +418,10 @@ static inline bool use_fixed_pseudo_encoding(u64 code)
 #define INTEL_TD_METRIC_MAX			INTEL_TD_METRIC_MEM_BOUND
 #define INTEL_TD_METRIC_NUM			8
 
+/* Width, in bits, of each metric's field within the PERF_METRICS MSR. */
+#define INTEL_TD_METRIC_FIELD_BITS		8
+#define INTEL_TD_METRIC_FIELD_MASK		GENMASK_ULL(INTEL_TD_METRIC_FIELD_BITS - 1, 0)
+
 #define INTEL_TD_CFG_METRIC_CLEAR_BIT		0
 #define INTEL_TD_CFG_METRIC_CLEAR		BIT_ULL(INTEL_TD_CFG_METRIC_CLEAR_BIT)
 
-- 
2.55.0


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

* [PATCH v9 10/12] perf/x86: Expose number of Topdown metric events to KVM
  2026-09-18 19:39 [PATCH v9 00/12] KVM: x86/pmu: Add hardware Topdown metrics support Zide Chen
                   ` (8 preceding siblings ...)
  2026-09-18 19:39 ` [PATCH v9 09/12] perf/x86: Add INTEL_TD_METRIC_FIELD_{BITS,MASK} constants Zide Chen
@ 2026-09-18 19:39 ` Zide Chen
  2026-09-21  7:06   ` Mi, Dapeng
  2026-09-18 19:39 ` [PATCH v9 11/12] KVM: x86/pmu: Reject writes to reserved MSR_PERF_METRICS bits Zide Chen
  2026-09-18 19:39 ` [PATCH v9 12/12] KVM: x86/pmu: Support RDPMC Metrics Clear Mode Zide Chen
  11 siblings, 1 reply; 20+ messages in thread
From: Zide Chen @ 2026-09-18 19:39 UTC (permalink / raw)
  To: Sean Christopherson, Paolo Bonzini, Peter Zijlstra
  Cc: kvm, Jim Mattson, Andi Kleen, Stephane Eranian, linux-kernel,
	Mingwei Zhang, Zide Chen, Das Sandipan, Shukla Manali, Dapeng Mi,
	Xudong Hao

The number of Topdown metric events varies by CPU: ICL/TGL/RKL
support 4, while SPR/GNR/DMR and some hybrid CPUs support 8.

Add num_topdown_events to struct x86_pmu_capability and populate it
in perf_get_x86_pmu_capability() so that KVM can determine how many
PERF_METRICS fields are valid on the host CPU and correctly identify
the reserved bits in IA32_PERF_METRICS.

Signed-off-by: Zide Chen <zide.chen@intel.com>
Cc: Peter Zijlstra <peterz@infradead.org>
---
v9: new patch
---
 arch/x86/events/core.c            | 1 +
 arch/x86/include/asm/perf_event.h | 1 +
 2 files changed, 2 insertions(+)

diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c
index 8b3ea0adb965..01c1c9447f30 100644
--- a/arch/x86/events/core.c
+++ b/arch/x86/events/core.c
@@ -3164,6 +3164,7 @@ void perf_get_x86_pmu_capability(struct x86_pmu_capability *cap)
 	cap->events_mask_len	= x86_pmu.events_mask_len;
 	cap->pebs_ept		= x86_pmu.pebs_ept;
 	cap->mediated		= !!(pmu.capabilities & PERF_PMU_CAP_MEDIATED_VPMU);
+	cap->num_topdown_events	= x86_pmu.num_topdown_events;
 }
 EXPORT_SYMBOL_FOR_KVM(perf_get_x86_pmu_capability);
 
diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h
index ceb6188e5217..3c02389b2ea6 100644
--- a/arch/x86/include/asm/perf_event.h
+++ b/arch/x86/include/asm/perf_event.h
@@ -306,6 +306,7 @@ struct x86_pmu_capability {
 	int		bit_width_fixed;
 	unsigned int	events_mask;
 	int		events_mask_len;
+	int		num_topdown_events;
 	unsigned int	pebs_ept	:1;
 	unsigned int	mediated	:1;
 };
-- 
2.55.0


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

* [PATCH v9 11/12] KVM: x86/pmu: Reject writes to reserved MSR_PERF_METRICS bits
  2026-09-18 19:39 [PATCH v9 00/12] KVM: x86/pmu: Add hardware Topdown metrics support Zide Chen
                   ` (9 preceding siblings ...)
  2026-09-18 19:39 ` [PATCH v9 10/12] perf/x86: Expose number of Topdown metric events to KVM Zide Chen
@ 2026-09-18 19:39 ` Zide Chen
  2026-09-21  7:11   ` Mi, Dapeng
  2026-10-23 18:45   ` Jim Mattson
  2026-09-18 19:39 ` [PATCH v9 12/12] KVM: x86/pmu: Support RDPMC Metrics Clear Mode Zide Chen
  11 siblings, 2 replies; 20+ messages in thread
From: Zide Chen @ 2026-09-18 19:39 UTC (permalink / raw)
  To: Sean Christopherson, Paolo Bonzini, Peter Zijlstra
  Cc: kvm, Jim Mattson, Andi Kleen, Stephane Eranian, linux-kernel,
	Mingwei Zhang, Zide Chen, Das Sandipan, Shukla Manali, Dapeng Mi,
	Xudong Hao

When the host CPU supports only 4 Topdown metrics, bits [63:32] of
MSR_PERF_METRICS are reserved.

Derive pmu->perf_metrics_rsvd from the number of supported Topdown
metrics and use it to validate writes. Reject any attempts to set
reserved bits in MSR_PERF_METRICS.

Signed-off-by: Zide Chen <zide.chen@intel.com>
---
v9: new patch.
---
 arch/x86/include/asm/kvm_host.h |  1 +
 arch/x86/kvm/pmu.c              |  1 +
 arch/x86/kvm/vmx/pmu_intel.c    | 18 +++++++++++++++++-
 3 files changed, 19 insertions(+), 1 deletion(-)

diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 052bf377dedb..31454ecee371 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -585,6 +585,7 @@ struct kvm_pmu {
 	u64 reserved_bits;
 	u64 raw_event_mask;
 	u64 perf_metrics;
+	u64 perf_metrics_rsvd;
 	struct kvm_pmc gp_counters[KVM_MAX_NR_GP_COUNTERS];
 	struct kvm_pmc fixed_counters[KVM_MAX_NR_FIXED_COUNTERS];
 
diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c
index 0df283a169b6..41f2d31c495f 100644
--- a/arch/x86/kvm/pmu.c
+++ b/arch/x86/kvm/pmu.c
@@ -1006,6 +1006,7 @@ void kvm_pmu_refresh(struct kvm_vcpu *vcpu)
 	pmu->fixed_ctr_ctrl_rsvd = ~0ull;
 	pmu->pebs_enable_rsvd = ~0ull;
 	pmu->pebs_data_cfg_rsvd = ~0ull;
+	pmu->perf_metrics_rsvd = ~0ull;
 	bitmap_zero(pmu->all_valid_pmc_idx, X86_PMC_IDX_MAX);
 
 	if (!vcpu->kvm->arch.enable_pmu)
diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c
index fab0891ad46a..98e2fb80347a 100644
--- a/arch/x86/kvm/vmx/pmu_intel.c
+++ b/arch/x86/kvm/vmx/pmu_intel.c
@@ -417,6 +417,9 @@ static int intel_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
 			reprogram_fixed_counters(pmu, data);
 		break;
 	case MSR_PERF_METRICS:
+		if (data & pmu->perf_metrics_rsvd)
+			return 1;
+
 		pmu->perf_metrics = data;
 		break;
 	case MSR_IA32_PEBS_ENABLE:
@@ -601,9 +604,22 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu)
 	counter_rsvd = ~((BIT_ULL(pmu->nr_arch_gp_counters) - 1) |
 			 ((BIT_ULL(pmu->nr_arch_fixed_counters) - 1) << KVM_FIXED_PMC_BASE_IDX));
 	pmu->global_ctrl_rsvd = counter_rsvd;
-	if (perf_capabilities & PERF_CAP_PERF_METRICS)
+	if (perf_capabilities & PERF_CAP_PERF_METRICS) {
 		pmu->global_ctrl_rsvd &= ~GLOBAL_CTRL_EN_PERF_METRICS;
 
+		/*
+		 * PERF_METRICS has one 8-bit field per metric. At 8 metrics,
+		 * skip computing the mask and set perf_metrics_rsvd to 0
+		 * directly, since BIT_ULL(64) is undefined.
+		 */
+		if (kvm_pmu_cap.num_topdown_events < INTEL_TD_METRIC_NUM)
+			pmu->perf_metrics_rsvd =
+				~(BIT_ULL(kvm_pmu_cap.num_topdown_events *
+					  INTEL_TD_METRIC_FIELD_BITS) - 1);
+		else
+			pmu->perf_metrics_rsvd = 0;
+	}
+
 	/*
 	 * GLOBAL_STATUS and GLOBAL_OVF_CONTROL (a.k.a. GLOBAL_STATUS_RESET)
 	 * share reserved bit definitions.  The kernel just happens to use
-- 
2.55.0


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

* [PATCH v9 12/12] KVM: x86/pmu: Support RDPMC Metrics Clear Mode
  2026-09-18 19:39 [PATCH v9 00/12] KVM: x86/pmu: Add hardware Topdown metrics support Zide Chen
                   ` (10 preceding siblings ...)
  2026-09-18 19:39 ` [PATCH v9 11/12] KVM: x86/pmu: Reject writes to reserved MSR_PERF_METRICS bits Zide Chen
@ 2026-09-18 19:39 ` Zide Chen
  2026-10-23 18:55   ` Jim Mattson
  11 siblings, 1 reply; 20+ messages in thread
From: Zide Chen @ 2026-09-18 19:39 UTC (permalink / raw)
  To: Sean Christopherson, Paolo Bonzini, Peter Zijlstra
  Cc: kvm, Jim Mattson, Andi Kleen, Stephane Eranian, linux-kernel,
	Mingwei Zhang, Zide Chen, Das Sandipan, Shukla Manali, Dapeng Mi,
	Xudong Hao

Add PERF_CAP_RDPMC_METRICS_CLEAR (IA32_PERF_CAPABILITIES bit 19)
support. When set, and IA32_FIXED_CTR_CTRL.METRICS_CLEAR_EN (bit 14)
is also set, RDPMC of PERF_METRICS clears both PERF_METRICS and fixed
counter 3 (SLOTS) after the read.

Advertise the capability to guests only when the mediated PMU is
enabled and the host reports it. Unmask FIXED_CTR_CTRL bit 14 in
intel_pmu_refresh(), scoped to fixed counter 3 only via
intel_fixed_bits_by_idx().

For the mediated PMU, METRICS_CLEAR_EN takes effect natively via
passthrough. But kvm_need_rdpmc_intercept() can still force
interception for unrelated reasons, so intel_emulate_rdpmc() also
replicates the clear-on-read behavior for consistency.

Signed-off-by: Zide Chen <zide.chen@intel.com>
---
v9: new patch.
---
 arch/x86/include/asm/msr-index.h |  1 +
 arch/x86/kvm/vmx/pmu_intel.c     | 28 ++++++++++++++++++++++++++++
 arch/x86/kvm/vmx/vmx.c           |  6 +++++-
 3 files changed, 34 insertions(+), 1 deletion(-)

diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h
index 8fad944197fb..5851edcdda05 100644
--- a/arch/x86/include/asm/msr-index.h
+++ b/arch/x86/include/asm/msr-index.h
@@ -333,6 +333,7 @@
 #define PERF_CAP_PEBS_BASELINE		BIT_ULL(14)
 #define PERF_CAP_PERF_METRICS		BIT_ULL(15)
 #define PERF_CAP_PEBS_TIMING_INFO	BIT_ULL(17)
+#define PERF_CAP_RDPMC_METRICS_CLEAR	BIT_ULL(19)
 #define PERF_CAP_PEBS_MASK		(PERF_CAP_PEBS_TRAP | PERF_CAP_ARCH_REG | \
 					 PERF_CAP_PEBS_FORMAT | PERF_CAP_PEBS_BASELINE | \
 					 PERF_CAP_PEBS_TIMING_INFO)
diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c
index 98e2fb80347a..3e5e8bae67ab 100644
--- a/arch/x86/kvm/vmx/pmu_intel.c
+++ b/arch/x86/kvm/vmx/pmu_intel.c
@@ -36,6 +36,9 @@
 #define INTEL_RDPMC_TYPE_MASK	GENMASK(31, 16)
 #define INTEL_RDPMC_INDEX_MASK	GENMASK(15, 0)
 
+/* Index of the SLOTS fixed counter relative to the first fixed counter. */
+#define INTEL_FIXED_SLOTS_IDX	(INTEL_PMC_IDX_FIXED_SLOTS - INTEL_PMC_IDX_FIXED)
+
 #define MSR_PMC_FULL_WIDTH_BIT      (MSR_IA32_PMC0 - MSR_IA32_PERFCTR0)
 
 static struct lbr_desc *vcpu_to_lbr_desc(struct kvm_vcpu *vcpu)
@@ -86,6 +89,14 @@ static void reprogram_fixed_counters(struct kvm_pmu *pmu, u64 data)
 	}
 }
 
+static bool intel_pmu_metrics_clear_enabled(struct kvm_pmu *pmu)
+{
+	u8 fixed_ctr_ctrl = fixed_ctrl_field(pmu->fixed_ctr_ctrl,
+					     INTEL_FIXED_SLOTS_IDX);
+
+	return fixed_ctr_ctrl & INTEL_FIXED_3_METRICS_CLEAR;
+}
+
 static int intel_emulate_rdpmc(struct kvm_vcpu *vcpu, unsigned int idx,
 			       u64 *data)
 {
@@ -138,6 +149,18 @@ static int intel_emulate_rdpmc(struct kvm_vcpu *vcpu, unsigned int idx,
 			return 1;
 
 		*data = pmu->perf_metrics;
+
+		/*
+		 * Per the SDM, RDPMC of PERF_METRICS clears both PERF_METRICS
+		 * and the SLOTS fixed counter when Metrics Clear Mode is
+		 * enabled via INTEL_FIXED_3_METRICS_CLEAR in FIXED_CTR_CTRL.
+		 */
+		pmc = kvm_pmc_idx_to_pmc(pmu, INTEL_PMC_IDX_FIXED_SLOTS);
+		if (pmc && intel_pmu_metrics_clear_enabled(pmu)) {
+			pmu->perf_metrics = 0;
+			pmc_write_counter(pmc, 0);
+		}
+
 		return 0;
 	default:
 		return 1;
@@ -601,6 +624,11 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu)
 						 INTEL_FIXED_0_USER |
 						 INTEL_FIXED_0_ENABLE_PMI);
 
+	if (perf_capabilities & PERF_CAP_RDPMC_METRICS_CLEAR)
+		pmu->fixed_ctr_ctrl_rsvd &=
+			~intel_fixed_bits_by_idx(INTEL_FIXED_SLOTS_IDX,
+						 INTEL_FIXED_3_METRICS_CLEAR);
+
 	counter_rsvd = ~((BIT_ULL(pmu->nr_arch_gp_counters) - 1) |
 			 ((BIT_ULL(pmu->nr_arch_fixed_counters) - 1) << KVM_FIXED_PMC_BASE_IDX));
 	pmu->global_ctrl_rsvd = counter_rsvd;
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index 372de66365ab..df02372c70e3 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -2616,6 +2616,9 @@ int vmx_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
 			if (!cpuid_model_is_consistent(vcpu))
 				return 1;
 		}
+		if ((data & PERF_CAP_RDPMC_METRICS_CLEAR) &&
+		    !(data & PERF_CAP_PERF_METRICS))
+			return 1;
 		ret = kvm_set_msr_common(vcpu, msr_info);
 		break;
 
@@ -7999,7 +8002,8 @@ static __init u64 vmx_get_perf_capabilities(void)
 	}
 
 	if (enable_mediated_pmu)
-		perf_cap |= kvm_host.perf_capabilities & PERF_CAP_PERF_METRICS;
+		perf_cap |= kvm_host.perf_capabilities &
+			    (PERF_CAP_PERF_METRICS | PERF_CAP_RDPMC_METRICS_CLEAR);
 
 	return perf_cap;
 }
-- 
2.55.0


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

* Re: [PATCH v9 09/12] perf/x86: Add INTEL_TD_METRIC_FIELD_{BITS,MASK} constants
  2026-09-18 19:39 ` [PATCH v9 09/12] perf/x86: Add INTEL_TD_METRIC_FIELD_{BITS,MASK} constants Zide Chen
@ 2026-09-21  7:01   ` Mi, Dapeng
  0 siblings, 0 replies; 20+ messages in thread
From: Mi, Dapeng @ 2026-09-21  7:01 UTC (permalink / raw)
  To: Zide Chen, Sean Christopherson, Paolo Bonzini, Peter Zijlstra
  Cc: kvm, Jim Mattson, Andi Kleen, Stephane Eranian, linux-kernel,
	Mingwei Zhang, Das Sandipan, Shukla Manali, Xudong Hao


On 9/19/2026 3:39 AM, Zide Chen wrote:
> Replace the hard coded field width (8) and the equivalent 0xff mask
> used for PERF_METRICS MSR field access with named constants
> INTEL_TD_METRIC_FIELD_{BITS,MASK}.
>
> INTEL_TD_METRIC_FIELD_BITS will be used by a subsequent KVM patch to
> validat reserved bits in the PERF_METRICS MSR.
>
> No functional change intended.
>
> Signed-off-by: Zide Chen <zide.chen@intel.com>
> Cc: Peter Zijlstra <peterz@infradead.org>
> ---
> v9: new patch.
> ---
>  arch/x86/events/intel/core.c      | 8 +++++---
>  arch/x86/include/asm/perf_event.h | 4 ++++
>  2 files changed, 9 insertions(+), 3 deletions(-)
>
> diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c
> index cc13164d948f..cbc9a179f2e5 100644
> --- a/arch/x86/events/intel/core.c
> +++ b/arch/x86/events/intel/core.c
> @@ -3159,6 +3159,7 @@ DEFINE_STATIC_CALL(intel_pmu_set_topdown_event_period, x86_perf_event_set_period
>  
>  static inline u64 icl_get_metrics_event_value(u64 metric, u64 slots, int idx)
>  {
> +	int shift = (idx - INTEL_PMC_IDX_METRIC_BASE) * INTEL_TD_METRIC_FIELD_BITS;
>  	u32 val;
>  
>  	/*
> @@ -3166,8 +3167,8 @@ static inline u64 icl_get_metrics_event_value(u64 metric, u64 slots, int idx)
>  	 * summing up to 0xff.
>  	 * slots-in-metric = (Metric / 0xff) * slots
>  	 */
> -	val = (metric >> ((idx - INTEL_PMC_IDX_METRIC_BASE) * 8)) & 0xff;
> -	return  mul_u64_u32_div(slots, val, 0xff);
> +	val = (metric >> shift) & INTEL_TD_METRIC_FIELD_MASK;
> +	return mul_u64_u32_div(slots, val, INTEL_TD_METRIC_FIELD_MASK);
>  }
>  
>  static u64 icl_get_topdown_value(struct perf_event *event,
> @@ -4742,7 +4743,8 @@ static int core_pmu_hw_config(struct perf_event *event)
>  }
>  
>  #define INTEL_TD_METRIC_AVAILABLE_MAX	(INTEL_TD_METRIC_RETIRING + \
> -					 ((x86_pmu.num_topdown_events - 1) << 8))
> +					 ((x86_pmu.num_topdown_events - 1) << \
> +					  INTEL_TD_METRIC_FIELD_BITS))

Sashiko comments,

"

Is it safe to replace the UMASK shift here with the hardware MSR metric
field width?
The original << 8 was used to shift the metric ID into the UMASK field
(bits 8-15) of the software perf event configuration (attr.config).
INTEL_TD_METRIC_FIELD_BITS represents the hardware width of metrics within
the PERF_METRICS MSR.
When evaluated in is_available_metric_event() during perf_event_open(), this
currently functions because both values are 8. However, conflating a software
API layout offset with a hardware register field width could cause this macro
to calculate an invalid configuration mask if the MSR field width changes in
future hardware.

"

It looks reasonable, we either don't change this, or define a macro to
represent the UMASK shift. Thanks.



>  
>  static bool is_available_metric_event(struct perf_event *event)
>  {
> diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h
> index 1eb13673e889..ceb6188e5217 100644
> --- a/arch/x86/include/asm/perf_event.h
> +++ b/arch/x86/include/asm/perf_event.h
> @@ -418,6 +418,10 @@ static inline bool use_fixed_pseudo_encoding(u64 code)
>  #define INTEL_TD_METRIC_MAX			INTEL_TD_METRIC_MEM_BOUND
>  #define INTEL_TD_METRIC_NUM			8
>  
> +/* Width, in bits, of each metric's field within the PERF_METRICS MSR. */
> +#define INTEL_TD_METRIC_FIELD_BITS		8
> +#define INTEL_TD_METRIC_FIELD_MASK		GENMASK_ULL(INTEL_TD_METRIC_FIELD_BITS - 1, 0)
> +
>  #define INTEL_TD_CFG_METRIC_CLEAR_BIT		0
>  #define INTEL_TD_CFG_METRIC_CLEAR		BIT_ULL(INTEL_TD_CFG_METRIC_CLEAR_BIT)
>  

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

* Re: [PATCH v9 10/12] perf/x86: Expose number of Topdown metric events to KVM
  2026-09-18 19:39 ` [PATCH v9 10/12] perf/x86: Expose number of Topdown metric events to KVM Zide Chen
@ 2026-09-21  7:06   ` Mi, Dapeng
  0 siblings, 0 replies; 20+ messages in thread
From: Mi, Dapeng @ 2026-09-21  7:06 UTC (permalink / raw)
  To: Zide Chen, Sean Christopherson, Paolo Bonzini, Peter Zijlstra
  Cc: kvm, Jim Mattson, Andi Kleen, Stephane Eranian, linux-kernel,
	Mingwei Zhang, Das Sandipan, Shukla Manali, Xudong Hao

Reviewed-by: Dapeng Mi <dapeng1.mi@linux.intel.com>

On 9/19/2026 3:39 AM, Zide Chen wrote:
> The number of Topdown metric events varies by CPU: ICL/TGL/RKL
> support 4, while SPR/GNR/DMR and some hybrid CPUs support 8.
>
> Add num_topdown_events to struct x86_pmu_capability and populate it
> in perf_get_x86_pmu_capability() so that KVM can determine how many
> PERF_METRICS fields are valid on the host CPU and correctly identify
> the reserved bits in IA32_PERF_METRICS.
>
> Signed-off-by: Zide Chen <zide.chen@intel.com>
> Cc: Peter Zijlstra <peterz@infradead.org>
> ---
> v9: new patch
> ---
>  arch/x86/events/core.c            | 1 +
>  arch/x86/include/asm/perf_event.h | 1 +
>  2 files changed, 2 insertions(+)
>
> diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c
> index 8b3ea0adb965..01c1c9447f30 100644
> --- a/arch/x86/events/core.c
> +++ b/arch/x86/events/core.c
> @@ -3164,6 +3164,7 @@ void perf_get_x86_pmu_capability(struct x86_pmu_capability *cap)
>  	cap->events_mask_len	= x86_pmu.events_mask_len;
>  	cap->pebs_ept		= x86_pmu.pebs_ept;
>  	cap->mediated		= !!(pmu.capabilities & PERF_PMU_CAP_MEDIATED_VPMU);
> +	cap->num_topdown_events	= x86_pmu.num_topdown_events;
>  }
>  EXPORT_SYMBOL_FOR_KVM(perf_get_x86_pmu_capability);
>  
> diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h
> index ceb6188e5217..3c02389b2ea6 100644
> --- a/arch/x86/include/asm/perf_event.h
> +++ b/arch/x86/include/asm/perf_event.h
> @@ -306,6 +306,7 @@ struct x86_pmu_capability {
>  	int		bit_width_fixed;
>  	unsigned int	events_mask;
>  	int		events_mask_len;
> +	int		num_topdown_events;
>  	unsigned int	pebs_ept	:1;
>  	unsigned int	mediated	:1;
>  };

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

* Re: [PATCH v9 11/12] KVM: x86/pmu: Reject writes to reserved MSR_PERF_METRICS bits
  2026-09-18 19:39 ` [PATCH v9 11/12] KVM: x86/pmu: Reject writes to reserved MSR_PERF_METRICS bits Zide Chen
@ 2026-09-21  7:11   ` Mi, Dapeng
  2026-10-23 18:45   ` Jim Mattson
  1 sibling, 0 replies; 20+ messages in thread
From: Mi, Dapeng @ 2026-09-21  7:11 UTC (permalink / raw)
  To: Zide Chen, Sean Christopherson, Paolo Bonzini, Peter Zijlstra
  Cc: kvm, Jim Mattson, Andi Kleen, Stephane Eranian, linux-kernel,
	Mingwei Zhang, Das Sandipan, Shukla Manali, Xudong Hao

LGTM. Thanks.

Reviewed-by: Dapeng Mi <dapeng1.mi@linux.intel.com>

On 9/19/2026 3:39 AM, Zide Chen wrote:
> When the host CPU supports only 4 Topdown metrics, bits [63:32] of
> MSR_PERF_METRICS are reserved.
>
> Derive pmu->perf_metrics_rsvd from the number of supported Topdown
> metrics and use it to validate writes. Reject any attempts to set
> reserved bits in MSR_PERF_METRICS.
>
> Signed-off-by: Zide Chen <zide.chen@intel.com>
> ---
> v9: new patch.
> ---
>  arch/x86/include/asm/kvm_host.h |  1 +
>  arch/x86/kvm/pmu.c              |  1 +
>  arch/x86/kvm/vmx/pmu_intel.c    | 18 +++++++++++++++++-
>  3 files changed, 19 insertions(+), 1 deletion(-)
>
> diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
> index 052bf377dedb..31454ecee371 100644
> --- a/arch/x86/include/asm/kvm_host.h
> +++ b/arch/x86/include/asm/kvm_host.h
> @@ -585,6 +585,7 @@ struct kvm_pmu {
>  	u64 reserved_bits;
>  	u64 raw_event_mask;
>  	u64 perf_metrics;
> +	u64 perf_metrics_rsvd;
>  	struct kvm_pmc gp_counters[KVM_MAX_NR_GP_COUNTERS];
>  	struct kvm_pmc fixed_counters[KVM_MAX_NR_FIXED_COUNTERS];
>  
> diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c
> index 0df283a169b6..41f2d31c495f 100644
> --- a/arch/x86/kvm/pmu.c
> +++ b/arch/x86/kvm/pmu.c
> @@ -1006,6 +1006,7 @@ void kvm_pmu_refresh(struct kvm_vcpu *vcpu)
>  	pmu->fixed_ctr_ctrl_rsvd = ~0ull;
>  	pmu->pebs_enable_rsvd = ~0ull;
>  	pmu->pebs_data_cfg_rsvd = ~0ull;
> +	pmu->perf_metrics_rsvd = ~0ull;
>  	bitmap_zero(pmu->all_valid_pmc_idx, X86_PMC_IDX_MAX);
>  
>  	if (!vcpu->kvm->arch.enable_pmu)
> diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c
> index fab0891ad46a..98e2fb80347a 100644
> --- a/arch/x86/kvm/vmx/pmu_intel.c
> +++ b/arch/x86/kvm/vmx/pmu_intel.c
> @@ -417,6 +417,9 @@ static int intel_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
>  			reprogram_fixed_counters(pmu, data);
>  		break;
>  	case MSR_PERF_METRICS:
> +		if (data & pmu->perf_metrics_rsvd)
> +			return 1;
> +
>  		pmu->perf_metrics = data;
>  		break;
>  	case MSR_IA32_PEBS_ENABLE:
> @@ -601,9 +604,22 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu)
>  	counter_rsvd = ~((BIT_ULL(pmu->nr_arch_gp_counters) - 1) |
>  			 ((BIT_ULL(pmu->nr_arch_fixed_counters) - 1) << KVM_FIXED_PMC_BASE_IDX));
>  	pmu->global_ctrl_rsvd = counter_rsvd;
> -	if (perf_capabilities & PERF_CAP_PERF_METRICS)
> +	if (perf_capabilities & PERF_CAP_PERF_METRICS) {
>  		pmu->global_ctrl_rsvd &= ~GLOBAL_CTRL_EN_PERF_METRICS;
>  
> +		/*
> +		 * PERF_METRICS has one 8-bit field per metric. At 8 metrics,
> +		 * skip computing the mask and set perf_metrics_rsvd to 0
> +		 * directly, since BIT_ULL(64) is undefined.
> +		 */
> +		if (kvm_pmu_cap.num_topdown_events < INTEL_TD_METRIC_NUM)
> +			pmu->perf_metrics_rsvd =
> +				~(BIT_ULL(kvm_pmu_cap.num_topdown_events *
> +					  INTEL_TD_METRIC_FIELD_BITS) - 1);
> +		else
> +			pmu->perf_metrics_rsvd = 0;
> +	}
> +
>  	/*
>  	 * GLOBAL_STATUS and GLOBAL_OVF_CONTROL (a.k.a. GLOBAL_STATUS_RESET)
>  	 * share reserved bit definitions.  The kernel just happens to use

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

* Re: [PATCH v9 05/12] KVM: x86/pmu: Support PERF_METRICS MSR in mediated vPMU
  2026-09-18 19:39 ` [PATCH v9 05/12] KVM: x86/pmu: Support PERF_METRICS MSR in mediated vPMU Zide Chen
@ 2026-10-23 18:27   ` Jim Mattson
  0 siblings, 0 replies; 20+ messages in thread
From: Jim Mattson @ 2026-10-23 18:27 UTC (permalink / raw)
  To: Zide Chen
  Cc: Sean Christopherson, Paolo Bonzini, Peter Zijlstra, kvm,
	Andi Kleen, Stephane Eranian, linux-kernel, Mingwei Zhang,
	Das Sandipan, Shukla Manali, Dapeng Mi, Xudong Hao

On Fri, Sep 18, 2026 at 12:39 PM Zide Chen <zide.chen@intel.com> wrote:
>
> +static inline bool kvm_vcpu_has_perf_metrics(struct kvm_vcpu *vcpu)
> +{
> +	return kvm_vcpu_get_perf_caps(vcpu) & PERF_CAP_PERF_METRICS;
> +}

The SDM, volume 3, section 22.3.9.3, says:

> IA32_PERF_GLOBAL_CTRL. EN_PERF_METRICS[48]: If this bit is set *and*
> fixed-function performance-monitoring counter 3 is enabled, built-in
> performance metrics are enabled. [emphasis mine]

and

> The values in MSR_PERF_METRICS are derived from fixed-function
> performance-monitoring counter 3.

Hence, a dependency on fixed counter 3 belongs in this predicate.
Perhaps something like:

	static inline bool kvm_pmu_has_fixed_counter(struct kvm_pmu *pmu,
						     unsigned int idx)
	{
		return idx < pmu->nr_arch_fixed_counters;
	}

	static inline bool kvm_vcpu_has_perf_metrics(struct kvm_vcpu *vcpu)
	{
		return (kvm_vcpu_get_perf_caps(vcpu) & PERF_CAP_PERF_METRICS) &&
		       kvm_pmu_has_fixed_counter(vcpu_to_pmu(vcpu),
						 INTEL_PMC_IDX_FIXED_SLOTS -
						 INTEL_PMC_IDX_FIXED);
	}

with intel_pmu_refresh() calling kvm_vcpu_has_perf_metrics() instead of
testing perf_capabilities directly. (nr_arch_fixed_counters has already
been computed at that point; all_valid_pmc_idx has not.)

KVM only supports contiguous fixed counters today (see patch 2), so a
simple comparison against nr_arch_fixed_counters suffices. However, if KVM
ever honors the CPUID.0AH:ECX fixed counter bitmap, a separate helper
keeps that assumption in one place.

As it stands, if userspace sets PERF_CAP_PERF_METRICS for a version 1
vPMU, MSR_PERF_METRICS is passed through (kvm_vcpu_has_mediated_pmu()
only requires a non-zero version), but intel_pmu_refresh() bails out
before it gets to the PERF_METRICS code. With patch 11, that leaves
perf_metrics_rsvd at ~0ull, so a value the guest writes directly to
hardware can be saved with KVM_GET_MSRS, but not restored with
KVM_SET_MSRS.

Since nr_arch_fixed_counters is 0 for a v1 vPMU, the above handles that
case too. It also takes care of the METRICS_CLEAR_EN unmasking without
fixed counter 3 that Sashiko flagged on patch 12, and it obviates the
NULL pmc check in intel_emulate_rdpmc().

[...]

> +	case MSR_PERF_METRICS:
> +		pmu->perf_metrics = data;
> +		break;

On a part with only four metrics (e.g. Ice Lake), userspace can stuff
bits 63:32 into pmu->perf_metrics here, and the unchecked wrmsrq() in
intel_mediated_pmu_load() will then #GP on every VM-entry. Patch 11
isn't an optional follow-on; it needs to be folded into this patch.

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

* Re: [PATCH v9 07/12] KVM: x86/pmu: Emulate RDPMC on performance metrics
  2026-09-18 19:39 ` [PATCH v9 07/12] KVM: x86/pmu: Emulate RDPMC on performance metrics Zide Chen
@ 2026-10-23 18:33   ` Jim Mattson
  0 siblings, 0 replies; 20+ messages in thread
From: Jim Mattson @ 2026-10-23 18:33 UTC (permalink / raw)
  To: Zide Chen
  Cc: Sean Christopherson, Paolo Bonzini, Peter Zijlstra, kvm,
	Andi Kleen, Stephane Eranian, linux-kernel, Mingwei Zhang,
	Das Sandipan, Shukla Manali, Dapeng Mi, Xudong Hao

On Fri, Sep 18, 2026 at 12:50 PM Zide Chen <zide.chen@intel.com> wrote:
>
> If the host has the PERF_METRICS capability but it's not present on
> the guest, RDPMC interception must be enabled and KVM should inject
> an #GP when the guest attempts a PERF_METRICS RDPMC.
>
> If the guest has PERF_METRICS but RDPMC interception is enabled for
> other reasons, KVM needs to emulate RDPMC with type 0x2000.
>
> Signed-off-by: Zide Chen <zide.chen@intel.com>
> Reviewed-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
Reviewed-by: Jim Mattson <jmattson@google.com>

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

* Re: [PATCH v9 11/12] KVM: x86/pmu: Reject writes to reserved MSR_PERF_METRICS bits
  2026-09-18 19:39 ` [PATCH v9 11/12] KVM: x86/pmu: Reject writes to reserved MSR_PERF_METRICS bits Zide Chen
  2026-09-21  7:11   ` Mi, Dapeng
@ 2026-10-23 18:45   ` Jim Mattson
  1 sibling, 0 replies; 20+ messages in thread
From: Jim Mattson @ 2026-10-23 18:45 UTC (permalink / raw)
  To: Zide Chen
  Cc: Sean Christopherson, Paolo Bonzini, Peter Zijlstra, kvm,
	Andi Kleen, Stephane Eranian, linux-kernel, Mingwei Zhang,
	Das Sandipan, Shukla Manali, Dapeng Mi, Xudong Hao

On Fri, Sep 18, 2026 at 12:39 PM Zide Chen <zide.chen@intel.com> wrote:
>
> +		if (kvm_pmu_cap.num_topdown_events < INTEL_TD_METRIC_NUM)
> +			pmu->perf_metrics_rsvd =
> +				~(BIT_ULL(kvm_pmu_cap.num_topdown_events *
> +					  INTEL_TD_METRIC_FIELD_BITS) - 1);
> +		else
> +			pmu->perf_metrics_rsvd = 0;

perf only sets x86_pmu.num_topdown_events for the models it knows about
(Ice Lake and intel_pmu_init_glc() users). On a non-hybrid part that
perf treats as "generic_arch_v5+", PERF_CAPABILITIES.PERF_METRICS_AVAILABLE
can be set while num_topdown_events is 0. (intel_pmu_init() even sets
GLOBAL_CTRL_EN_PERF_METRICS in intel_ctrl in that case.)

vmx_get_perf_capabilities() will still advertise PERF_CAP_PERF_METRICS,
but perf_metrics_rsvd will be ~0ull. The guest writes PERF_METRICS
directly, and the hardware updates it on its own, so KVM_GET_MSRS can
return a non-zero value that KVM_SET_MSRS will then reject. That breaks
save/restore. (The selftest's write of 0xdeaddead would also fail.)

Shouldn't vmx_get_perf_capabilities() withhold PERF_CAP_PERF_METRICS
(and PERF_CAP_RDPMC_METRICS_CLEAR) when kvm_pmu_cap.num_topdown_events
is 0? The SDM, volume 3, section 22.8, says:

> IA32_PERF_CAPABILITIES.PERF_METRICS_AVAILABLE[15]: If set, indicates
> that the architecture provides built in support for [four] TMA L1
> metrics through the PERF_METRICS MSR.

So, four could be a fallback, but if the part actually implements
eight, the upper fields would break the round trip in the same way.

Also, since the guest's own writes go straight to hardware, this check
really exists to keep the wrmsrq() in intel_mediated_pmu_load() from
faulting on a bogus userspace value. The commit message should say so.

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

* Re: [PATCH v9 12/12] KVM: x86/pmu: Support RDPMC Metrics Clear Mode
  2026-09-18 19:39 ` [PATCH v9 12/12] KVM: x86/pmu: Support RDPMC Metrics Clear Mode Zide Chen
@ 2026-10-23 18:55   ` Jim Mattson
  0 siblings, 0 replies; 20+ messages in thread
From: Jim Mattson @ 2026-10-23 18:55 UTC (permalink / raw)
  To: Zide Chen
  Cc: Sean Christopherson, Paolo Bonzini, Peter Zijlstra, kvm,
	Andi Kleen, Stephane Eranian, linux-kernel, Mingwei Zhang,
	Das Sandipan, Shukla Manali, Dapeng Mi, Xudong Hao

On Fri, Sep 18, 2026 at 12:39 PM Zide Chen <zide.chen@intel.com> wrote:
>
> +		pmc = kvm_pmc_idx_to_pmc(pmu, INTEL_PMC_IDX_FIXED_SLOTS);
> +		if (pmc && intel_pmu_metrics_clear_enabled(pmu)) {

If kvm_vcpu_has_perf_metrics() requires fixed counter 3 (see my reply
to 5/12), pmc can't be NULL here.

[...]

> +	if (perf_capabilities & PERF_CAP_RDPMC_METRICS_CLEAR)
> +		pmu->fixed_ctr_ctrl_rsvd &=
> +			~intel_fixed_bits_by_idx(INTEL_FIXED_SLOTS_IDX,
> +						 INTEL_FIXED_3_METRICS_CLEAR);

This should be predicated on the guest actually having fixed counter 3
(and PERF_METRICS). intel_pmu_enable_fixed_counter_bits() only unmasks
bits for counters below nr_arch_fixed_counters.

[...]

> +		if ((data & PERF_CAP_RDPMC_METRICS_CLEAR) &&
> +		    !(data & PERF_CAP_PERF_METRICS))
> +			return 1;

If IA32_FIXED_CTR_CTRL.METRICS_CLEAR_EN[14] is only unmasked when the
vCPU has PERF_METRICS, is this check still necessary? KVM generally
doesn't police userspace's feature combinations unless it has to.

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

end of thread, other threads:[~2026-10-23 18:55 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-18 19:39 [PATCH v9 00/12] KVM: x86/pmu: Add hardware Topdown metrics support Zide Chen
2026-09-18 19:39 ` [PATCH v9 01/12] KVM: x86/pmu: Do not map fixed counters >= 3 to generic perf events Zide Chen
2026-09-18 19:39 ` [PATCH v9 02/12] KVM: x86/pmu: Support Intel fixed counter 3 on mediated vPMU Zide Chen
2026-09-18 19:39 ` [PATCH v9 03/12] KVM: x86/pmu: Rename and move vcpu_get_perf_capabilities() to pmu.h Zide Chen
2026-09-18 19:39 ` [PATCH v9 04/12] KVM: x86/pmu: Snapshot host IA32_PERF_CAPABILITIES in kvm_host Zide Chen
2026-09-18 19:39 ` [PATCH v9 05/12] KVM: x86/pmu: Support PERF_METRICS MSR in mediated vPMU Zide Chen
2026-10-23 18:27   ` Jim Mattson
2026-09-18 19:39 ` [PATCH v9 06/12] KVM: x86/pmu: Move RDPMC emulation into per-vendor callbacks Zide Chen
2026-09-18 19:39 ` [PATCH v9 07/12] KVM: x86/pmu: Emulate RDPMC on performance metrics Zide Chen
2026-10-23 18:33   ` Jim Mattson
2026-09-18 19:39 ` [PATCH v9 08/12] KVM: selftests: Add PERF_METRICS and fixed counter 3 tests Zide Chen
2026-09-18 19:39 ` [PATCH v9 09/12] perf/x86: Add INTEL_TD_METRIC_FIELD_{BITS,MASK} constants Zide Chen
2026-09-21  7:01   ` Mi, Dapeng
2026-09-18 19:39 ` [PATCH v9 10/12] perf/x86: Expose number of Topdown metric events to KVM Zide Chen
2026-09-21  7:06   ` Mi, Dapeng
2026-09-18 19:39 ` [PATCH v9 11/12] KVM: x86/pmu: Reject writes to reserved MSR_PERF_METRICS bits Zide Chen
2026-09-21  7:11   ` Mi, Dapeng
2026-10-23 18:45   ` Jim Mattson
2026-09-18 19:39 ` [PATCH v9 12/12] KVM: x86/pmu: Support RDPMC Metrics Clear Mode Zide Chen
2026-10-23 18:55   ` Jim Mattson

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

all inboxes | Powered by JetHome®