kvm.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v8 0/8] KVM: x86/pmu: Add hardware Topdown metrics support
@ 2026-08-17 18:31 Zide Chen
  2026-08-17 18:31 ` [PATCH v8 1/8] KVM: x86/pmu: Do not map fixed counters >= 3 to generic perf events Zide Chen
                   ` (7 more replies)
  0 siblings, 8 replies; 17+ messages in thread
From: Zide Chen @ 2026-08-17 18:31 UTC (permalink / raw)
  To: Sean Christopherson, Paolo Bonzini
  Cc: kvm, Jim Mattson, Andi Kleen, linux-kernel, Mingwei Zhang,
	Zide Chen, Das Sandipan, Shukla Manali, Dapeng Mi, Xudong Hao

The Topdown Microarchitecture Analysis (TMA) method is a structured
approach for identifying performance bottlenecks in out-of-order
processors.

Currently, guests support the TMA method by collecting Topdown events
using GP counters, which may trigger multiplexing.  To free up scarce
GP counters, eliminate multiplexing-induced skew, and obtain coherent
Topdown metric ratios, it is desirable to expose fixed counter 3 and
the IA32_PERF_METRICS MSR to guests.

Several attempts have been made to virtualize this under the legacy
vPMU model [1][2][3], but they were unsuccessful.  With the new mediated
vPMU, enabling TMA support in guests becomes much simpler.  It avoids
invasive changes to the perf core, eliminates CPU pinning and
fixed-counter affinity issues, and reduces the large overhead of
trapping and emulating MSR accesses.

[1] https://lore.kernel.org/kvm/20231031090613.2872700-1-dapeng1.mi@linux.intel.com/
[2] https://lore.kernel.org/all/20230927033124.1226509-1-dapeng1.mi@linux.intel.com/T/
[3] https://lwn.net/ml/linux-kernel/20221212125844.41157-1-likexu@tencent.com/

Tested on a Sapphire Rapids. Without this series, only raw topdown.*_slots
events work in the guest, and metric events (e.g. cpu/topdown-bad-spec/) are
not available.

With this series, metric events are visible in the guest.  Run this
command on both host and guest:

$ perf stat --topdown --no-metric-only -- taskset -c 2 perf bench sched messaging

Host results:

     Total time: 1.500 [sec]

 Performance counter stats for 'taskset -c 2 perf bench sched messaging':

     4,266,060,558      TOPDOWN.SLOTS:u              #     32.0 %  tma_frontend_bound
                                                     #      5.2 %  tma_bad_speculation
       588,397,905      topdown-retiring:u           #     13.8 %  tma_retiring
                                                     #     49.0 %  tma_backend_bound
     1,376,283,990      topdown-fe-bound:u
     2,096,827,304      topdown-be-bound:u
       217,425,841      topdown-bad-spec:u
         5,050,520      INT_MISC.UOP_DROPPING:u

Rebased to kvm-x86/next: 1b731e5ded480bd1

v8 changes:
- patch 5/8: Intercept global ctrl MSR if host supports PerfMetrics but
  not enabled in the guest.
- patch 3/9: Remove include "cpuid.h" from pmu_intel.h.
- Drop patch v7 7/9 to audit CPU RDPMC encodings.
v7 changes:
- Add "reviewed-by" tags.
- patch 5/8: Optimize bit 48 handling in global_status_rsvd;
  document the PERF_METRICS/FC3 write-order exception;
  add nested_vmx_merge_msr_bitmaps_rw().
- patch 7/9: new patch to restrict RDPMC passthrough to known CPUs.
- patch 9/9: Use RDPMC instead of RDMSR in PERF_METRICS sanity test.
v6 changes:
- patch 6/8: New patch to refactor rdpmc emulation code.
- patch 7/8: More strict handling of RDPMC ECX argument.
- patch 8/8: Move perf metrics out of test_arch_events().
- patch 2/8: Minor fix of comments.
v5 changes:
- patch 3,5,6/7: new patches to handle RDPMC on metrics.
- patch 6/7: remove host_initiated check.
v4 changes:
- patch 3/4: Remove WARN_ON_ONCE() and simply reject the guest accesses
  by checking host_initiated. (Sashiko)
- patch 3/4: Passthru MSR_PERF_METRICS only if has_mediated_pmu is
  true. (Sashiko)
v3 changes:
- patch 2/4: Move the non-contiguous counter filter code to pmu.c (Dapeng)
- patch 3/4: Replace WARN_ON() with WARN_ON_ONCE(). (Dapeng)
- patch 4/4: Change abs() with explicit bounds (sum >= 0xfd && sum <= 0x102).
- Minor comment cleanups.
v2 changes:
- As suggested by Dapeng, implement a new selftest patch.
- Don't advertise fixed counter 3 if the host doesn't support it.
- Minor change in patch 1 to remove a magic number.

v7: https://lore.kernel.org/kvm/20260727192131.582105-1-zide.chen@intel.com/
v6: https://lore.kernel.org/kvm/20260629231938.15129-1-zide.chen@intel.com/T/#t
v5: https://lore.kernel.org/kvm/20260625034555.141453-1-zide.chen@intel.com/
v4: https://lore.kernel.org/kvm/20260623041927.178256-1-zide.chen@intel.com/
v3: https://lore.kernel.org/kvm/20260615230118.50718-1-zide.chen@intel.com/T/#t
v2: https://lore.kernel.org/kvm/20260423174639.56149-1-zide.chen@intel.com/T/#u
v1: https://lore.kernel.org/kvm/20260226230606.146532-1-zide.chen@intel.com/T/#t
QEMU:
https://lore.kernel.org/qemu-devel/20260604025546.19378-7-zide.chen@intel.com/

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 (5):
  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

 arch/x86/include/asm/kvm-x86-pmu-ops.h        |  2 +-
 arch/x86/include/asm/kvm_host.h               |  4 +-
 arch/x86/include/asm/msr-index.h              |  1 +
 arch/x86/kvm/msrs.c                           | 10 +-
 arch/x86/kvm/pmu.c                            | 36 +++++--
 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                  | 93 ++++++++++++++-----
 arch/x86/kvm/vmx/pmu_intel.h                  | 12 +--
 arch/x86/kvm/vmx/vmx.c                        | 15 +--
 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     | 90 +++++++++++++++++-
 15 files changed, 239 insertions(+), 64 deletions(-)

