* [PATCH v9 00/12] KVM: x86/pmu: Add hardware Topdown metrics support
@ 2026-09-18 19:39 Zide Chen
2026-09-18 19:39 ` [PATCH v9 01/12] KVM: x86/pmu: Do not map fixed counters >= 3 to generic perf events Zide Chen
` (11 more replies)
0 siblings, 12 replies; 23+ messages in thread
From: Zide Chen @ 2026-09-18 19:39 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini, Peter Zijlstra
Cc: kvm, Jim Mattson, Andi Kleen, Stephane Eranian, linux-kernel,
Mingwei Zhang, Zide Chen, Das Sandipan, Shukla Manali, Dapeng Mi,
Xudong Hao
Previous revisions were missing two things that are now addressed:
- A previous comment incorrectly claimed that MSR_PERF_METRICS bits
[63:32] are ignored on hardware that only implements level-1 metrics;
patch 10/12 fixes this by rejecting such writes.
- Patch 11/12 adds support for RDPMC Metrics Clear Mode. Without
this feature, for user programs using RDPMC directly, the counter
has to be reset explicitly using ioctl:
ioctl(perf_fd, PERF_EVENT_IOC_RESET, 0);
Tests: besides the tests mentioned on v8 cover letter, tested RDPMC
Metrics Clear on a DMR machine, and tested Topdown metrics on nested
setup on top of the vPMU support series [1].
perf tool misidentifies "cpu/slots,metrics_clear=1/" as not being the
slots event, so it inserts an additional slots event into the group;
event scheduling then fails because the group needs fixed counter 3
(SLOTS) twice:
$ perf stat -e "{cpu/slots,metrics_clear=1/,cpu/topdown-retiring/}" -- sleep 1
WARNING: events were regrouped to match PMUs
<not counted> slots
<not supported> cpu/slots,metrics_clear=1/
<not counted> cpu/topdown-retiring/
A fix for this perf tool bug is posted at [2].
New patches:
patch 8/12: Add INTEL_TD_METRIC_FIELD_BITS for code cleanup.
patch 9/12: perf/x86: Expose number of Topdown metric events to KVM.
patch 10/12: Reject writes to reserved MSR_PERF_METRICS bits.
patch 11/12: Support RDPMC Metrics Clear Mode.
Minor non-logical changes to existing patches:
patch 5/12: Remove the incorrect comment about writing to PERF_METRICS.
patch 7/12: Remove references to Metrics Clear from the changelog.
Patch 11/12 could be folded into patch 5/12, but I kept them separate
because patches 1-8 were already well reviewed, and keeping them
unchanged should make reviewing v9 easier.
Rebased to kvm-x86/next: 70c944caf570fda2d79baa71435589a8db39f048
v8: https://lore.kernel.org/kvm/20260817183143.226156-1-zide.chen@intel.com/
QEMU: https://lore.kernel.org/qemu-devel/20260604025546.19378-7-zide.chen@intel.com/
[1] https://lore.kernel.org/kvm/20260827223755.143247-1-zide.chen@intel.com/
[2] https://lore.kernel.org/linux-perf-users/20260917182525.3DE801F000FF@smtp.kernel.org/T/#t
Dapeng Mi (2):
KVM: x86/pmu: Support Intel fixed counter 3 on mediated vPMU
KVM: x86/pmu: Support PERF_METRICS MSR in mediated vPMU
Mingwei Zhang (1):
KVM: x86/pmu: Snapshot host IA32_PERF_CAPABILITIES in kvm_host
Zide Chen (9):
KVM: x86/pmu: Do not map fixed counters >= 3 to generic perf events
KVM: x86/pmu: Rename and move vcpu_get_perf_capabilities() to pmu.h
KVM: x86/pmu: Move RDPMC emulation into per-vendor callbacks
KVM: x86/pmu: Emulate RDPMC on performance metrics
KVM: selftests: Add PERF_METRICS and fixed counter 3 tests
perf/x86: Add INTEL_TD_METRIC_FIELD_{BITS,MASK} constants
perf/x86: Expose number of Topdown metric events to KVM
KVM: x86/pmu: Reject writes to reserved MSR_PERF_METRICS bits
KVM: x86/pmu: Support RDPMC Metrics Clear Mode
arch/x86/events/core.c | 1 +
arch/x86/events/intel/core.c | 8 +-
arch/x86/include/asm/kvm-x86-pmu-ops.h | 2 +-
arch/x86/include/asm/kvm_host.h | 5 +-
arch/x86/include/asm/msr-index.h | 2 +
arch/x86/include/asm/perf_event.h | 5 +
arch/x86/kvm/msrs.c | 10 +-
arch/x86/kvm/pmu.c | 37 +++--
arch/x86/kvm/pmu.h | 17 ++-
arch/x86/kvm/svm/pmu.c | 13 +-
arch/x86/kvm/vmx/nested.c | 2 +
arch/x86/kvm/vmx/pmu_intel.c | 132 +++++++++++++++---
arch/x86/kvm/vmx/pmu_intel.h | 12 +-
arch/x86/kvm/vmx/vmx.c | 19 ++-
arch/x86/kvm/x86.c | 4 +
tools/arch/x86/include/asm/msr-index.h | 1 +
tools/testing/selftests/kvm/include/x86/pmu.h | 3 +
.../selftests/kvm/x86/pmu_counters_test.c | 97 ++++++++++++-
18 files changed, 303 insertions(+), 67 deletions(-)
--
2.55.0
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH v9 01/12] KVM: x86/pmu: Do not map fixed counters >= 3 to generic perf events
2026-09-18 19:39 [PATCH v9 00/12] KVM: x86/pmu: Add hardware Topdown metrics support Zide Chen
@ 2026-09-18 19:39 ` Zide Chen
2026-09-18 19:39 ` [PATCH v9 02/12] KVM: x86/pmu: Support Intel fixed counter 3 on mediated vPMU Zide Chen
` (10 subsequent siblings)
11 siblings, 0 replies; 23+ messages in thread
From: Zide Chen @ 2026-09-18 19:39 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini, Peter Zijlstra
Cc: kvm, Jim Mattson, Andi Kleen, Stephane Eranian, linux-kernel,
Mingwei Zhang, Zide Chen, Das Sandipan, Shukla Manali, Dapeng Mi,
Xudong Hao
Only fixed counters 0..2 have matching generic cross-platform
hardware perf events (INSTRUCTIONS, CPU_CYCLES, REF_CPU_CYCLES).
Therefore, perf_get_hw_event_config() is only applicable to these
counters.
KVM does not intend to emulate fixed counters >= 3 on legacy
(non-mediated) vPMU, while for mediated vPMU, KVM does not care what
the fixed counter event mappings are. Therefore, return 0 for their
eventsel.
The two BUILD_BUG_ON() checks are no longer needed, so drop them along
with __always_inline.
Signed-off-by: Zide Chen <zide.chen@intel.com>
Reviewed-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
Reviewed-by: Jim Mattson <jmattson@google.com>
---
v6:
- Re-arrange the code for early return. Clearer.
---
arch/x86/kvm/vmx/pmu_intel.c | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c
index 70a8c4816135..11ad64d43e35 100644
--- a/arch/x86/kvm/vmx/pmu_intel.c
+++ b/arch/x86/kvm/vmx/pmu_intel.c
@@ -468,11 +468,8 @@ static int intel_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
* different perf_event is already utilizing the requested counter, but the end
* result is the same (ignoring the fact that using a general purpose counter
* will likely exacerbate counter contention).
- *
- * Forcibly inlined to allow asserting on @index at build time, and there should
- * never be more than one user.
*/
-static __always_inline u64 intel_get_fixed_pmc_eventsel(unsigned int index)
+static u64 intel_get_fixed_pmc_eventsel(unsigned int index)
{
const enum perf_hw_id fixed_pmc_perf_ids[] = {
[0] = PERF_COUNT_HW_INSTRUCTIONS,
@@ -481,8 +478,13 @@ static __always_inline u64 intel_get_fixed_pmc_eventsel(unsigned int index)
};
u64 eventsel;
- BUILD_BUG_ON(ARRAY_SIZE(fixed_pmc_perf_ids) != KVM_MAX_NR_INTEL_FIXED_COUNTERS);
- BUILD_BUG_ON(index >= KVM_MAX_NR_INTEL_FIXED_COUNTERS);
+ /*
+ * Fixed counters 3 and above don't have a corresponding generic
+ * hardware perf event, and KVM does not intend to emulate them on
+ * non-mediated vPMU.
+ */
+ if (index >= ARRAY_SIZE(fixed_pmc_perf_ids))
+ return 0;
/*
* Yell if perf reports support for a fixed counter but perf doesn't
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v9 02/12] KVM: x86/pmu: Support Intel fixed counter 3 on mediated vPMU
2026-09-18 19:39 [PATCH v9 00/12] KVM: x86/pmu: Add hardware Topdown metrics support Zide Chen
2026-09-18 19:39 ` [PATCH v9 01/12] KVM: x86/pmu: Do not map fixed counters >= 3 to generic perf events Zide Chen
@ 2026-09-18 19:39 ` Zide Chen
2026-09-18 19:39 ` [PATCH v9 03/12] KVM: x86/pmu: Rename and move vcpu_get_perf_capabilities() to pmu.h Zide Chen
` (9 subsequent siblings)
11 siblings, 0 replies; 23+ messages in thread
From: Zide Chen @ 2026-09-18 19:39 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini, Peter Zijlstra
Cc: kvm, Jim Mattson, Andi Kleen, Stephane Eranian, linux-kernel,
Mingwei Zhang, Zide Chen, Das Sandipan, Shukla Manali, Dapeng Mi,
Xudong Hao
From: Dapeng Mi <dapeng1.mi@linux.intel.com>
Starting with Ice Lake, Intel introduced fixed counter 3, which counts
TOPDOWN.SLOTS - the number of available slots for an unhalted logical
processor. It serves as the denominator for top-level metrics in the
Top-down Microarchitecture Analysis method.
Emulating this counter on legacy vPMU would require introducing a new
generic perf encoding for the Intel-specific TOPDOWN.SLOTS event in
order to call perf_get_hw_event_config(). This is undesirable as it
would pollute the generic perf event encoding.
Moreover, KVM does not intend to emulate IA32_PERF_METRICS in the
legacy vPMU model, and without IA32_PERF_METRICS, emulating this
counter has little practical value. Therefore, expose fixed counter
3 to guests only when mediated vPMU is enabled.
Signed-off-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
Co-developed-by: Zide Chen <zide.chen@intel.com>
Signed-off-by: Zide Chen <zide.chen@intel.com>
Reviewed-by: Jim Mattson <jmattson@google.com>
---
v6:
- Update comments to replace 2 with KVM_MAX_NR_INTEL_FIXED_COUNTERS - 1.
---
arch/x86/include/asm/kvm_host.h | 2 +-
arch/x86/kvm/msrs.c | 4 ++--
arch/x86/kvm/pmu.c | 19 ++++++++++++++++++-
3 files changed, 21 insertions(+), 4 deletions(-)
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 30ffaa65f589..00584fcffa33 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -563,7 +563,7 @@ struct kvm_pmc {
#define KVM_MAX_NR_GP_COUNTERS KVM_MAX(KVM_MAX_NR_INTEL_GP_COUNTERS, \
KVM_MAX_NR_AMD_GP_COUNTERS)
-#define KVM_MAX_NR_INTEL_FIXED_COUNTERS 3
+#define KVM_MAX_NR_INTEL_FIXED_COUNTERS 4
#define KVM_MAX_NR_AMD_FIXED_COUNTERS 0
#define KVM_MAX_NR_FIXED_COUNTERS KVM_MAX(KVM_MAX_NR_INTEL_FIXED_COUNTERS, \
KVM_MAX_NR_AMD_FIXED_COUNTERS)
diff --git a/arch/x86/kvm/msrs.c b/arch/x86/kvm/msrs.c
index 37d5bed3529f..45e4c8a32436 100644
--- a/arch/x86/kvm/msrs.c
+++ b/arch/x86/kvm/msrs.c
@@ -217,7 +217,7 @@ static const u32 msrs_to_save_base[] = {
static const u32 msrs_to_save_pmu[] = {
MSR_ARCH_PERFMON_FIXED_CTR0, MSR_ARCH_PERFMON_FIXED_CTR1,
- MSR_ARCH_PERFMON_FIXED_CTR0 + 2,
+ MSR_ARCH_PERFMON_FIXED_CTR2, MSR_ARCH_PERFMON_FIXED_CTR3,
MSR_CORE_PERF_FIXED_CTR_CTRL, MSR_CORE_PERF_GLOBAL_STATUS,
MSR_CORE_PERF_GLOBAL_CTRL,
MSR_IA32_PEBS_ENABLE, MSR_IA32_DS_AREA, MSR_PEBS_DATA_CFG,
@@ -2672,7 +2672,7 @@ void kvm_init_msr_lists(void)
{
unsigned i;
- BUILD_BUG_ON_MSG(KVM_MAX_NR_FIXED_COUNTERS != 3,
+ BUILD_BUG_ON_MSG(KVM_MAX_NR_FIXED_COUNTERS != 4,
"Please update the fixed PMCs in msrs_to_save_pmu[]");
num_msrs_to_save = 0;
diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c
index d2fd47ee5ec8..448044a2b121 100644
--- a/arch/x86/kvm/pmu.c
+++ b/arch/x86/kvm/pmu.c
@@ -19,6 +19,7 @@
#include <linux/moduleparam.h>
#include <asm/perf_event.h>
#include <asm/cpu_device_id.h>
+#include <asm/cpuid/api.h>
#include "x86.h"
#include "cpuid.h"
#include "lapic.h"
@@ -99,7 +100,8 @@ static const struct x86_cpu_id vmx_pebs_pdist_cpu[] = {
* all perf counters (both gp and fixed). The mapping relationship
* between pmc and perf counters is as the following:
* * Intel: [0 .. KVM_MAX_NR_INTEL_GP_COUNTERS-1] <=> gp counters
- * [KVM_FIXED_PMC_BASE_IDX .. KVM_FIXED_PMC_BASE_IDX + 2] <=> fixed
+ * KVM_MAX_NR_INTEL_FIXED_COUNTERS counters starting at
+ * KVM_FIXED_PMC_BASE_IDX <=> fixed
* * AMD: [0 .. AMD64_NUM_COUNTERS-1] and, for families 15H
* and later, [0 .. AMD64_NUM_COUNTERS_CORE-1] <=> gp counters
*/
@@ -134,6 +136,8 @@ void kvm_init_pmu_capability(struct kvm_pmu_ops *pmu_ops)
{
bool is_intel = boot_cpu_data.x86_vendor == X86_VENDOR_INTEL;
int min_nr_gp_ctrs = pmu_ops->MIN_NR_GP_COUNTERS;
+ union cpuid10_edx edx;
+ u32 eax, ebx, ecx;
/*
* Hybrid PMUs don't play nice with virtualization without careful
@@ -181,6 +185,19 @@ void kvm_init_pmu_capability(struct kvm_pmu_ops *pmu_ops)
kvm_pmu_cap.num_counters_fixed = min(kvm_pmu_cap.num_counters_fixed,
KVM_MAX_NR_FIXED_COUNTERS);
+ /*
+ * Currently, KVM doesn't support non-contiguous fixed counters; make
+ * sure only contiguous ones are retained in kvm_pmu_cap.
+ */
+ if (kvm_host_pmu.version >= 5) {
+ cpuid(0xa, &eax, &ebx, &ecx, &edx.full);
+ if (kvm_pmu_cap.num_counters_fixed > edx.split.num_counters_fixed)
+ kvm_pmu_cap.num_counters_fixed = edx.split.num_counters_fixed;
+ }
+
+ if (!enable_mediated_pmu && kvm_pmu_cap.num_counters_fixed > 3)
+ kvm_pmu_cap.num_counters_fixed = 3;
+
kvm_pmu_eventsel.INSTRUCTIONS_RETIRED =
perf_get_hw_event_config(PERF_COUNT_HW_INSTRUCTIONS);
kvm_pmu_eventsel.BRANCH_INSTRUCTIONS_RETIRED =
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v9 03/12] KVM: x86/pmu: Rename and move vcpu_get_perf_capabilities() to pmu.h
2026-09-18 19:39 [PATCH v9 00/12] KVM: x86/pmu: Add hardware Topdown metrics support Zide Chen
2026-09-18 19:39 ` [PATCH v9 01/12] KVM: x86/pmu: Do not map fixed counters >= 3 to generic perf events Zide Chen
2026-09-18 19:39 ` [PATCH v9 02/12] KVM: x86/pmu: Support Intel fixed counter 3 on mediated vPMU Zide Chen
@ 2026-09-18 19:39 ` Zide Chen
2026-09-18 19:39 ` [PATCH v9 04/12] KVM: x86/pmu: Snapshot host IA32_PERF_CAPABILITIES in kvm_host Zide Chen
` (8 subsequent siblings)
11 siblings, 0 replies; 23+ messages in thread
From: Zide Chen @ 2026-09-18 19:39 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini, Peter Zijlstra
Cc: kvm, Jim Mattson, Andi Kleen, Stephane Eranian, linux-kernel,
Mingwei Zhang, Zide Chen, Das Sandipan, Shukla Manali, Dapeng Mi,
Xudong Hao
This is in preparation for it to be called from common x86 code, for
example kvm_need_rdpmc_intercept(), to check the guest's PERF_METRICS
capability.
Rename it to kvm_vcpu_get_perf_caps() to indicate that it's part of
the common API, and shorten _capabilities to _caps.
No functional change intended.
Signed-off-by: Zide Chen <zide.chen@intel.com>
Reviewed-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
Reviewed-by: Jim Mattson <jmattson@google.com>
---
v8:
- remove include "cpuid.h" from pmu_intel.h.
---
arch/x86/kvm/pmu.h | 8 ++++++++
arch/x86/kvm/vmx/pmu_intel.c | 6 +++---
arch/x86/kvm/vmx/pmu_intel.h | 12 +-----------
3 files changed, 12 insertions(+), 14 deletions(-)
diff --git a/arch/x86/kvm/pmu.h b/arch/x86/kvm/pmu.h
index 090c9bbb74f4..82f955e5a450 100644
--- a/arch/x86/kvm/pmu.h
+++ b/arch/x86/kvm/pmu.h
@@ -271,6 +271,14 @@ static inline bool kvm_pmu_is_fastpath_emulation_allowed(struct kvm_vcpu *vcpu)
X86_PMC_IDX_MAX);
}
+static inline u64 kvm_vcpu_get_perf_caps(struct kvm_vcpu *vcpu)
+{
+ if (!guest_cpu_cap_has(vcpu, X86_FEATURE_PDCM))
+ return 0;
+
+ return vcpu->arch.perf_capabilities;
+}
+
void kvm_pmu_deliver_pmi(struct kvm_vcpu *vcpu);
int kvm_pmu_rdpmc(struct kvm_vcpu *vcpu, unsigned pmc, u64 *data);
int kvm_pmu_check_rdpmc_early(struct kvm_vcpu *vcpu, unsigned int idx);
diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c
index 11ad64d43e35..28c1542e5ba5 100644
--- a/arch/x86/kvm/vmx/pmu_intel.c
+++ b/arch/x86/kvm/vmx/pmu_intel.c
@@ -193,13 +193,13 @@ static bool intel_is_valid_msr(struct kvm_vcpu *vcpu, u32 msr)
case MSR_CORE_PERF_FIXED_CTR_CTRL:
return kvm_pmu_has_perf_global_ctrl(pmu);
case MSR_IA32_PEBS_ENABLE:
- ret = vcpu_get_perf_capabilities(vcpu) & PERF_CAP_PEBS_FORMAT;
+ ret = kvm_vcpu_get_perf_caps(vcpu) & PERF_CAP_PEBS_FORMAT;
break;
case MSR_IA32_DS_AREA:
ret = guest_cpu_cap_has(vcpu, X86_FEATURE_DS);
break;
case MSR_PEBS_DATA_CFG:
- perf_capabilities = vcpu_get_perf_capabilities(vcpu);
+ perf_capabilities = kvm_vcpu_get_perf_caps(vcpu);
ret = (perf_capabilities & PERF_CAP_PEBS_BASELINE) &&
((perf_capabilities & PERF_CAP_PEBS_FORMAT) > 3);
break;
@@ -554,7 +554,7 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu)
pmu->raw_event_mask |= (HSW_IN_TX|HSW_IN_TX_CHECKPOINTED);
}
- perf_capabilities = vcpu_get_perf_capabilities(vcpu);
+ perf_capabilities = kvm_vcpu_get_perf_caps(vcpu);
if (intel_pmu_lbr_is_compatible(vcpu) &&
(perf_capabilities & PERF_CAP_LBR_FMT))
memcpy(&lbr_desc->records, &vmx_lbr_caps, sizeof(vmx_lbr_caps));
diff --git a/arch/x86/kvm/vmx/pmu_intel.h b/arch/x86/kvm/vmx/pmu_intel.h
index 5d9357640aa1..dd447d8b6fdd 100644
--- a/arch/x86/kvm/vmx/pmu_intel.h
+++ b/arch/x86/kvm/vmx/pmu_intel.h
@@ -4,19 +4,9 @@
#include <linux/kvm_host.h>
-#include "cpuid.h"
-
-static inline u64 vcpu_get_perf_capabilities(struct kvm_vcpu *vcpu)
-{
- if (!guest_cpu_cap_has(vcpu, X86_FEATURE_PDCM))
- return 0;
-
- return vcpu->arch.perf_capabilities;
-}
-
static inline bool fw_writes_is_enabled(struct kvm_vcpu *vcpu)
{
- return (vcpu_get_perf_capabilities(vcpu) & PERF_CAP_FW_WRITES) != 0;
+ return (kvm_vcpu_get_perf_caps(vcpu) & PERF_CAP_FW_WRITES) != 0;
}
bool intel_pmu_lbr_is_enabled(struct kvm_vcpu *vcpu);
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v9 04/12] KVM: x86/pmu: Snapshot host IA32_PERF_CAPABILITIES in kvm_host
2026-09-18 19:39 [PATCH v9 00/12] KVM: x86/pmu: Add hardware Topdown metrics support Zide Chen
` (2 preceding siblings ...)
2026-09-18 19:39 ` [PATCH v9 03/12] KVM: x86/pmu: Rename and move vcpu_get_perf_capabilities() to pmu.h Zide Chen
@ 2026-09-18 19:39 ` Zide Chen
2026-09-18 19:39 ` [PATCH v9 05/12] KVM: x86/pmu: Support PERF_METRICS MSR in mediated vPMU Zide Chen
` (7 subsequent siblings)
11 siblings, 0 replies; 23+ messages in thread
From: Zide Chen @ 2026-09-18 19:39 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini, Peter Zijlstra
Cc: kvm, Jim Mattson, Andi Kleen, Stephane Eranian, linux-kernel,
Mingwei Zhang, Zide Chen, Das Sandipan, Shukla Manali, Dapeng Mi,
Xudong Hao
From: Mingwei Zhang <mizhang@google.com>
Cache the unadulterated snapshot of perf_capabilities so that KVM can
compare guest vPMU capabilities against raw hardware capabilities.
For example, if the host supports PERF_METRICS but it is not configured
for the guest, KVM can use it to determine that RDPMC accesses must be
intercepted.
Signed-off-by: Mingwei Zhang <mizhang@google.com>
Signed-off-by: Zide Chen <zide.chen@intel.com>
Reviewed-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
Reviewed-by: Jim Mattson <jmattson@google.com>
---
v5: new patch.
---
arch/x86/include/asm/kvm_host.h | 1 +
arch/x86/kvm/vmx/vmx.c | 8 ++------
arch/x86/kvm/x86.c | 4 ++++
3 files changed, 7 insertions(+), 6 deletions(-)
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 00584fcffa33..d00c7cbcfa14 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -295,6 +295,7 @@ struct kvm_host_values {
u64 xss;
u64 s_cet;
u64 arch_capabilities;
+ u64 perf_capabilities;
};
extern struct kvm_host_values kvm_host;
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index 56b44e032395..715e44489e22 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -7949,14 +7949,10 @@ void vmx_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu)
static __init u64 vmx_get_perf_capabilities(void)
{
u64 perf_cap = PERF_CAP_FW_WRITES;
- u64 host_perf_cap = 0;
if (!enable_pmu)
return 0;
- if (boot_cpu_has(X86_FEATURE_PDCM))
- rdmsrq(MSR_IA32_PERF_CAPABILITIES, host_perf_cap);
-
if (!cpu_feature_enabled(X86_FEATURE_ARCH_LBR) &&
!enable_mediated_pmu) {
x86_perf_get_lbr(&vmx_lbr_caps);
@@ -7969,11 +7965,11 @@ static __init u64 vmx_get_perf_capabilities(void)
if (!vmx_lbr_caps.has_callstack)
memset(&vmx_lbr_caps, 0, sizeof(vmx_lbr_caps));
else if (vmx_lbr_caps.nr)
- perf_cap |= host_perf_cap & PERF_CAP_LBR_FMT;
+ perf_cap |= kvm_host.perf_capabilities & PERF_CAP_LBR_FMT;
}
if (vmx_pebs_supported()) {
- perf_cap |= host_perf_cap & PERF_CAP_PEBS_MASK;
+ perf_cap |= kvm_host.perf_capabilities & PERF_CAP_PEBS_MASK;
/*
* Disallow adaptive PEBS as it is functionally broken, can be
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index af3ceee714c9..83106ce72d76 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -7037,6 +7037,10 @@ int kvm_x86_vendor_init(struct kvm_x86_init_ops *ops)
if (boot_cpu_has(X86_FEATURE_ARCH_CAPABILITIES))
rdmsrq(MSR_IA32_ARCH_CAPABILITIES, kvm_host.arch_capabilities);
+ if (boot_cpu_has(X86_FEATURE_PDCM))
+ rdmsrq_safe(MSR_IA32_PERF_CAPABILITIES,
+ &kvm_host.perf_capabilities);
+
WARN_ON_ONCE(kvm_nr_uret_msrs);
r = ops->hardware_setup();
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v9 05/12] KVM: x86/pmu: Support PERF_METRICS MSR in mediated vPMU
2026-09-18 19:39 [PATCH v9 00/12] KVM: x86/pmu: Add hardware Topdown metrics support Zide Chen
` (3 preceding siblings ...)
2026-09-18 19:39 ` [PATCH v9 04/12] KVM: x86/pmu: Snapshot host IA32_PERF_CAPABILITIES in kvm_host Zide Chen
@ 2026-09-18 19:39 ` Zide Chen
2026-09-18 19:39 ` [PATCH v9 06/12] KVM: x86/pmu: Move RDPMC emulation into per-vendor callbacks Zide Chen
` (6 subsequent siblings)
11 siblings, 0 replies; 23+ messages in thread
From: Zide Chen @ 2026-09-18 19:39 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini, Peter Zijlstra
Cc: kvm, Jim Mattson, Andi Kleen, Stephane Eranian, linux-kernel,
Mingwei Zhang, Zide Chen, Das Sandipan, Shukla Manali, Dapeng Mi,
Xudong Hao
From: Dapeng Mi <dapeng1.mi@linux.intel.com>
Bit 15 in IA32_PERF_CAPABILITIES indicates that the CPU provides
built-in support for Topdown Microarchitecture Analysis (TMA) L1
metrics via the IA32_PERF_METRICS MSR.
Expose this capability only when mediated vPMU is enabled, as emulating
IA32_PERF_METRICS in the legacy vPMU model is impractical.
Pass IA32_PERF_METRICS through to the guest only when mediated vPMU is
enabled and bit 15 is set in guest IA32_PERF_CAPABILITIES. Allow
kvm_pmu_{get,set}_msr() to handle this MSR for host accesses.
Save and restore this MSR on host/guest PMU context switches so that
host PMU activity does not clobber the guest value, and guest state
is not leaked into the host.
If the host supports PERF_METRICS but it is not exposed to the guest,
MSR_CORE_PERF_GLOBAL_CTRL must be intercepted so the guest cannot
enable PERF_METRICS via GLOBAL_CTRL[48].
Signed-off-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
Co-developed-by: Zide Chen <zide.chen@intel.com>
Signed-off-by: Zide Chen <zide.chen@intel.com>
---
v9:
- Remove the incorrect comment about writing to MSR_PERF_METRICS.
v8:
- Intercept global ctrl MSR if host supports PerfMetrics but not
enabled in the guest.
---
arch/x86/include/asm/kvm_host.h | 1 +
arch/x86/include/asm/msr-index.h | 1 +
arch/x86/kvm/msrs.c | 6 +++++-
arch/x86/kvm/pmu.c | 7 +++++++
arch/x86/kvm/pmu.h | 5 +++++
arch/x86/kvm/vmx/nested.c | 2 ++
arch/x86/kvm/vmx/pmu_intel.c | 29 +++++++++++++++++++++++++++++
arch/x86/kvm/vmx/vmx.c | 7 +++++++
8 files changed, 57 insertions(+), 1 deletion(-)
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index d00c7cbcfa14..052bf377dedb 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -584,6 +584,7 @@ struct kvm_pmu {
u64 global_status_rsvd;
u64 reserved_bits;
u64 raw_event_mask;
+ u64 perf_metrics;
struct kvm_pmc gp_counters[KVM_MAX_NR_GP_COUNTERS];
struct kvm_pmc fixed_counters[KVM_MAX_NR_FIXED_COUNTERS];
diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h
index 3a8e51a0c9e8..8fad944197fb 100644
--- a/arch/x86/include/asm/msr-index.h
+++ b/arch/x86/include/asm/msr-index.h
@@ -331,6 +331,7 @@
#define PERF_CAP_PEBS_FORMAT 0xf00
#define PERF_CAP_FW_WRITES BIT_ULL(13)
#define PERF_CAP_PEBS_BASELINE BIT_ULL(14)
+#define PERF_CAP_PERF_METRICS BIT_ULL(15)
#define PERF_CAP_PEBS_TIMING_INFO BIT_ULL(17)
#define PERF_CAP_PEBS_MASK (PERF_CAP_PEBS_TRAP | PERF_CAP_ARCH_REG | \
PERF_CAP_PEBS_FORMAT | PERF_CAP_PEBS_BASELINE | \
diff --git a/arch/x86/kvm/msrs.c b/arch/x86/kvm/msrs.c
index 45e4c8a32436..7b7b5db00f54 100644
--- a/arch/x86/kvm/msrs.c
+++ b/arch/x86/kvm/msrs.c
@@ -219,7 +219,7 @@ static const u32 msrs_to_save_pmu[] = {
MSR_ARCH_PERFMON_FIXED_CTR0, MSR_ARCH_PERFMON_FIXED_CTR1,
MSR_ARCH_PERFMON_FIXED_CTR2, MSR_ARCH_PERFMON_FIXED_CTR3,
MSR_CORE_PERF_FIXED_CTR_CTRL, MSR_CORE_PERF_GLOBAL_STATUS,
- MSR_CORE_PERF_GLOBAL_CTRL,
+ MSR_CORE_PERF_GLOBAL_CTRL, MSR_PERF_METRICS,
MSR_IA32_PEBS_ENABLE, MSR_IA32_DS_AREA, MSR_PEBS_DATA_CFG,
/* This part of MSRs should match KVM_MAX_NR_INTEL_GP_COUNTERS. */
@@ -2609,6 +2609,10 @@ static void kvm_probe_msr_to_save(u32 msr_index)
intel_pt_validate_hw_cap(PT_CAP_num_address_ranges) * 2))
return;
break;
+ case MSR_PERF_METRICS:
+ if (!(kvm_caps.supported_perf_cap & PERF_CAP_PERF_METRICS))
+ return;
+ break;
case MSR_ARCH_PERFMON_PERFCTR0 ...
MSR_ARCH_PERFMON_PERFCTR0 + KVM_MAX_NR_GP_COUNTERS - 1:
if (msr_index - MSR_ARCH_PERFMON_PERFCTR0 >=
diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c
index 448044a2b121..f4d3801b97bc 100644
--- a/arch/x86/kvm/pmu.c
+++ b/arch/x86/kvm/pmu.c
@@ -806,9 +806,16 @@ static bool kvm_need_any_pmc_intercept(struct kvm_vcpu *vcpu)
pmu->nr_arch_fixed_counters != kvm_host_pmu.num_counters_fixed;
}
+static bool kvm_need_perf_metrics_intercept(struct kvm_vcpu *vcpu)
+{
+ return (kvm_host.perf_capabilities & PERF_CAP_PERF_METRICS) &&
+ !kvm_vcpu_has_perf_metrics(vcpu);
+}
+
bool kvm_need_perf_global_ctrl_intercept(struct kvm_vcpu *vcpu)
{
return kvm_need_any_pmc_intercept(vcpu) ||
+ kvm_need_perf_metrics_intercept(vcpu) ||
!kvm_pmu_has_perf_global_ctrl(vcpu_to_pmu(vcpu));
}
EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_need_perf_global_ctrl_intercept);
diff --git a/arch/x86/kvm/pmu.h b/arch/x86/kvm/pmu.h
index 82f955e5a450..be578013ed83 100644
--- a/arch/x86/kvm/pmu.h
+++ b/arch/x86/kvm/pmu.h
@@ -279,6 +279,11 @@ static inline u64 kvm_vcpu_get_perf_caps(struct kvm_vcpu *vcpu)
return vcpu->arch.perf_capabilities;
}
+static inline bool kvm_vcpu_has_perf_metrics(struct kvm_vcpu *vcpu)
+{
+ return kvm_vcpu_get_perf_caps(vcpu) & PERF_CAP_PERF_METRICS;
+}
+
void kvm_pmu_deliver_pmi(struct kvm_vcpu *vcpu);
int kvm_pmu_rdpmc(struct kvm_vcpu *vcpu, unsigned pmc, u64 *data);
int kvm_pmu_check_rdpmc_early(struct kvm_vcpu *vcpu, unsigned int idx);
diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c
index 0e5f4d7ce9eb..a1cf57c6bea1 100644
--- a/arch/x86/kvm/vmx/nested.c
+++ b/arch/x86/kvm/vmx/nested.c
@@ -724,6 +724,8 @@ static void nested_vmx_merge_pmu_msr_bitmaps(struct kvm_vcpu *vcpu,
nested_vmx_merge_msr_bitmaps_rw(MSR_CORE_PERF_GLOBAL_CTRL);
nested_vmx_merge_msr_bitmaps_read(MSR_CORE_PERF_GLOBAL_STATUS);
nested_vmx_merge_msr_bitmaps_write(MSR_CORE_PERF_GLOBAL_OVF_CTRL);
+
+ nested_vmx_merge_msr_bitmaps_rw(MSR_PERF_METRICS);
}
/*
diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c
index 28c1542e5ba5..fe66f03b006b 100644
--- a/arch/x86/kvm/vmx/pmu_intel.c
+++ b/arch/x86/kvm/vmx/pmu_intel.c
@@ -192,6 +192,8 @@ static bool intel_is_valid_msr(struct kvm_vcpu *vcpu, u32 msr)
case MSR_CORE_PERF_GLOBAL_OVF_CTRL:
case MSR_CORE_PERF_FIXED_CTR_CTRL:
return kvm_pmu_has_perf_global_ctrl(pmu);
+ case MSR_PERF_METRICS:
+ return kvm_vcpu_has_perf_metrics(vcpu);
case MSR_IA32_PEBS_ENABLE:
ret = kvm_vcpu_get_perf_caps(vcpu) & PERF_CAP_PEBS_FORMAT;
break;
@@ -349,6 +351,9 @@ static int intel_pmu_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
case MSR_CORE_PERF_FIXED_CTR_CTRL:
msr_info->data = pmu->fixed_ctr_ctrl;
break;
+ case MSR_PERF_METRICS:
+ msr_info->data = pmu->perf_metrics;
+ break;
case MSR_IA32_PEBS_ENABLE:
msr_info->data = pmu->pebs_enable;
break;
@@ -398,6 +403,9 @@ static int intel_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
if (pmu->fixed_ctr_ctrl != data)
reprogram_fixed_counters(pmu, data);
break;
+ case MSR_PERF_METRICS:
+ pmu->perf_metrics = data;
+ break;
case MSR_IA32_PEBS_ENABLE:
if (data & pmu->pebs_enable_rsvd)
return 1;
@@ -580,6 +588,8 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu)
counter_rsvd = ~((BIT_ULL(pmu->nr_arch_gp_counters) - 1) |
((BIT_ULL(pmu->nr_arch_fixed_counters) - 1) << KVM_FIXED_PMC_BASE_IDX));
pmu->global_ctrl_rsvd = counter_rsvd;
+ if (perf_capabilities & PERF_CAP_PERF_METRICS)
+ pmu->global_ctrl_rsvd &= ~GLOBAL_CTRL_EN_PERF_METRICS;
/*
* GLOBAL_STATUS and GLOBAL_OVF_CONTROL (a.k.a. GLOBAL_STATUS_RESET)
@@ -636,6 +646,9 @@ static void intel_pmu_init(struct kvm_vcpu *vcpu)
static void intel_pmu_reset(struct kvm_vcpu *vcpu)
{
+ struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
+
+ pmu->perf_metrics = 0;
intel_pmu_release_guest_lbr_event(vcpu);
}
@@ -806,6 +819,9 @@ static void intel_mediated_pmu_load(struct kvm_vcpu *vcpu)
struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
u64 global_status, toggle;
+ if (kvm_vcpu_has_perf_metrics(vcpu))
+ wrmsrq(MSR_PERF_METRICS, pmu->perf_metrics);
+
rdmsrq(MSR_CORE_PERF_GLOBAL_STATUS, global_status);
toggle = pmu->global_status ^ global_status;
if (global_status & toggle)
@@ -834,6 +850,19 @@ static void intel_mediated_pmu_put(struct kvm_vcpu *vcpu)
*/
if (pmu->fixed_ctr_ctrl_hw)
wrmsrq(MSR_CORE_PERF_FIXED_CTR_CTRL, 0);
+
+ if (kvm_vcpu_has_perf_metrics(vcpu)) {
+ pmu->perf_metrics = rdpmc(INTEL_PMC_FIXED_RDPMC_METRICS);
+ /*
+ * The SDM requires restoring fixed counter 3 before
+ * PERF_METRICS. However, this path writes 0 to PERF_METRICS
+ * before fixed counter 3. For this all-zero case, the
+ * resulting hardware state is therefore the same regardless
+ * of write order.
+ */
+ if (pmu->perf_metrics)
+ wrmsrq(MSR_PERF_METRICS, 0);
+ }
}
struct kvm_pmu_ops intel_pmu_ops __initdata = {
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index 715e44489e22..372de66365ab 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -4289,6 +4289,10 @@ static void vmx_recalc_pmu_msr_intercepts(struct kvm_vcpu *vcpu)
MSR_TYPE_RW, intercept);
vmx_set_intercept_for_msr(vcpu, MSR_CORE_PERF_GLOBAL_OVF_CTRL,
MSR_TYPE_RW, intercept);
+
+ intercept = !has_mediated_pmu || !kvm_vcpu_has_perf_metrics(vcpu);
+ vmx_set_intercept_for_msr(vcpu, MSR_PERF_METRICS,
+ MSR_TYPE_RW, intercept);
}
static void vmx_recalc_msr_intercepts(struct kvm_vcpu *vcpu)
@@ -7994,6 +7998,9 @@ static __init u64 vmx_get_perf_capabilities(void)
perf_cap &= ~PERF_CAP_PEBS_BASELINE;
}
+ if (enable_mediated_pmu)
+ perf_cap |= kvm_host.perf_capabilities & PERF_CAP_PERF_METRICS;
+
return perf_cap;
}
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v9 06/12] KVM: x86/pmu: Move RDPMC emulation into per-vendor callbacks
2026-09-18 19:39 [PATCH v9 00/12] KVM: x86/pmu: Add hardware Topdown metrics support Zide Chen
` (4 preceding siblings ...)
2026-09-18 19:39 ` [PATCH v9 05/12] KVM: x86/pmu: Support PERF_METRICS MSR in mediated vPMU Zide Chen
@ 2026-09-18 19:39 ` Zide Chen
2026-09-18 19:39 ` [PATCH v9 07/12] KVM: x86/pmu: Emulate RDPMC on performance metrics Zide Chen
` (5 subsequent siblings)
11 siblings, 0 replies; 23+ messages in thread
From: Zide Chen @ 2026-09-18 19:39 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini, Peter Zijlstra
Cc: kvm, Jim Mattson, Andi Kleen, Stephane Eranian, linux-kernel,
Mingwei Zhang, Zide Chen, Das Sandipan, Shukla Manali, Dapeng Mi,
Xudong Hao
The current RDPMC emulation splits responsibility: rdpmc_ecx_to_pmc()
in each vendor returns a kvm_pmc, then common code calls
pmc_read_counter().
This design cannot support RDPMC reads that don't map to a counter,
such as PERF_METRICS on Intel platforms.
Replace rdpmc_ecx_to_pmc() with emulate_rdpmc(), which takes full
ownership of the emulation and writes the result directly into @data.
Opportunistically drop the redundant bitmask in intel_emulate_rdpmc()
since pmc_read_counter() already applies the counter's bit-width mask.
No functional change intended.
Signed-off-by: Zide Chen <zide.chen@intel.com>
Reviewed-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
Reviewed-by: Jim Mattson <jmattson@google.com>
Reviewed-by: Sandipan Das <sandipan.das@amd.com>
---
v6: new patch.
---
arch/x86/include/asm/kvm-x86-pmu-ops.h | 2 +-
arch/x86/kvm/pmu.c | 9 +--------
arch/x86/kvm/pmu.h | 4 ++--
arch/x86/kvm/svm/pmu.c | 13 +++++++++----
arch/x86/kvm/vmx/pmu_intel.c | 25 ++++++++++++-------------
5 files changed, 25 insertions(+), 28 deletions(-)
diff --git a/arch/x86/include/asm/kvm-x86-pmu-ops.h b/arch/x86/include/asm/kvm-x86-pmu-ops.h
index 4a223c2793e3..4b50ed058aed 100644
--- a/arch/x86/include/asm/kvm-x86-pmu-ops.h
+++ b/arch/x86/include/asm/kvm-x86-pmu-ops.h
@@ -13,7 +13,7 @@
* KVM_X86_PMU_OP_OPTIONAL() can be used for those functions that can have
* a NULL definition.
*/
-KVM_X86_PMU_OP(rdpmc_ecx_to_pmc)
+KVM_X86_PMU_OP(emulate_rdpmc)
KVM_X86_PMU_OP(msr_idx_to_pmc)
KVM_X86_PMU_OP_OPTIONAL(check_rdpmc_early)
KVM_X86_PMU_OP(is_valid_msr)
diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c
index f4d3801b97bc..897c1428b650 100644
--- a/arch/x86/kvm/pmu.c
+++ b/arch/x86/kvm/pmu.c
@@ -768,8 +768,6 @@ static int kvm_pmu_rdpmc_vmware(struct kvm_vcpu *vcpu, unsigned idx, u64 *data)
int kvm_pmu_rdpmc(struct kvm_vcpu *vcpu, unsigned idx, u64 *data)
{
struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
- struct kvm_pmc *pmc;
- u64 mask = ~0ull;
if (!pmu->version)
return 1;
@@ -777,17 +775,12 @@ int kvm_pmu_rdpmc(struct kvm_vcpu *vcpu, unsigned idx, u64 *data)
if (is_vmware_backdoor_pmc(idx))
return kvm_pmu_rdpmc_vmware(vcpu, idx, data);
- pmc = kvm_pmu_call(rdpmc_ecx_to_pmc)(vcpu, idx, &mask);
- if (!pmc)
- return 1;
-
if (!kvm_is_cr4_bit_set(vcpu, X86_CR4_PCE) &&
(kvm_x86_call(get_cpl)(vcpu) != 0) &&
kvm_is_cr0_bit_set(vcpu, X86_CR0_PE))
return 1;
- *data = pmc_read_counter(pmc) & mask;
- return 0;
+ return kvm_pmu_call(emulate_rdpmc)(vcpu, idx, data);
}
static bool kvm_need_any_pmc_intercept(struct kvm_vcpu *vcpu)
diff --git a/arch/x86/kvm/pmu.h b/arch/x86/kvm/pmu.h
index be578013ed83..eb6bbc658f5b 100644
--- a/arch/x86/kvm/pmu.h
+++ b/arch/x86/kvm/pmu.h
@@ -24,8 +24,8 @@
#define KVM_FIXED_PMC_BASE_IDX INTEL_PMC_IDX_FIXED
struct kvm_pmu_ops {
- struct kvm_pmc *(*rdpmc_ecx_to_pmc)(struct kvm_vcpu *vcpu,
- unsigned int idx, u64 *mask);
+ int (*emulate_rdpmc)(struct kvm_vcpu *vcpu, unsigned int idx,
+ u64 *data);
struct kvm_pmc *(*msr_idx_to_pmc)(struct kvm_vcpu *vcpu, u32 msr);
int (*check_rdpmc_early)(struct kvm_vcpu *vcpu, unsigned int idx);
bool (*is_valid_msr)(struct kvm_vcpu *vcpu, u32 msr);
diff --git a/arch/x86/kvm/svm/pmu.c b/arch/x86/kvm/svm/pmu.c
index c18286545a7a..0517fd4bbcd7 100644
--- a/arch/x86/kvm/svm/pmu.c
+++ b/arch/x86/kvm/svm/pmu.c
@@ -84,10 +84,15 @@ static int amd_check_rdpmc_early(struct kvm_vcpu *vcpu, unsigned int idx)
}
/* idx is the ECX register of RDPMC instruction */
-static struct kvm_pmc *amd_rdpmc_ecx_to_pmc(struct kvm_vcpu *vcpu,
- unsigned int idx, u64 *mask)
+static int amd_emulate_rdpmc(struct kvm_vcpu *vcpu, unsigned int idx, u64 *data)
{
- return amd_pmu_get_pmc(vcpu_to_pmu(vcpu), idx);
+ struct kvm_pmc *pmc = amd_pmu_get_pmc(vcpu_to_pmu(vcpu), idx);
+
+ if (!pmc)
+ return 1;
+
+ *data = pmc_read_counter(pmc);
+ return 0;
}
static struct kvm_pmc *amd_msr_idx_to_pmc(struct kvm_vcpu *vcpu, u32 msr)
@@ -302,7 +307,7 @@ static bool amd_pmc_is_disabled_in_current_mode(struct kvm_pmc *pmc)
}
struct kvm_pmu_ops amd_pmu_ops __initdata = {
- .rdpmc_ecx_to_pmc = amd_rdpmc_ecx_to_pmc,
+ .emulate_rdpmc = amd_emulate_rdpmc,
.msr_idx_to_pmc = amd_msr_idx_to_pmc,
.check_rdpmc_early = amd_check_rdpmc_early,
.is_valid_msr = amd_is_valid_msr,
diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c
index fe66f03b006b..2eb1bff75e60 100644
--- a/arch/x86/kvm/vmx/pmu_intel.c
+++ b/arch/x86/kvm/vmx/pmu_intel.c
@@ -85,14 +85,13 @@ static void reprogram_fixed_counters(struct kvm_pmu *pmu, u64 data)
}
}
-static struct kvm_pmc *intel_rdpmc_ecx_to_pmc(struct kvm_vcpu *vcpu,
- unsigned int idx, u64 *mask)
+static int intel_emulate_rdpmc(struct kvm_vcpu *vcpu, unsigned int idx,
+ u64 *data)
{
unsigned int type = idx & INTEL_RDPMC_TYPE_MASK;
struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
- struct kvm_pmc *counters;
+ struct kvm_pmc *counters, *pmc;
unsigned int num_counters;
- u64 bitmask;
/*
* The encoding of ECX for RDPMC is different for architectural versus
@@ -105,7 +104,9 @@ static struct kvm_pmc *intel_rdpmc_ecx_to_pmc(struct kvm_vcpu *vcpu,
* as KVM doesn't support such PMUs.
*/
if (WARN_ON_ONCE(!pmu->version))
- return NULL;
+ return 1;
+
+ idx &= INTEL_RDPMC_INDEX_MASK;
/*
* General Purpose (GP) PMCs are supported on all PMUs, and fixed PMCs
@@ -119,23 +120,21 @@ static struct kvm_pmc *intel_rdpmc_ecx_to_pmc(struct kvm_vcpu *vcpu,
case INTEL_RDPMC_FIXED:
counters = pmu->fixed_counters;
num_counters = pmu->nr_arch_fixed_counters;
- bitmask = pmu->counter_bitmask[KVM_PMC_FIXED];
break;
case INTEL_RDPMC_GP:
counters = pmu->gp_counters;
num_counters = pmu->nr_arch_gp_counters;
- bitmask = pmu->counter_bitmask[KVM_PMC_GP];
break;
default:
- return NULL;
+ return 1;
}
- idx &= INTEL_RDPMC_INDEX_MASK;
if (idx >= num_counters)
- return NULL;
+ return 1;
- *mask &= bitmask;
- return &counters[array_index_nospec(idx, num_counters)];
+ pmc = &counters[array_index_nospec(idx, num_counters)];
+ *data = pmc_read_counter(pmc);
+ return 0;
}
static inline struct kvm_pmc *get_fw_gp_pmc(struct kvm_pmu *pmu, u32 msr)
@@ -866,7 +865,7 @@ static void intel_mediated_pmu_put(struct kvm_vcpu *vcpu)
}
struct kvm_pmu_ops intel_pmu_ops __initdata = {
- .rdpmc_ecx_to_pmc = intel_rdpmc_ecx_to_pmc,
+ .emulate_rdpmc = intel_emulate_rdpmc,
.msr_idx_to_pmc = intel_msr_idx_to_pmc,
.is_valid_msr = intel_is_valid_msr,
.get_msr = intel_pmu_get_msr,
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v9 07/12] KVM: x86/pmu: Emulate RDPMC on performance metrics
2026-09-18 19:39 [PATCH v9 00/12] KVM: x86/pmu: Add hardware Topdown metrics support Zide Chen
` (5 preceding siblings ...)
2026-09-18 19:39 ` [PATCH v9 06/12] KVM: x86/pmu: Move RDPMC emulation into per-vendor callbacks Zide Chen
@ 2026-09-18 19:39 ` Zide Chen
2026-09-18 19:39 ` [PATCH v9 08/12] KVM: selftests: Add PERF_METRICS and fixed counter 3 tests Zide Chen
` (4 subsequent siblings)
11 siblings, 0 replies; 23+ messages in thread
From: Zide Chen @ 2026-09-18 19:39 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini, Peter Zijlstra
Cc: kvm, Jim Mattson, Andi Kleen, Stephane Eranian, linux-kernel,
Mingwei Zhang, Zide Chen, Das Sandipan, Shukla Manali, Dapeng Mi,
Xudong Hao
If the host has the PERF_METRICS capability but it's not present on
the guest, RDPMC interception must be enabled and KVM should inject
an #GP when the guest attempts a PERF_METRICS RDPMC.
If the guest has PERF_METRICS but RDPMC interception is enabled for
other reasons, KVM needs to emulate RDPMC with type 0x2000.
Signed-off-by: Zide Chen <zide.chen@intel.com>
Reviewed-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
---
v9:
- Remove references to Metrics Clear from the changelog.
v8:
- Move kvm_need_perf_metrics_intercept() to patch 5/9.
---
arch/x86/kvm/pmu.c | 1 +
arch/x86/kvm/vmx/pmu_intel.c | 14 ++++++++++++++
2 files changed, 15 insertions(+)
diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c
index 897c1428b650..0df283a169b6 100644
--- a/arch/x86/kvm/pmu.c
+++ b/arch/x86/kvm/pmu.c
@@ -825,6 +825,7 @@ bool kvm_need_rdpmc_intercept(struct kvm_vcpu *vcpu)
return true;
return kvm_need_any_pmc_intercept(vcpu) ||
+ kvm_need_perf_metrics_intercept(vcpu) ||
pmu->counter_bitmask[KVM_PMC_GP] != (BIT_ULL(kvm_host_pmu.bit_width_gp) - 1) ||
pmu->counter_bitmask[KVM_PMC_FIXED] != (BIT_ULL(kvm_host_pmu.bit_width_fixed) - 1);
}
diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c
index 2eb1bff75e60..fab0891ad46a 100644
--- a/arch/x86/kvm/vmx/pmu_intel.c
+++ b/arch/x86/kvm/vmx/pmu_intel.c
@@ -31,6 +31,7 @@
*/
#define INTEL_RDPMC_GP 0
#define INTEL_RDPMC_FIXED INTEL_PMC_FIXED_RDPMC_BASE
+#define INTEL_RDPMC_METRICS INTEL_PMC_FIXED_RDPMC_METRICS
#define INTEL_RDPMC_TYPE_MASK GENMASK(31, 16)
#define INTEL_RDPMC_INDEX_MASK GENMASK(15, 0)
@@ -125,6 +126,19 @@ static int intel_emulate_rdpmc(struct kvm_vcpu *vcpu, unsigned int idx,
counters = pmu->gp_counters;
num_counters = pmu->nr_arch_gp_counters;
break;
+ case INTEL_RDPMC_METRICS:
+ if (!kvm_vcpu_has_perf_metrics(vcpu))
+ return 1;
+
+ /*
+ * The index in ECX[15:0] is implementation specific, but no
+ * platform currently supports a non-zero index.
+ */
+ if (idx)
+ return 1;
+
+ *data = pmu->perf_metrics;
+ return 0;
default:
return 1;
}
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v9 08/12] KVM: selftests: Add PERF_METRICS and fixed counter 3 tests
2026-09-18 19:39 [PATCH v9 00/12] KVM: x86/pmu: Add hardware Topdown metrics support Zide Chen
` (6 preceding siblings ...)
2026-09-18 19:39 ` [PATCH v9 07/12] KVM: x86/pmu: Emulate RDPMC on performance metrics Zide Chen
@ 2026-09-18 19:39 ` Zide Chen
2026-09-18 20:08 ` sashiko-bot
2026-09-18 19:39 ` [PATCH v9 09/12] perf/x86: Add INTEL_TD_METRIC_FIELD_{BITS,MASK} constants Zide Chen
` (3 subsequent siblings)
11 siblings, 1 reply; 23+ messages in thread
From: Zide Chen @ 2026-09-18 19:39 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini, Peter Zijlstra
Cc: kvm, Jim Mattson, Andi Kleen, Stephane Eranian, linux-kernel,
Mingwei Zhang, Zide Chen, Das Sandipan, Shukla Manali, Dapeng Mi,
Xudong Hao
Add a test case to exercise IA32_PERF_METRICS, i.e. architectural
support for Topdown (TMA) Level 1 metrics, enumerated by
IA32_PERF_CAPABILITIES[15].
Only check for non-zero metrics, as they are derived and depend on
the workload, CPU model, and host scheduling, making precise
expectations fragile.
Extend the PMU selftest to cover Intel fixed counter 3 by bumping
MAX_NR_FIXED_COUNTERS to 4 and validating basic functionality.
Signed-off-by: Zide Chen <zide.chen@intel.com>
Reviewed-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
---
v7:
- Use RDPMC instead of RDMSR in PERF_METRICS sanity test.
---
tools/arch/x86/include/asm/msr-index.h | 1 +
tools/testing/selftests/kvm/include/x86/pmu.h | 3 +
.../selftests/kvm/x86/pmu_counters_test.c | 97 ++++++++++++++++++-
3 files changed, 96 insertions(+), 5 deletions(-)
diff --git a/tools/arch/x86/include/asm/msr-index.h b/tools/arch/x86/include/asm/msr-index.h
index 18c4be75e927..fdcaeb6c8352 100644
--- a/tools/arch/x86/include/asm/msr-index.h
+++ b/tools/arch/x86/include/asm/msr-index.h
@@ -331,6 +331,7 @@
#define PERF_CAP_PEBS_FORMAT 0xf00
#define PERF_CAP_FW_WRITES BIT_ULL(13)
#define PERF_CAP_PEBS_BASELINE BIT_ULL(14)
+#define PERF_CAP_PERF_METRICS BIT_ULL(15)
#define PERF_CAP_PEBS_TIMING_INFO BIT_ULL(17)
#define PERF_CAP_PEBS_MASK (PERF_CAP_PEBS_TRAP | PERF_CAP_ARCH_REG | \
PERF_CAP_PEBS_FORMAT | PERF_CAP_PEBS_BASELINE | \
diff --git a/tools/testing/selftests/kvm/include/x86/pmu.h b/tools/testing/selftests/kvm/include/x86/pmu.h
index 608ed83d7c6a..6c19503e0bb7 100644
--- a/tools/testing/selftests/kvm/include/x86/pmu.h
+++ b/tools/testing/selftests/kvm/include/x86/pmu.h
@@ -52,6 +52,9 @@
/* Fixed PMC controls, Intel only. */
#define FIXED_PMC_GLOBAL_CTRL_ENABLE(_idx) BIT_ULL((32 + (_idx)))
+/* PERF_METRICS enable, Intel only. */
+#define PERF_METRICS_GLOBAL_CTRL_ENABLE BIT_ULL(48)
+
#define FIXED_PMC_KERNEL BIT_ULL(0)
#define FIXED_PMC_USER BIT_ULL(1)
#define FIXED_PMC_ANYTHREAD BIT_ULL(2)
diff --git a/tools/testing/selftests/kvm/x86/pmu_counters_test.c b/tools/testing/selftests/kvm/x86/pmu_counters_test.c
index c3e784e16348..cf09b587a4ac 100644
--- a/tools/testing/selftests/kvm/x86/pmu_counters_test.c
+++ b/tools/testing/selftests/kvm/x86/pmu_counters_test.c
@@ -3,6 +3,7 @@
* Copyright (C) 2023, Tencent, Inc.
*/
#include <x86intrin.h>
+#include <linux/bitfield.h>
#include "pmu.h"
#include "processor.h"
@@ -254,17 +255,20 @@ do { \
); \
} while (0)
-#define GUEST_TEST_EVENT(_idx, _pmc, _pmc_msr, _ctrl_msr, _value, FEP) \
+#define GUEST_RUN_PAYLOAD(_ctrl_msr, _value, FEP) \
do { \
- wrmsr(_pmc_msr, 0); \
- \
if (this_cpu_has(X86_FEATURE_CLFLUSHOPT)) \
GUEST_MEASURE_EVENT(_ctrl_msr, _value, "clflushopt %[m]", FEP); \
else if (this_cpu_has(X86_FEATURE_CLFLUSH)) \
GUEST_MEASURE_EVENT(_ctrl_msr, _value, "clflush %[m]", FEP); \
else \
GUEST_MEASURE_EVENT(_ctrl_msr, _value, "nop", FEP); \
- \
+} while (0)
+
+#define GUEST_TEST_EVENT(_idx, _pmc, _pmc_msr, _ctrl_msr, _value, FEP) \
+do { \
+ wrmsr(_pmc_msr, 0); \
+ GUEST_RUN_PAYLOAD(_ctrl_msr, _value, FEP); \
guest_assert_event_count(_idx, _pmc, _pmc_msr); \
} while (0)
@@ -412,7 +416,7 @@ static void test_arch_events(u8 pmu_version, u64 perf_capabilities)
* other than PMCs in the future.
*/
#define MAX_NR_GP_COUNTERS 8
-#define MAX_NR_FIXED_COUNTERS 3
+#define MAX_NR_FIXED_COUNTERS 4
#define GUEST_ASSERT_PMC_MSR_ACCESS(insn, msr, expect_gp, vector) \
__GUEST_ASSERT(expect_gp ? vector == GP_VECTOR : !vector, \
@@ -653,8 +657,85 @@ static void test_fixed_counters(u8 pmu_version, u64 perf_capabilities)
pmu_vm_free(vm, vcpus);
}
+static void __guest_test_perf_metrics(void)
+{
+ int retiring, bad_spec, fe_bound, be_bound, sum;
+ u64 global_ctrl, metrics;
+
+ if ((guest_get_pmu_version() < 2) || /* Does guest have GLOBAL_CTRL? */
+ !this_cpu_has(X86_FEATURE_PDCM) ||
+ !(rdmsr(MSR_IA32_PERF_CAPABILITIES) & PERF_CAP_PERF_METRICS))
+ return;
+
+ wrmsr(MSR_CORE_PERF_GLOBAL_CTRL, 0);
+ wrmsr(MSR_CORE_PERF_FIXED_CTR3, 0);
+ wrmsr(MSR_PERF_METRICS, 0);
+
+ /* Enable fixed ctr3 (TOPDOWN.SLOTS) and PERF_METRICS. */
+ wrmsr(MSR_CORE_PERF_FIXED_CTR_CTRL, FIXED_PMC_CTRL(3, FIXED_PMC_KERNEL));
+ global_ctrl = FIXED_PMC_GLOBAL_CTRL_ENABLE(3) |
+ PERF_METRICS_GLOBAL_CTRL_ENABLE;
+
+ GUEST_RUN_PAYLOAD(MSR_CORE_PERF_GLOBAL_CTRL, global_ctrl, "");
+
+ /* Check test results. */
+ metrics = rdmsr(MSR_PERF_METRICS);
+ retiring = FIELD_GET(GENMASK_ULL(7, 0), metrics);
+ bad_spec = FIELD_GET(GENMASK_ULL(15, 8), metrics);
+ fe_bound = FIELD_GET(GENMASK_ULL(23, 16), metrics);
+ be_bound = FIELD_GET(GENMASK_ULL(31, 24), metrics);
+
+ /*
+ * Be conservative: the measured payload definitely retires work, so
+ * Retiring should be non-zero.
+ */
+ GUEST_ASSERT_NE(metrics, 0);
+ GUEST_ASSERT_NE(retiring, 0);
+
+ /*
+ * Each level-1 Topdown metric is an integer fraction of 0xff.
+ * A +/-3 error margin is chosen for a loose sanity check.
+ */
+ sum = retiring + bad_spec + fe_bound + be_bound;
+ GUEST_ASSERT(sum >= 0xfc && sum <= 0x102);
+
+ /*
+ * Burn some cycles; MSR_CORE_PERF_GLOBAL_CTRL is left zeroed after
+ * GUEST_RUN_PAYLOAD() returns, so PERF_METRICS is not counting.
+ */
+ __asm__ __volatile__("loop ." : "+c"((int){NUM_LOOPS}));
+ /* Confirm PERF_METRICS did not advance while counting was disabled. */
+ GUEST_ASSERT_EQ(rdmsr(MSR_PERF_METRICS), metrics);
+ wrmsr(MSR_PERF_METRICS, 0xdeaddead);
+
+ guest_test_rdpmc(INTEL_RDPMC_METRICS, true, 0xdeaddead);
+}
+
+static void guest_test_perf_metrics(void)
+{
+ __guest_test_perf_metrics();
+ GUEST_DONE();
+}
+
+static void test_perf_metrics(u8 pmu_version, u64 perf_capabilities)
+{
+ struct kvm_vcpu **vcpus;
+ struct kvm_vm *vm;
+
+ pr_info("Testing Perf Metrics, PMU version %u, perf_caps = %lx\n",
+ pmu_version, perf_capabilities);
+
+ vm = pmu_vm_create_with_vcpus(1, guest_test_perf_metrics,
+ pmu_version, perf_capabilities, &vcpus);
+
+ run_vcpu(vcpus[0]);
+
+ pmu_vm_free(vm, vcpus);
+}
+
static void test_intel_counters(void)
{
+ u64 advertised_perf_caps = kvm_get_feature_msr(MSR_IA32_PERF_CAPABILITIES);
u8 pmu_version = kvm_cpu_property(X86_PROPERTY_PMU_VERSION);
unsigned int i;
u8 v;
@@ -662,6 +743,7 @@ static void test_intel_counters(void)
const u64 perf_caps[] = {
0,
PMU_CAP_FW_WRITES,
+ PERF_CAP_PERF_METRICS,
};
/*
@@ -699,9 +781,14 @@ static void test_intel_counters(void)
if (!kvm_has_perf_caps && perf_caps[i])
continue;
+ /* Ignore unsupported features. */
+ if (perf_caps[i] & ~advertised_perf_caps)
+ continue;
+
test_arch_events(v, perf_caps[i]);
test_gp_counters(v, perf_caps[i]);
test_fixed_counters(v, perf_caps[i]);
+ test_perf_metrics(v, perf_caps[i]);
}
}
}
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v9 09/12] perf/x86: Add INTEL_TD_METRIC_FIELD_{BITS,MASK} constants
2026-09-18 19:39 [PATCH v9 00/12] KVM: x86/pmu: Add hardware Topdown metrics support Zide Chen
` (7 preceding siblings ...)
2026-09-18 19:39 ` [PATCH v9 08/12] KVM: selftests: Add PERF_METRICS and fixed counter 3 tests Zide Chen
@ 2026-09-18 19:39 ` Zide Chen
2026-09-18 19:59 ` sashiko-bot
2026-09-21 7:01 ` Mi, Dapeng
2026-09-18 19:39 ` [PATCH v9 10/12] perf/x86: Expose number of Topdown metric events to KVM Zide Chen
` (2 subsequent siblings)
11 siblings, 2 replies; 23+ messages in thread
From: Zide Chen @ 2026-09-18 19:39 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini, Peter Zijlstra
Cc: kvm, Jim Mattson, Andi Kleen, Stephane Eranian, linux-kernel,
Mingwei Zhang, Zide Chen, Das Sandipan, Shukla Manali, Dapeng Mi,
Xudong Hao
Replace the hard coded field width (8) and the equivalent 0xff mask
used for PERF_METRICS MSR field access with named constants
INTEL_TD_METRIC_FIELD_{BITS,MASK}.
INTEL_TD_METRIC_FIELD_BITS will be used by a subsequent KVM patch to
validat reserved bits in the PERF_METRICS MSR.
No functional change intended.
Signed-off-by: Zide Chen <zide.chen@intel.com>
Cc: Peter Zijlstra <peterz@infradead.org>
---
v9: new patch.
---
arch/x86/events/intel/core.c | 8 +++++---
arch/x86/include/asm/perf_event.h | 4 ++++
2 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c
index cc13164d948f..cbc9a179f2e5 100644
--- a/arch/x86/events/intel/core.c
+++ b/arch/x86/events/intel/core.c
@@ -3159,6 +3159,7 @@ DEFINE_STATIC_CALL(intel_pmu_set_topdown_event_period, x86_perf_event_set_period
static inline u64 icl_get_metrics_event_value(u64 metric, u64 slots, int idx)
{
+ int shift = (idx - INTEL_PMC_IDX_METRIC_BASE) * INTEL_TD_METRIC_FIELD_BITS;
u32 val;
/*
@@ -3166,8 +3167,8 @@ static inline u64 icl_get_metrics_event_value(u64 metric, u64 slots, int idx)
* summing up to 0xff.
* slots-in-metric = (Metric / 0xff) * slots
*/
- val = (metric >> ((idx - INTEL_PMC_IDX_METRIC_BASE) * 8)) & 0xff;
- return mul_u64_u32_div(slots, val, 0xff);
+ val = (metric >> shift) & INTEL_TD_METRIC_FIELD_MASK;
+ return mul_u64_u32_div(slots, val, INTEL_TD_METRIC_FIELD_MASK);
}
static u64 icl_get_topdown_value(struct perf_event *event,
@@ -4742,7 +4743,8 @@ static int core_pmu_hw_config(struct perf_event *event)
}
#define INTEL_TD_METRIC_AVAILABLE_MAX (INTEL_TD_METRIC_RETIRING + \
- ((x86_pmu.num_topdown_events - 1) << 8))
+ ((x86_pmu.num_topdown_events - 1) << \
+ INTEL_TD_METRIC_FIELD_BITS))
static bool is_available_metric_event(struct perf_event *event)
{
diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h
index 1eb13673e889..ceb6188e5217 100644
--- a/arch/x86/include/asm/perf_event.h
+++ b/arch/x86/include/asm/perf_event.h
@@ -418,6 +418,10 @@ static inline bool use_fixed_pseudo_encoding(u64 code)
#define INTEL_TD_METRIC_MAX INTEL_TD_METRIC_MEM_BOUND
#define INTEL_TD_METRIC_NUM 8
+/* Width, in bits, of each metric's field within the PERF_METRICS MSR. */
+#define INTEL_TD_METRIC_FIELD_BITS 8
+#define INTEL_TD_METRIC_FIELD_MASK GENMASK_ULL(INTEL_TD_METRIC_FIELD_BITS - 1, 0)
+
#define INTEL_TD_CFG_METRIC_CLEAR_BIT 0
#define INTEL_TD_CFG_METRIC_CLEAR BIT_ULL(INTEL_TD_CFG_METRIC_CLEAR_BIT)
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v9 10/12] perf/x86: Expose number of Topdown metric events to KVM
2026-09-18 19:39 [PATCH v9 00/12] KVM: x86/pmu: Add hardware Topdown metrics support Zide Chen
` (8 preceding siblings ...)
2026-09-18 19:39 ` [PATCH v9 09/12] perf/x86: Add INTEL_TD_METRIC_FIELD_{BITS,MASK} constants Zide Chen
@ 2026-09-18 19:39 ` Zide Chen
2026-09-21 7:06 ` Mi, Dapeng
2026-09-18 19:39 ` [PATCH v9 11/12] KVM: x86/pmu: Reject writes to reserved MSR_PERF_METRICS bits Zide Chen
2026-09-18 19:39 ` [PATCH v9 12/12] KVM: x86/pmu: Support RDPMC Metrics Clear Mode Zide Chen
11 siblings, 1 reply; 23+ messages in thread
From: Zide Chen @ 2026-09-18 19:39 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini, Peter Zijlstra
Cc: kvm, Jim Mattson, Andi Kleen, Stephane Eranian, linux-kernel,
Mingwei Zhang, Zide Chen, Das Sandipan, Shukla Manali, Dapeng Mi,
Xudong Hao
The number of Topdown metric events varies by CPU: ICL/TGL/RKL
support 4, while SPR/GNR/DMR and some hybrid CPUs support 8.
Add num_topdown_events to struct x86_pmu_capability and populate it
in perf_get_x86_pmu_capability() so that KVM can determine how many
PERF_METRICS fields are valid on the host CPU and correctly identify
the reserved bits in IA32_PERF_METRICS.
Signed-off-by: Zide Chen <zide.chen@intel.com>
Cc: Peter Zijlstra <peterz@infradead.org>
---
v9: new patch
---
arch/x86/events/core.c | 1 +
arch/x86/include/asm/perf_event.h | 1 +
2 files changed, 2 insertions(+)
diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c
index 8b3ea0adb965..01c1c9447f30 100644
--- a/arch/x86/events/core.c
+++ b/arch/x86/events/core.c
@@ -3164,6 +3164,7 @@ void perf_get_x86_pmu_capability(struct x86_pmu_capability *cap)
cap->events_mask_len = x86_pmu.events_mask_len;
cap->pebs_ept = x86_pmu.pebs_ept;
cap->mediated = !!(pmu.capabilities & PERF_PMU_CAP_MEDIATED_VPMU);
+ cap->num_topdown_events = x86_pmu.num_topdown_events;
}
EXPORT_SYMBOL_FOR_KVM(perf_get_x86_pmu_capability);
diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h
index ceb6188e5217..3c02389b2ea6 100644
--- a/arch/x86/include/asm/perf_event.h
+++ b/arch/x86/include/asm/perf_event.h
@@ -306,6 +306,7 @@ struct x86_pmu_capability {
int bit_width_fixed;
unsigned int events_mask;
int events_mask_len;
+ int num_topdown_events;
unsigned int pebs_ept :1;
unsigned int mediated :1;
};
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v9 11/12] KVM: x86/pmu: Reject writes to reserved MSR_PERF_METRICS bits
2026-09-18 19:39 [PATCH v9 00/12] KVM: x86/pmu: Add hardware Topdown metrics support Zide Chen
` (9 preceding siblings ...)
2026-09-18 19:39 ` [PATCH v9 10/12] perf/x86: Expose number of Topdown metric events to KVM Zide Chen
@ 2026-09-18 19:39 ` Zide Chen
2026-09-21 7:11 ` Mi, Dapeng
2026-09-18 19:39 ` [PATCH v9 12/12] KVM: x86/pmu: Support RDPMC Metrics Clear Mode Zide Chen
11 siblings, 1 reply; 23+ messages in thread
From: Zide Chen @ 2026-09-18 19:39 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini, Peter Zijlstra
Cc: kvm, Jim Mattson, Andi Kleen, Stephane Eranian, linux-kernel,
Mingwei Zhang, Zide Chen, Das Sandipan, Shukla Manali, Dapeng Mi,
Xudong Hao
When the host CPU supports only 4 Topdown metrics, bits [63:32] of
MSR_PERF_METRICS are reserved.
Derive pmu->perf_metrics_rsvd from the number of supported Topdown
metrics and use it to validate writes. Reject any attempts to set
reserved bits in MSR_PERF_METRICS.
Signed-off-by: Zide Chen <zide.chen@intel.com>
---
v9: new patch.
---
arch/x86/include/asm/kvm_host.h | 1 +
arch/x86/kvm/pmu.c | 1 +
arch/x86/kvm/vmx/pmu_intel.c | 18 +++++++++++++++++-
3 files changed, 19 insertions(+), 1 deletion(-)
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 052bf377dedb..31454ecee371 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -585,6 +585,7 @@ struct kvm_pmu {
u64 reserved_bits;
u64 raw_event_mask;
u64 perf_metrics;
+ u64 perf_metrics_rsvd;
struct kvm_pmc gp_counters[KVM_MAX_NR_GP_COUNTERS];
struct kvm_pmc fixed_counters[KVM_MAX_NR_FIXED_COUNTERS];
diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c
index 0df283a169b6..41f2d31c495f 100644
--- a/arch/x86/kvm/pmu.c
+++ b/arch/x86/kvm/pmu.c
@@ -1006,6 +1006,7 @@ void kvm_pmu_refresh(struct kvm_vcpu *vcpu)
pmu->fixed_ctr_ctrl_rsvd = ~0ull;
pmu->pebs_enable_rsvd = ~0ull;
pmu->pebs_data_cfg_rsvd = ~0ull;
+ pmu->perf_metrics_rsvd = ~0ull;
bitmap_zero(pmu->all_valid_pmc_idx, X86_PMC_IDX_MAX);
if (!vcpu->kvm->arch.enable_pmu)
diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c
index fab0891ad46a..98e2fb80347a 100644
--- a/arch/x86/kvm/vmx/pmu_intel.c
+++ b/arch/x86/kvm/vmx/pmu_intel.c
@@ -417,6 +417,9 @@ static int intel_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
reprogram_fixed_counters(pmu, data);
break;
case MSR_PERF_METRICS:
+ if (data & pmu->perf_metrics_rsvd)
+ return 1;
+
pmu->perf_metrics = data;
break;
case MSR_IA32_PEBS_ENABLE:
@@ -601,9 +604,22 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu)
counter_rsvd = ~((BIT_ULL(pmu->nr_arch_gp_counters) - 1) |
((BIT_ULL(pmu->nr_arch_fixed_counters) - 1) << KVM_FIXED_PMC_BASE_IDX));
pmu->global_ctrl_rsvd = counter_rsvd;
- if (perf_capabilities & PERF_CAP_PERF_METRICS)
+ if (perf_capabilities & PERF_CAP_PERF_METRICS) {
pmu->global_ctrl_rsvd &= ~GLOBAL_CTRL_EN_PERF_METRICS;
+ /*
+ * PERF_METRICS has one 8-bit field per metric. At 8 metrics,
+ * skip computing the mask and set perf_metrics_rsvd to 0
+ * directly, since BIT_ULL(64) is undefined.
+ */
+ if (kvm_pmu_cap.num_topdown_events < INTEL_TD_METRIC_NUM)
+ pmu->perf_metrics_rsvd =
+ ~(BIT_ULL(kvm_pmu_cap.num_topdown_events *
+ INTEL_TD_METRIC_FIELD_BITS) - 1);
+ else
+ pmu->perf_metrics_rsvd = 0;
+ }
+
/*
* GLOBAL_STATUS and GLOBAL_OVF_CONTROL (a.k.a. GLOBAL_STATUS_RESET)
* share reserved bit definitions. The kernel just happens to use
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v9 12/12] KVM: x86/pmu: Support RDPMC Metrics Clear Mode
2026-09-18 19:39 [PATCH v9 00/12] KVM: x86/pmu: Add hardware Topdown metrics support Zide Chen
` (10 preceding siblings ...)
2026-09-18 19:39 ` [PATCH v9 11/12] KVM: x86/pmu: Reject writes to reserved MSR_PERF_METRICS bits Zide Chen
@ 2026-09-18 19:39 ` Zide Chen
2026-09-18 20:22 ` sashiko-bot
11 siblings, 1 reply; 23+ messages in thread
From: Zide Chen @ 2026-09-18 19:39 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini, Peter Zijlstra
Cc: kvm, Jim Mattson, Andi Kleen, Stephane Eranian, linux-kernel,
Mingwei Zhang, Zide Chen, Das Sandipan, Shukla Manali, Dapeng Mi,
Xudong Hao
Add PERF_CAP_RDPMC_METRICS_CLEAR (IA32_PERF_CAPABILITIES bit 19)
support. When set, and IA32_FIXED_CTR_CTRL.METRICS_CLEAR_EN (bit 14)
is also set, RDPMC of PERF_METRICS clears both PERF_METRICS and fixed
counter 3 (SLOTS) after the read.
Advertise the capability to guests only when the mediated PMU is
enabled and the host reports it. Unmask FIXED_CTR_CTRL bit 14 in
intel_pmu_refresh(), scoped to fixed counter 3 only via
intel_fixed_bits_by_idx().
For the mediated PMU, METRICS_CLEAR_EN takes effect natively via
passthrough. But kvm_need_rdpmc_intercept() can still force
interception for unrelated reasons, so intel_emulate_rdpmc() also
replicates the clear-on-read behavior for consistency.
Signed-off-by: Zide Chen <zide.chen@intel.com>
---
v9: new patch.
---
arch/x86/include/asm/msr-index.h | 1 +
arch/x86/kvm/vmx/pmu_intel.c | 28 ++++++++++++++++++++++++++++
arch/x86/kvm/vmx/vmx.c | 6 +++++-
3 files changed, 34 insertions(+), 1 deletion(-)
diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h
index 8fad944197fb..5851edcdda05 100644
--- a/arch/x86/include/asm/msr-index.h
+++ b/arch/x86/include/asm/msr-index.h
@@ -333,6 +333,7 @@
#define PERF_CAP_PEBS_BASELINE BIT_ULL(14)
#define PERF_CAP_PERF_METRICS BIT_ULL(15)
#define PERF_CAP_PEBS_TIMING_INFO BIT_ULL(17)
+#define PERF_CAP_RDPMC_METRICS_CLEAR BIT_ULL(19)
#define PERF_CAP_PEBS_MASK (PERF_CAP_PEBS_TRAP | PERF_CAP_ARCH_REG | \
PERF_CAP_PEBS_FORMAT | PERF_CAP_PEBS_BASELINE | \
PERF_CAP_PEBS_TIMING_INFO)
diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c
index 98e2fb80347a..3e5e8bae67ab 100644
--- a/arch/x86/kvm/vmx/pmu_intel.c
+++ b/arch/x86/kvm/vmx/pmu_intel.c
@@ -36,6 +36,9 @@
#define INTEL_RDPMC_TYPE_MASK GENMASK(31, 16)
#define INTEL_RDPMC_INDEX_MASK GENMASK(15, 0)
+/* Index of the SLOTS fixed counter relative to the first fixed counter. */
+#define INTEL_FIXED_SLOTS_IDX (INTEL_PMC_IDX_FIXED_SLOTS - INTEL_PMC_IDX_FIXED)
+
#define MSR_PMC_FULL_WIDTH_BIT (MSR_IA32_PMC0 - MSR_IA32_PERFCTR0)
static struct lbr_desc *vcpu_to_lbr_desc(struct kvm_vcpu *vcpu)
@@ -86,6 +89,14 @@ static void reprogram_fixed_counters(struct kvm_pmu *pmu, u64 data)
}
}
+static bool intel_pmu_metrics_clear_enabled(struct kvm_pmu *pmu)
+{
+ u8 fixed_ctr_ctrl = fixed_ctrl_field(pmu->fixed_ctr_ctrl,
+ INTEL_FIXED_SLOTS_IDX);
+
+ return fixed_ctr_ctrl & INTEL_FIXED_3_METRICS_CLEAR;
+}
+
static int intel_emulate_rdpmc(struct kvm_vcpu *vcpu, unsigned int idx,
u64 *data)
{
@@ -138,6 +149,18 @@ static int intel_emulate_rdpmc(struct kvm_vcpu *vcpu, unsigned int idx,
return 1;
*data = pmu->perf_metrics;
+
+ /*
+ * Per the SDM, RDPMC of PERF_METRICS clears both PERF_METRICS
+ * and the SLOTS fixed counter when Metrics Clear Mode is
+ * enabled via INTEL_FIXED_3_METRICS_CLEAR in FIXED_CTR_CTRL.
+ */
+ pmc = kvm_pmc_idx_to_pmc(pmu, INTEL_PMC_IDX_FIXED_SLOTS);
+ if (pmc && intel_pmu_metrics_clear_enabled(pmu)) {
+ pmu->perf_metrics = 0;
+ pmc_write_counter(pmc, 0);
+ }
+
return 0;
default:
return 1;
@@ -601,6 +624,11 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu)
INTEL_FIXED_0_USER |
INTEL_FIXED_0_ENABLE_PMI);
+ if (perf_capabilities & PERF_CAP_RDPMC_METRICS_CLEAR)
+ pmu->fixed_ctr_ctrl_rsvd &=
+ ~intel_fixed_bits_by_idx(INTEL_FIXED_SLOTS_IDX,
+ INTEL_FIXED_3_METRICS_CLEAR);
+
counter_rsvd = ~((BIT_ULL(pmu->nr_arch_gp_counters) - 1) |
((BIT_ULL(pmu->nr_arch_fixed_counters) - 1) << KVM_FIXED_PMC_BASE_IDX));
pmu->global_ctrl_rsvd = counter_rsvd;
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index 372de66365ab..df02372c70e3 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -2616,6 +2616,9 @@ int vmx_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
if (!cpuid_model_is_consistent(vcpu))
return 1;
}
+ if ((data & PERF_CAP_RDPMC_METRICS_CLEAR) &&
+ !(data & PERF_CAP_PERF_METRICS))
+ return 1;
ret = kvm_set_msr_common(vcpu, msr_info);
break;
@@ -7999,7 +8002,8 @@ static __init u64 vmx_get_perf_capabilities(void)
}
if (enable_mediated_pmu)
- perf_cap |= kvm_host.perf_capabilities & PERF_CAP_PERF_METRICS;
+ perf_cap |= kvm_host.perf_capabilities &
+ (PERF_CAP_PERF_METRICS | PERF_CAP_RDPMC_METRICS_CLEAR);
return perf_cap;
}
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* Re: [PATCH v9 09/12] perf/x86: Add INTEL_TD_METRIC_FIELD_{BITS,MASK} constants
2026-09-18 19:39 ` [PATCH v9 09/12] perf/x86: Add INTEL_TD_METRIC_FIELD_{BITS,MASK} constants Zide Chen
@ 2026-09-18 19:59 ` sashiko-bot
2026-09-18 20:45 ` Chen, Zide
2026-09-21 7:01 ` Mi, Dapeng
1 sibling, 1 reply; 23+ messages in thread
From: sashiko-bot @ 2026-09-18 19:59 UTC (permalink / raw)
To: Zide Chen; +Cc: kvm
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] perf/x86: conflation of UMASK shift and MSR metric field width
--
commit ca73afb21aaf968ca13fd3d0ba413055c3dc1861
Author: Zide Chen <zide.chen@intel.com>
perf/x86: Add INTEL_TD_METRIC_FIELD_{BITS,MASK} constants
Replace the hard coded field width (8) and the equivalent 0xff mask
used for PERF_METRICS MSR field access with named constants
INTEL_TD_METRIC_FIELD_{BITS,MASK}.
> diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c
> index cc13164d948f2..cbc9a179f2e5c 100644
> --- a/arch/x86/events/intel/core.c
> +++ b/arch/x86/events/intel/core.c
[ ... ]
> @@ -4742,7 +4743,8 @@ static int core_pmu_hw_config(struct perf_event *event)
> }
>
> #define INTEL_TD_METRIC_AVAILABLE_MAX (INTEL_TD_METRIC_RETIRING + \
> - ((x86_pmu.num_topdown_events - 1) << 8))
> + ((x86_pmu.num_topdown_events - 1) << \
> + INTEL_TD_METRIC_FIELD_BITS))
[Severity: Low]
Is it safe to replace the UMASK shift here with the hardware MSR metric
field width?
The original << 8 was used to shift the metric ID into the UMASK field
(bits 8-15) of the software perf event configuration (attr.config).
INTEL_TD_METRIC_FIELD_BITS represents the hardware width of metrics within
the PERF_METRICS MSR.
When evaluated in is_available_metric_event() during perf_event_open(), this
currently functions because both values are 8. However, conflating a software
API layout offset with a hardware register field width could cause this macro
to calculate an invalid configuration mask if the MSR field width changes in
future hardware.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260918193937.569414-1-zide.chen@intel.com?part=9
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v9 08/12] KVM: selftests: Add PERF_METRICS and fixed counter 3 tests
2026-09-18 19:39 ` [PATCH v9 08/12] KVM: selftests: Add PERF_METRICS and fixed counter 3 tests Zide Chen
@ 2026-09-18 20:08 ` sashiko-bot
2026-09-18 20:45 ` Chen, Zide
0 siblings, 1 reply; 23+ messages in thread
From: sashiko-bot @ 2026-09-18 20:08 UTC (permalink / raw)
To: Zide Chen; +Cc: kvm
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] selftests: unconditional MSR read causes assertion failure on older kernels
--
commit c5ae582e90bae306393c4bf2f37f04a11e37ba5f
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 Level
1 metrics. It also extends the PMU selftest to cover Intel fixed counter 3 by
bumping the maximum number of fixed counters and validating functionality.
> 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
[ ... ]
> @@ -653,8 +657,85 @@ static void test_fixed_counters(u8 pmu_version, u64 perf_capabilities)
[ ... ]
> static void test_intel_counters(void)
> {
> + u64 advertised_perf_caps = kvm_get_feature_msr(MSR_IA32_PERF_CAPABILITIES);
[Severity: Medium]
Will this unconditional MSR read cause an assertion failure on older kernels?
When running the selftest on a host kernel prior to 5.9, KVM does not
expose MSR_IA32_PERF_CAPABILITIES as a feature MSR. Calling
kvm_get_feature_msr() unconditionally in test_intel_counters() causes the
underlying ioctl to fail and return 0, which triggers an assertion failure
rather than skipping the feature gracefully.
Other MSR accesses in this file appear to be guarded by checking
kvm_has_perf_caps first. Could we apply a similar check before reading
advertised_perf_caps so the test remains compatible with older environments?
> u8 pmu_version = kvm_cpu_property(X86_PROPERTY_PMU_VERSION);
> unsigned int i;
> u8 v;
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260918193937.569414-1-zide.chen@intel.com?part=8
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v9 12/12] KVM: x86/pmu: Support RDPMC Metrics Clear Mode
2026-09-18 19:39 ` [PATCH v9 12/12] KVM: x86/pmu: Support RDPMC Metrics Clear Mode Zide Chen
@ 2026-09-18 20:22 ` sashiko-bot
2026-09-18 21:08 ` Chen, Zide
2026-09-21 9:39 ` Mi, Dapeng
0 siblings, 2 replies; 23+ messages in thread
From: sashiko-bot @ 2026-09-18 20:22 UTC (permalink / raw)
To: Zide Chen; +Cc: kvm
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] pmu: event filter mistakenly strips METRICS_CLEAR_EN from fixed_ctr_ctrl_hw
- [Medium] pmu: METRICS_CLEAR_EN unmasked without validating fixed counter bounds
- [Low] pmu: emulation incorrectly ties PERF_METRICS clearing to SLOTS PMC existence
--
commit 8ba3725c32dbde4a1c3935cb433e898e06425b83
Author: Zide Chen <zide.chen@intel.com>
KVM: x86/pmu: Support RDPMC Metrics Clear Mode
This commit adds support for PERF_CAP_RDPMC_METRICS_CLEAR (bit 19 of
IA32_PERF_CAPABILITIES). It advertises this capability to guests when the
mediated PMU is enabled and the host reports it, unmasking the relevant
control bit. It also updates intel_emulate_rdpmc() to replicate the
clear-on-read behavior for consistency.
> diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c
> index 98e2fb80347a1..3e5e8bae67ab0 100644
> --- a/arch/x86/kvm/vmx/pmu_intel.c
> +++ b/arch/x86/kvm/vmx/pmu_intel.c
[ ... ]
> @@ -138,6 +149,18 @@ static int intel_emulate_rdpmc(struct kvm_vcpu *vcpu, unsigned int idx,
> return 1;
>
> *data = pmu->perf_metrics;
> +
> + /*
> + * Per the SDM, RDPMC of PERF_METRICS clears both PERF_METRICS
> + * and the SLOTS fixed counter when Metrics Clear Mode is
> + * enabled via INTEL_FIXED_3_METRICS_CLEAR in FIXED_CTR_CTRL.
> + */
> + pmc = kvm_pmc_idx_to_pmc(pmu, INTEL_PMC_IDX_FIXED_SLOTS);
> + if (pmc && intel_pmu_metrics_clear_enabled(pmu)) {
> + pmu->perf_metrics = 0;
> + pmc_write_counter(pmc, 0);
> + }
> +
[Severity: Low]
Does tying the clearing of PERF_METRICS to the existence of the SLOTS PMC in
intel_emulate_rdpmc() violate the hardware specification?
If the guest executes RDPMC natively and triggers an interception, and the
SLOTS counter is not exposed to the guest (making kvm_pmc_idx_to_pmc return
NULL), but METRICS_CLEAR_EN is enabled, this code silently skips clearing
PERF_METRICS.
Should the clearing of pmu->perf_metrics be performed independently of whether
the software PMC object for the SLOTS counter exists?
> return 0;
> default:
> return 1;
[ ... ]
> @@ -601,6 +624,11 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu)
> INTEL_FIXED_0_USER |
> INTEL_FIXED_0_ENABLE_PMI);
>
> + if (perf_capabilities & PERF_CAP_RDPMC_METRICS_CLEAR)
> + pmu->fixed_ctr_ctrl_rsvd &=
> + ~intel_fixed_bits_by_idx(INTEL_FIXED_SLOTS_IDX,
> + INTEL_FIXED_3_METRICS_CLEAR);
> +
[Severity: Medium]
Does intel_pmu_refresh() unconditionally unmask the METRICS_CLEAR_EN bit
without verifying if the guest topology actually exposes the SLOTS counter?
If a VM is configured with 3 or fewer fixed counters, this unmasking would
allow the unprivileged guest OS to write to bit 14 in
MSR_CORE_PERF_FIXED_CTR_CTRL without triggering the expected #GP.
Should this be gated by checking if pmu->nr_arch_fixed_counters >
INTEL_FIXED_SLOTS_IDX?
[Severity: High]
Does this patch inadvertently cause event filters to strip the METRICS_CLEAR_EN
bit in kvm_mediated_pmu_refresh_event_filter()?
When a host-level event filter denies the SLOTS counter (counter 3) from
counting, KVM applies a 0xf hardware mask that zeroes all 4 configuration bits
for the counter:
arch/x86/kvm/pmu.c:kvm_mediated_pmu_refresh_event_filter() {
...
} else {
u64 mask = intel_fixed_bits_by_idx(pmc->idx - KVM_FIXED_PMC_BASE_IDX, 0xf);
pmu->fixed_ctr_ctrl_hw &= ~mask;
if (allowed)
pmu->fixed_ctr_ctrl_hw |= pmu->fixed_ctr_ctrl & mask;
}
}
Since this mask covers the entire 4-bit configuration block, it will also clear
bit 14 (METRICS_CLEAR_EN) from hardware. If the guest sets METRICS_CLEAR_EN and
then executes RDPMC natively, it will lead to un-cleared stale metrics.
Should the mask be updated to avoid interfering with this newly introduced
feature bit?
> 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;
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260918193937.569414-1-zide.chen@intel.com?part=12
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v9 09/12] perf/x86: Add INTEL_TD_METRIC_FIELD_{BITS,MASK} constants
2026-09-18 19:59 ` sashiko-bot
@ 2026-09-18 20:45 ` Chen, Zide
0 siblings, 0 replies; 23+ messages in thread
From: Chen, Zide @ 2026-09-18 20:45 UTC (permalink / raw)
To: sashiko-reviews; +Cc: kvm
On 9/18/2026 2:59 PM, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> - [Low] perf/x86: conflation of UMASK shift and MSR metric field width
> --
>
> commit ca73afb21aaf968ca13fd3d0ba413055c3dc1861
> Author: Zide Chen <zide.chen@intel.com>
>
> perf/x86: Add INTEL_TD_METRIC_FIELD_{BITS,MASK} constants
>
> Replace the hard coded field width (8) and the equivalent 0xff mask
> used for PERF_METRICS MSR field access with named constants
> INTEL_TD_METRIC_FIELD_{BITS,MASK}.
>
>> diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c
>> index cc13164d948f2..cbc9a179f2e5c 100644
>> --- a/arch/x86/events/intel/core.c
>> +++ b/arch/x86/events/intel/core.c
>
> [ ... ]
>
>> @@ -4742,7 +4743,8 @@ static int core_pmu_hw_config(struct perf_event *event)
>> }
>>
>> #define INTEL_TD_METRIC_AVAILABLE_MAX (INTEL_TD_METRIC_RETIRING + \
>> - ((x86_pmu.num_topdown_events - 1) << 8))
>> + ((x86_pmu.num_topdown_events - 1) << \
>> + INTEL_TD_METRIC_FIELD_BITS))
>
> [Severity: Low]
> Is it safe to replace the UMASK shift here with the hardware MSR metric
> field width?
It's safe since x86_pmu.num_topdown_events is guaranteed to be <= 8, and
it's called when is_metric_event(event) is true.
> The original << 8 was used to shift the metric ID into the UMASK field
> (bits 8-15) of the software perf event configuration (attr.config).
> INTEL_TD_METRIC_FIELD_BITS represents the hardware width of metrics within
> the PERF_METRICS MSR.
>
> When evaluated in is_available_metric_event() during perf_event_open(), this
> currently functions because both values are 8. However, conflating a software
> API layout offset with a hardware register field width could cause this macro
> to calculate an invalid configuration mask if the MSR field width changes in
> future hardware.
>
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v9 08/12] KVM: selftests: Add PERF_METRICS and fixed counter 3 tests
2026-09-18 20:08 ` sashiko-bot
@ 2026-09-18 20:45 ` Chen, Zide
0 siblings, 0 replies; 23+ messages in thread
From: Chen, Zide @ 2026-09-18 20:45 UTC (permalink / raw)
To: sashiko-reviews; +Cc: kvm
On 9/18/2026 3:08 PM, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> - [Medium] selftests: unconditional MSR read causes assertion failure on older kernels
> --
>
> commit c5ae582e90bae306393c4bf2f37f04a11e37ba5f
> 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 Level
> 1 metrics. It also extends the PMU selftest to cover Intel fixed counter 3 by
> bumping the maximum number of fixed counters and validating functionality.
>
>> 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
> [ ... ]
>> @@ -653,8 +657,85 @@ static void test_fixed_counters(u8 pmu_version, u64 perf_capabilities)
> [ ... ]
>> static void test_intel_counters(void)
>> {
>> + u64 advertised_perf_caps = kvm_get_feature_msr(MSR_IA32_PERF_CAPABILITIES);
>
> [Severity: Medium]
> Will this unconditional MSR read cause an assertion failure on older kernels?
>
> When running the selftest on a host kernel prior to 5.9, KVM does not
> expose MSR_IA32_PERF_CAPABILITIES as a feature MSR. Calling
> kvm_get_feature_msr() unconditionally in test_intel_counters() causes the
> underlying ioctl to fail and return 0, which triggers an assertion failure
> rather than skipping the feature gracefully.
>
> Other MSR accesses in this file appear to be guarded by checking
> kvm_has_perf_caps first. Could we apply a similar check before reading
> advertised_perf_caps so the test remains compatible with older environments?
Yes, you are right. It should be guarded with kvm_has_perf_caps.
>> u8 pmu_version = kvm_cpu_property(X86_PROPERTY_PMU_VERSION);
>> unsigned int i;
>> u8 v;
> [ ... ]
>
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v9 12/12] KVM: x86/pmu: Support RDPMC Metrics Clear Mode
2026-09-18 20:22 ` sashiko-bot
@ 2026-09-18 21:08 ` Chen, Zide
2026-09-21 9:39 ` Mi, Dapeng
1 sibling, 0 replies; 23+ messages in thread
From: Chen, Zide @ 2026-09-18 21:08 UTC (permalink / raw)
To: sashiko-reviews; +Cc: kvm
On 9/18/2026 3:22 PM, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
> - [High] pmu: event filter mistakenly strips METRICS_CLEAR_EN from fixed_ctr_ctrl_hw
> - [Medium] pmu: METRICS_CLEAR_EN unmasked without validating fixed counter bounds
> - [Low] pmu: emulation incorrectly ties PERF_METRICS clearing to SLOTS PMC existence
> --
>
> commit 8ba3725c32dbde4a1c3935cb433e898e06425b83
> Author: Zide Chen <zide.chen@intel.com>
>
> KVM: x86/pmu: Support RDPMC Metrics Clear Mode
>
> This commit adds support for PERF_CAP_RDPMC_METRICS_CLEAR (bit 19 of
> IA32_PERF_CAPABILITIES). It advertises this capability to guests when the
> mediated PMU is enabled and the host reports it, unmasking the relevant
> control bit. It also updates intel_emulate_rdpmc() to replicate the
> clear-on-read behavior for consistency.
>
>> diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c
>> index 98e2fb80347a1..3e5e8bae67ab0 100644
>> --- a/arch/x86/kvm/vmx/pmu_intel.c
>> +++ b/arch/x86/kvm/vmx/pmu_intel.c
> [ ... ]
>> @@ -138,6 +149,18 @@ static int intel_emulate_rdpmc(struct kvm_vcpu *vcpu, unsigned int idx,
>> return 1;
>>
>> *data = pmu->perf_metrics;
>> +
>> + /*
>> + * Per the SDM, RDPMC of PERF_METRICS clears both PERF_METRICS
>> + * and the SLOTS fixed counter when Metrics Clear Mode is
>> + * enabled via INTEL_FIXED_3_METRICS_CLEAR in FIXED_CTR_CTRL.
>> + */
>> + pmc = kvm_pmc_idx_to_pmc(pmu, INTEL_PMC_IDX_FIXED_SLOTS);
>> + if (pmc && intel_pmu_metrics_clear_enabled(pmu)) {
>> + pmu->perf_metrics = 0;
>> + pmc_write_counter(pmc, 0);
>> + }
>> +
>
> [Severity: Low]
> Does tying the clearing of PERF_METRICS to the existence of the SLOTS PMC in
> intel_emulate_rdpmc() violate the hardware specification?
>
> If the guest executes RDPMC natively and triggers an interception, and the
> SLOTS counter is not exposed to the guest (making kvm_pmc_idx_to_pmc return
> NULL), but METRICS_CLEAR_EN is enabled, this code silently skips clearing
> PERF_METRICS.
>
> Should the clearing of pmu->perf_metrics be performed independently of whether
> the software PMC object for the SLOTS counter exists?
This is a defensive check against invalid guest CPUID configuration. If
the SLOTS counter is not available, clearing PERF_METRICS is meaningless
and messy.
>> return 0;
>> default:
>> return 1;
> [ ... ]
>> @@ -601,6 +624,11 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu)
>> INTEL_FIXED_0_USER |
>> INTEL_FIXED_0_ENABLE_PMI);
>>
>> + if (perf_capabilities & PERF_CAP_RDPMC_METRICS_CLEAR)
>> + pmu->fixed_ctr_ctrl_rsvd &=
>> + ~intel_fixed_bits_by_idx(INTEL_FIXED_SLOTS_IDX,
>> + INTEL_FIXED_3_METRICS_CLEAR);
>> +
>
> [Severity: Medium]
> Does intel_pmu_refresh() unconditionally unmask the METRICS_CLEAR_EN bit
> without verifying if the guest topology actually exposes the SLOTS counter?
>
> If a VM is configured with 3 or fewer fixed counters, this unmasking would
> allow the unprivileged guest OS to write to bit 14 in
> MSR_CORE_PERF_FIXED_CTR_CTRL without triggering the expected #GP.
>
> Should this be gated by checking if pmu->nr_arch_fixed_counters >
> INTEL_FIXED_SLOTS_IDX?
KVM is not intended to check against all possible invalid
configurations, as long as it protects host integrity, I think this is fine.
> [Severity: High]
> Does this patch inadvertently cause event filters to strip the METRICS_CLEAR_EN
> bit in kvm_mediated_pmu_refresh_event_filter()?
>
> When a host-level event filter denies the SLOTS counter (counter 3) from
> counting, KVM applies a 0xf hardware mask that zeroes all 4 configuration bits
> for the counter:
>
> arch/x86/kvm/pmu.c:kvm_mediated_pmu_refresh_event_filter() {
> ...
> } else {
> u64 mask = intel_fixed_bits_by_idx(pmc->idx - KVM_FIXED_PMC_BASE_IDX, 0xf);
>
> pmu->fixed_ctr_ctrl_hw &= ~mask;
> if (allowed)
> pmu->fixed_ctr_ctrl_hw |= pmu->fixed_ctr_ctrl & mask;
> }
> }
>
> Since this mask covers the entire 4-bit configuration block, it will also clear
> bit 14 (METRICS_CLEAR_EN) from hardware. If the guest sets METRICS_CLEAR_EN and
> then executes RDPMC natively, it will lead to un-cleared stale metrics.
>
> Should the mask be updated to avoid interfering with this newly introduced
> feature bit?
I don't think this needs a change.
The clear/re-apply here happen while the vCPU isn't running, so there's
no window for a native RDPMC to see it cleared: if allowed, the guest's
METRICS_CLEAR_EN bit is copied through; if not allowed, counter 3 isn't
counting anyway.
>
>> 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;
>
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v9 09/12] perf/x86: Add INTEL_TD_METRIC_FIELD_{BITS,MASK} constants
2026-09-18 19:39 ` [PATCH v9 09/12] perf/x86: Add INTEL_TD_METRIC_FIELD_{BITS,MASK} constants Zide Chen
2026-09-18 19:59 ` sashiko-bot
@ 2026-09-21 7:01 ` Mi, Dapeng
1 sibling, 0 replies; 23+ messages in thread
From: Mi, Dapeng @ 2026-09-21 7:01 UTC (permalink / raw)
To: Zide Chen, Sean Christopherson, Paolo Bonzini, Peter Zijlstra
Cc: kvm, Jim Mattson, Andi Kleen, Stephane Eranian, linux-kernel,
Mingwei Zhang, Das Sandipan, Shukla Manali, Xudong Hao
On 9/19/2026 3:39 AM, Zide Chen wrote:
> Replace the hard coded field width (8) and the equivalent 0xff mask
> used for PERF_METRICS MSR field access with named constants
> INTEL_TD_METRIC_FIELD_{BITS,MASK}.
>
> INTEL_TD_METRIC_FIELD_BITS will be used by a subsequent KVM patch to
> validat reserved bits in the PERF_METRICS MSR.
>
> No functional change intended.
>
> Signed-off-by: Zide Chen <zide.chen@intel.com>
> Cc: Peter Zijlstra <peterz@infradead.org>
> ---
> v9: new patch.
> ---
> arch/x86/events/intel/core.c | 8 +++++---
> arch/x86/include/asm/perf_event.h | 4 ++++
> 2 files changed, 9 insertions(+), 3 deletions(-)
>
> diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c
> index cc13164d948f..cbc9a179f2e5 100644
> --- a/arch/x86/events/intel/core.c
> +++ b/arch/x86/events/intel/core.c
> @@ -3159,6 +3159,7 @@ DEFINE_STATIC_CALL(intel_pmu_set_topdown_event_period, x86_perf_event_set_period
>
> static inline u64 icl_get_metrics_event_value(u64 metric, u64 slots, int idx)
> {
> + int shift = (idx - INTEL_PMC_IDX_METRIC_BASE) * INTEL_TD_METRIC_FIELD_BITS;
> u32 val;
>
> /*
> @@ -3166,8 +3167,8 @@ static inline u64 icl_get_metrics_event_value(u64 metric, u64 slots, int idx)
> * summing up to 0xff.
> * slots-in-metric = (Metric / 0xff) * slots
> */
> - val = (metric >> ((idx - INTEL_PMC_IDX_METRIC_BASE) * 8)) & 0xff;
> - return mul_u64_u32_div(slots, val, 0xff);
> + val = (metric >> shift) & INTEL_TD_METRIC_FIELD_MASK;
> + return mul_u64_u32_div(slots, val, INTEL_TD_METRIC_FIELD_MASK);
> }
>
> static u64 icl_get_topdown_value(struct perf_event *event,
> @@ -4742,7 +4743,8 @@ static int core_pmu_hw_config(struct perf_event *event)
> }
>
> #define INTEL_TD_METRIC_AVAILABLE_MAX (INTEL_TD_METRIC_RETIRING + \
> - ((x86_pmu.num_topdown_events - 1) << 8))
> + ((x86_pmu.num_topdown_events - 1) << \
> + INTEL_TD_METRIC_FIELD_BITS))
Sashiko comments,
"
Is it safe to replace the UMASK shift here with the hardware MSR metric
field width?
The original << 8 was used to shift the metric ID into the UMASK field
(bits 8-15) of the software perf event configuration (attr.config).
INTEL_TD_METRIC_FIELD_BITS represents the hardware width of metrics within
the PERF_METRICS MSR.
When evaluated in is_available_metric_event() during perf_event_open(), this
currently functions because both values are 8. However, conflating a software
API layout offset with a hardware register field width could cause this macro
to calculate an invalid configuration mask if the MSR field width changes in
future hardware.
"
It looks reasonable, we either don't change this, or define a macro to
represent the UMASK shift. Thanks.
>
> static bool is_available_metric_event(struct perf_event *event)
> {
> diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h
> index 1eb13673e889..ceb6188e5217 100644
> --- a/arch/x86/include/asm/perf_event.h
> +++ b/arch/x86/include/asm/perf_event.h
> @@ -418,6 +418,10 @@ static inline bool use_fixed_pseudo_encoding(u64 code)
> #define INTEL_TD_METRIC_MAX INTEL_TD_METRIC_MEM_BOUND
> #define INTEL_TD_METRIC_NUM 8
>
> +/* Width, in bits, of each metric's field within the PERF_METRICS MSR. */
> +#define INTEL_TD_METRIC_FIELD_BITS 8
> +#define INTEL_TD_METRIC_FIELD_MASK GENMASK_ULL(INTEL_TD_METRIC_FIELD_BITS - 1, 0)
> +
> #define INTEL_TD_CFG_METRIC_CLEAR_BIT 0
> #define INTEL_TD_CFG_METRIC_CLEAR BIT_ULL(INTEL_TD_CFG_METRIC_CLEAR_BIT)
>
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v9 10/12] perf/x86: Expose number of Topdown metric events to KVM
2026-09-18 19:39 ` [PATCH v9 10/12] perf/x86: Expose number of Topdown metric events to KVM Zide Chen
@ 2026-09-21 7:06 ` Mi, Dapeng
0 siblings, 0 replies; 23+ messages in thread
From: Mi, Dapeng @ 2026-09-21 7:06 UTC (permalink / raw)
To: Zide Chen, Sean Christopherson, Paolo Bonzini, Peter Zijlstra
Cc: kvm, Jim Mattson, Andi Kleen, Stephane Eranian, linux-kernel,
Mingwei Zhang, Das Sandipan, Shukla Manali, Xudong Hao
Reviewed-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
On 9/19/2026 3:39 AM, Zide Chen wrote:
> The number of Topdown metric events varies by CPU: ICL/TGL/RKL
> support 4, while SPR/GNR/DMR and some hybrid CPUs support 8.
>
> Add num_topdown_events to struct x86_pmu_capability and populate it
> in perf_get_x86_pmu_capability() so that KVM can determine how many
> PERF_METRICS fields are valid on the host CPU and correctly identify
> the reserved bits in IA32_PERF_METRICS.
>
> Signed-off-by: Zide Chen <zide.chen@intel.com>
> Cc: Peter Zijlstra <peterz@infradead.org>
> ---
> v9: new patch
> ---
> arch/x86/events/core.c | 1 +
> arch/x86/include/asm/perf_event.h | 1 +
> 2 files changed, 2 insertions(+)
>
> diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c
> index 8b3ea0adb965..01c1c9447f30 100644
> --- a/arch/x86/events/core.c
> +++ b/arch/x86/events/core.c
> @@ -3164,6 +3164,7 @@ void perf_get_x86_pmu_capability(struct x86_pmu_capability *cap)
> cap->events_mask_len = x86_pmu.events_mask_len;
> cap->pebs_ept = x86_pmu.pebs_ept;
> cap->mediated = !!(pmu.capabilities & PERF_PMU_CAP_MEDIATED_VPMU);
> + cap->num_topdown_events = x86_pmu.num_topdown_events;
> }
> EXPORT_SYMBOL_FOR_KVM(perf_get_x86_pmu_capability);
>
> diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h
> index ceb6188e5217..3c02389b2ea6 100644
> --- a/arch/x86/include/asm/perf_event.h
> +++ b/arch/x86/include/asm/perf_event.h
> @@ -306,6 +306,7 @@ struct x86_pmu_capability {
> int bit_width_fixed;
> unsigned int events_mask;
> int events_mask_len;
> + int num_topdown_events;
> unsigned int pebs_ept :1;
> unsigned int mediated :1;
> };
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v9 11/12] KVM: x86/pmu: Reject writes to reserved MSR_PERF_METRICS bits
2026-09-18 19:39 ` [PATCH v9 11/12] KVM: x86/pmu: Reject writes to reserved MSR_PERF_METRICS bits Zide Chen
@ 2026-09-21 7:11 ` Mi, Dapeng
0 siblings, 0 replies; 23+ messages in thread
From: Mi, Dapeng @ 2026-09-21 7:11 UTC (permalink / raw)
To: Zide Chen, Sean Christopherson, Paolo Bonzini, Peter Zijlstra
Cc: kvm, Jim Mattson, Andi Kleen, Stephane Eranian, linux-kernel,
Mingwei Zhang, Das Sandipan, Shukla Manali, Xudong Hao
LGTM. Thanks.
Reviewed-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
On 9/19/2026 3:39 AM, Zide Chen wrote:
> When the host CPU supports only 4 Topdown metrics, bits [63:32] of
> MSR_PERF_METRICS are reserved.
>
> Derive pmu->perf_metrics_rsvd from the number of supported Topdown
> metrics and use it to validate writes. Reject any attempts to set
> reserved bits in MSR_PERF_METRICS.
>
> Signed-off-by: Zide Chen <zide.chen@intel.com>
> ---
> v9: new patch.
> ---
> arch/x86/include/asm/kvm_host.h | 1 +
> arch/x86/kvm/pmu.c | 1 +
> arch/x86/kvm/vmx/pmu_intel.c | 18 +++++++++++++++++-
> 3 files changed, 19 insertions(+), 1 deletion(-)
>
> diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
> index 052bf377dedb..31454ecee371 100644
> --- a/arch/x86/include/asm/kvm_host.h
> +++ b/arch/x86/include/asm/kvm_host.h
> @@ -585,6 +585,7 @@ struct kvm_pmu {
> u64 reserved_bits;
> u64 raw_event_mask;
> u64 perf_metrics;
> + u64 perf_metrics_rsvd;
> struct kvm_pmc gp_counters[KVM_MAX_NR_GP_COUNTERS];
> struct kvm_pmc fixed_counters[KVM_MAX_NR_FIXED_COUNTERS];
>
> diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c
> index 0df283a169b6..41f2d31c495f 100644
> --- a/arch/x86/kvm/pmu.c
> +++ b/arch/x86/kvm/pmu.c
> @@ -1006,6 +1006,7 @@ void kvm_pmu_refresh(struct kvm_vcpu *vcpu)
> pmu->fixed_ctr_ctrl_rsvd = ~0ull;
> pmu->pebs_enable_rsvd = ~0ull;
> pmu->pebs_data_cfg_rsvd = ~0ull;
> + pmu->perf_metrics_rsvd = ~0ull;
> bitmap_zero(pmu->all_valid_pmc_idx, X86_PMC_IDX_MAX);
>
> if (!vcpu->kvm->arch.enable_pmu)
> diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c
> index fab0891ad46a..98e2fb80347a 100644
> --- a/arch/x86/kvm/vmx/pmu_intel.c
> +++ b/arch/x86/kvm/vmx/pmu_intel.c
> @@ -417,6 +417,9 @@ static int intel_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
> reprogram_fixed_counters(pmu, data);
> break;
> case MSR_PERF_METRICS:
> + if (data & pmu->perf_metrics_rsvd)
> + return 1;
> +
> pmu->perf_metrics = data;
> break;
> case MSR_IA32_PEBS_ENABLE:
> @@ -601,9 +604,22 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu)
> counter_rsvd = ~((BIT_ULL(pmu->nr_arch_gp_counters) - 1) |
> ((BIT_ULL(pmu->nr_arch_fixed_counters) - 1) << KVM_FIXED_PMC_BASE_IDX));
> pmu->global_ctrl_rsvd = counter_rsvd;
> - if (perf_capabilities & PERF_CAP_PERF_METRICS)
> + if (perf_capabilities & PERF_CAP_PERF_METRICS) {
> pmu->global_ctrl_rsvd &= ~GLOBAL_CTRL_EN_PERF_METRICS;
>
> + /*
> + * PERF_METRICS has one 8-bit field per metric. At 8 metrics,
> + * skip computing the mask and set perf_metrics_rsvd to 0
> + * directly, since BIT_ULL(64) is undefined.
> + */
> + if (kvm_pmu_cap.num_topdown_events < INTEL_TD_METRIC_NUM)
> + pmu->perf_metrics_rsvd =
> + ~(BIT_ULL(kvm_pmu_cap.num_topdown_events *
> + INTEL_TD_METRIC_FIELD_BITS) - 1);
> + else
> + pmu->perf_metrics_rsvd = 0;
> + }
> +
> /*
> * GLOBAL_STATUS and GLOBAL_OVF_CONTROL (a.k.a. GLOBAL_STATUS_RESET)
> * share reserved bit definitions. The kernel just happens to use
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v9 12/12] KVM: x86/pmu: Support RDPMC Metrics Clear Mode
2026-09-18 20:22 ` sashiko-bot
2026-09-18 21:08 ` Chen, Zide
@ 2026-09-21 9:39 ` Mi, Dapeng
1 sibling, 0 replies; 23+ messages in thread
From: Mi, Dapeng @ 2026-09-21 9:39 UTC (permalink / raw)
To: sashiko-reviews, Zide Chen; +Cc: kvm
On 9/19/2026 4:22 AM, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
> - [High] pmu: event filter mistakenly strips METRICS_CLEAR_EN from fixed_ctr_ctrl_hw
> - [Medium] pmu: METRICS_CLEAR_EN unmasked without validating fixed counter bounds
> - [Low] pmu: emulation incorrectly ties PERF_METRICS clearing to SLOTS PMC existence
> --
>
> commit 8ba3725c32dbde4a1c3935cb433e898e06425b83
> Author: Zide Chen <zide.chen@intel.com>
>
> KVM: x86/pmu: Support RDPMC Metrics Clear Mode
>
> This commit adds support for PERF_CAP_RDPMC_METRICS_CLEAR (bit 19 of
> IA32_PERF_CAPABILITIES). It advertises this capability to guests when the
> mediated PMU is enabled and the host reports it, unmasking the relevant
> control bit. It also updates intel_emulate_rdpmc() to replicate the
> clear-on-read behavior for consistency.
>
>> diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c
>> index 98e2fb80347a1..3e5e8bae67ab0 100644
>> --- a/arch/x86/kvm/vmx/pmu_intel.c
>> +++ b/arch/x86/kvm/vmx/pmu_intel.c
> [ ... ]
>> @@ -138,6 +149,18 @@ static int intel_emulate_rdpmc(struct kvm_vcpu *vcpu, unsigned int idx,
>> return 1;
>>
>> *data = pmu->perf_metrics;
>> +
>> + /*
>> + * Per the SDM, RDPMC of PERF_METRICS clears both PERF_METRICS
>> + * and the SLOTS fixed counter when Metrics Clear Mode is
>> + * enabled via INTEL_FIXED_3_METRICS_CLEAR in FIXED_CTR_CTRL.
>> + */
>> + pmc = kvm_pmc_idx_to_pmc(pmu, INTEL_PMC_IDX_FIXED_SLOTS);
>> + if (pmc && intel_pmu_metrics_clear_enabled(pmu)) {
>> + pmu->perf_metrics = 0;
>> + pmc_write_counter(pmc, 0);
>> + }
>> +
> [Severity: Low]
> Does tying the clearing of PERF_METRICS to the existence of the SLOTS PMC in
> intel_emulate_rdpmc() violate the hardware specification?
>
> If the guest executes RDPMC natively and triggers an interception, and the
> SLOTS counter is not exposed to the guest (making kvm_pmc_idx_to_pmc return
> NULL), but METRICS_CLEAR_EN is enabled, this code silently skips clearing
> PERF_METRICS.
>
> Should the clearing of pmu->perf_metrics be performed independently of whether
> the software PMC object for the SLOTS counter exists?
>
>> return 0;
>> default:
>> return 1;
> [ ... ]
>> @@ -601,6 +624,11 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu)
>> INTEL_FIXED_0_USER |
>> INTEL_FIXED_0_ENABLE_PMI);
>>
>> + if (perf_capabilities & PERF_CAP_RDPMC_METRICS_CLEAR)
>> + pmu->fixed_ctr_ctrl_rsvd &=
>> + ~intel_fixed_bits_by_idx(INTEL_FIXED_SLOTS_IDX,
>> + INTEL_FIXED_3_METRICS_CLEAR);
>> +
> [Severity: Medium]
> Does intel_pmu_refresh() unconditionally unmask the METRICS_CLEAR_EN bit
> without verifying if the guest topology actually exposes the SLOTS counter?
>
> If a VM is configured with 3 or fewer fixed counters, this unmasking would
> allow the unprivileged guest OS to write to bit 14 in
> MSR_CORE_PERF_FIXED_CTR_CTRL without triggering the expected #GP.
>
> Should this be gated by checking if pmu->nr_arch_fixed_counters >
> INTEL_FIXED_SLOTS_IDX?
This sounds like a real risk. Should we mask out PERF_METRICS from guest
PERF_CAPABILITIES in intel_pmu_refresh() if guest doesn't advertise fixed
counter 3?
>
> [Severity: High]
> Does this patch inadvertently cause event filters to strip the METRICS_CLEAR_EN
> bit in kvm_mediated_pmu_refresh_event_filter()?
>
> When a host-level event filter denies the SLOTS counter (counter 3) from
> counting, KVM applies a 0xf hardware mask that zeroes all 4 configuration bits
> for the counter:
>
> arch/x86/kvm/pmu.c:kvm_mediated_pmu_refresh_event_filter() {
> ...
> } else {
> u64 mask = intel_fixed_bits_by_idx(pmc->idx - KVM_FIXED_PMC_BASE_IDX, 0xf);
>
> pmu->fixed_ctr_ctrl_hw &= ~mask;
> if (allowed)
> pmu->fixed_ctr_ctrl_hw |= pmu->fixed_ctr_ctrl & mask;
> }
> }
>
> Since this mask covers the entire 4-bit configuration block, it will also clear
> bit 14 (METRICS_CLEAR_EN) from hardware. If the guest sets METRICS_CLEAR_EN and
> then executes RDPMC natively, it will lead to un-cleared stale metrics.
>
> Should the mask be updated to avoid interfering with this newly introduced
> feature bit?
This is another tricky corner case. The best way should be disabling
PERF_METRICS along with fixed counter 3. But Intel HW doesn't provide a way
to disable PERF_METRICS except the EN_PERF_METRICS bit in PERF_GLOBAL_CTRL.
Since PERF_GLOBAL_CTRL could be in pass-through mode and guest could
directly overwrite PERF_GLOBAL_CTRL, it becomes meaningless for KVM
clearing EN_PERF_METRICS bit for guest PERF_GLOBAL_CTRL.
For the filter mask, it may be good enough to only mask out
(INTEL_FIXED_0_KERNEL | INTEL_FIXED_0_USER).
Thanks.
>
>> 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;
^ permalink raw reply [flat|nested] 23+ messages in thread
end of thread, other threads:[~2026-09-21 9:39 UTC | newest]
Thread overview: 23+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-18 19:39 [PATCH v9 00/12] KVM: x86/pmu: Add hardware Topdown metrics support Zide Chen
2026-09-18 19:39 ` [PATCH v9 01/12] KVM: x86/pmu: Do not map fixed counters >= 3 to generic perf events Zide Chen
2026-09-18 19:39 ` [PATCH v9 02/12] KVM: x86/pmu: Support Intel fixed counter 3 on mediated vPMU Zide Chen
2026-09-18 19:39 ` [PATCH v9 03/12] KVM: x86/pmu: Rename and move vcpu_get_perf_capabilities() to pmu.h Zide Chen
2026-09-18 19:39 ` [PATCH v9 04/12] KVM: x86/pmu: Snapshot host IA32_PERF_CAPABILITIES in kvm_host Zide Chen
2026-09-18 19:39 ` [PATCH v9 05/12] KVM: x86/pmu: Support PERF_METRICS MSR in mediated vPMU Zide Chen
2026-09-18 19:39 ` [PATCH v9 06/12] KVM: x86/pmu: Move RDPMC emulation into per-vendor callbacks Zide Chen
2026-09-18 19:39 ` [PATCH v9 07/12] KVM: x86/pmu: Emulate RDPMC on performance metrics Zide Chen
2026-09-18 19:39 ` [PATCH v9 08/12] KVM: selftests: Add PERF_METRICS and fixed counter 3 tests Zide Chen
2026-09-18 20:08 ` sashiko-bot
2026-09-18 20:45 ` Chen, Zide
2026-09-18 19:39 ` [PATCH v9 09/12] perf/x86: Add INTEL_TD_METRIC_FIELD_{BITS,MASK} constants Zide Chen
2026-09-18 19:59 ` sashiko-bot
2026-09-18 20:45 ` Chen, Zide
2026-09-21 7:01 ` Mi, Dapeng
2026-09-18 19:39 ` [PATCH v9 10/12] perf/x86: Expose number of Topdown metric events to KVM Zide Chen
2026-09-21 7:06 ` Mi, Dapeng
2026-09-18 19:39 ` [PATCH v9 11/12] KVM: x86/pmu: Reject writes to reserved MSR_PERF_METRICS bits Zide Chen
2026-09-21 7:11 ` Mi, Dapeng
2026-09-18 19:39 ` [PATCH v9 12/12] KVM: x86/pmu: Support RDPMC Metrics Clear Mode Zide Chen
2026-09-18 20:22 ` sashiko-bot
2026-09-18 21:08 ` Chen, Zide
2026-09-21 9:39 ` Mi, Dapeng
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox