* [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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.