-- 
2.55.0


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

* [PATCH v8 1/8] KVM: x86/pmu: Do not map fixed counters >= 3 to generic perf events
  2026-08-17 18:31 [PATCH v8 0/8] KVM: x86/pmu: Add hardware Topdown metrics support Zide Chen
@ 2026-08-17 18:31 ` Zide Chen
  2026-08-17 18:31 ` [PATCH v8 2/8] KVM: x86/pmu: Support Intel fixed counter 3 on mediated vPMU Zide Chen
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 17+ messages in thread
From: Zide Chen @ 2026-08-17 18:31 UTC (permalink / raw)
  To: Sean Christopherson, Paolo Bonzini
  Cc: kvm, Jim Mattson, Andi Kleen, 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.
v2:
- Replace 3 in "if (index < 3)" with ARRAY_SIZE(fixed_pmc_perf_ids).
---
 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 bfa8612fb450..df51a17b6e17 100644
--- a/arch/x86/kvm/vmx/pmu_intel.c
+++ b/arch/x86/kvm/vmx/pmu_intel.c
@@ -465,11 +465,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,
@@ -478,8 +475,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 related	[flat|nested] 17+ messages in thread

* [PATCH v8 2/8] KVM: x86/pmu: Support Intel fixed counter 3 on mediated vPMU
  2026-08-17 18:31 [PATCH v8 0/8] KVM: x86/pmu: Add hardware Topdown metrics support Zide Chen
  2026-08-17 18:31 ` [PATCH v8 1/8] KVM: x86/pmu: Do not map fixed counters >= 3 to generic perf events Zide Chen
@ 2026-08-17 18:31 ` Zide Chen
  2026-08-17 18:31 ` [PATCH v8 3/8] KVM: x86/pmu: Rename and move vcpu_get_perf_capabilities() to pmu.h Zide Chen
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 17+ messages in thread
From: Zide Chen @ 2026-08-17 18:31 UTC (permalink / raw)
  To: Sean Christopherson, Paolo Bonzini
  Cc: kvm, Jim Mattson, Andi Kleen, 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.
v3:
- Move the non-contiguous counter filter code to pmu.c
v2:
- Don't advertise fixed counter 3 to userspace if the host doesn't
  support it.
---
 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 283847619ff8..8a4e542218a7 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -562,7 +562,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 66fa7140d65d..1fbd8f08a7f0 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,
@@ -2671,7 +2671,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 a7d60c8785cd..913a0ecbf260 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_FIXED_PMC_BASE_IDX .. KVM_FIXED_PMC_BASE_IDX +
+ *                  KVM_MAX_NR_INTEL_FIXED_COUNTERS - 1] <=> 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 related	[flat|nested] 17+ messages in thread

* [PATCH v8 3/8] KVM: x86/pmu: Rename and move vcpu_get_perf_capabilities() to pmu.h
  2026-08-17 18:31 [PATCH v8 0/8] KVM: x86/pmu: Add hardware Topdown metrics support Zide Chen
  2026-08-17 18:31 ` [PATCH v8 1/8] KVM: x86/pmu: Do not map fixed counters >= 3 to generic perf events Zide Chen
  2026-08-17 18:31 ` [PATCH v8 2/8] KVM: x86/pmu: Support Intel fixed counter 3 on mediated vPMU Zide Chen
@ 2026-08-17 18:31 ` Zide Chen
  2026-08-17 18:52   ` sashiko-bot
  2026-08-17 18:31 ` [PATCH v8 4/8] KVM: x86/pmu: Snapshot host IA32_PERF_CAPABILITIES in kvm_host Zide Chen
                   ` (4 subsequent siblings)
  7 siblings, 1 reply; 17+ messages in thread
From: Zide Chen @ 2026-08-17 18:31 UTC (permalink / raw)
  To: Sean Christopherson, Paolo Bonzini
  Cc: kvm, Jim Mattson, Andi Kleen, 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.
v5: new patch.
---
 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 df51a17b6e17..100c072b7fb3 100644
--- a/arch/x86/kvm/vmx/pmu_intel.c
+++ b/arch/x86/kvm/vmx/pmu_intel.c
@@ -190,13 +190,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;
@@ -551,7 +551,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 related	[flat|nested] 17+ messages in thread

* [PATCH v8 4/8] KVM: x86/pmu: Snapshot host IA32_PERF_CAPABILITIES in kvm_host
  2026-08-17 18:31 [PATCH v8 0/8] KVM: x86/pmu: Add hardware Topdown metrics support Zide Chen
                   ` (2 preceding siblings ...)
  2026-08-17 18:31 ` [PATCH v8 3/8] KVM: x86/pmu: Rename and move vcpu_get_perf_capabilities() to pmu.h Zide Chen
@ 2026-08-17 18:31 ` Zide Chen
  2026-08-17 18:31 ` [PATCH v8 5/8] KVM: x86/pmu: Support PERF_METRICS MSR in mediated vPMU Zide Chen
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 17+ messages in thread
From: Zide Chen @ 2026-08-17 18:31 UTC (permalink / raw)
  To: Sean Christopherson, Paolo Bonzini
  Cc: kvm, Jim Mattson, Andi Kleen, 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 8a4e542218a7..d893fa5be838 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -294,6 +294,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 e3bfe6aca1a0..6e9d197982b7 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -8023,14 +8023,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);
@@ -8043,11 +8039,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 d94b59140c45..d349224d2734 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -7083,6 +7083,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 related	[flat|nested] 17+ messages in thread

* [PATCH v8 5/8] KVM: x86/pmu: Support PERF_METRICS MSR in mediated vPMU
  2026-08-17 18:31 [PATCH v8 0/8] KVM: x86/pmu: Add hardware Topdown metrics support Zide Chen
                   ` (3 preceding siblings ...)
  2026-08-17 18:31 ` [PATCH v8 4/8] KVM: x86/pmu: Snapshot host IA32_PERF_CAPABILITIES in kvm_host Zide Chen
@ 2026-08-17 18:31 ` Zide Chen
  2026-08-17 19:04   ` sashiko-bot
  2026-08-17 18:31 ` [PATCH v8 6/8] KVM: x86/pmu: Move RDPMC emulation into per-vendor callbacks Zide Chen
                   ` (2 subsequent siblings)
  7 siblings, 1 reply; 17+ messages in thread
From: Zide Chen @ 2026-08-17 18:31 UTC (permalink / raw)
  To: Sean Christopherson, Paolo Bonzini
  Cc: kvm, Jim Mattson, Andi Kleen, 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>
---
v8:
- Intercept global ctrl MSR if host supports PerfMetrics but not
  enabled in the guest.
v7:
- Clear bit 48 in global_ctrl_rsvd instead of explicitly clearing it in
  global_status_rsvd, allowing it to propagate through the assignment
  from global_ctrl_rsvd.
- Add comments documenting the all-zero PERF_METRICS/FC3 write-order
  exception.
- Add nested_vmx_merge_msr_bitmaps_rw(MSR_PERF_METRICS);
v5:
- Remove host_initiated check in set/get MSR handlers.
v4:
- Remove WARN_ON_ONCE() and simply reject the guest accesses by checking
  host_initiated. (Sashiko)
- Passthru MSR_PERF_METRICS only if has_mediated_pmu is true. (Sashiko)
- Remove the redundant !! in vcpu_has_perf_metrics().
v3:
- Replace WARN_ON() with WARN_ON_ONCE(). (Dapeng)
- Add comments to explain why we don't validate writes on PERF_METRICS.
---
 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     | 34 ++++++++++++++++++++++++++++++++
 arch/x86/kvm/vmx/vmx.c           |  7 +++++++
 8 files changed, 62 insertions(+), 1 deletion(-)

diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index d893fa5be838..7166efdf79ce 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -583,6 +583,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 18c4be75e927..fdcaeb6c8352 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 1fbd8f08a7f0..2b2cc6bf3ce7 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. */
@@ -2608,6 +2608,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 913a0ecbf260..2fddcbc8924d 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 7ed79894d11d..e81b8a5423c8 100644
--- a/arch/x86/kvm/vmx/nested.c
+++ b/arch/x86/kvm/vmx/nested.c
@@ -717,6 +717,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 100c072b7fb3..1fd0480baf68 100644
--- a/arch/x86/kvm/vmx/pmu_intel.c
+++ b/arch/x86/kvm/vmx/pmu_intel.c
@@ -189,6 +189,8 @@ static bool intel_is_valid_msr(struct kvm_vcpu *vcpu, u32 msr)
 	switch (msr) {
 	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;
@@ -346,6 +348,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;
@@ -395,6 +400,14 @@ 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:
+		/*
+		 * Bits [63:32] are ignored on hardware that supports only
+		 * level-1 metrics, but may be valid on hardware that supports
+		 * level-2 metrics. Preserve guest writes verbatim.
+		 */
+		pmu->perf_metrics = data;
+		break;
 	case MSR_IA32_PEBS_ENABLE:
 		if (data & pmu->pebs_enable_rsvd)
 			return 1;
@@ -577,6 +590,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)
@@ -633,6 +648,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);
 }
 
@@ -803,6 +821,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)
@@ -831,6 +852,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 6e9d197982b7..8cb9d642277a 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -4277,6 +4277,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)
@@ -8068,6 +8072,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 related	[flat|nested] 17+ messages in thread

* [PATCH v8 6/8] KVM: x86/pmu: Move RDPMC emulation into per-vendor callbacks
  2026-08-17 18:31 [PATCH v8 0/8] KVM: x86/pmu: Add hardware Topdown metrics support Zide Chen
                   ` (4 preceding siblings ...)
  2026-08-17 18:31 ` [PATCH v8 5/8] KVM: x86/pmu: Support PERF_METRICS MSR in mediated vPMU Zide Chen
@ 2026-08-17 18:31 ` Zide Chen
  2026-08-17 18:31 ` [PATCH v8 7/8] KVM: x86/pmu: Emulate RDPMC on performance metrics Zide Chen
  2026-08-17 18:31 ` [PATCH v8 8/8] KVM: selftests: Add PERF_METRICS and fixed counter 3 tests Zide Chen
  7 siblings, 0 replies; 17+ messages in thread
From: Zide Chen @ 2026-08-17 18:31 UTC (permalink / raw)
  To: Sean Christopherson, Paolo Bonzini
  Cc: kvm, Jim Mattson, Andi Kleen, 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>
---
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 2fddcbc8924d..789624658e8f 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 1fd0480baf68..bbf4afcffafc 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)
@@ -868,7 +867,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 related	[flat|nested] 17+ messages in thread

* [PATCH v8 7/8] KVM: x86/pmu: Emulate RDPMC on performance metrics
  2026-08-17 18:31 [PATCH v8 0/8] KVM: x86/pmu: Add hardware Topdown metrics support Zide Chen
                   ` (5 preceding siblings ...)
  2026-08-17 18:31 ` [PATCH v8 6/8] KVM: x86/pmu: Move RDPMC emulation into per-vendor callbacks Zide Chen
@ 2026-08-17 18:31 ` Zide Chen
  2026-08-17 19:01   ` sashiko-bot
  2026-08-17 18:31 ` [PATCH v8 8/8] KVM: selftests: Add PERF_METRICS and fixed counter 3 tests Zide Chen
  7 siblings, 1 reply; 17+ messages in thread
From: Zide Chen @ 2026-08-17 18:31 UTC (permalink / raw)
  To: Sean Christopherson, Paolo Bonzini
  Cc: kvm, Jim Mattson, Andi Kleen, 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.

For simplicity, Metrics Clear Mode is not supported.

Signed-off-by: Zide Chen <zide.chen@intel.com>
Reviewed-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
---
v8:
- Move kvm_need_perf_metrics_intercept() to patch 5/9.
v6:
- Merge kvm_pmu_rdpmc_metrics() into intel_emulate_rdpmc().
- Reject non-zero index.
v5:
- new patch.
---
 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 789624658e8f..dab0bcabaa8f 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 bbf4afcffafc..3ca61e4b0ba6 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 related	[flat|nested] 17+ messages in thread

* [PATCH v8 8/8] KVM: selftests: Add PERF_METRICS and fixed counter 3 tests
  2026-08-17 18:31 [PATCH v8 0/8] KVM: x86/pmu: Add hardware Topdown metrics support Zide Chen
                   ` (6 preceding siblings ...)
  2026-08-17 18:31 ` [PATCH v8 7/8] KVM: x86/pmu: Emulate RDPMC on performance metrics Zide Chen
@ 2026-08-17 18:31 ` Zide Chen
  2026-08-17 18:57   ` sashiko-bot
  7 siblings, 1 reply; 17+ messages in thread
From: Zide Chen @ 2026-08-17 18:31 UTC (permalink / raw)
  To: Sean Christopherson, Paolo Bonzini
  Cc: kvm, Jim Mattson, Andi Kleen, 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.
v6:
- Move perf metrics test out of test_arch_events(); it doesn't belong
  there, and this also avoids redundant runs of perf metrics test.
- Correct the +/-3 error margin.
v3:
- Slightly reword comment to explain the sum of Topdown metrics
  is close to 100%.
- Change abs() with explicit bounds (sum >= 0xfd && sum <= 0x102)
  for better readability.
v2:
- New patch.
---
 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     | 93 ++++++++++++++++++-
 3 files changed, 92 insertions(+), 5 deletions(-)

diff --git a/tools/arch/x86/include/asm/msr-index.h b/tools/arch/x86/include/asm/msr-index.h
index eff29645719b..e7745e2cd543 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..5abcf21d8612 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,81 @@ 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);
+
+	/* Sanity check after PERF_METRICS disabled. */
+	__asm__ __volatile__("loop ." : "+c"((int){NUM_LOOPS}));
+	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 +739,7 @@ static void test_intel_counters(void)
 	const u64 perf_caps[] = {
 		0,
 		PMU_CAP_FW_WRITES,
+		PERF_CAP_PERF_METRICS,
 	};
 
 	/*
@@ -699,9 +777,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 related	[flat|nested] 17+ messages in thread

* Re: [PATCH v8 3/8] KVM: x86/pmu: Rename and move vcpu_get_perf_capabilities() to pmu.h
  2026-08-17 18:31 ` [PATCH v8 3/8] KVM: x86/pmu: Rename and move vcpu_get_perf_capabilities() to pmu.h Zide Chen
@ 2026-08-17 18:52   ` sashiko-bot
  2026-08-17 21:45     ` Chen, Zide
  0 siblings, 1 reply; 17+ messages in thread
From: sashiko-bot @ 2026-08-17 18:52 UTC (permalink / raw)
  To: Zide Chen; +Cc: kvm

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] Header files pmu.h and pmu_intel.h lack required includes, violating self-contained header practices.
--

--- Patch [3]: [PATCH v8 3/8] KVM: x86/pmu: Rename and move vcpu_get_perf_capabilities() to pmu.h ---
commit 7eb009fb5328b80368e10acda6cfe621b3e1e1c0
Author: Zide Chen <zide.chen@intel.com>

KVM: x86/pmu: Rename and move vcpu_get_perf_capabilities() to pmu.h

This commit renames vcpu_get_perf_capabilities() to kvm_vcpu_get_perf_caps()
and moves it to the common x86 PMU header. This prepares the function to be
called from common x86 code to check the guest's PERF_METRICS capability.

> diff --git a/arch/x86/kvm/pmu.h b/arch/x86/kvm/pmu.h
> index 090c9bbb74f48..82f955e5a4507 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;

[Severity: Low]
Does this file need to include cpuid.h? guest_cpu_cap_has() is used here,
but pmu.h does not include the header that defines it.

> +
> +	return vcpu->arch.perf_capabilities;
> +}
> +
>  void kvm_pmu_deliver_pmi(struct kvm_vcpu *vcpu);
[ ... ]
> diff --git a/arch/x86/kvm/vmx/pmu_intel.h b/arch/x86/kvm/vmx/pmu_intel.h
> index 5d9357640aa18..dd447d8b6fdde 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;

[Severity: Low]
Should pmu_intel.h include pmu.h? It calls kvm_vcpu_get_perf_caps() which
is now defined in pmu.h, but the include appears to be missing.

>  }
>  
>  bool intel_pmu_lbr_is_enabled(struct kvm_vcpu *vcpu);

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260817183143.226156-1-zide.chen@intel.com?part=3

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

* Re: [PATCH v8 8/8] KVM: selftests: Add PERF_METRICS and fixed counter 3 tests
  2026-08-17 18:31 ` [PATCH v8 8/8] KVM: selftests: Add PERF_METRICS and fixed counter 3 tests Zide Chen
@ 2026-08-17 18:57   ` sashiko-bot
  2026-08-17 21:44     ` Chen, Zide
  0 siblings, 1 reply; 17+ messages in thread
From: sashiko-bot @ 2026-08-17 18:57 UTC (permalink / raw)
  To: Zide Chen; +Cc: kvm

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] Unhandled #GP / Selftest Crash when writing 0xdeaddead to MSR_PERF_METRICS.
--

--- Patch [8]: [PATCH v8 8/8] KVM: selftests: Add PERF_METRICS and fixed counter 3 tests ---
commit 19d6869fac1fbdcc63ff54623403cec33b922848
Author: Zide Chen <zide.chen@intel.com>

KVM: selftests: Add PERF_METRICS and fixed counter 3 tests

This commit adds a test case to exercise IA32_PERF_METRICS for Topdown
(TMA) Level 1 metrics and extends the PMU selftest to cover Intel fixed
counter 3.

> diff --git a/tools/testing/selftests/kvm/x86/pmu_counters_test.c b/tools/testing/selftests/kvm/x86/pmu_counters_test.c
> --- a/tools/testing/selftests/kvm/x86/pmu_counters_test.c
> +++ b/tools/testing/selftests/kvm/x86/pmu_counters_test.c
[ ... ]
> +static void __guest_test_perf_metrics(void)
> +{
[ ... ]
> +	/* Sanity check after PERF_METRICS disabled. */
> +	__asm__ __volatile__("loop ." : "+c"((int){NUM_LOOPS}));
> +	GUEST_ASSERT_EQ(rdmsr(MSR_PERF_METRICS), metrics);
> +	wrmsr(MSR_PERF_METRICS, 0xdeaddead);
> +
> +	guest_test_rdpmc(INTEL_RDPMC_METRICS, true, 0xdeaddead);
> +}

[Severity: Medium]
Will this wrmsr(MSR_PERF_METRICS, 0xdeaddead) cause an unhandled #GP and
crash the selftest on hosts with Mediated PMU pass-through?

Writing an architecturally invalid non-zero value to MSR_PERF_METRICS
generates a #GP on real hardware. Under Mediated PMU, KVM configures the
VMCS to pass-through MSR accesses directly to hardware, so this fault
would be injected into the guest.

If the hardware silently discards the write instead of faulting, will the
subsequent guest_test_rdpmc() assertion fail because it expects to read back
0xdeaddead, while the hardware holds the previous value?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260817183143.226156-1-zide.chen@intel.com?part=8

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

* Re: [PATCH v8 7/8] KVM: x86/pmu: Emulate RDPMC on performance metrics
  2026-08-17 18:31 ` [PATCH v8 7/8] KVM: x86/pmu: Emulate RDPMC on performance metrics Zide Chen
@ 2026-08-17 19:01   ` sashiko-bot
  2026-08-17 22:02     ` Chen, Zide
  0 siblings, 1 reply; 17+ messages in thread
From: sashiko-bot @ 2026-08-17 19:01 UTC (permalink / raw)
  To: Zide Chen; +Cc: kvm

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] Metrics Clear Mode is not emulated, causing guest application crashes (#GP) when RDPMC is intercepted.
--

--- Patch [7]: [PATCH v8 7/8] KVM: x86/pmu: Emulate RDPMC on performance metrics ---
commit 5022d9b3f098d7ca5a383495c52fb9e9c119df3b
Author: Zide Chen <zide.chen@intel.com>

KVM: x86/pmu: Emulate RDPMC on performance metrics

This commit adds emulation for RDPMC performance metrics when RDPMC
interception is enabled. It handles type 0x2000 but skips Metrics Clear Mode
for simplicity.

[ ... ]

> diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c
> index bbf4afcffafc8..3ca61e4b0ba68 100644
> --- a/arch/x86/kvm/vmx/pmu_intel.c
> +++ b/arch/x86/kvm/vmx/pmu_intel.c
[ ... ]
> @@ -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;

[Severity: Medium]
The commit message notes that Metrics Clear Mode is not supported for
simplicity. However, if a guest application uses Metrics Clear Mode (setting
bit 16 in ECX, making the type 0x20010000), it will fall through to this
default case since it doesn't match INTEL_RDPMC_METRICS.

Returning 1 here causes KVM to inject a #GP. Does this cause unexpected
crashes (like a SIGSEGV) for unprivileged guest userspace applications that
legitimately try to use this architecturally valid mode when RDPMC is
intercepted?

>  	}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260817183143.226156-1-zide.chen@intel.com?part=7

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

* Re: [PATCH v8 5/8] KVM: x86/pmu: Support PERF_METRICS MSR in mediated vPMU
  2026-08-17 18:31 ` [PATCH v8 5/8] KVM: x86/pmu: Support PERF_METRICS MSR in mediated vPMU Zide Chen
@ 2026-08-17 19:04   ` sashiko-bot
  2026-08-17 22:07     ` Chen, Zide
  0 siblings, 1 reply; 17+ messages in thread
From: sashiko-bot @ 2026-08-17 19:04 UTC (permalink / raw)
  To: Zide Chen; +Cc: kvm

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:

New issues:
- [Critical] Unvalidated guest/userspace writes to MSR_PERF_METRICS cause host kernel WARN/Oops during PMU context load.
- [High] Missing RDPMC emulation for PERF_METRICS causes guest #GP when RDPMC is intercepted.
- [High] Missing RDPMC intercept allows information leak of host MSR_PERF_METRICS.

Pre-existing issues:
- [High] Intercepting MSR_CORE_PERF_GLOBAL_STATUS during mediated PMU operation breaks PMIs by returning stale overflow data to the guest.
--

--- Patch [5]: [PATCH v8 5/8] KVM: x86/pmu: Support PERF_METRICS MSR in mediated vPMU ---
commit 3c012e6d223702ac2057eaf2c6a15810c5334d8c
Author: Dapeng Mi <dapeng1.mi@linux.intel.com>

KVM: x86/pmu: Support PERF_METRICS MSR in mediated vPMU

This patch exposes the IA32_PERF_METRICS MSR to the guest when mediated
vPMU is enabled. It allows host/guest PMU context switching to save and
restore this MSR, and intercepts MSR_CORE_PERF_GLOBAL_CTRL if the host
supports PERF_METRICS but it is not exposed to the guest.

> diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c
> index 913a0ecbf2609..2fddcbc8924d4 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);
> +}
> +

[Severity: High]
Does this code miss checking for kvm_need_perf_metrics_intercept() in
kvm_need_rdpmc_intercept()?

If mediated PMU is active and the PMU counter topology matches the host,
RDPMC exiting is disabled. The guest can execute RDPMC with ECX=(1<<29)
directly on the hardware. Since intel_mediated_pmu_load() skips clearing
MSR_PERF_METRICS when the guest lacks the feature, the guest might observe
the host's raw active MSR_PERF_METRICS value.

>  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));
>  }

[Severity: High]
This is a pre-existing issue, but does forcing intercepts here break PMIs
for guests lacking PERF_METRICS?

By returning true here, MSR_CORE_PERF_GLOBAL_STATUS and
MSR_CORE_PERF_GLOBAL_OVF_CTRL are intercepted. During mediated PMU
operation, hardware updates the physical GLOBAL_STATUS directly on
overflow. Because KVM does not sync the physical MSR to
pmu->global_status on every VM-exit (only on vcpu_put()), guest reads
of the intercepted GLOBAL_STATUS could return stale cached data (0),
potentially hiding hardware PMIs from the guest.

[ ... ]
> diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c
> index 100c072b7fb38..1fd0480baf680 100644
> --- a/arch/x86/kvm/vmx/pmu_intel.c
> +++ b/arch/x86/kvm/vmx/pmu_intel.c
[ ... ]
> @@ -346,6 +348,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;

[Severity: High]
Does KVM also need to emulate RDPMC for PERF_METRICS?

The SDM specifies that IA32_PERF_METRICS can be read using the RDPMC
instruction with ECX = (1<<29) | 0. However, intel_rdpmc_ecx_to_pmc() only
recognizes GP counters (type 0) and fixed counters (type 1<<30).

If KVM intercepts RDPMC, could a guest attempt to read PERF_METRICS via
RDPMC fall through to the default case and return NULL, causing KVM to
inject a #GP?

>  	case MSR_IA32_PEBS_ENABLE:
>  		msr_info->data = pmu->pebs_enable;
>  		break;
[ ... ]
> @@ -395,6 +400,14 @@ 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:
> +		/*
> +		 * Bits [63:32] are ignored on hardware that supports only
> +		 * level-1 metrics, but may be valid on hardware that supports
> +		 * level-2 metrics. Preserve guest writes verbatim.
> +		 */
> +		pmu->perf_metrics = data;
> +		break;

[Severity: Critical]
Is it safe to accept untrusted guest or userspace data here without
validation?

If a malicious guest or host userspace (via KVM_SET_MSRS) injects a value
with reserved bits set into pmu->perf_metrics, it could cause a host #GP
during vcpu_load() when intel_mediated_pmu_load() blindly writes this
value to the physical MSR using the unsafe wrmsrq() macro.

>  	case MSR_IA32_PEBS_ENABLE:
>  		if (data & pmu->pebs_enable_rsvd)
>  			return 1;
[ ... ]
> @@ -803,6 +821,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);
> +

[Severity: Critical]
Could this wrmsrq() cause a host oops or panic?

As mentioned above, if pmu->perf_metrics contains unvalidated data with
reserved bits set, wrmsrq() (which lacks an exception table entry) will
trigger a host #GP.

>  	rdmsrq(MSR_CORE_PERF_GLOBAL_STATUS, global_status);
>  	toggle = pmu->global_status ^ global_status;

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260817183143.226156-1-zide.chen@intel.com?part=5

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

* Re: [PATCH v8 8/8] KVM: selftests: Add PERF_METRICS and fixed counter 3 tests
  2026-08-17 18:57   ` sashiko-bot
@ 2026-08-17 21:44     ` Chen, Zide
  0 siblings, 0 replies; 17+ messages in thread
From: Chen, Zide @ 2026-08-17 21:44 UTC (permalink / raw)
  To: sashiko-reviews; +Cc: kvm



On 8/17/2026 1:57 PM, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> - [Medium] Unhandled #GP / Selftest Crash when writing 0xdeaddead to MSR_PERF_METRICS.
> --
> 
> --- Patch [8]: [PATCH v8 8/8] KVM: selftests: Add PERF_METRICS and fixed counter 3 tests ---
> commit 19d6869fac1fbdcc63ff54623403cec33b922848
> Author: Zide Chen <zide.chen@intel.com>
> 
> KVM: selftests: Add PERF_METRICS and fixed counter 3 tests
> 
> This commit adds a test case to exercise IA32_PERF_METRICS for Topdown
> (TMA) Level 1 metrics and extends the PMU selftest to cover Intel fixed
> counter 3.
> 
>> diff --git a/tools/testing/selftests/kvm/x86/pmu_counters_test.c b/tools/testing/selftests/kvm/x86/pmu_counters_test.c
>> --- a/tools/testing/selftests/kvm/x86/pmu_counters_test.c
>> +++ b/tools/testing/selftests/kvm/x86/pmu_counters_test.c
> [ ... ]
>> +static void __guest_test_perf_metrics(void)
>> +{
> [ ... ]
>> +	/* Sanity check after PERF_METRICS disabled. */
>> +	__asm__ __volatile__("loop ." : "+c"((int){NUM_LOOPS}));
>> +	GUEST_ASSERT_EQ(rdmsr(MSR_PERF_METRICS), metrics);
>> +	wrmsr(MSR_PERF_METRICS, 0xdeaddead);
>> +
>> +	guest_test_rdpmc(INTEL_RDPMC_METRICS, true, 0xdeaddead);
>> +}
> 
> [Severity: Medium]
> Will this wrmsr(MSR_PERF_METRICS, 0xdeaddead) cause an unhandled #GP and
> crash the selftest on hosts with Mediated PMU pass-through?
> 
> Writing an architecturally invalid non-zero value to MSR_PERF_METRICS
> generates a #GP on real hardware. Under Mediated PMU, KVM configures the
> VMCS to pass-through MSR accesses directly to hardware, so this fault
> would be injected into the guest.

0xdeaddead is a valid non-zero value. So this statement is supposed to
work well.

> 
> If the hardware silently discards the write instead of faulting, will the
> subsequent guest_test_rdpmc() assertion fail because it expects to read back
> 0xdeaddead, while the hardware holds the previous value?
> 


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

* Re: [PATCH v8 3/8] KVM: x86/pmu: Rename and move vcpu_get_perf_capabilities() to pmu.h
  2026-08-17 18:52   ` sashiko-bot
@ 2026-08-17 21:45     ` Chen, Zide
  0 siblings, 0 replies; 17+ messages in thread
From: Chen, Zide @ 2026-08-17 21:45 UTC (permalink / raw)
  To: sashiko-reviews; +Cc: kvm



On 8/17/2026 1:52 PM, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> - [Low] Header files pmu.h and pmu_intel.h lack required includes, violating self-contained header practices.
> --
> 
> --- Patch [3]: [PATCH v8 3/8] KVM: x86/pmu: Rename and move vcpu_get_perf_capabilities() to pmu.h ---
> commit 7eb009fb5328b80368e10acda6cfe621b3e1e1c0
> Author: Zide Chen <zide.chen@intel.com>
> 
> KVM: x86/pmu: Rename and move vcpu_get_perf_capabilities() to pmu.h
> 
> This commit renames vcpu_get_perf_capabilities() to kvm_vcpu_get_perf_caps()
> and moves it to the common x86 PMU header. This prepares the function to be
> called from common x86 code to check the guest's PERF_METRICS capability.
> 
>> diff --git a/arch/x86/kvm/pmu.h b/arch/x86/kvm/pmu.h
>> index 090c9bbb74f48..82f955e5a4507 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;
> 
> [Severity: Low]
> Does this file need to include cpuid.h? guest_cpu_cap_has() is used here,
> but pmu.h does not include the header that defines it.

Copied from my reply in v7 review:

It sounds reasonable, but I'd prefer to keep it as-is.

Adding cpuid.h to pmu.h would make pmu.h more self-contained, but it
would also naturally lead to removing now-redundant cpuid.h includes
from PMU-related files such as vmx/pmu_intel.c, svm/pmu.c, and
potentially others.

I'd like to keep the current patch focused on the functional change and
avoid mixing it with include dependency cleanup.


>> +
>> +	return vcpu->arch.perf_capabilities;
>> +}
>> +
>>  void kvm_pmu_deliver_pmi(struct kvm_vcpu *vcpu);
> [ ... ]
>> diff --git a/arch/x86/kvm/vmx/pmu_intel.h b/arch/x86/kvm/vmx/pmu_intel.h
>> index 5d9357640aa18..dd447d8b6fdde 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;
> 
> [Severity: Low]
> Should pmu_intel.h include pmu.h? It calls kvm_vcpu_get_perf_caps() which
> is now defined in pmu.h, but the include appears to be missing.
> 
>>  }
>>  
>>  bool intel_pmu_lbr_is_enabled(struct kvm_vcpu *vcpu);
> 


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

* Re: [PATCH v8 7/8] KVM: x86/pmu: Emulate RDPMC on performance metrics
  2026-08-17 19:01   ` sashiko-bot
@ 2026-08-17 22:02     ` Chen, Zide
  0 siblings, 0 replies; 17+ messages in thread
From: Chen, Zide @ 2026-08-17 22:02 UTC (permalink / raw)
  To: sashiko-reviews; +Cc: kvm



On 8/17/2026 2:01 PM, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> - [Medium] Metrics Clear Mode is not emulated, causing guest application crashes (#GP) when RDPMC is intercepted.
> --
> 
> --- Patch [7]: [PATCH v8 7/8] KVM: x86/pmu: Emulate RDPMC on performance metrics ---
> commit 5022d9b3f098d7ca5a383495c52fb9e9c119df3b
> Author: Zide Chen <zide.chen@intel.com>
> 
> KVM: x86/pmu: Emulate RDPMC on performance metrics
> 
> This commit adds emulation for RDPMC performance metrics when RDPMC
> interception is enabled. It handles type 0x2000 but skips Metrics Clear Mode
> for simplicity.
> 
> [ ... ]
> 
>> diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c
>> index bbf4afcffafc8..3ca61e4b0ba68 100644
>> --- a/arch/x86/kvm/vmx/pmu_intel.c
>> +++ b/arch/x86/kvm/vmx/pmu_intel.c
> [ ... ]
>> @@ -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;
> 
> [Severity: Medium]
> The commit message notes that Metrics Clear Mode is not supported for
> simplicity. However, if a guest application uses Metrics Clear Mode (setting
> bit 16 in ECX, making the type 0x20010000), it will fall through to this
> default case since it doesn't match INTEL_RDPMC_METRICS.
> 
> Returning 1 here causes KVM to inject a #GP. Does this cause unexpected
> crashes (like a SIGSEGV) for unprivileged guest userspace applications that
> legitimately try to use this architecturally valid mode when RDPMC is
> intercepted?

This is a false positive. Setting bit 16 in ECX is not indicating
Metrics Clear mode. Whether or not clear metrics, depends on
IA32_FIXED_CTR_CTRL.METRICS_CLEAR_EN[bit 14].

BTW, IA32_PERF_CAPABILITIES.RDPMC_METRICS_CLEAR[bit 19] is not exposed.
>>  	}
> 


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

* Re: [PATCH v8 5/8] KVM: x86/pmu: Support PERF_METRICS MSR in mediated vPMU
  2026-08-17 19:04   ` sashiko-bot
@ 2026-08-17 22:07     ` Chen, Zide
  0 siblings, 0 replies; 17+ messages in thread
From: Chen, Zide @ 2026-08-17 22:07 UTC (permalink / raw)
  To: sashiko-reviews; +Cc: kvm



On 8/17/2026 2:04 PM, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
> 
> New issues:
> - [Critical] Unvalidated guest/userspace writes to MSR_PERF_METRICS cause host kernel WARN/Oops during PMU context load.
> - [High] Missing RDPMC emulation for PERF_METRICS causes guest #GP when RDPMC is intercepted.
> - [High] Missing RDPMC intercept allows information leak of host MSR_PERF_METRICS.
> 
> Pre-existing issues:
> - [High] Intercepting MSR_CORE_PERF_GLOBAL_STATUS during mediated PMU operation breaks PMIs by returning stale overflow data to the guest.
> --
> 
> --- Patch [5]: [PATCH v8 5/8] KVM: x86/pmu: Support PERF_METRICS MSR in mediated vPMU ---
> commit 3c012e6d223702ac2057eaf2c6a15810c5334d8c
> Author: Dapeng Mi <dapeng1.mi@linux.intel.com>
> 
> KVM: x86/pmu: Support PERF_METRICS MSR in mediated vPMU
> 
> This patch exposes the IA32_PERF_METRICS MSR to the guest when mediated
> vPMU is enabled. It allows host/guest PMU context switching to save and
> restore this MSR, and intercepts MSR_CORE_PERF_GLOBAL_CTRL if the host
> supports PERF_METRICS but it is not exposed to the guest.
> 
>> diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c
>> index 913a0ecbf2609..2fddcbc8924d4 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);
>> +}
>> +
> 
> [Severity: High]
> Does this code miss checking for kvm_need_perf_metrics_intercept() in
> kvm_need_rdpmc_intercept()?

Yes, it's included in patch 7/8.

> If mediated PMU is active and the PMU counter topology matches the host,
> RDPMC exiting is disabled. The guest can execute RDPMC with ECX=(1<<29)
> directly on the hardware. Since intel_mediated_pmu_load() skips clearing
> MSR_PERF_METRICS when the guest lacks the feature, the guest might observe
> the host's raw active MSR_PERF_METRICS value.
> 
>>  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));
>>  }
> 
> [Severity: High]
> This is a pre-existing issue, but does forcing intercepts here break PMIs
> for guests lacking PERF_METRICS?
> 
> By returning true here, MSR_CORE_PERF_GLOBAL_STATUS and
> MSR_CORE_PERF_GLOBAL_OVF_CTRL are intercepted. During mediated PMU
> operation, hardware updates the physical GLOBAL_STATUS directly on
> overflow. Because KVM does not sync the physical MSR to
> pmu->global_status on every VM-exit (only on vcpu_put()), guest reads
> of the intercepted GLOBAL_STATUS could return stale cached data (0),
> potentially hiding hardware PMIs from the guest.

False positive.

GLOBAL_STATUS is synced to pmu->global_status in
intel_mediated_pmu_load() in every VM exit. So it won't return stale
data to guest reads of the intercepted GLOBAL_STATUS.

> 
> [ ... ]
>> diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c
>> index 100c072b7fb38..1fd0480baf680 100644
>> --- a/arch/x86/kvm/vmx/pmu_intel.c
>> +++ b/arch/x86/kvm/vmx/pmu_intel.c
> [ ... ]
>> @@ -346,6 +348,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;
> 
> [Severity: High]
> Does KVM also need to emulate RDPMC for PERF_METRICS?

Yes, it's implemented in the following patches.>
> The SDM specifies that IA32_PERF_METRICS can be read using the RDPMC
> instruction with ECX = (1<<29) | 0. However, intel_rdpmc_ecx_to_pmc() only
> recognizes GP counters (type 0) and fixed counters (type 1<<30).
> 
> If KVM intercepts RDPMC, could a guest attempt to read PERF_METRICS via
> RDPMC fall through to the default case and return NULL, causing KVM to
> inject a #GP?
> 
>>  	case MSR_IA32_PEBS_ENABLE:
>>  		msr_info->data = pmu->pebs_enable;
>>  		break;
> [ ... ]
>> @@ -395,6 +400,14 @@ 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:
>> +		/*
>> +		 * Bits [63:32] are ignored on hardware that supports only
>> +		 * level-1 metrics, but may be valid on hardware that supports
>> +		 * level-2 metrics. Preserve guest writes verbatim.
>> +		 */
>> +		pmu->perf_metrics = data;
>> +		break;
> 
> [Severity: Critical]
> Is it safe to accept untrusted guest or userspace data here without
> validation?

As the comments suggest, no #GP is caused.
> If a malicious guest or host userspace (via KVM_SET_MSRS) injects a value
> with reserved bits set into pmu->perf_metrics, it could cause a host #GP
> during vcpu_load() when intel_mediated_pmu_load() blindly writes this
> value to the physical MSR using the unsafe wrmsrq() macro.
> 
>>  	case MSR_IA32_PEBS_ENABLE:
>>  		if (data & pmu->pebs_enable_rsvd)
>>  			return 1;
> [ ... ]
>> @@ -803,6 +821,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);
>> +
> 
> [Severity: Critical]
> Could this wrmsrq() cause a host oops or panic?
> 
> As mentioned above, if pmu->perf_metrics contains unvalidated data with
> reserved bits set, wrmsrq() (which lacks an exception table entry) will
> trigger a host #GP.

No, no #GP in this case.
>>  	rdmsrq(MSR_CORE_PERF_GLOBAL_STATUS, global_status);
>>  	toggle = pmu->global_status ^ global_status;
> 


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

end of thread, other threads:[~2026-08-17 22:07 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-17 18:31 [PATCH v8 0/8] KVM: x86/pmu: Add hardware Topdown metrics support Zide Chen
2026-08-17 18:31 ` [PATCH v8 1/8] KVM: x86/pmu: Do not map fixed counters >= 3 to generic perf events Zide Chen
2026-08-17 18:31 ` [PATCH v8 2/8] KVM: x86/pmu: Support Intel fixed counter 3 on mediated vPMU Zide Chen
2026-08-17 18:31 ` [PATCH v8 3/8] KVM: x86/pmu: Rename and move vcpu_get_perf_capabilities() to pmu.h Zide Chen
2026-08-17 18:52   ` sashiko-bot
2026-08-17 21:45     ` Chen, Zide
2026-08-17 18:31 ` [PATCH v8 4/8] KVM: x86/pmu: Snapshot host IA32_PERF_CAPABILITIES in kvm_host Zide Chen
2026-08-17 18:31 ` [PATCH v8 5/8] KVM: x86/pmu: Support PERF_METRICS MSR in mediated vPMU Zide Chen
2026-08-17 19:04   ` sashiko-bot
2026-08-17 22:07     ` Chen, Zide
2026-08-17 18:31 ` [PATCH v8 6/8] KVM: x86/pmu: Move RDPMC emulation into per-vendor callbacks Zide Chen
2026-08-17 18:31 ` [PATCH v8 7/8] KVM: x86/pmu: Emulate RDPMC on performance metrics Zide Chen
2026-08-17 19:01   ` sashiko-bot
2026-08-17 22:02     ` Chen, Zide
2026-08-17 18:31 ` [PATCH v8 8/8] KVM: selftests: Add PERF_METRICS and fixed counter 3 tests Zide Chen
2026-08-17 18:57   ` sashiko-bot
2026-08-17 21:44     ` Chen, Zide

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).