* [PATCH v2 01/16] KVM: x86/pmu: Remove redundant Perf Global Status MSR bit definitions
2026-08-27 22:37 [PATCH] KVM: x86/pmu: Add mediated vPMU PerfMon v5 support Zide Chen
@ 2026-08-27 22:37 ` Zide Chen
2026-08-27 22:57 ` sashiko-bot
2026-08-27 22:37 ` [PATCH v2 02/16] KVM: x86/pmu: Rename all_valid_pmc_idx to pmc_exists Zide Chen
` (14 subsequent siblings)
15 siblings, 1 reply; 21+ messages in thread
From: Zide Chen @ 2026-08-27 22:37 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini, Peter Zijlstra
Cc: kvm, Andi Kleen, Jim Mattson, Stephane Eranian, linux-kernel,
Mingwei Zhang, Zide Chen, Das Sandipan, Shukla Manali, Dapeng Mi,
Xudong Hao
IA32_PERF_GLOBAL_STATUS and its SET/RESET counterparts share bit
definitions with perf_event.h, which is the canonical home for Intel
PMU bit definitions (e.g., PERFEVTSEL, FIXED_CTR_CTRL, GLOBAL_STATUS).
Drop the duplicate definitions from msr-index.h and update KVM code to
use the macros from perf_event.h.
Drop the comment as well, since the macros now match the MSR
naming.
No functional change intended.
Signed-off-by: Zide Chen <zide.chen@intel.com>
---
arch/x86/include/asm/msr-index.h | 8 --------
arch/x86/kvm/vmx/pmu_intel.c | 11 ++---------
arch/x86/kvm/vmx/vmx.c | 2 +-
3 files changed, 3 insertions(+), 18 deletions(-)
diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h
index 18c4be75e927..11b99d237e05 100644
--- a/arch/x86/include/asm/msr-index.h
+++ b/arch/x86/include/asm/msr-index.h
@@ -1243,14 +1243,6 @@
#define MSR_PERF_METRICS 0x00000329
-/* PERF_GLOBAL_OVF_CTL bits */
-#define MSR_CORE_PERF_GLOBAL_OVF_CTRL_TRACE_TOPA_PMI_BIT 55
-#define MSR_CORE_PERF_GLOBAL_OVF_CTRL_TRACE_TOPA_PMI (1ULL << MSR_CORE_PERF_GLOBAL_OVF_CTRL_TRACE_TOPA_PMI_BIT)
-#define MSR_CORE_PERF_GLOBAL_OVF_CTRL_OVF_BUF_BIT 62
-#define MSR_CORE_PERF_GLOBAL_OVF_CTRL_OVF_BUF (1ULL << MSR_CORE_PERF_GLOBAL_OVF_CTRL_OVF_BUF_BIT)
-#define MSR_CORE_PERF_GLOBAL_OVF_CTRL_COND_CHGD_BIT 63
-#define MSR_CORE_PERF_GLOBAL_OVF_CTRL_COND_CHGD (1ULL << MSR_CORE_PERF_GLOBAL_OVF_CTRL_COND_CHGD_BIT)
-
/* Geode defined MSRs */
#define MSR_GEODE_BUSCONT_CONF0 0x00001900
diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c
index bfa8612fb450..e71ddd51ae56 100644
--- a/arch/x86/kvm/vmx/pmu_intel.c
+++ b/arch/x86/kvm/vmx/pmu_intel.c
@@ -576,17 +576,10 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu)
((BIT_ULL(pmu->nr_arch_fixed_counters) - 1) << KVM_FIXED_PMC_BASE_IDX));
pmu->global_ctrl_rsvd = counter_rsvd;
- /*
- * GLOBAL_STATUS and GLOBAL_OVF_CONTROL (a.k.a. GLOBAL_STATUS_RESET)
- * share reserved bit definitions. The kernel just happens to use
- * OVF_CTRL for the names.
- */
pmu->global_status_rsvd = pmu->global_ctrl_rsvd
- & ~(MSR_CORE_PERF_GLOBAL_OVF_CTRL_OVF_BUF |
- MSR_CORE_PERF_GLOBAL_OVF_CTRL_COND_CHGD);
+ & ~(GLOBAL_STATUS_BUFFER_OVF | GLOBAL_STATUS_COND_CHG);
if (vmx_pt_mode_is_host_guest())
- pmu->global_status_rsvd &=
- ~MSR_CORE_PERF_GLOBAL_OVF_CTRL_TRACE_TOPA_PMI;
+ pmu->global_status_rsvd &= ~GLOBAL_STATUS_TRACE_TOPAPMI;
if (perf_capabilities & PERF_CAP_PEBS_FORMAT) {
if (perf_capabilities & PERF_CAP_PEBS_BASELINE) {
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index e3bfe6aca1a0..d3789b2ee18e 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -8612,7 +8612,7 @@ static unsigned int vmx_handle_intel_pt_intr(void)
return 0;
kvm_make_request(KVM_REQ_PMI, vcpu);
- __set_bit(MSR_CORE_PERF_GLOBAL_OVF_CTRL_TRACE_TOPA_PMI_BIT,
+ __set_bit(GLOBAL_STATUS_TRACE_TOPAPMI_BIT,
(unsigned long *)&vcpu->arch.pmu.global_status);
return 1;
}
--
2.55.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* Re: [PATCH v2 01/16] KVM: x86/pmu: Remove redundant Perf Global Status MSR bit definitions
2026-08-27 22:37 ` [PATCH v2 01/16] KVM: x86/pmu: Remove redundant Perf Global Status MSR bit definitions Zide Chen
@ 2026-08-27 22:57 ` sashiko-bot
2026-08-28 13:51 ` Chen, Zide
0 siblings, 1 reply; 21+ messages in thread
From: sashiko-bot @ 2026-08-27 22:57 UTC (permalink / raw)
To: Zide Chen; +Cc: kvm
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] Missing tools header synchronization for msr-index.h
--
commit e6cc32e583cfbb8bac5003f58672184385cc895a
Author: Zide Chen <zide.chen@intel.com>
KVM: x86/pmu: Remove redundant Perf Global Status MSR bit definitions
This commit removes duplicate Perf Global Status MSR bit definitions from
arch/x86/include/asm/msr-index.h, utilizing the canonical macros from
perf_event.h instead. It also updates the KVM PMU code to match.
> diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h
> index 18c4be75e9271..11b99d237e05b 100644
> --- a/arch/x86/include/asm/msr-index.h
> +++ b/arch/x86/include/asm/msr-index.h
> @@ -1243,14 +1243,6 @@
>
> #define MSR_PERF_METRICS 0x00000329
>
> -/* PERF_GLOBAL_OVF_CTL bits */
> -#define MSR_CORE_PERF_GLOBAL_OVF_CTRL_TRACE_TOPA_PMI_BIT 55
[Severity: Low]
Does this removal also need to be synchronized with the tools copy of this
header?
Since the definitions are being removed from the kernel's
arch/x86/include/asm/msr-index.h, leaving
tools/arch/x86/include/asm/msr-index.h out of sync might cause the build
system's check-headers.sh script to emit a warning when building tools like
perf.
> -#define MSR_CORE_PERF_GLOBAL_OVF_CTRL_TRACE_TOPA_PMI (1ULL << MSR_CORE_PERF_GLOBAL_OVF_CTRL_TRACE_TOPA_PMI_BIT)
> -#define MSR_CORE_PERF_GLOBAL_OVF_CTRL_OVF_BUF_BIT 62
> -#define MSR_CORE_PERF_GLOBAL_OVF_CTRL_OVF_BUF (1ULL << MSR_CORE_PERF_GLOBAL_OVF_CTRL_OVF_BUF_BIT)
> -#define MSR_CORE_PERF_GLOBAL_OVF_CTRL_COND_CHGD_BIT 63
> -#define MSR_CORE_PERF_GLOBAL_OVF_CTRL_COND_CHGD (1ULL << MSR_CORE_PERF_GLOBAL_OVF_CTRL_COND_CHGD_BIT)
> -
> /* Geode defined MSRs */
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260827223755.143247-1-zide.chen@intel.com?part=1
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v2 01/16] KVM: x86/pmu: Remove redundant Perf Global Status MSR bit definitions
2026-08-27 22:57 ` sashiko-bot
@ 2026-08-28 13:51 ` Chen, Zide
0 siblings, 0 replies; 21+ messages in thread
From: Chen, Zide @ 2026-08-28 13:51 UTC (permalink / raw)
To: sashiko-reviews; +Cc: kvm
On 8/27/2026 5:57 PM, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> - [Low] Missing tools header synchronization for msr-index.h
> --
>
> commit e6cc32e583cfbb8bac5003f58672184385cc895a
> Author: Zide Chen <zide.chen@intel.com>
>
> KVM: x86/pmu: Remove redundant Perf Global Status MSR bit definitions
>
> This commit removes duplicate Perf Global Status MSR bit definitions from
> arch/x86/include/asm/msr-index.h, utilizing the canonical macros from
> perf_event.h instead. It also updates the KVM PMU code to match.
>
>> diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h
>> index 18c4be75e9271..11b99d237e05b 100644
>> --- a/arch/x86/include/asm/msr-index.h
>> +++ b/arch/x86/include/asm/msr-index.h
>> @@ -1243,14 +1243,6 @@
>>
>> #define MSR_PERF_METRICS 0x00000329
>>
>> -/* PERF_GLOBAL_OVF_CTL bits */
>> -#define MSR_CORE_PERF_GLOBAL_OVF_CTRL_TRACE_TOPA_PMI_BIT 55
>
> [Severity: Low]
> Does this removal also need to be synchronized with the tools copy of this
> header?
>
> Since the definitions are being removed from the kernel's
> arch/x86/include/asm/msr-index.h, leaving
> tools/arch/x86/include/asm/msr-index.h out of sync might cause the build
> system's check-headers.sh script to emit a warning when building tools like
> perf.
The removed definitions are not used by tools/perf.
More generally, the usual practice is to update the kernel header only
and let the tools-side copies be synchronized separately by the perf
maintainers when needed. The check-headers.sh warning is typically
addressed later by a dedicated header synchronization patch.
>
>> -#define MSR_CORE_PERF_GLOBAL_OVF_CTRL_TRACE_TOPA_PMI (1ULL << MSR_CORE_PERF_GLOBAL_OVF_CTRL_TRACE_TOPA_PMI_BIT)
>> -#define MSR_CORE_PERF_GLOBAL_OVF_CTRL_OVF_BUF_BIT 62
>> -#define MSR_CORE_PERF_GLOBAL_OVF_CTRL_OVF_BUF (1ULL << MSR_CORE_PERF_GLOBAL_OVF_CTRL_OVF_BUF_BIT)
>> -#define MSR_CORE_PERF_GLOBAL_OVF_CTRL_COND_CHGD_BIT 63
>> -#define MSR_CORE_PERF_GLOBAL_OVF_CTRL_COND_CHGD (1ULL << MSR_CORE_PERF_GLOBAL_OVF_CTRL_COND_CHGD_BIT)
>> -
>> /* Geode defined MSRs */
>
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH v2 02/16] KVM: x86/pmu: Rename all_valid_pmc_idx to pmc_exists
2026-08-27 22:37 [PATCH] KVM: x86/pmu: Add mediated vPMU PerfMon v5 support Zide Chen
2026-08-27 22:37 ` [PATCH v2 01/16] KVM: x86/pmu: Remove redundant Perf Global Status MSR bit definitions Zide Chen
@ 2026-08-27 22:37 ` Zide Chen
2026-08-27 22:37 ` [PATCH v2 03/16] KVM: x86/pmu: Rename reserved_bits to eventsel_rsvd in kvm_pmu Zide Chen
` (13 subsequent siblings)
15 siblings, 0 replies; 21+ messages in thread
From: Zide Chen @ 2026-08-27 22:37 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini, Peter Zijlstra
Cc: kvm, Andi Kleen, Jim Mattson, Stephane Eranian, linux-kernel,
Mingwei Zhang, Zide Chen, Das Sandipan, Shukla Manali, Dapeng Mi,
Xudong Hao
all_valid_pmc_idx is a bitmap of valid PMC indices, not an index
itself; rename it to pmc_exists to better reflect what it holds and
keep the name consistent with other fields in the same struct.
No functional change intended.
Signed-off-by: Zide Chen <zide.chen@intel.com>
---
v2:
- Pick the name pmc_exists other than all_valid_pmc_mask. (Sean)
---
arch/x86/include/asm/kvm_host.h | 2 +-
arch/x86/kvm/pmu.c | 10 +++++-----
arch/x86/kvm/vmx/pmu_intel.c | 2 +-
3 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 283847619ff8..e473796da7d9 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -594,7 +594,7 @@ struct kvm_pmu {
DECLARE_BITMAP(reprogram_pmi, X86_PMC_IDX_MAX);
atomic64_t __reprogram_pmi;
};
- DECLARE_BITMAP(all_valid_pmc_idx, X86_PMC_IDX_MAX);
+ DECLARE_BITMAP(pmc_exists, X86_PMC_IDX_MAX);
DECLARE_BITMAP(pmc_in_use, X86_PMC_IDX_MAX);
DECLARE_BITMAP(pmc_counting_instructions, X86_PMC_IDX_MAX);
diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c
index a7d60c8785cd..8f2effee3b7b 100644
--- a/arch/x86/kvm/pmu.c
+++ b/arch/x86/kvm/pmu.c
@@ -950,7 +950,7 @@ static void kvm_pmu_reset(struct kvm_vcpu *vcpu)
bitmap_zero(pmu->reprogram_pmi, X86_PMC_IDX_MAX);
bitmap_zero(pmu->pmc_has_mode_specific_enables, X86_PMC_IDX_MAX);
- kvm_for_each_pmc(pmu, pmc, i, pmu->all_valid_pmc_idx) {
+ kvm_for_each_pmc(pmu, pmc, i, pmu->pmc_exists) {
pmc_stop_counter(pmc);
pmc->counter = 0;
pmc->emulated_counter = 0;
@@ -996,7 +996,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;
- bitmap_zero(pmu->all_valid_pmc_idx, X86_PMC_IDX_MAX);
+ bitmap_zero(pmu->pmc_exists, X86_PMC_IDX_MAX);
if (!vcpu->kvm->arch.enable_pmu)
return;
@@ -1017,8 +1017,8 @@ void kvm_pmu_refresh(struct kvm_vcpu *vcpu)
if (kvm_vcpu_has_mediated_pmu(vcpu))
kvm_pmu_call(write_global_ctrl)(pmu->global_ctrl);
- bitmap_set(pmu->all_valid_pmc_idx, 0, pmu->nr_arch_gp_counters);
- bitmap_set(pmu->all_valid_pmc_idx, KVM_FIXED_PMC_BASE_IDX,
+ bitmap_set(pmu->pmc_exists, 0, pmu->nr_arch_gp_counters);
+ bitmap_set(pmu->pmc_exists, KVM_FIXED_PMC_BASE_IDX,
pmu->nr_arch_fixed_counters);
}
@@ -1040,7 +1040,7 @@ void kvm_pmu_cleanup(struct kvm_vcpu *vcpu)
pmu->need_cleanup = false;
- bitmap_andnot(bitmask, pmu->all_valid_pmc_idx,
+ bitmap_andnot(bitmask, pmu->pmc_exists,
pmu->pmc_in_use, X86_PMC_IDX_MAX);
kvm_for_each_pmc(pmu, pmc, i, bitmask) {
diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c
index e71ddd51ae56..322a3ad580dd 100644
--- a/arch/x86/kvm/vmx/pmu_intel.c
+++ b/arch/x86/kvm/vmx/pmu_intel.c
@@ -557,7 +557,7 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu)
lbr_desc->records.nr = 0;
if (lbr_desc->records.nr)
- bitmap_set(pmu->all_valid_pmc_idx, INTEL_PMC_IDX_FIXED_VLBR, 1);
+ bitmap_set(pmu->pmc_exists, INTEL_PMC_IDX_FIXED_VLBR, 1);
if (pmu->version == 1)
return;
--
2.55.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH v2 03/16] KVM: x86/pmu: Rename reserved_bits to eventsel_rsvd in kvm_pmu
2026-08-27 22:37 [PATCH] KVM: x86/pmu: Add mediated vPMU PerfMon v5 support Zide Chen
2026-08-27 22:37 ` [PATCH v2 01/16] KVM: x86/pmu: Remove redundant Perf Global Status MSR bit definitions Zide Chen
2026-08-27 22:37 ` [PATCH v2 02/16] KVM: x86/pmu: Rename all_valid_pmc_idx to pmc_exists Zide Chen
@ 2026-08-27 22:37 ` Zide Chen
2026-08-27 22:37 ` [PATCH v2 04/16] KVM: x86/pmu: Gate BUFFER_OVF reserved bit on guest DS Zide Chen
` (12 subsequent siblings)
15 siblings, 0 replies; 21+ messages in thread
From: Zide Chen @ 2026-08-27 22:37 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini, Peter Zijlstra
Cc: kvm, Andi Kleen, Jim Mattson, Stephane Eranian, linux-kernel,
Mingwei Zhang, Zide Chen, Das Sandipan, Shukla Manali, Dapeng Mi,
Xudong Hao
This field stores the bits that are reserved in guest IA32_PERFEVTSELx
MSRs. Rename it to eventsel_rsvd to better reflect its purpose and to
align with other field names in struct kvm_pmu.
Opportunistically, replace the magic number 0xffffffff00200000ull with
macros.
No functional change intended.
Suggested-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
Signed-off-by: Zide Chen <zide.chen@intel.com>
---
v2:
- Replace magic numbers in AMD pmu->eventsel_rsvd.
---
arch/x86/include/asm/kvm_host.h | 2 +-
arch/x86/kvm/pmu.c | 6 +++++-
arch/x86/kvm/svm/pmu.c | 12 +++++++++---
arch/x86/kvm/vmx/pmu_intel.c | 12 ++++++------
4 files changed, 21 insertions(+), 11 deletions(-)
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index e473796da7d9..90d80483df7f 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -580,7 +580,7 @@ struct kvm_pmu {
u64 counter_bitmask[2];
u64 global_ctrl_rsvd;
u64 global_status_rsvd;
- u64 reserved_bits;
+ u64 eventsel_rsvd;
u64 raw_event_mask;
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 8f2effee3b7b..964e74bdaaa7 100644
--- a/arch/x86/kvm/pmu.c
+++ b/arch/x86/kvm/pmu.c
@@ -989,7 +989,11 @@ void kvm_pmu_refresh(struct kvm_vcpu *vcpu)
pmu->nr_arch_fixed_counters = 0;
pmu->counter_bitmask[KVM_PMC_GP] = 0;
pmu->counter_bitmask[KVM_PMC_FIXED] = 0;
- pmu->reserved_bits = 0xffffffff00200000ull;
+ /*
+ * KVM is not able to emulate the AnyThread bit due to cross-VM
+ * information leakage on SMT cores.
+ */
+ pmu->eventsel_rsvd = GENMASK_ULL(63, 32) | ARCH_PERFMON_EVENTSEL_ANY;
pmu->raw_event_mask = X86_RAW_EVENT_MASK;
pmu->global_ctrl_rsvd = ~0ull;
pmu->global_status_rsvd = ~0ull;
diff --git a/arch/x86/kvm/svm/pmu.c b/arch/x86/kvm/svm/pmu.c
index c18286545a7a..52224872e0d4 100644
--- a/arch/x86/kvm/svm/pmu.c
+++ b/arch/x86/kvm/svm/pmu.c
@@ -163,7 +163,7 @@ static int amd_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
/* MSR_EVNTSELn */
pmc = get_gp_pmc_amd(pmu, msr, PMU_TYPE_EVNTSEL);
if (pmc) {
- data &= ~pmu->reserved_bits;
+ data &= ~pmu->eventsel_rsvd;
if (data != pmc->eventsel) {
pmc->eventsel = data;
pmc->eventsel_hw = (data & ~AMD64_EVENTSEL_HOSTONLY) |
@@ -214,9 +214,15 @@ static void amd_pmu_refresh(struct kvm_vcpu *vcpu)
pmu->counter_bitmask[KVM_PMC_GP] = BIT_ULL(48) - 1;
- pmu->reserved_bits = 0xfffffff000280000ull;
+ /*
+ * AMD PerfEvtSeln registers do not support PinControl, and KVM does
+ * not emulate AnyThread due to cross-VM information leakage on SMT
+ * cores (as on the Intel side).
+ */
+ pmu->eventsel_rsvd = GENMASK_ULL(63, 36) | ARCH_PERFMON_EVENTSEL_PIN_CONTROL |
+ ARCH_PERFMON_EVENTSEL_ANY;
if (guest_cpu_cap_has(vcpu, X86_FEATURE_SVM) && kvm_vcpu_has_mediated_pmu(vcpu))
- pmu->reserved_bits &= ~AMD64_EVENTSEL_HOST_GUEST_MASK;
+ pmu->eventsel_rsvd &= ~AMD64_EVENTSEL_HOST_GUEST_MASK;
pmu->raw_event_mask = AMD64_RAW_EVENT_MASK;
/* not applicable to AMD; but clean them to prevent any fall out */
diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c
index 322a3ad580dd..11c9161dc853 100644
--- a/arch/x86/kvm/vmx/pmu_intel.c
+++ b/arch/x86/kvm/vmx/pmu_intel.c
@@ -385,7 +385,7 @@ static int intel_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
struct kvm_pmc *pmc;
u32 msr = msr_info->index;
u64 data = msr_info->data;
- u64 reserved_bits, diff;
+ u64 eventsel_rsvd, diff;
switch (msr) {
case MSR_CORE_PERF_FIXED_CTR_CTRL:
@@ -433,11 +433,11 @@ static int intel_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
pmc_write_counter(pmc, data);
break;
} else if ((pmc = get_gp_pmc(pmu, msr, MSR_P6_EVNTSEL0))) {
- reserved_bits = pmu->reserved_bits;
+ eventsel_rsvd = pmu->eventsel_rsvd;
if ((pmc->idx == 2) &&
(pmu->raw_event_mask & HSW_IN_TX_CHECKPOINTED))
- reserved_bits ^= HSW_IN_TX_CHECKPOINTED;
- if (data & reserved_bits)
+ eventsel_rsvd ^= HSW_IN_TX_CHECKPOINTED;
+ if (data & eventsel_rsvd)
return 1;
if (data != pmc->eventsel) {
@@ -545,7 +545,7 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu)
if (entry &&
(boot_cpu_has(X86_FEATURE_HLE) || boot_cpu_has(X86_FEATURE_RTM)) &&
(entry->ebx & (X86_FEATURE_HLE|X86_FEATURE_RTM))) {
- pmu->reserved_bits ^= HSW_IN_TX;
+ pmu->eventsel_rsvd ^= HSW_IN_TX;
pmu->raw_event_mask |= (HSW_IN_TX|HSW_IN_TX_CHECKPOINTED);
}
@@ -584,7 +584,7 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu)
if (perf_capabilities & PERF_CAP_PEBS_FORMAT) {
if (perf_capabilities & PERF_CAP_PEBS_BASELINE) {
pmu->pebs_enable_rsvd = counter_rsvd;
- pmu->reserved_bits &= ~ICL_EVENTSEL_ADAPTIVE;
+ pmu->eventsel_rsvd &= ~ICL_EVENTSEL_ADAPTIVE;
pmu->pebs_data_cfg_rsvd = ~0xff00000full;
intel_pmu_enable_fixed_counter_bits(pmu, ICL_FIXED_0_ADAPTIVE);
} else {
--
2.55.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH v2 04/16] KVM: x86/pmu: Gate BUFFER_OVF reserved bit on guest DS
2026-08-27 22:37 [PATCH] KVM: x86/pmu: Add mediated vPMU PerfMon v5 support Zide Chen
` (2 preceding siblings ...)
2026-08-27 22:37 ` [PATCH v2 03/16] KVM: x86/pmu: Rename reserved_bits to eventsel_rsvd in kvm_pmu Zide Chen
@ 2026-08-27 22:37 ` Zide Chen
2026-08-27 22:37 ` [PATCH v2 05/16] KVM: x86/pmu: Add PMC bitmap accessor helpers Zide Chen
` (11 subsequent siblings)
15 siblings, 0 replies; 21+ messages in thread
From: Zide Chen @ 2026-08-27 22:37 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini, Peter Zijlstra
Cc: kvm, Andi Kleen, Jim Mattson, Stephane Eranian, linux-kernel,
Mingwei Zhang, Zide Chen, Das Sandipan, Shukla Manali, Dapeng Mi,
Xudong Hao
global_status_rsvd unconditionally clears GLOBAL_STATUS_BUFFER_OVF
(bit 62), even when the guest does not have X86_FEATURE_DS feature.
Clear it only when guest_cpu_cap_has(vcpu, X86_FEATURE_DS), matching
hardware behavior.
Fixes: f5132b01386b ("KVM: Expose a version 2 architectural PMU to a guests")
Signed-off-by: Zide Chen <zide.chen@intel.com>
---
v2: new patch.
---
arch/x86/kvm/vmx/pmu_intel.c | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c
index 11c9161dc853..9fdae437e569 100644
--- a/arch/x86/kvm/vmx/pmu_intel.c
+++ b/arch/x86/kvm/vmx/pmu_intel.c
@@ -576,8 +576,16 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu)
((BIT_ULL(pmu->nr_arch_fixed_counters) - 1) << KVM_FIXED_PMC_BASE_IDX));
pmu->global_ctrl_rsvd = counter_rsvd;
- pmu->global_status_rsvd = pmu->global_ctrl_rsvd
- & ~(GLOBAL_STATUS_BUFFER_OVF | GLOBAL_STATUS_COND_CHG);
+ /*
+ * Layout of bits 48:0 in IA32_PERF_GLOBAL_CTRL is identical to
+ * IA32_PERF_GLOBAL_STATUS, but IA32_PERF_GLOBAL_STATUS has additional
+ * bits.
+ */
+ pmu->global_status_rsvd = pmu->global_ctrl_rsvd;
+ if (guest_cpu_cap_has(vcpu, X86_FEATURE_DS))
+ pmu->global_status_rsvd &= ~GLOBAL_STATUS_BUFFER_OVF;
+ pmu->global_status_rsvd &= ~GLOBAL_STATUS_COND_CHG;
+
if (vmx_pt_mode_is_host_guest())
pmu->global_status_rsvd &= ~GLOBAL_STATUS_TRACE_TOPAPMI;
--
2.55.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH v2 05/16] KVM: x86/pmu: Add PMC bitmap accessor helpers
2026-08-27 22:37 [PATCH] KVM: x86/pmu: Add mediated vPMU PerfMon v5 support Zide Chen
` (3 preceding siblings ...)
2026-08-27 22:37 ` [PATCH v2 04/16] KVM: x86/pmu: Gate BUFFER_OVF reserved bit on guest DS Zide Chen
@ 2026-08-27 22:37 ` Zide Chen
2026-08-27 22:37 ` [PATCH v2 06/16] KVM: x86/pmu: Drop nr_arch_{gp,fixed}_counters from kvm_pmu Zide Chen
` (10 subsequent siblings)
15 siblings, 0 replies; 21+ messages in thread
From: Zide Chen @ 2026-08-27 22:37 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini, Peter Zijlstra
Cc: kvm, Andi Kleen, Jim Mattson, Stephane Eranian, linux-kernel,
Mingwei Zhang, Zide Chen, Das Sandipan, Shukla Manali, Dapeng Mi,
Xudong Hao
pmu->nr_arch_{gp,fixed}_counters is not able to represent that a PMU
may include non-contiguous GP or fixed counters.
pmu->pmc_exists already holds a bitmap indicating both fixed and
general-purpose counters, and loops over valid counters can be done via
pmu->pmc_exists alone. Extend it to a union so that the u64 alias is
available for convenient mask arithmetic operations.
Add the necessary helpers to prepare for bitmap-based PMC counter
implementation.
No functional change intended.
Co-developed-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
Signed-off-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
Signed-off-by: Zide Chen <zide.chen@intel.com>
---
v2:
- Rename kvm_{gp,fixed}_pmc_supported() to kvm_is_{gp,fixed}_pmc_supported()
and simplify them by removing the temporary bitmap. (Sean)
- Split kvm_for_each_set_pmc_idx() to kvm_for_each_gp_counter()
and kvm_for_each_fixed_counter(). (Sean)
---
arch/x86/include/asm/kvm_host.h | 5 +++-
arch/x86/kvm/pmu.h | 53 +++++++++++++++++++++++++++++----
2 files changed, 51 insertions(+), 7 deletions(-)
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 90d80483df7f..49a8a2e9cee0 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -594,7 +594,10 @@ struct kvm_pmu {
DECLARE_BITMAP(reprogram_pmi, X86_PMC_IDX_MAX);
atomic64_t __reprogram_pmi;
};
- DECLARE_BITMAP(pmc_exists, X86_PMC_IDX_MAX);
+ union {
+ DECLARE_BITMAP(pmc_exists, X86_PMC_IDX_MAX);
+ u64 pmc_exists64;
+ };
DECLARE_BITMAP(pmc_in_use, X86_PMC_IDX_MAX);
DECLARE_BITMAP(pmc_counting_instructions, X86_PMC_IDX_MAX);
diff --git a/arch/x86/kvm/pmu.h b/arch/x86/kvm/pmu.h
index 090c9bbb74f4..b7a319b1eccc 100644
--- a/arch/x86/kvm/pmu.h
+++ b/arch/x86/kvm/pmu.h
@@ -88,6 +88,30 @@ static inline bool kvm_vcpu_has_mediated_pmu(struct kvm_vcpu *vcpu)
return enable_mediated_pmu && vcpu_to_pmu(vcpu)->version;
}
+static inline unsigned long kvm_gp_pmc_mask(struct kvm_pmu *pmu)
+{
+ return pmu->pmc_exists64 &
+ GENMASK_ULL(KVM_MAX_NR_GP_COUNTERS - 1, 0);
+}
+
+static inline unsigned long kvm_fixed_pmc_mask(struct kvm_pmu *pmu)
+{
+ return (pmu->pmc_exists64 >> KVM_FIXED_PMC_BASE_IDX) &
+ GENMASK_ULL(KVM_MAX_NR_FIXED_COUNTERS - 1, 0);
+}
+
+static inline bool kvm_is_gp_pmc_supported(struct kvm_pmu *pmu, unsigned int idx)
+{
+ return idx < KVM_MAX_NR_GP_COUNTERS &&
+ test_bit(idx, pmu->pmc_exists);
+}
+
+static inline bool kvm_is_fixed_pmc_supported(struct kvm_pmu *pmu, unsigned int idx)
+{
+ return idx < KVM_MAX_NR_FIXED_COUNTERS &&
+ test_bit(KVM_FIXED_PMC_BASE_IDX + idx, pmu->pmc_exists);
+}
+
/*
* KVM tracks all counters in 64-bit bitmaps, with general purpose counters
* mapped to bits 31:0 and fixed counters mapped to 63:32, e.g. fixed counter 0
@@ -104,11 +128,11 @@ static inline bool kvm_vcpu_has_mediated_pmu(struct kvm_vcpu *vcpu)
*/
static inline struct kvm_pmc *kvm_pmc_idx_to_pmc(struct kvm_pmu *pmu, int idx)
{
- if (idx < pmu->nr_arch_gp_counters)
+ if (kvm_is_gp_pmc_supported(pmu, idx))
return &pmu->gp_counters[idx];
idx -= KVM_FIXED_PMC_BASE_IDX;
- if (idx >= 0 && idx < pmu->nr_arch_fixed_counters)
+ if (kvm_is_fixed_pmc_supported(pmu, idx))
return &pmu->fixed_counters[idx];
return NULL;
@@ -120,6 +144,17 @@ static inline struct kvm_pmc *kvm_pmc_idx_to_pmc(struct kvm_pmu *pmu, int idx)
continue; \
else \
+/*
+ * @mask is expected to be a scalar unsigned long derived from pmu->pmc_exists,
+ * which is already constrained by KVM_MAX_NR_{AMD,INTEL}_{GP,FIXED}_COUNTERS,
+ * so iteration up to KVM's maximum counter count is safe.
+ */
+#define kvm_for_each_gp_counter(i, mask) \
+ for_each_set_bit((i), &(mask), KVM_MAX_NR_GP_COUNTERS)
+
+#define kvm_for_each_fixed_counter(i, mask) \
+ for_each_set_bit((i), &(mask), KVM_MAX_NR_FIXED_COUNTERS)
+
static inline u64 pmc_bitmask(struct kvm_pmc *pmc)
{
struct kvm_pmu *pmu = pmc_to_pmu(pmc);
@@ -168,9 +203,12 @@ static inline bool kvm_valid_perf_global_ctrl(struct kvm_pmu *pmu,
static inline struct kvm_pmc *get_gp_pmc(struct kvm_pmu *pmu, u32 msr,
u32 base)
{
- if (msr >= base && msr < base + pmu->nr_arch_gp_counters) {
+ if (msr >= base && msr < base + KVM_MAX_NR_GP_COUNTERS) {
u32 index = array_index_nospec(msr - base,
- pmu->nr_arch_gp_counters);
+ KVM_MAX_NR_GP_COUNTERS);
+
+ if (!kvm_is_gp_pmc_supported(pmu, index))
+ return NULL;
return &pmu->gp_counters[index];
}
@@ -183,9 +221,12 @@ static inline struct kvm_pmc *get_fixed_pmc(struct kvm_pmu *pmu, u32 msr)
{
int base = MSR_CORE_PERF_FIXED_CTR0;
- if (msr >= base && msr < base + pmu->nr_arch_fixed_counters) {
+ if (msr >= base && msr < base + KVM_MAX_NR_FIXED_COUNTERS) {
u32 index = array_index_nospec(msr - base,
- pmu->nr_arch_fixed_counters);
+ KVM_MAX_NR_FIXED_COUNTERS);
+
+ if (!kvm_is_fixed_pmc_supported(pmu, index))
+ return NULL;
return &pmu->fixed_counters[index];
}
--
2.55.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH v2 06/16] KVM: x86/pmu: Drop nr_arch_{gp,fixed}_counters from kvm_pmu
2026-08-27 22:37 [PATCH] KVM: x86/pmu: Add mediated vPMU PerfMon v5 support Zide Chen
` (4 preceding siblings ...)
2026-08-27 22:37 ` [PATCH v2 05/16] KVM: x86/pmu: Add PMC bitmap accessor helpers Zide Chen
@ 2026-08-27 22:37 ` Zide Chen
2026-08-27 22:37 ` [PATCH v2 07/16] KVM: x86/pmu: Expose kvm_host_pmu to vendor modules Zide Chen
` (9 subsequent siblings)
15 siblings, 0 replies; 21+ messages in thread
From: Zide Chen @ 2026-08-27 22:37 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini, Peter Zijlstra
Cc: kvm, Andi Kleen, Jim Mattson, Stephane Eranian, linux-kernel,
Mingwei Zhang, Zide Chen, Das Sandipan, Shukla Manali, Dapeng Mi,
Xudong Hao
From: Dapeng Mi <dapeng1.mi@linux.intel.com>
This is a preparatory step toward fully bitmap-based PMU capability
handling. Later patches switch KVM's view of host PMU capabilities from
counter counts to counter bitmaps.
Populate and use pmc_exists directly when walking guest-visible PMCs,
instead of relying on pmu->nr_arch_{gp,fixed}_counters as intermediate
state.
Iterate counters via the newly added pmc_exists based helpers, and
remove the now-redundant nr_arch_{gp,fixed}_counters fields from struct
kvm_pmu.
Note: {vmx,svm}_recalc_pmu_msr_intercepts() may leave stale intercepts
on GP MSRs after CPUID narrowing. This corner case is resolved by the
following patch: "KVM: x86/pmu: Switch to bitmask-based KVM PMU
capabilities".
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>
---
v2:
- Fix AMD legacy PMU MSR intercept handling for guest PMU
narrowing reconfiguration by adding for_each_clear_bit_from().
- Re-word the stale interception changelog part.
---
arch/x86/include/asm/kvm_host.h | 2 --
arch/x86/kvm/pmu.c | 26 +++++++++----------
arch/x86/kvm/svm/pmu.c | 25 ++++++++----------
arch/x86/kvm/svm/svm.c | 8 ++++--
arch/x86/kvm/vmx/nested.c | 6 +++--
arch/x86/kvm/vmx/pmu_intel.c | 46 ++++++++++++++++++++++-----------
arch/x86/kvm/vmx/vmx.c | 6 +++--
7 files changed, 68 insertions(+), 51 deletions(-)
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 49a8a2e9cee0..717ba98c08c9 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -569,8 +569,6 @@ struct kvm_pmc {
struct kvm_pmu {
u8 version;
- unsigned nr_arch_gp_counters;
- unsigned nr_arch_fixed_counters;
unsigned available_event_types;
u64 fixed_ctr_ctrl;
u64 fixed_ctr_ctrl_hw;
diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c
index 964e74bdaaa7..b0fcd0a538d2 100644
--- a/arch/x86/kvm/pmu.c
+++ b/arch/x86/kvm/pmu.c
@@ -785,8 +785,8 @@ static bool kvm_need_any_pmc_intercept(struct kvm_vcpu *vcpu)
* KVM's capabilities are constrained based on KVM support, i.e. KVM's
* capabilities themselves may be a subset of hardware capabilities.
*/
- return pmu->nr_arch_gp_counters != kvm_host_pmu.num_counters_gp ||
- pmu->nr_arch_fixed_counters != kvm_host_pmu.num_counters_fixed;
+ return kvm_gp_pmc_mask(pmu) != BIT_ULL(kvm_host_pmu.num_counters_gp) - 1 ||
+ kvm_fixed_pmc_mask(pmu) != BIT_ULL(kvm_host_pmu.num_counters_fixed) - 1;
}
bool kvm_need_perf_global_ctrl_intercept(struct kvm_vcpu *vcpu)
@@ -985,8 +985,6 @@ void kvm_pmu_refresh(struct kvm_vcpu *vcpu)
kvm_pmu_reset(vcpu);
pmu->version = 0;
- pmu->nr_arch_gp_counters = 0;
- pmu->nr_arch_fixed_counters = 0;
pmu->counter_bitmask[KVM_PMC_GP] = 0;
pmu->counter_bitmask[KVM_PMC_FIXED] = 0;
/*
@@ -1014,16 +1012,12 @@ void kvm_pmu_refresh(struct kvm_vcpu *vcpu)
* in the global controls). Emulate that behavior when refreshing the
* PMU so that userspace doesn't need to manually set PERF_GLOBAL_CTRL.
*/
- if (pmu->nr_arch_gp_counters &&
+ if (kvm_gp_pmc_mask(pmu) &&
(kvm_pmu_has_perf_global_ctrl(pmu) || kvm_vcpu_has_mediated_pmu(vcpu)))
- pmu->global_ctrl = GENMASK_ULL(pmu->nr_arch_gp_counters - 1, 0);
+ pmu->global_ctrl = kvm_gp_pmc_mask(pmu);
if (kvm_vcpu_has_mediated_pmu(vcpu))
kvm_pmu_call(write_global_ctrl)(pmu->global_ctrl);
-
- bitmap_set(pmu->pmc_exists, 0, pmu->nr_arch_gp_counters);
- bitmap_set(pmu->pmc_exists, KVM_FIXED_PMC_BASE_IDX,
- pmu->nr_arch_fixed_counters);
}
void kvm_pmu_init(struct kvm_vcpu *vcpu)
@@ -1332,6 +1326,8 @@ static __always_inline u32 gp_eventsel_msr(u32 idx)
static void kvm_pmu_load_guest_pmcs(struct kvm_vcpu *vcpu)
{
struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
+ unsigned long fixed_mask = kvm_fixed_pmc_mask(pmu);
+ unsigned long gp_mask = kvm_gp_pmc_mask(pmu);
struct kvm_pmc *pmc;
u32 i;
@@ -1340,14 +1336,14 @@ static void kvm_pmu_load_guest_pmcs(struct kvm_vcpu *vcpu)
* is intercepted if hardware has counters that aren't visible to the
* guest (KVM will inject #GP as appropriate).
*/
- for (i = 0; i < pmu->nr_arch_gp_counters; i++) {
+ kvm_for_each_gp_counter(i, gp_mask) {
pmc = &pmu->gp_counters[i];
if (pmc->counter != rdpmc(i))
wrmsrq(gp_counter_msr(i), pmc->counter);
wrmsrq(gp_eventsel_msr(i), pmc->eventsel_hw);
}
- for (i = 0; i < pmu->nr_arch_fixed_counters; i++) {
+ kvm_for_each_fixed_counter(i, fixed_mask) {
pmc = &pmu->fixed_counters[i];
if (pmc->counter != rdpmc(INTEL_PMC_FIXED_RDPMC_BASE | i))
@@ -1390,6 +1386,8 @@ void kvm_mediated_pmu_load(struct kvm_vcpu *vcpu)
static void kvm_pmu_put_guest_pmcs(struct kvm_vcpu *vcpu)
{
struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
+ unsigned long fixed_mask = kvm_fixed_pmc_mask(pmu);
+ unsigned long gp_mask = kvm_gp_pmc_mask(pmu);
struct kvm_pmc *pmc;
u32 i;
@@ -1397,7 +1395,7 @@ static void kvm_pmu_put_guest_pmcs(struct kvm_vcpu *vcpu)
* Clear selectors and counters to ensure hardware doesn't count using
* guest controls when the host (perf) restores its state.
*/
- for (i = 0; i < pmu->nr_arch_gp_counters; i++) {
+ kvm_for_each_gp_counter(i, gp_mask) {
pmc = &pmu->gp_counters[i];
pmc->counter = rdpmc(i);
@@ -1407,7 +1405,7 @@ static void kvm_pmu_put_guest_pmcs(struct kvm_vcpu *vcpu)
wrmsrq(gp_eventsel_msr(i), 0);
}
- for (i = 0; i < pmu->nr_arch_fixed_counters; i++) {
+ kvm_for_each_fixed_counter(i, fixed_mask) {
pmc = &pmu->fixed_counters[i];
pmc->counter = rdpmc(INTEL_PMC_FIXED_RDPMC_BASE | i);
diff --git a/arch/x86/kvm/svm/pmu.c b/arch/x86/kvm/svm/pmu.c
index 52224872e0d4..0943ccc1d6b8 100644
--- a/arch/x86/kvm/svm/pmu.c
+++ b/arch/x86/kvm/svm/pmu.c
@@ -27,12 +27,11 @@ enum pmu_type {
static struct kvm_pmc *amd_pmu_get_pmc(struct kvm_pmu *pmu, int pmc_idx)
{
- unsigned int num_counters = pmu->nr_arch_gp_counters;
-
- if (pmc_idx >= num_counters)
+ if (!kvm_is_gp_pmc_supported(pmu, pmc_idx))
return NULL;
- return &pmu->gp_counters[array_index_nospec(pmc_idx, num_counters)];
+ pmc_idx = array_index_nospec(pmc_idx, KVM_MAX_NR_AMD_GP_COUNTERS);
+ return &pmu->gp_counters[pmc_idx];
}
static inline struct kvm_pmc *get_gp_pmc_amd(struct kvm_pmu *pmu, u32 msr,
@@ -77,7 +76,7 @@ static int amd_check_rdpmc_early(struct kvm_vcpu *vcpu, unsigned int idx)
{
struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
- if (idx >= pmu->nr_arch_gp_counters)
+ if (!kvm_is_gp_pmc_supported(pmu, idx))
return -EINVAL;
return 0;
@@ -117,7 +116,7 @@ static bool amd_is_valid_msr(struct kvm_vcpu *vcpu, u32 msr)
return pmu->version > 1;
default:
if (msr > MSR_F15H_PERF_CTR5 &&
- msr < MSR_F15H_PERF_CTL0 + 2 * pmu->nr_arch_gp_counters)
+ msr < MSR_F15H_PERF_CTL0 + 2 * hweight_long(kvm_gp_pmc_mask(pmu)))
return pmu->version > 1;
break;
}
@@ -184,6 +183,7 @@ static int amd_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
static void amd_pmu_refresh(struct kvm_vcpu *vcpu)
{
+ unsigned int nr_gp_counters = AMD64_NUM_COUNTERS;
struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
union cpuid_0x80000022_ebx ebx;
@@ -197,18 +197,16 @@ static void amd_pmu_refresh(struct kvm_vcpu *vcpu)
BUILD_BUG_ON(x86_feature_cpuid(X86_FEATURE_PERFMON_V2).function != 0x80000022 ||
x86_feature_cpuid(X86_FEATURE_PERFMON_V2).index);
ebx.full = kvm_find_cpuid_entry_index(vcpu, 0x80000022, 0)->ebx;
- pmu->nr_arch_gp_counters = ebx.split.num_core_pmc;
+ nr_gp_counters = ebx.split.num_core_pmc;
} else if (guest_cpu_cap_has(vcpu, X86_FEATURE_PERFCTR_CORE)) {
- pmu->nr_arch_gp_counters = AMD64_NUM_COUNTERS_CORE;
- } else {
- pmu->nr_arch_gp_counters = AMD64_NUM_COUNTERS;
+ nr_gp_counters = AMD64_NUM_COUNTERS_CORE;
}
- pmu->nr_arch_gp_counters = min_t(unsigned int, pmu->nr_arch_gp_counters,
- kvm_pmu_cap.num_counters_gp);
+ pmu->pmc_exists64 = (BIT_ULL(nr_gp_counters) - 1) &
+ (BIT_ULL(kvm_pmu_cap.num_counters_gp) - 1);
if (pmu->version > 1) {
- pmu->global_ctrl_rsvd = ~(BIT_ULL(pmu->nr_arch_gp_counters) - 1);
+ pmu->global_ctrl_rsvd = ~pmu->pmc_exists64;
pmu->global_status_rsvd = pmu->global_ctrl_rsvd;
}
@@ -227,7 +225,6 @@ static void amd_pmu_refresh(struct kvm_vcpu *vcpu)
pmu->raw_event_mask = AMD64_RAW_EVENT_MASK;
/* not applicable to AMD; but clean them to prevent any fall out */
pmu->counter_bitmask[KVM_PMC_FIXED] = 0;
- pmu->nr_arch_fixed_counters = 0;
}
static void amd_pmu_init(struct kvm_vcpu *vcpu)
diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
index 9d607b98bd06..fb8442a08b63 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -753,18 +753,22 @@ static void svm_recalc_pmu_msr_intercepts(struct kvm_vcpu *vcpu)
{
bool intercept = !kvm_vcpu_has_mediated_pmu(vcpu);
struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
+ unsigned long gp_mask = kvm_gp_pmc_mask(pmu);
int i;
if (!enable_mediated_pmu)
return;
/* Legacy counters are always available for AMD CPUs with a PMU. */
- for (i = 0; i < min(pmu->nr_arch_gp_counters, AMD64_NUM_COUNTERS); i++)
+ for_each_set_bit(i, &gp_mask, AMD64_NUM_COUNTERS)
svm_set_intercept_for_msr(vcpu, MSR_K7_PERFCTR0 + i,
MSR_TYPE_RW, intercept);
+ for_each_clear_bit(i, &gp_mask, AMD64_NUM_COUNTERS)
+ svm_enable_intercept_for_msr(vcpu, MSR_K7_PERFCTR0 + i,
+ MSR_TYPE_RW);
intercept |= !guest_cpu_cap_has(vcpu, X86_FEATURE_PERFCTR_CORE);
- for (i = 0; i < pmu->nr_arch_gp_counters; i++)
+ kvm_for_each_gp_counter(i, gp_mask)
svm_set_intercept_for_msr(vcpu, MSR_F15H_PERF_CTR + 2 * i,
MSR_TYPE_RW, intercept);
diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c
index 7ed79894d11d..0cff369982ae 100644
--- a/arch/x86/kvm/vmx/nested.c
+++ b/arch/x86/kvm/vmx/nested.c
@@ -697,6 +697,8 @@ static void nested_vmx_merge_pmu_msr_bitmaps(struct kvm_vcpu *vcpu,
{
struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
struct vcpu_vmx *vmx = to_vmx(vcpu);
+ unsigned long gp_mask = kvm_gp_pmc_mask(pmu);
+ unsigned long fixed_mask = kvm_fixed_pmc_mask(pmu);
int i;
/*
@@ -706,12 +708,12 @@ static void nested_vmx_merge_pmu_msr_bitmaps(struct kvm_vcpu *vcpu,
if (!kvm_vcpu_has_mediated_pmu(vcpu))
return;
- for (i = 0; i < pmu->nr_arch_gp_counters; i++) {
+ kvm_for_each_gp_counter(i, gp_mask) {
nested_vmx_merge_msr_bitmaps_rw(MSR_IA32_PERFCTR0 + i);
nested_vmx_merge_msr_bitmaps_rw(MSR_IA32_PMC0 + i);
}
- for (i = 0; i < pmu->nr_arch_fixed_counters; i++)
+ kvm_for_each_fixed_counter(i, fixed_mask)
nested_vmx_merge_msr_bitmaps_rw(MSR_CORE_PERF_FIXED_CTR0 + i);
nested_vmx_merge_msr_bitmaps_rw(MSR_CORE_PERF_GLOBAL_CTRL);
diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c
index 9fdae437e569..926d1c57f8bf 100644
--- a/arch/x86/kvm/vmx/pmu_intel.c
+++ b/arch/x86/kvm/vmx/pmu_intel.c
@@ -66,12 +66,13 @@ static void reprogram_fixed_counters(struct kvm_pmu *pmu, u64 data)
* hardware, e.g. to ensure the event filter is enforced.
*/
u64 old_fixed_ctr_ctrl = pmu->fixed_ctr_ctrl_hw;
+ unsigned long fixed_mask = kvm_fixed_pmc_mask(pmu);
struct kvm_pmc *pmc;
int i;
pmu->fixed_ctr_ctrl = data;
pmu->fixed_ctr_ctrl_hw = data;
- for (i = 0; i < pmu->nr_arch_fixed_counters; i++) {
+ kvm_for_each_fixed_counter(i, fixed_mask) {
u8 new_ctrl = fixed_ctrl_field(data, i);
u8 old_ctrl = fixed_ctrl_field(old_fixed_ctr_ctrl, i);
@@ -107,6 +108,8 @@ static struct kvm_pmc *intel_rdpmc_ecx_to_pmc(struct kvm_vcpu *vcpu,
if (WARN_ON_ONCE(!pmu->version))
return NULL;
+ idx &= INTEL_RDPMC_INDEX_MASK;
+
/*
* General Purpose (GP) PMCs are supported on all PMUs, and fixed PMCs
* are supported on all architectural PMUs, i.e. on all virtual PMUs
@@ -117,23 +120,25 @@ static struct kvm_pmc *intel_rdpmc_ecx_to_pmc(struct kvm_vcpu *vcpu,
*/
switch (type) {
case INTEL_RDPMC_FIXED:
+ if (!kvm_is_fixed_pmc_supported(pmu, idx))
+ return NULL;
+
counters = pmu->fixed_counters;
- num_counters = pmu->nr_arch_fixed_counters;
+ num_counters = KVM_MAX_NR_INTEL_FIXED_COUNTERS;
bitmask = pmu->counter_bitmask[KVM_PMC_FIXED];
break;
case INTEL_RDPMC_GP:
+ if (!kvm_is_gp_pmc_supported(pmu, idx))
+ return NULL;
+
counters = pmu->gp_counters;
- num_counters = pmu->nr_arch_gp_counters;
+ num_counters = KVM_MAX_NR_INTEL_GP_COUNTERS;
bitmask = pmu->counter_bitmask[KVM_PMC_GP];
break;
default:
return NULL;
}
- idx &= INTEL_RDPMC_INDEX_MASK;
- if (idx >= num_counters)
- return NULL;
-
*mask &= bitmask;
return &counters[array_index_nospec(idx, num_counters)];
}
@@ -492,9 +497,10 @@ static __always_inline u64 intel_get_fixed_pmc_eventsel(unsigned int index)
static void intel_pmu_enable_fixed_counter_bits(struct kvm_pmu *pmu, u64 bits)
{
+ unsigned long fixed_mask = kvm_fixed_pmc_mask(pmu);
int i;
- for (i = 0; i < pmu->nr_arch_fixed_counters; i++)
+ kvm_for_each_fixed_counter(i, fixed_mask)
pmu->fixed_ctr_ctrl_rsvd &= ~intel_fixed_bits_by_idx(i, bits);
}
@@ -506,6 +512,8 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu)
union cpuid10_eax eax;
union cpuid10_edx edx;
u64 perf_capabilities;
+ u64 fixed_cntr_mask;
+ int nr_gp_counters;
u64 counter_rsvd;
if (!lbr_desc)
@@ -532,8 +540,6 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu)
if (!pmu->version)
return;
- pmu->nr_arch_gp_counters = min_t(int, eax.split.num_counters,
- kvm_pmu_cap.num_counters_gp);
eax.split.bit_width = min_t(int, eax.split.bit_width,
kvm_pmu_cap.bit_width_gp);
pmu->counter_bitmask[KVM_PMC_GP] = BIT_ULL(eax.split.bit_width) - 1;
@@ -541,6 +547,17 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu)
kvm_pmu_cap.events_mask_len);
pmu->available_event_types = ~entry->ebx & (BIT_ULL(eax.split.mask_length) - 1);
+ fixed_cntr_mask = BIT_ULL(edx.split.num_counters_fixed) - 1;
+ fixed_cntr_mask &= BIT_ULL(kvm_pmu_cap.num_counters_fixed) - 1;
+
+ /*
+ * The number of counters comes from guest CPUID data. Clamp the value
+ * to avoid a shift-by-64 in BIT_ULL().
+ */
+ nr_gp_counters = min_t(int, eax.split.num_counters, X86_PMC_IDX_MAX - 1);
+ pmu->pmc_exists64 = (BIT_ULL(nr_gp_counters) - 1) &
+ (BIT_ULL(kvm_pmu_cap.num_counters_gp) - 1);
+
entry = kvm_find_cpuid_entry_index(vcpu, 7, 0);
if (entry &&
(boot_cpu_has(X86_FEATURE_HLE) || boot_cpu_has(X86_FEATURE_RTM)) &&
@@ -562,8 +579,7 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu)
if (pmu->version == 1)
return;
- pmu->nr_arch_fixed_counters = min_t(int, edx.split.num_counters_fixed,
- kvm_pmu_cap.num_counters_fixed);
+ pmu->pmc_exists64 |= fixed_cntr_mask << INTEL_PMC_IDX_FIXED;
edx.split.bit_width_fixed = min_t(int, edx.split.bit_width_fixed,
kvm_pmu_cap.bit_width_fixed);
pmu->counter_bitmask[KVM_PMC_FIXED] = BIT_ULL(edx.split.bit_width_fixed) - 1;
@@ -572,8 +588,8 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu)
INTEL_FIXED_0_USER |
INTEL_FIXED_0_ENABLE_PMI);
- counter_rsvd = ~((BIT_ULL(pmu->nr_arch_gp_counters) - 1) |
- ((BIT_ULL(pmu->nr_arch_fixed_counters) - 1) << KVM_FIXED_PMC_BASE_IDX));
+ counter_rsvd = ~(kvm_gp_pmc_mask(pmu) |
+ ((u64)kvm_fixed_pmc_mask(pmu) << KVM_FIXED_PMC_BASE_IDX));
pmu->global_ctrl_rsvd = counter_rsvd;
/*
@@ -596,7 +612,7 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu)
pmu->pebs_data_cfg_rsvd = ~0xff00000full;
intel_pmu_enable_fixed_counter_bits(pmu, ICL_FIXED_0_ADAPTIVE);
} else {
- pmu->pebs_enable_rsvd = ~(BIT_ULL(pmu->nr_arch_gp_counters) - 1);
+ pmu->pebs_enable_rsvd = ~kvm_gp_pmc_mask(pmu);
}
}
}
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index d3789b2ee18e..be994adbd954 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -4230,6 +4230,8 @@ static void vmx_recalc_pmu_msr_intercepts(struct kvm_vcpu *vcpu)
bool has_mediated_pmu = kvm_vcpu_has_mediated_pmu(vcpu);
struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
struct vcpu_vmx *vmx = to_vmx(vcpu);
+ unsigned long fixed_mask = kvm_fixed_pmc_mask(pmu);
+ unsigned long gp_mask = kvm_gp_pmc_mask(pmu);
bool intercept = !has_mediated_pmu;
int i;
@@ -4250,7 +4252,7 @@ static void vmx_recalc_pmu_msr_intercepts(struct kvm_vcpu *vcpu)
vm_exit_controls_changebit(vmx, vm_exit_controls_bits, has_mediated_pmu);
- for (i = 0; i < pmu->nr_arch_gp_counters; i++) {
+ kvm_for_each_gp_counter(i, gp_mask) {
vmx_set_intercept_for_msr(vcpu, MSR_IA32_PERFCTR0 + i,
MSR_TYPE_RW, intercept);
vmx_set_intercept_for_msr(vcpu, MSR_IA32_PMC0 + i, MSR_TYPE_RW,
@@ -4263,7 +4265,7 @@ static void vmx_recalc_pmu_msr_intercepts(struct kvm_vcpu *vcpu)
MSR_TYPE_RW, true);
}
- for (i = 0; i < pmu->nr_arch_fixed_counters; i++)
+ kvm_for_each_fixed_counter(i, fixed_mask)
vmx_set_intercept_for_msr(vcpu, MSR_CORE_PERF_FIXED_CTR0 + i,
MSR_TYPE_RW, intercept);
for ( ; i < kvm_pmu_cap.num_counters_fixed; i++)
--
2.55.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH v2 07/16] KVM: x86/pmu: Expose kvm_host_pmu to vendor modules
2026-08-27 22:37 [PATCH] KVM: x86/pmu: Add mediated vPMU PerfMon v5 support Zide Chen
` (5 preceding siblings ...)
2026-08-27 22:37 ` [PATCH v2 06/16] KVM: x86/pmu: Drop nr_arch_{gp,fixed}_counters from kvm_pmu Zide Chen
@ 2026-08-27 22:37 ` Zide Chen
2026-08-27 22:37 ` [PATCH v2 08/16] perf/x86: Plumb counter bitmap from x86_pmu to x86_pmu_cap Zide Chen
` (8 subsequent siblings)
15 siblings, 0 replies; 21+ messages in thread
From: Zide Chen @ 2026-08-27 22:37 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini, Peter Zijlstra
Cc: kvm, Andi Kleen, Jim Mattson, Stephane Eranian, linux-kernel,
Mingwei Zhang, Zide Chen, Das Sandipan, Shukla Manali, Dapeng Mi,
Xudong Hao
kvm_host_pmu holds the unadulterated host PMU capabilities, which
can be used to compare against guest capabilities to determine whether
certain MSRs should be intercepted by KVM.
Exposing it directly avoids the need to introduce per-field accessors,
which would add boilerplate without hiding any implementation detail
worth encapsulating.
Opportunistically, fix typo "Unadultered" in the comment.
Signed-off-by: Zide Chen <zide.chen@intel.com>
---
arch/x86/kvm/pmu.c | 5 +++--
arch/x86/kvm/pmu.h | 1 +
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c
index b0fcd0a538d2..7837e2e1af98 100644
--- a/arch/x86/kvm/pmu.c
+++ b/arch/x86/kvm/pmu.c
@@ -27,8 +27,9 @@
/* This is enough to filter the vast majority of currently defined events. */
#define KVM_PMU_EVENT_FILTER_MAX_EVENTS 300
-/* Unadultered PMU capabilities of the host, i.e. of hardware. */
-static struct x86_pmu_capability __read_mostly kvm_host_pmu;
+/* Unadulterated PMU capabilities of the host, i.e. of hardware. */
+struct x86_pmu_capability __read_mostly kvm_host_pmu;
+EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_host_pmu);
/* KVM's PMU capabilities, i.e. the intersection of KVM and hardware support. */
struct x86_pmu_capability __read_mostly kvm_pmu_cap;
diff --git a/arch/x86/kvm/pmu.h b/arch/x86/kvm/pmu.h
index b7a319b1eccc..9a658ae5b268 100644
--- a/arch/x86/kvm/pmu.h
+++ b/arch/x86/kvm/pmu.h
@@ -252,6 +252,7 @@ static inline bool pmc_is_locally_enabled(struct kvm_pmc *pmc)
return !kvm_pmu_call(pmc_is_disabled_in_current_mode)(pmc);
}
+extern struct x86_pmu_capability kvm_host_pmu;
extern struct x86_pmu_capability kvm_pmu_cap;
void kvm_init_pmu_capability(struct kvm_pmu_ops *pmu_ops);
--
2.55.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH v2 08/16] perf/x86: Plumb counter bitmap from x86_pmu to x86_pmu_cap
2026-08-27 22:37 [PATCH] KVM: x86/pmu: Add mediated vPMU PerfMon v5 support Zide Chen
` (6 preceding siblings ...)
2026-08-27 22:37 ` [PATCH v2 07/16] KVM: x86/pmu: Expose kvm_host_pmu to vendor modules Zide Chen
@ 2026-08-27 22:37 ` Zide Chen
2026-08-27 22:37 ` [PATCH v2 09/16] KVM: x86/pmu: Switch to bitmask-based KVM PMU capabilities Zide Chen
` (7 subsequent siblings)
15 siblings, 0 replies; 21+ messages in thread
From: Zide Chen @ 2026-08-27 22:37 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini, Peter Zijlstra
Cc: kvm, Andi Kleen, Jim Mattson, Stephane Eranian, linux-kernel,
Mingwei Zhang, Zide Chen, Das Sandipan, Shukla Manali, Dapeng Mi,
Xudong Hao
From: Dapeng Mi <dapeng1.mi@linux.intel.com>
Intel PerfMon v5 introduced CPUID.0AH:ECX to support non-contiguous
fixed counters and Architectural PerfMon Extension leaf (0x23) further
supports non-contiguous general-purpose counters.
num_counters_{gp,fixed} indicates the total number of GP or fixed
counters, but cannot represent non-contiguous counters.
Add cntr_mask and fixed_cntr_mask unions so that KVM can get the
accurate counter availability directly from x86_pmu_cap. The u64
alias is convenient for mask arithmetic, while the bitmap form works
with for_each_set_bit() and friends.
num_counters_{gp,fixed} in x86_pmu_capability will be removed once
callers have been converted to the use of {,fixed_}cntr_mask.
Signed-off-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
Signed-off-by: Zide Chen <zide.chen@intel.com>
---
arch/x86/events/core.c | 6 ++++--
arch/x86/include/asm/perf_event.h | 8 ++++++++
2 files changed, 12 insertions(+), 2 deletions(-)
diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c
index d1af33d96d0a..3fb95090cd8f 100644
--- a/arch/x86/events/core.c
+++ b/arch/x86/events/core.c
@@ -3134,8 +3134,10 @@ void perf_get_x86_pmu_capability(struct x86_pmu_capability *cap)
cap->version = x86_pmu.version;
cap->num_counters_gp = x86_pmu_num_counters(NULL);
cap->num_counters_fixed = x86_pmu_num_counters_fixed(NULL);
- cap->bit_width_gp = cap->num_counters_gp ? x86_pmu.cntval_bits : 0;
- cap->bit_width_fixed = cap->num_counters_fixed ? x86_pmu.cntval_bits : 0;
+ cap->cntr_mask64 = x86_pmu.cntr_mask64;
+ cap->fixed_cntr_mask64 = x86_pmu.fixed_cntr_mask64;
+ cap->bit_width_gp = cap->cntr_mask64 ? x86_pmu.cntval_bits : 0;
+ cap->bit_width_fixed = cap->fixed_cntr_mask64 ? x86_pmu.cntval_bits : 0;
cap->events_mask = (unsigned int)x86_pmu.events_maskl;
cap->events_mask_len = x86_pmu.events_mask_len;
cap->pebs_ept = x86_pmu.pebs_ept;
diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h
index 1eb13673e889..190fda412a61 100644
--- a/arch/x86/include/asm/perf_event.h
+++ b/arch/x86/include/asm/perf_event.h
@@ -302,6 +302,14 @@ struct x86_pmu_capability {
int version;
int num_counters_gp;
int num_counters_fixed;
+ union {
+ u64 cntr_mask64;
+ DECLARE_BITMAP(cntr_mask, X86_PMC_IDX_MAX);
+ };
+ union {
+ u64 fixed_cntr_mask64;
+ DECLARE_BITMAP(fixed_cntr_mask, X86_PMC_IDX_MAX);
+ };
int bit_width_gp;
int bit_width_fixed;
unsigned int events_mask;
--
2.55.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH v2 09/16] KVM: x86/pmu: Switch to bitmask-based KVM PMU capabilities
2026-08-27 22:37 [PATCH] KVM: x86/pmu: Add mediated vPMU PerfMon v5 support Zide Chen
` (7 preceding siblings ...)
2026-08-27 22:37 ` [PATCH v2 08/16] perf/x86: Plumb counter bitmap from x86_pmu to x86_pmu_cap Zide Chen
@ 2026-08-27 22:37 ` Zide Chen
2026-08-27 22:37 ` [PATCH v2 10/16] perf/x86: Remove num_counters_{gp,fixed} from x86_pmu_capability Zide Chen
` (6 subsequent siblings)
15 siblings, 0 replies; 21+ messages in thread
From: Zide Chen @ 2026-08-27 22:37 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini, Peter Zijlstra
Cc: kvm, Andi Kleen, Jim Mattson, Stephane Eranian, linux-kernel,
Mingwei Zhang, Zide Chen, Das Sandipan, Shukla Manali, Dapeng Mi,
Xudong Hao
From: Dapeng Mi <dapeng1.mi@linux.intel.com>
Intel platforms support non-contiguous fixed counters via CPUID.0AH:ECX
starting with PerfMon v5, and support non-contiguous GP counters
through the Architectural PerfMon Extension (CPUID leaf 23H).
struct x86_pmu_capability now exposes {,fixed_}cntr_mask64 bitmaps,
which may contain sparse bits representing non-contiguous counters.
Switch KVM's kvm_host_pmu and kvm_pmu_cap consumers over to the new
bitmask fields.
CPUID.0AH:EAX[15:8] and CPUID.0AH:EDX[4:0] enumerate only contiguous
counters. Derive these values from kvm_pmu_cap.{,fixed_}cntr_mask64 as
the number of consecutive counters starting at index 0.
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>
---
arch/x86/kvm/cpuid.c | 14 +++++++++++---
arch/x86/kvm/msrs.c | 12 ++++++------
arch/x86/kvm/pmu.c | 21 ++++++++++++---------
arch/x86/kvm/svm/pmu.c | 2 +-
arch/x86/kvm/svm/svm.c | 9 +++++----
arch/x86/kvm/vmx/pmu_intel.c | 7 ++++---
arch/x86/kvm/vmx/vmx.c | 7 +++++--
7 files changed, 44 insertions(+), 28 deletions(-)
diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c
index ddb022cb203a..106e719e7aee 100644
--- a/arch/x86/kvm/cpuid.c
+++ b/arch/x86/kvm/cpuid.c
@@ -1528,10 +1528,18 @@ static inline int __do_cpuid_func(struct kvm_cpuid_array *array, u32 function)
}
eax.split.version_id = kvm_pmu_cap.version;
- eax.split.num_counters = kvm_pmu_cap.num_counters_gp;
+
+ /* Contiguous GP counters only. */
+ eax.split.num_counters =
+ find_first_zero_bit(kvm_pmu_cap.cntr_mask,
+ KVM_MAX_NR_GP_COUNTERS);
eax.split.bit_width = kvm_pmu_cap.bit_width_gp;
eax.split.mask_length = kvm_pmu_cap.events_mask_len;
- edx.split.num_counters_fixed = kvm_pmu_cap.num_counters_fixed;
+
+ /* Contiguous fixed counters only. */
+ edx.split.num_counters_fixed =
+ find_first_zero_bit(kvm_pmu_cap.fixed_cntr_mask,
+ KVM_MAX_NR_FIXED_COUNTERS);
edx.split.bit_width_fixed = kvm_pmu_cap.bit_width_fixed;
if (kvm_pmu_cap.version)
@@ -1896,7 +1904,7 @@ static inline int __do_cpuid_func(struct kvm_cpuid_array *array, u32 function)
cpuid_entry_override(entry, CPUID_8000_0022_EAX);
- ebx.split.num_core_pmc = kvm_pmu_cap.num_counters_gp;
+ ebx.split.num_core_pmc = hweight64(kvm_pmu_cap.cntr_mask64);
entry->ebx = ebx.full;
break;
}
diff --git a/arch/x86/kvm/msrs.c b/arch/x86/kvm/msrs.c
index 66fa7140d65d..49ab49f7c96e 100644
--- a/arch/x86/kvm/msrs.c
+++ b/arch/x86/kvm/msrs.c
@@ -2610,20 +2610,20 @@ static void kvm_probe_msr_to_save(u32 msr_index)
break;
case MSR_ARCH_PERFMON_PERFCTR0 ...
MSR_ARCH_PERFMON_PERFCTR0 + KVM_MAX_NR_GP_COUNTERS - 1:
- if (msr_index - MSR_ARCH_PERFMON_PERFCTR0 >=
- kvm_pmu_cap.num_counters_gp)
+ if (!(BIT_ULL(msr_index - MSR_ARCH_PERFMON_PERFCTR0) &
+ kvm_pmu_cap.cntr_mask64))
return;
break;
case MSR_ARCH_PERFMON_EVENTSEL0 ...
MSR_ARCH_PERFMON_EVENTSEL0 + KVM_MAX_NR_GP_COUNTERS - 1:
- if (msr_index - MSR_ARCH_PERFMON_EVENTSEL0 >=
- kvm_pmu_cap.num_counters_gp)
+ if (!(BIT_ULL(msr_index - MSR_ARCH_PERFMON_EVENTSEL0) &
+ kvm_pmu_cap.cntr_mask64))
return;
break;
case MSR_ARCH_PERFMON_FIXED_CTR0 ...
MSR_ARCH_PERFMON_FIXED_CTR0 + KVM_MAX_NR_FIXED_COUNTERS - 1:
- if (msr_index - MSR_ARCH_PERFMON_FIXED_CTR0 >=
- kvm_pmu_cap.num_counters_fixed)
+ if (!(BIT_ULL(msr_index - MSR_ARCH_PERFMON_FIXED_CTR0) &
+ kvm_pmu_cap.fixed_cntr_mask64))
return;
break;
case MSR_AMD64_PERF_CNTR_GLOBAL_CTL:
diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c
index 7837e2e1af98..437a7bc49bf8 100644
--- a/arch/x86/kvm/pmu.c
+++ b/arch/x86/kvm/pmu.c
@@ -20,7 +20,6 @@
#include <asm/perf_event.h>
#include <asm/cpu_device_id.h>
#include "x86.h"
-#include "cpuid.h"
#include "lapic.h"
#include "pmu.h"
@@ -156,8 +155,8 @@ void kvm_init_pmu_capability(struct kvm_pmu_ops *pmu_ops)
* there are a non-zero number of counters, but fewer than what
* is architecturally required.
*/
- if (!kvm_host_pmu.num_counters_gp ||
- WARN_ON_ONCE(kvm_host_pmu.num_counters_gp < min_nr_gp_ctrs))
+ if (!kvm_host_pmu.cntr_mask64 ||
+ WARN_ON_ONCE(hweight64(kvm_host_pmu.cntr_mask64) < min_nr_gp_ctrs))
enable_pmu = false;
else if (is_intel && !kvm_host_pmu.version)
enable_pmu = false;
@@ -177,10 +176,14 @@ void kvm_init_pmu_capability(struct kvm_pmu_ops *pmu_ops)
memcpy(&kvm_pmu_cap, &kvm_host_pmu, sizeof(kvm_host_pmu));
kvm_pmu_cap.version = min(kvm_pmu_cap.version, 2);
- kvm_pmu_cap.num_counters_gp = min(kvm_pmu_cap.num_counters_gp,
- pmu_ops->MAX_NR_GP_COUNTERS);
- kvm_pmu_cap.num_counters_fixed = min(kvm_pmu_cap.num_counters_fixed,
- KVM_MAX_NR_FIXED_COUNTERS);
+ kvm_pmu_cap.cntr_mask64 &=
+ GENMASK_ULL(pmu_ops->MAX_NR_GP_COUNTERS - 1, 0);
+ kvm_pmu_cap.fixed_cntr_mask64 &=
+ GENMASK_ULL(KVM_MAX_NR_FIXED_COUNTERS - 1, 0);
+
+ /* Legacy vPMU exposes at most 3 fixed counters. */
+ if (!enable_mediated_pmu)
+ kvm_pmu_cap.fixed_cntr_mask64 &= GENMASK_ULL(2, 0);
kvm_pmu_eventsel.INSTRUCTIONS_RETIRED =
perf_get_hw_event_config(PERF_COUNT_HW_INSTRUCTIONS);
@@ -786,8 +789,8 @@ static bool kvm_need_any_pmc_intercept(struct kvm_vcpu *vcpu)
* KVM's capabilities are constrained based on KVM support, i.e. KVM's
* capabilities themselves may be a subset of hardware capabilities.
*/
- return kvm_gp_pmc_mask(pmu) != BIT_ULL(kvm_host_pmu.num_counters_gp) - 1 ||
- kvm_fixed_pmc_mask(pmu) != BIT_ULL(kvm_host_pmu.num_counters_fixed) - 1;
+ return kvm_gp_pmc_mask(pmu) != kvm_host_pmu.cntr_mask64 ||
+ kvm_fixed_pmc_mask(pmu) != kvm_host_pmu.fixed_cntr_mask64;
}
bool kvm_need_perf_global_ctrl_intercept(struct kvm_vcpu *vcpu)
diff --git a/arch/x86/kvm/svm/pmu.c b/arch/x86/kvm/svm/pmu.c
index 0943ccc1d6b8..fb5a298244ec 100644
--- a/arch/x86/kvm/svm/pmu.c
+++ b/arch/x86/kvm/svm/pmu.c
@@ -203,7 +203,7 @@ static void amd_pmu_refresh(struct kvm_vcpu *vcpu)
}
pmu->pmc_exists64 = (BIT_ULL(nr_gp_counters) - 1) &
- (BIT_ULL(kvm_pmu_cap.num_counters_gp) - 1);
+ kvm_pmu_cap.cntr_mask64;
if (pmu->version > 1) {
pmu->global_ctrl_rsvd = ~pmu->pmc_exists64;
diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
index fb8442a08b63..6f3427027e99 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -754,6 +754,7 @@ static void svm_recalc_pmu_msr_intercepts(struct kvm_vcpu *vcpu)
bool intercept = !kvm_vcpu_has_mediated_pmu(vcpu);
struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
unsigned long gp_mask = kvm_gp_pmc_mask(pmu);
+ unsigned long host_only_gp_mask;
int i;
if (!enable_mediated_pmu)
@@ -772,7 +773,8 @@ static void svm_recalc_pmu_msr_intercepts(struct kvm_vcpu *vcpu)
svm_set_intercept_for_msr(vcpu, MSR_F15H_PERF_CTR + 2 * i,
MSR_TYPE_RW, intercept);
- for ( ; i < kvm_pmu_cap.num_counters_gp; i++)
+ host_only_gp_mask = kvm_pmu_cap.cntr_mask64 & ~gp_mask;
+ kvm_for_each_gp_counter(i, host_only_gp_mask)
svm_enable_intercept_for_msr(vcpu, MSR_F15H_PERF_CTR + 2 * i,
MSR_TYPE_RW);
@@ -5563,9 +5565,8 @@ static __init void svm_set_cpu_caps(void)
* access to enough counters to virtualize "core" support,
* otherwise limit vPMU support to the legacy number of counters.
*/
- if (kvm_pmu_cap.num_counters_gp < AMD64_NUM_COUNTERS_CORE)
- kvm_pmu_cap.num_counters_gp = min(AMD64_NUM_COUNTERS,
- kvm_pmu_cap.num_counters_gp);
+ if (hweight64(kvm_pmu_cap.cntr_mask64) < AMD64_NUM_COUNTERS_CORE)
+ kvm_pmu_cap.cntr_mask64 &= GENMASK_ULL(AMD64_NUM_COUNTERS - 1, 0);
else
kvm_cpu_cap_check_and_set(X86_FEATURE_PERFCTR_CORE);
diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c
index 926d1c57f8bf..4df55a3e21da 100644
--- a/arch/x86/kvm/vmx/pmu_intel.c
+++ b/arch/x86/kvm/vmx/pmu_intel.c
@@ -491,7 +491,8 @@ static __always_inline u64 intel_get_fixed_pmc_eventsel(unsigned int index)
* have a known encoding for the associated general purpose event.
*/
eventsel = perf_get_hw_event_config(fixed_pmc_perf_ids[index]);
- WARN_ON_ONCE(!eventsel && index < kvm_pmu_cap.num_counters_fixed);
+ WARN_ON_ONCE(!eventsel &&
+ (kvm_pmu_cap.fixed_cntr_mask64 & BIT_ULL(index)));
return eventsel;
}
@@ -548,7 +549,7 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu)
pmu->available_event_types = ~entry->ebx & (BIT_ULL(eax.split.mask_length) - 1);
fixed_cntr_mask = BIT_ULL(edx.split.num_counters_fixed) - 1;
- fixed_cntr_mask &= BIT_ULL(kvm_pmu_cap.num_counters_fixed) - 1;
+ fixed_cntr_mask &= kvm_pmu_cap.fixed_cntr_mask64;
/*
* The number of counters comes from guest CPUID data. Clamp the value
@@ -556,7 +557,7 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu)
*/
nr_gp_counters = min_t(int, eax.split.num_counters, X86_PMC_IDX_MAX - 1);
pmu->pmc_exists64 = (BIT_ULL(nr_gp_counters) - 1) &
- (BIT_ULL(kvm_pmu_cap.num_counters_gp) - 1);
+ kvm_pmu_cap.cntr_mask64;
entry = kvm_find_cpuid_entry_index(vcpu, 7, 0);
if (entry &&
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index be994adbd954..56daf5c61082 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -4230,6 +4230,7 @@ static void vmx_recalc_pmu_msr_intercepts(struct kvm_vcpu *vcpu)
bool has_mediated_pmu = kvm_vcpu_has_mediated_pmu(vcpu);
struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
struct vcpu_vmx *vmx = to_vmx(vcpu);
+ unsigned long host_only_gp_mask, host_only_fixed_mask;
unsigned long fixed_mask = kvm_fixed_pmc_mask(pmu);
unsigned long gp_mask = kvm_gp_pmc_mask(pmu);
bool intercept = !has_mediated_pmu;
@@ -4252,23 +4253,25 @@ static void vmx_recalc_pmu_msr_intercepts(struct kvm_vcpu *vcpu)
vm_exit_controls_changebit(vmx, vm_exit_controls_bits, has_mediated_pmu);
+ host_only_gp_mask = kvm_host_pmu.cntr_mask64 & ~gp_mask;
kvm_for_each_gp_counter(i, gp_mask) {
vmx_set_intercept_for_msr(vcpu, MSR_IA32_PERFCTR0 + i,
MSR_TYPE_RW, intercept);
vmx_set_intercept_for_msr(vcpu, MSR_IA32_PMC0 + i, MSR_TYPE_RW,
intercept || !fw_writes_is_enabled(vcpu));
}
- for ( ; i < kvm_pmu_cap.num_counters_gp; i++) {
+ for_each_set_bit(i, &host_only_gp_mask, INTEL_PMC_MAX_GENERIC) {
vmx_set_intercept_for_msr(vcpu, MSR_IA32_PERFCTR0 + i,
MSR_TYPE_RW, true);
vmx_set_intercept_for_msr(vcpu, MSR_IA32_PMC0 + i,
MSR_TYPE_RW, true);
}
+ host_only_fixed_mask = kvm_host_pmu.fixed_cntr_mask64 & ~fixed_mask;
kvm_for_each_fixed_counter(i, fixed_mask)
vmx_set_intercept_for_msr(vcpu, MSR_CORE_PERF_FIXED_CTR0 + i,
MSR_TYPE_RW, intercept);
- for ( ; i < kvm_pmu_cap.num_counters_fixed; i++)
+ for_each_set_bit(i, &host_only_fixed_mask, INTEL_PMC_MAX_FIXED)
vmx_set_intercept_for_msr(vcpu, MSR_CORE_PERF_FIXED_CTR0 + i,
MSR_TYPE_RW, true);
--
2.55.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH v2 10/16] perf/x86: Remove num_counters_{gp,fixed} from x86_pmu_capability
2026-08-27 22:37 [PATCH] KVM: x86/pmu: Add mediated vPMU PerfMon v5 support Zide Chen
` (8 preceding siblings ...)
2026-08-27 22:37 ` [PATCH v2 09/16] KVM: x86/pmu: Switch to bitmask-based KVM PMU capabilities Zide Chen
@ 2026-08-27 22:37 ` Zide Chen
2026-08-27 22:37 ` [PATCH v2 11/16] KVM: x86/pmu: Emulate the GLOBAL_STATUS_SET and GLOBAL_INUSE MSRs Zide Chen
` (5 subsequent siblings)
15 siblings, 0 replies; 21+ messages in thread
From: Zide Chen @ 2026-08-27 22:37 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini, Peter Zijlstra
Cc: kvm, Andi Kleen, Jim Mattson, Stephane Eranian, linux-kernel,
Mingwei Zhang, Zide Chen, Das Sandipan, Shukla Manali, Dapeng Mi,
Xudong Hao
From: Dapeng Mi <dapeng1.mi@linux.intel.com>
Now that KVM has switched to bitmap-based PMU capabilities,
num_counters_{gp,fixed} can be removed from x86_pmu_capability.
Signed-off-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
Signed-off-by: Zide Chen <zide.chen@intel.com>
---
arch/x86/events/core.c | 2 --
arch/x86/include/asm/perf_event.h | 2 --
2 files changed, 4 deletions(-)
diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c
index 3fb95090cd8f..9b6df8bc9059 100644
--- a/arch/x86/events/core.c
+++ b/arch/x86/events/core.c
@@ -3132,8 +3132,6 @@ void perf_get_x86_pmu_capability(struct x86_pmu_capability *cap)
* base PMU holds the correct number of counters for P-cores.
*/
cap->version = x86_pmu.version;
- cap->num_counters_gp = x86_pmu_num_counters(NULL);
- cap->num_counters_fixed = x86_pmu_num_counters_fixed(NULL);
cap->cntr_mask64 = x86_pmu.cntr_mask64;
cap->fixed_cntr_mask64 = x86_pmu.fixed_cntr_mask64;
cap->bit_width_gp = cap->cntr_mask64 ? x86_pmu.cntval_bits : 0;
diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h
index 190fda412a61..17b0bc7dfce7 100644
--- a/arch/x86/include/asm/perf_event.h
+++ b/arch/x86/include/asm/perf_event.h
@@ -300,8 +300,6 @@ union cpuid_0x80000022_ebx {
struct x86_pmu_capability {
int version;
- int num_counters_gp;
- int num_counters_fixed;
union {
u64 cntr_mask64;
DECLARE_BITMAP(cntr_mask, X86_PMC_IDX_MAX);
--
2.55.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH v2 11/16] KVM: x86/pmu: Emulate the GLOBAL_STATUS_SET and GLOBAL_INUSE MSRs
2026-08-27 22:37 [PATCH] KVM: x86/pmu: Add mediated vPMU PerfMon v5 support Zide Chen
` (9 preceding siblings ...)
2026-08-27 22:37 ` [PATCH v2 10/16] perf/x86: Remove num_counters_{gp,fixed} from x86_pmu_capability Zide Chen
@ 2026-08-27 22:37 ` Zide Chen
2026-08-27 23:05 ` sashiko-bot
2026-08-27 22:37 ` [PATCH v2 12/16] KVM: x86/pmu: Populate CPUID.0AH:ECX fixed-counter bitmap Zide Chen
` (4 subsequent siblings)
15 siblings, 1 reply; 21+ messages in thread
From: Zide Chen @ 2026-08-27 22:37 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini, Peter Zijlstra
Cc: kvm, Andi Kleen, Jim Mattson, Stephane Eranian, linux-kernel,
Mingwei Zhang, Zide Chen, Das Sandipan, Shukla Manali, Dapeng Mi,
Xudong Hao
Intel PerfMon v4 introduces IA32_PERF_GLOBAL_STATUS_SET (0x391) to
allow software to set individual bits in the global status MSR. Reads
of IA32_PERF_GLOBAL_STATUS_SET always return zero.
IA32_PERF_GLOBAL_INUSE (0x392) is also introduced in v4, to track
which counters and the PMI are currently claimed by other agents,
allowing independent software agents to check counter availability
without a shared scheduler arbitrating between them.
IA32_PERF_GLOBAL_INUSE is an read-only MSR, and any write attempt
results in a #GP.
Neither MSR is part of the VM state, so they don't need to be
advertised to userspace, nor saved and restored during live
migration.
Originally-by: Yang Weijiang <weijiang.yang@intel.com>
Signed-off-by: Zide Chen <zide.chen@intel.com>
---
v2:
- Change intel_pmu_get_global_inuse() to return u64, to match the
surrounding code style.
- Add the missing vmcs02 updates for these two MSRs.
- Change "> 3" to ">= 4" to make the "v4-gated" more obvious and match
the existing code style.
---
arch/x86/include/asm/msr-index.h | 4 ++++
arch/x86/kvm/pmu.c | 9 ++++++++
arch/x86/kvm/vmx/nested.c | 2 ++
arch/x86/kvm/vmx/pmu_intel.c | 37 ++++++++++++++++++++++++++++++++
arch/x86/kvm/vmx/vmx.c | 4 ++++
5 files changed, 56 insertions(+)
diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h
index 11b99d237e05..0b093cf41edf 100644
--- a/arch/x86/include/asm/msr-index.h
+++ b/arch/x86/include/asm/msr-index.h
@@ -1240,6 +1240,10 @@
#define MSR_CORE_PERF_GLOBAL_CTRL 0x0000038f
#define MSR_CORE_PERF_GLOBAL_OVF_CTRL 0x00000390
#define MSR_CORE_PERF_GLOBAL_STATUS_SET 0x00000391
+#define MSR_CORE_PERF_GLOBAL_INUSE 0x00000392
+
+/* Intel IA32_PERF_GLOBAL_INUSE MSR */
+#define PERF_GLOBAL_INUSE_PMI_INUSE BIT_ULL(63)
#define MSR_PERF_METRICS 0x00000329
diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c
index 437a7bc49bf8..7c05cf5157bf 100644
--- a/arch/x86/kvm/pmu.c
+++ b/arch/x86/kvm/pmu.c
@@ -832,6 +832,8 @@ bool kvm_pmu_is_valid_msr(struct kvm_vcpu *vcpu, u32 msr)
case MSR_CORE_PERF_GLOBAL_CTRL:
case MSR_CORE_PERF_GLOBAL_OVF_CTRL:
return kvm_pmu_has_perf_global_ctrl(vcpu_to_pmu(vcpu));
+ case MSR_CORE_PERF_GLOBAL_STATUS_SET:
+ return vcpu_to_pmu(vcpu)->version >= 4;
default:
break;
}
@@ -865,6 +867,7 @@ int kvm_pmu_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
case MSR_AMD64_PERF_CNTR_GLOBAL_STATUS_CLR:
case MSR_AMD64_PERF_CNTR_GLOBAL_STATUS_SET:
case MSR_CORE_PERF_GLOBAL_OVF_CTRL:
+ case MSR_CORE_PERF_GLOBAL_STATUS_SET:
msr_info->data = 0;
break;
default:
@@ -931,6 +934,12 @@ int kvm_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
if (!msr_info->host_initiated)
pmu->global_status &= ~data;
break;
+ case MSR_CORE_PERF_GLOBAL_STATUS_SET:
+ if (data & pmu->global_status_rsvd)
+ return 1;
+ if (!msr_info->host_initiated)
+ pmu->global_status |= data;
+ break;
case MSR_AMD64_PERF_CNTR_GLOBAL_STATUS_SET:
if (!msr_info->host_initiated)
pmu->global_status |= data & ~pmu->global_status_rsvd;
diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c
index 0cff369982ae..ae7dd3636e70 100644
--- a/arch/x86/kvm/vmx/nested.c
+++ b/arch/x86/kvm/vmx/nested.c
@@ -719,6 +719,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_write(MSR_CORE_PERF_GLOBAL_STATUS_SET);
+ nested_vmx_merge_msr_bitmaps_read(MSR_CORE_PERF_GLOBAL_INUSE);
}
/*
diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c
index 4df55a3e21da..3070fba2687f 100644
--- a/arch/x86/kvm/vmx/pmu_intel.c
+++ b/arch/x86/kvm/vmx/pmu_intel.c
@@ -194,6 +194,8 @@ static bool intel_is_valid_msr(struct kvm_vcpu *vcpu, u32 msr)
switch (msr) {
case MSR_CORE_PERF_FIXED_CTR_CTRL:
return kvm_pmu_has_perf_global_ctrl(pmu);
+ case MSR_CORE_PERF_GLOBAL_INUSE:
+ return pmu->version >= 4;
case MSR_IA32_PEBS_ENABLE:
ret = vcpu_get_perf_capabilities(vcpu) & PERF_CAP_PEBS_FORMAT;
break;
@@ -341,6 +343,38 @@ static bool intel_pmu_handle_lbr_msrs_access(struct kvm_vcpu *vcpu,
return true;
}
+static u64 intel_pmu_get_global_inuse(struct kvm_vcpu *vcpu)
+{
+ struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
+ unsigned long fixed_mask = kvm_fixed_pmc_mask(pmu);
+ unsigned long gp_mask = kvm_gp_pmc_mask(pmu);
+ bool pmi_inuse = false;
+ u64 eventsel, data = 0;
+ u32 fixed_ctrl;
+ int i;
+
+ kvm_for_each_gp_counter(i, gp_mask) {
+ eventsel = pmu->gp_counters[i].eventsel;
+
+ if (eventsel & ARCH_PERFMON_EVENTSEL_EVENT)
+ data |= BIT_ULL(i);
+ pmi_inuse |= eventsel & ARCH_PERFMON_EVENTSEL_INT;
+ }
+ kvm_for_each_fixed_counter(i, fixed_mask) {
+ fixed_ctrl = fixed_ctrl_field(pmu->fixed_ctr_ctrl, i);
+
+ if (fixed_ctrl & (INTEL_FIXED_0_KERNEL | INTEL_FIXED_0_USER))
+ data |= BIT_ULL(KVM_FIXED_PMC_BASE_IDX + i);
+ pmi_inuse |= fixed_ctrl & INTEL_FIXED_0_ENABLE_PMI;
+ }
+ pmi_inuse |= pmu->pebs_enable;
+
+ if (pmi_inuse)
+ data |= PERF_GLOBAL_INUSE_PMI_INUSE;
+
+ return data;
+}
+
static int intel_pmu_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
{
struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
@@ -351,6 +385,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_CORE_PERF_GLOBAL_INUSE:
+ msr_info->data = intel_pmu_get_global_inuse(vcpu);
+ break;
case MSR_IA32_PEBS_ENABLE:
msr_info->data = pmu->pebs_enable;
break;
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index 56daf5c61082..2dabb7e64956 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -4282,6 +4282,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);
+ vmx_set_intercept_for_msr(vcpu, MSR_CORE_PERF_GLOBAL_STATUS_SET,
+ MSR_TYPE_RW, intercept || pmu->version < 4);
+ vmx_set_intercept_for_msr(vcpu, MSR_CORE_PERF_GLOBAL_INUSE,
+ MSR_TYPE_RW, intercept || pmu->version < 4);
}
static void vmx_recalc_msr_intercepts(struct kvm_vcpu *vcpu)
--
2.55.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* Re: [PATCH v2 11/16] KVM: x86/pmu: Emulate the GLOBAL_STATUS_SET and GLOBAL_INUSE MSRs
2026-08-27 22:37 ` [PATCH v2 11/16] KVM: x86/pmu: Emulate the GLOBAL_STATUS_SET and GLOBAL_INUSE MSRs Zide Chen
@ 2026-08-27 23:05 ` sashiko-bot
2026-08-28 20:22 ` Chen, Zide
0 siblings, 1 reply; 21+ messages in thread
From: sashiko-bot @ 2026-08-27 23:05 UTC (permalink / raw)
To: Zide Chen; +Cc: kvm
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] Incorrect IN_USE bit calculation for fixed counters in IA32_PERF_GLOBAL_INUSE omits the INTEL_FIXED_0_ENABLE_PMI condition.
- [Medium] Emulated writes to MSR_CORE_PERF_GLOBAL_STATUS_SET fail to trigger a PMI when the corresponding global_ctrl bit is set.
--
commit a29f967a55c3b6e45cb682091b4b9a33c0b976dc
Author: Zide Chen <zide.chen@intel.com>
KVM: x86/pmu: Emulate the GLOBAL_STATUS_SET and GLOBAL_INUSE MSRs
Intel PerfMon v4 introduces IA32_PERF_GLOBAL_STATUS_SET (0x391) and
IA32_PERF_GLOBAL_INUSE (0x392). This patch adds emulation for both MSRs
in KVM, but there appear to be a few issues with the implementation.
> diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c
> index 437a7bc49bf84..7c05cf5157bfc 100644
> --- a/arch/x86/kvm/pmu.c
> +++ b/arch/x86/kvm/pmu.c
[ ... ]
> @@ -931,6 +934,12 @@ int kvm_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
> if (!msr_info->host_initiated)
> pmu->global_status &= ~data;
> break;
> + case MSR_CORE_PERF_GLOBAL_STATUS_SET:
> + if (data & pmu->global_status_rsvd)
> + return 1;
> + if (!msr_info->host_initiated)
> + pmu->global_status |= data;
> + break;
[Severity: Medium]
Will emulated guest writes to MSR_CORE_PERF_GLOBAL_STATUS_SET fail to trigger
a PMI when the corresponding global_ctrl bit is set?
This handler correctly updates the global status, but it appears to omit the
required secondary side-effect of generating a performance monitoring interrupt.
If a guest writes a 1 to a bit in MSR_CORE_PERF_GLOBAL_STATUS_SET while the
corresponding bit in IA32_PERF_GLOBAL_CTRL is enabled, it seems the interrupt
will silently fail to generate because the code doesn't check global_ctrl or
set KVM_REQ_PMI.
> case MSR_AMD64_PERF_CNTR_GLOBAL_STATUS_SET:
> if (!msr_info->host_initiated)
> pmu->global_status |= data & ~pmu->global_status_rsvd;
[ ... ]
> diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c
> index 4df55a3e21da9..3070fba2687f4 100644
> --- a/arch/x86/kvm/vmx/pmu_intel.c
> +++ b/arch/x86/kvm/vmx/pmu_intel.c
[ ... ]
> @@ -341,6 +343,38 @@ static bool intel_pmu_handle_lbr_msrs_access(struct kvm_vcpu *vcpu,
> return true;
> }
>
> +static u64 intel_pmu_get_global_inuse(struct kvm_vcpu *vcpu)
> +{
> + struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
> + unsigned long fixed_mask = kvm_fixed_pmc_mask(pmu);
> + unsigned long gp_mask = kvm_gp_pmc_mask(pmu);
> + bool pmi_inuse = false;
> + u64 eventsel, data = 0;
> + u32 fixed_ctrl;
> + int i;
> +
> + kvm_for_each_gp_counter(i, gp_mask) {
> + eventsel = pmu->gp_counters[i].eventsel;
> +
> + if (eventsel & ARCH_PERFMON_EVENTSEL_EVENT)
> + data |= BIT_ULL(i);
> + pmi_inuse |= eventsel & ARCH_PERFMON_EVENTSEL_INT;
> + }
> + kvm_for_each_fixed_counter(i, fixed_mask) {
> + fixed_ctrl = fixed_ctrl_field(pmu->fixed_ctr_ctrl, i);
> +
> + if (fixed_ctrl & (INTEL_FIXED_0_KERNEL | INTEL_FIXED_0_USER))
> + data |= BIT_ULL(KVM_FIXED_PMC_BASE_IDX + i);
[Severity: Medium]
Does this code improperly omit INTEL_FIXED_0_ENABLE_PMI when calculating the
IN_USE bit for fixed counters?
If a fixed counter is configured only for PMI (without OS or User bits), this
code seems to falsely report it as unused, which violates the Intel SDM
specification for FCn_InUse.
Should INTEL_FIXED_0_ENABLE_PMI be included in this condition to ensure the
availability check remains accurate?
> + pmi_inuse |= fixed_ctrl & INTEL_FIXED_0_ENABLE_PMI;
> + }
> + pmi_inuse |= pmu->pebs_enable;
> +
> + if (pmi_inuse)
> + data |= PERF_GLOBAL_INUSE_PMI_INUSE;
> +
> + return data;
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260827223755.143247-1-zide.chen@intel.com?part=11
^ permalink raw reply [flat|nested] 21+ messages in thread* Re: [PATCH v2 11/16] KVM: x86/pmu: Emulate the GLOBAL_STATUS_SET and GLOBAL_INUSE MSRs
2026-08-27 23:05 ` sashiko-bot
@ 2026-08-28 20:22 ` Chen, Zide
0 siblings, 0 replies; 21+ messages in thread
From: Chen, Zide @ 2026-08-28 20:22 UTC (permalink / raw)
To: sashiko-reviews; +Cc: kvm
On 8/27/2026 6:05 PM, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
> - [Medium] Incorrect IN_USE bit calculation for fixed counters in IA32_PERF_GLOBAL_INUSE omits the INTEL_FIXED_0_ENABLE_PMI condition.
> - [Medium] Emulated writes to MSR_CORE_PERF_GLOBAL_STATUS_SET fail to trigger a PMI when the corresponding global_ctrl bit is set.
> --
>
> commit a29f967a55c3b6e45cb682091b4b9a33c0b976dc
> Author: Zide Chen <zide.chen@intel.com>
>
> KVM: x86/pmu: Emulate the GLOBAL_STATUS_SET and GLOBAL_INUSE MSRs
>
> Intel PerfMon v4 introduces IA32_PERF_GLOBAL_STATUS_SET (0x391) and
> IA32_PERF_GLOBAL_INUSE (0x392). This patch adds emulation for both MSRs
> in KVM, but there appear to be a few issues with the implementation.
>
>> diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c
>> index 437a7bc49bf84..7c05cf5157bfc 100644
>> --- a/arch/x86/kvm/pmu.c
>> +++ b/arch/x86/kvm/pmu.c
> [ ... ]
>> @@ -931,6 +934,12 @@ int kvm_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
>> if (!msr_info->host_initiated)
>> pmu->global_status &= ~data;
>> break;
>> + case MSR_CORE_PERF_GLOBAL_STATUS_SET:
>> + if (data & pmu->global_status_rsvd)
>> + return 1;
>> + if (!msr_info->host_initiated)
>> + pmu->global_status |= data;
>> + break;
>
> [Severity: Medium]
> Will emulated guest writes to MSR_CORE_PERF_GLOBAL_STATUS_SET fail to trigger
> a PMI when the corresponding global_ctrl bit is set?
>
> This handler correctly updates the global status, but it appears to omit the
> required secondary side-effect of generating a performance monitoring interrupt.
>
> If a guest writes a 1 to a bit in MSR_CORE_PERF_GLOBAL_STATUS_SET while the
> corresponding bit in IA32_PERF_GLOBAL_CTRL is enabled, it seems the interrupt
> will silently fail to generate because the code doesn't check global_ctrl or
> set KVM_REQ_PMI.
According to the Intel SDM, IA32_PERF_GLOBAL_STATUS_SET only sets the
corresponding bits in IA32_PERF_GLOBAL_STATUS, but not generate a PMI.
Generating a PMI / KVM_REQ_PMI on writes to this MSR would deviate from
hardware behavior and cause spurious PMIs in the guest, e.g., during
live migration state restoration, or during KVM pmu context switch.
>
>> case MSR_AMD64_PERF_CNTR_GLOBAL_STATUS_SET:
>> if (!msr_info->host_initiated)
>> pmu->global_status |= data & ~pmu->global_status_rsvd;
> [ ... ]
>> diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c
>> index 4df55a3e21da9..3070fba2687f4 100644
>> --- a/arch/x86/kvm/vmx/pmu_intel.c
>> +++ b/arch/x86/kvm/vmx/pmu_intel.c
> [ ... ]
>> @@ -341,6 +343,38 @@ static bool intel_pmu_handle_lbr_msrs_access(struct kvm_vcpu *vcpu,
>> return true;
>> }
>>
>> +static u64 intel_pmu_get_global_inuse(struct kvm_vcpu *vcpu)
>> +{
>> + struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
>> + unsigned long fixed_mask = kvm_fixed_pmc_mask(pmu);
>> + unsigned long gp_mask = kvm_gp_pmc_mask(pmu);
>> + bool pmi_inuse = false;
>> + u64 eventsel, data = 0;
>> + u32 fixed_ctrl;
>> + int i;
>> +
>> + kvm_for_each_gp_counter(i, gp_mask) {
>> + eventsel = pmu->gp_counters[i].eventsel;
>> +
>> + if (eventsel & ARCH_PERFMON_EVENTSEL_EVENT)
>> + data |= BIT_ULL(i);
>> + pmi_inuse |= eventsel & ARCH_PERFMON_EVENTSEL_INT;
>> + }
>> + kvm_for_each_fixed_counter(i, fixed_mask) {
>> + fixed_ctrl = fixed_ctrl_field(pmu->fixed_ctr_ctrl, i);
>> +
>> + if (fixed_ctrl & (INTEL_FIXED_0_KERNEL | INTEL_FIXED_0_USER))
>> + data |= BIT_ULL(KVM_FIXED_PMC_BASE_IDX + i);
>
> [Severity: Medium]
> Does this code improperly omit INTEL_FIXED_0_ENABLE_PMI when calculating the
> IN_USE bit for fixed counters?
>
> If a fixed counter is configured only for PMI (without OS or User bits), this
> code seems to falsely report it as unused, which violates the Intel SDM
> specification for FCn_InUse.
>
> Should INTEL_FIXED_0_ENABLE_PMI be included in this condition to ensure the
> availability check remains accurate?
False positive. The implementation strictly follows the SDM:
IA32_PERF_GLOBAL_INUSE.FC0_InUse[bit 32]: This bit reflects the logical
state of (IA32_FIXED_CTR_CTRL[1:0] != 0).
Also I verified that the behavior is same between the host and the guest
in this regard.
Note that in practice, reading this MSR on the host may be inconsistent.
The Linux perf driver clear the EN bit but not the whole EvtSel MSRs
after a perf event finishes, so the corresponding bits in INUSE may
remain set. In contrast, kvm_pmu_put_guest_pmcs() explicitly clears
these MSRs, which means the in-use bits will be cleared on physical CPUs
running a vCPU.
>
>> + pmi_inuse |= fixed_ctrl & INTEL_FIXED_0_ENABLE_PMI;
>> + }
>> + pmi_inuse |= pmu->pebs_enable;
>> +
>> + if (pmi_inuse)
>> + data |= PERF_GLOBAL_INUSE_PMI_INUSE;
>> +
>> + return data;
>> +}
>
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH v2 12/16] KVM: x86/pmu: Populate CPUID.0AH:ECX fixed-counter bitmap
2026-08-27 22:37 [PATCH] KVM: x86/pmu: Add mediated vPMU PerfMon v5 support Zide Chen
` (10 preceding siblings ...)
2026-08-27 22:37 ` [PATCH v2 11/16] KVM: x86/pmu: Emulate the GLOBAL_STATUS_SET and GLOBAL_INUSE MSRs Zide Chen
@ 2026-08-27 22:37 ` Zide Chen
2026-08-27 22:37 ` [PATCH v2 13/16] KVM: x86/pmu: Factor out fixed counter control bit calculation Zide Chen
` (3 subsequent siblings)
15 siblings, 0 replies; 21+ messages in thread
From: Zide Chen @ 2026-08-27 22:37 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini, Peter Zijlstra
Cc: kvm, Andi Kleen, Jim Mattson, Stephane Eranian, linux-kernel,
Mingwei Zhang, Zide Chen, Das Sandipan, Shukla Manali, Dapeng Mi,
Xudong Hao
PerfMon v5 adds a fixed-counter support bitmap in CPUID.0AH:ECX.
This is the superset of EDX[4:0] which indicates the contiguous
counters starting from 0. For backward compatibility, it's recommended
to use the following to determine if a fixed counter is supported:
FxCtr[i]_is_supported := ECX[i] || (EDX[4:0] > i);
Originally-by: Yang Weijiang <weijiang.yang@intel.com>
Signed-off-by: Zide Chen <zide.chen@intel.com>
---
arch/x86/kvm/cpuid.c | 5 ++++-
arch/x86/kvm/vmx/pmu_intel.c | 3 +++
2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c
index 106e719e7aee..63201d4ea4a1 100644
--- a/arch/x86/kvm/cpuid.c
+++ b/arch/x86/kvm/cpuid.c
@@ -1547,7 +1547,10 @@ static inline int __do_cpuid_func(struct kvm_cpuid_array *array, u32 function)
entry->eax = eax.full;
entry->ebx = kvm_pmu_cap.events_mask;
- entry->ecx = 0;
+ if (kvm_pmu_cap.version >= 5)
+ entry->ecx &= (u32)kvm_pmu_cap.fixed_cntr_mask64;
+ else
+ entry->ecx = 0;
entry->edx = edx.full;
break;
}
diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c
index a6d2ebff7cc9..1ce69e764ae1 100644
--- a/arch/x86/kvm/vmx/pmu_intel.c
+++ b/arch/x86/kvm/vmx/pmu_intel.c
@@ -585,7 +585,10 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu)
kvm_pmu_cap.events_mask_len);
pmu->available_event_types = ~entry->ebx & (BIT_ULL(eax.split.mask_length) - 1);
+ /* FxCtr[i]_is_supported := ECX[i] || (EDX[4:0] > i) */
fixed_cntr_mask = BIT_ULL(edx.split.num_counters_fixed) - 1;
+ if (pmu->version >= 5)
+ fixed_cntr_mask |= entry->ecx;
fixed_cntr_mask &= kvm_pmu_cap.fixed_cntr_mask64;
/*
--
2.55.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH v2 13/16] KVM: x86/pmu: Factor out fixed counter control bit calculation
2026-08-27 22:37 [PATCH] KVM: x86/pmu: Add mediated vPMU PerfMon v5 support Zide Chen
` (11 preceding siblings ...)
2026-08-27 22:37 ` [PATCH v2 12/16] KVM: x86/pmu: Populate CPUID.0AH:ECX fixed-counter bitmap Zide Chen
@ 2026-08-27 22:37 ` Zide Chen
2026-08-27 22:37 ` [PATCH v2 14/16] KVM: x86/pmu: Ignore AnyThread bit if CPUID.0AH:EDX[15] is set Zide Chen
` (2 subsequent siblings)
15 siblings, 0 replies; 21+ messages in thread
From: Zide Chen @ 2026-08-27 22:37 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini, Peter Zijlstra
Cc: kvm, Andi Kleen, Jim Mattson, Stephane Eranian, linux-kernel,
Mingwei Zhang, Zide Chen, Das Sandipan, Shukla Manali, Dapeng Mi,
Xudong Hao
Rename intel_pmu_enable_fixed_counter_bits() to
intel_pmu_fixed_ctrl_bits() and have it return the computed fixed
counter control mask instead of updating fixed_ctr_ctrl_rsvd directly.
Accumulate the desired fixed counter control bits during PMU setup and
apply them once in intel_pmu_refresh().
Move the helper next to intel_pmu_set_msr() so it can also be reused
there, which will be needed for upcoming ANYTHREAD_DEPRECATION
emulation.
No functional change intended.
Signed-off-by: Zide Chen <zide.chen@intel.com>
---
v2: New patch
---
arch/x86/kvm/vmx/pmu_intel.c | 32 +++++++++++++++++++-------------
1 file changed, 19 insertions(+), 13 deletions(-)
diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c
index 1ce69e764ae1..c7472778137a 100644
--- a/arch/x86/kvm/vmx/pmu_intel.c
+++ b/arch/x86/kvm/vmx/pmu_intel.c
@@ -421,6 +421,18 @@ static int intel_pmu_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
return 0;
}
+static u64 intel_pmu_fixed_ctrl_bits(struct kvm_pmu *pmu, u64 bits)
+{
+ unsigned long fixed_mask = kvm_fixed_pmc_mask(pmu);
+ u64 fixed_ctrl_bits = 0;
+ int i;
+
+ kvm_for_each_fixed_counter(i, fixed_mask)
+ fixed_ctrl_bits |= intel_fixed_bits_by_idx(i, bits);
+
+ return fixed_ctrl_bits;
+}
+
static int intel_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
{
struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
@@ -533,20 +545,12 @@ static __always_inline u64 intel_get_fixed_pmc_eventsel(unsigned int index)
return eventsel;
}
-static void intel_pmu_enable_fixed_counter_bits(struct kvm_pmu *pmu, u64 bits)
-{
- unsigned long fixed_mask = kvm_fixed_pmc_mask(pmu);
- int i;
-
- kvm_for_each_fixed_counter(i, fixed_mask)
- pmu->fixed_ctr_ctrl_rsvd &= ~intel_fixed_bits_by_idx(i, bits);
-}
-
static void intel_pmu_refresh(struct kvm_vcpu *vcpu)
{
struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
struct lbr_desc *lbr_desc = vcpu_to_lbr_desc(vcpu);
struct kvm_cpuid_entry2 *entry;
+ u64 fixed_enable_bits = 0;
union cpuid10_eax eax;
union cpuid10_edx edx;
u64 perf_capabilities;
@@ -625,9 +629,8 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu)
kvm_pmu_cap.bit_width_fixed);
pmu->counter_bitmask[KVM_PMC_FIXED] = BIT_ULL(edx.split.bit_width_fixed) - 1;
- intel_pmu_enable_fixed_counter_bits(pmu, INTEL_FIXED_0_KERNEL |
- INTEL_FIXED_0_USER |
- INTEL_FIXED_0_ENABLE_PMI);
+ fixed_enable_bits |= INTEL_FIXED_0_KERNEL | INTEL_FIXED_0_USER |
+ INTEL_FIXED_0_ENABLE_PMI;
counter_rsvd = ~(kvm_gp_pmc_mask(pmu) |
((u64)kvm_fixed_pmc_mask(pmu) << KVM_FIXED_PMC_BASE_IDX));
@@ -651,11 +654,14 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu)
pmu->pebs_enable_rsvd = counter_rsvd;
pmu->eventsel_rsvd &= ~ICL_EVENTSEL_ADAPTIVE;
pmu->pebs_data_cfg_rsvd = ~0xff00000full;
- intel_pmu_enable_fixed_counter_bits(pmu, ICL_FIXED_0_ADAPTIVE);
+ fixed_enable_bits |= ICL_FIXED_0_ADAPTIVE;
} else {
pmu->pebs_enable_rsvd = ~kvm_gp_pmc_mask(pmu);
}
}
+
+ pmu->fixed_ctr_ctrl_rsvd &=
+ ~intel_pmu_fixed_ctrl_bits(pmu, fixed_enable_bits);
}
static void intel_pmu_init(struct kvm_vcpu *vcpu)
--
2.55.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH v2 14/16] KVM: x86/pmu: Ignore AnyThread bit if CPUID.0AH:EDX[15] is set
2026-08-27 22:37 [PATCH] KVM: x86/pmu: Add mediated vPMU PerfMon v5 support Zide Chen
` (12 preceding siblings ...)
2026-08-27 22:37 ` [PATCH v2 13/16] KVM: x86/pmu: Factor out fixed counter control bit calculation Zide Chen
@ 2026-08-27 22:37 ` Zide Chen
2026-08-27 22:37 ` [PATCH v2 15/16] KVM: x86/pmu: Advertise PerfMon version 5 on Intel hosts Zide Chen
2026-08-27 22:37 ` [PATCH v2 16/16] KVM: selftests: Support fixed counters bitmap in pmu_counters_test Zide Chen
15 siblings, 0 replies; 21+ messages in thread
From: Zide Chen @ 2026-08-27 22:37 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini, Peter Zijlstra
Cc: kvm, Andi Kleen, Jim Mattson, Stephane Eranian, linux-kernel,
Mingwei Zhang, Zide Chen, Das Sandipan, Shukla Manali, Dapeng Mi,
Xudong Hao
Intel PerfMon v5 introduces the ANYTHREAD_DEPRECATION capability
(CPUID.0AH:EDX[15]) to indicate that AnyThread counting is deprecated
and that writes to the AnyThread bit in IA32_PERFEVTSELx are ignored.
When ANYTHREAD_DEPRECATION is present in the guest CPUID, emulate the
architectural behavior and silently ignore writes to the AnyThread bit
instead of injecting #GP.
Continue to inject #GP when ANYTHREAD_DEPRECATION is not present in the
guest CPUID, e.g. for PerfMon v3/v4 guests or when the capability is
not configured for a PerfMon v5+ guest.
Advertise CPUID.0AH:EDX[15] only for PMU version 5+ guests, since the
bit doesn't exist below version 5.
Signed-off-by: Zide Chen <zide.chen@intel.com>
---
v2:
- Advertise anythread_deprecated only if kvm_pmu_cap.version >= 5.
- Add the missing AnyThr bit handling for IA32_FIXED_CTR_CTRL.
- Update the comments to be clearer.
---
arch/x86/kvm/cpuid.c | 2 +-
arch/x86/kvm/vmx/pmu_intel.c | 19 +++++++++++++++++++
2 files changed, 20 insertions(+), 1 deletion(-)
diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c
index 63201d4ea4a1..ef208906b3ee 100644
--- a/arch/x86/kvm/cpuid.c
+++ b/arch/x86/kvm/cpuid.c
@@ -1542,7 +1542,7 @@ static inline int __do_cpuid_func(struct kvm_cpuid_array *array, u32 function)
KVM_MAX_NR_FIXED_COUNTERS);
edx.split.bit_width_fixed = kvm_pmu_cap.bit_width_fixed;
- if (kvm_pmu_cap.version)
+ if (kvm_pmu_cap.version >= 5)
edx.split.anythread_deprecated = 1;
entry->eax = eax.full;
diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c
index c7472778137a..599c69dfa6e3 100644
--- a/arch/x86/kvm/vmx/pmu_intel.c
+++ b/arch/x86/kvm/vmx/pmu_intel.c
@@ -446,6 +446,12 @@ static int intel_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
if (data & pmu->fixed_ctr_ctrl_rsvd)
return 1;
+ /*
+ * This is to emulate ANYTHREAD_DEPRECATION. Guests without
+ * support for it would have had the write rejected by
+ * pmu->fixed_ctr_ctrl_rsvd above.
+ */
+ data &= ~intel_pmu_fixed_ctrl_bits(pmu, INTEL_FIXED_0_ANYTHREAD);
if (pmu->fixed_ctr_ctrl != data)
reprogram_fixed_counters(pmu, data);
break;
@@ -494,6 +500,8 @@ static int intel_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
if (data & eventsel_rsvd)
return 1;
+ /* See above comments for MSR_CORE_PERF_FIXED_CTR_CTRL. */
+ data &= ~ARCH_PERFMON_EVENTSEL_ANY;
if (data != pmc->eventsel) {
pmc->eventsel = data;
pmc->eventsel_hw = data;
@@ -603,6 +611,17 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu)
pmu->pmc_exists64 = (BIT_ULL(nr_gp_counters) - 1) &
kvm_pmu_cap.cntr_mask64;
+ /*
+ * AnyThread counting is not supported by KVM due to cross-VM
+ * information leakage concerns on SMT cores. Therefore, AnyThread
+ * remains unavailable for PerfMon v3/v4 guests, where AnyThread
+ * deprecation is not enumerated.
+ */
+ if (pmu->version >= 5 && edx.split.anythread_deprecated) {
+ pmu->eventsel_rsvd &= ~ARCH_PERFMON_EVENTSEL_ANY;
+ fixed_enable_bits |= INTEL_FIXED_0_ANYTHREAD;
+ }
+
entry = kvm_find_cpuid_entry_index(vcpu, 7, 0);
if (entry &&
(boot_cpu_has(X86_FEATURE_HLE) || boot_cpu_has(X86_FEATURE_RTM)) &&
--
2.55.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH v2 15/16] KVM: x86/pmu: Advertise PerfMon version 5 on Intel hosts
2026-08-27 22:37 [PATCH] KVM: x86/pmu: Add mediated vPMU PerfMon v5 support Zide Chen
` (13 preceding siblings ...)
2026-08-27 22:37 ` [PATCH v2 14/16] KVM: x86/pmu: Ignore AnyThread bit if CPUID.0AH:EDX[15] is set Zide Chen
@ 2026-08-27 22:37 ` Zide Chen
2026-08-27 22:37 ` [PATCH v2 16/16] KVM: selftests: Support fixed counters bitmap in pmu_counters_test Zide Chen
15 siblings, 0 replies; 21+ messages in thread
From: Zide Chen @ 2026-08-27 22:37 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini, Peter Zijlstra
Cc: kvm, Andi Kleen, Jim Mattson, Stephane Eranian, linux-kernel,
Mingwei Zhang, Zide Chen, Das Sandipan, Shukla Manali, Dapeng Mi,
Xudong Hao
From: Dapeng Mi <dapeng1.mi@linux.intel.com>
KVM currently caps the guest PerfMon version at 2 for all Intel
platforms. Now that KVM emulates the basic architectural PMU features
introduced in PerfMon versions 3 through 5, raise the guest PerfMon
version to 5. Features that require additional emulation support, e.g.
architectural LBR will be enabled separately.
PerfMon versions 3 and 4 continue to be exposed as version 2 because
KVM cannot safely virtualize AnyThread due to cross-VM information
leakage. PerfMon v5 is supported because it provides the
AnyThread-deprecated capability, allowing KVM to indicate that
AnyThread is deprecated.
When enable_mediated_pmu is disabled or in the non-Intel paths, KVM
retains the existing cap of version 2.
Signed-off-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
Signed-off-by: Zide Chen <zide.chen@intel.com>
---
v2:
- Cap vPMU v3/v4 to v2, as AnyThread cannot be safely virtualized.
(Sashiko)
---
arch/x86/kvm/pmu.c | 16 +++++++++++++++-
1 file changed, 15 insertions(+), 1 deletion(-)
diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c
index 4cb7c796daac..1a7be7d73b62 100644
--- a/arch/x86/kvm/pmu.c
+++ b/arch/x86/kvm/pmu.c
@@ -175,7 +175,21 @@ void kvm_init_pmu_capability(struct kvm_pmu_ops *pmu_ops)
}
memcpy(&kvm_pmu_cap, &kvm_host_pmu, sizeof(kvm_host_pmu));
- kvm_pmu_cap.version = min(kvm_pmu_cap.version, 2);
+
+ if (is_intel && enable_mediated_pmu) {
+ kvm_pmu_cap.version = min(kvm_pmu_cap.version, 5);
+
+ /*
+ * PMU v3/v4 has AnyThread support, which KVM cannot safely
+ * virtualize. Expose PMU v2 instead of advertising a partially
+ * functional PMU v3/v4 to guests.
+ */
+ if (kvm_pmu_cap.version == 3 || kvm_pmu_cap.version == 4)
+ kvm_pmu_cap.version = 2;
+ } else {
+ kvm_pmu_cap.version = min(kvm_pmu_cap.version, 2);
+ }
+
kvm_pmu_cap.cntr_mask64 &=
GENMASK_ULL(pmu_ops->MAX_NR_GP_COUNTERS - 1, 0);
kvm_pmu_cap.fixed_cntr_mask64 &=
--
2.55.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH v2 16/16] KVM: selftests: Support fixed counters bitmap in pmu_counters_test
2026-08-27 22:37 [PATCH] KVM: x86/pmu: Add mediated vPMU PerfMon v5 support Zide Chen
` (14 preceding siblings ...)
2026-08-27 22:37 ` [PATCH v2 15/16] KVM: x86/pmu: Advertise PerfMon version 5 on Intel hosts Zide Chen
@ 2026-08-27 22:37 ` Zide Chen
15 siblings, 0 replies; 21+ messages in thread
From: Zide Chen @ 2026-08-27 22:37 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini, Peter Zijlstra
Cc: kvm, Andi Kleen, Jim Mattson, Stephane Eranian, linux-kernel,
Mingwei Zhang, Zide Chen, Das Sandipan, Shukla Manali, Dapeng Mi,
Xudong Hao
From: Dapeng Mi <dapeng1.mi@linux.intel.com>
On PerfMon v5, CPUID.0AH:ECX represents the fixed counter bitmask,
while EDX[4:0] indicates the number of contiguous fixed counters
starting from 0:
FxCtr[i]_is_supported := ECX[i] || (EDX[4:0] > i).
Fix test_fixed_counters() to derive supported fixed counters using the
above formula instead of relying solely on EDX[4:0]. This allows the
test to cover non-contiguous fixed counter configurations.
Update this_pmu_has() to take the PMU version into consideration.
Without this fix, it could return incorrect results in test cases such
as test_arch_events(), which configure a PMU version different from
the one originally advertised by KVM without explicitly updating
CPUID.0AH.{ECX,EDX} to match the new PMU version.
For example, after downgrading a guest from PMU v5 to PMU v2,
this_pmu_has() would still consult CPUID.0AH.ECX while KVM ignores it,
resulting in a mismatch in the reported fixed counter capabilities.
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>
---
v2:
- Fix this_pmu_has() to honor the guest's configured PMU version.
- Update the pmu_vm_create_with_vcpus() calls after rebasing.
---
.../selftests/kvm/include/x86/processor.h | 6 ++++-
.../selftests/kvm/x86/pmu_counters_test.c | 26 ++++++++++++++-----
2 files changed, 25 insertions(+), 7 deletions(-)
diff --git a/tools/testing/selftests/kvm/include/x86/processor.h b/tools/testing/selftests/kvm/include/x86/processor.h
index 6e6f70035508..c983dbfd9d70 100644
--- a/tools/testing/selftests/kvm/include/x86/processor.h
+++ b/tools/testing/selftests/kvm/include/x86/processor.h
@@ -827,6 +827,7 @@ static __always_inline bool this_cpu_has_p(struct kvm_x86_cpu_property property)
static inline bool this_pmu_has(struct kvm_x86_pmu_feature feature)
{
+ u8 pmu_version;
u32 nr_bits;
if (feature.f.reg == KVM_CPUID_EBX) {
@@ -836,7 +837,10 @@ static inline bool this_pmu_has(struct kvm_x86_pmu_feature feature)
GUEST_ASSERT(feature.f.reg == KVM_CPUID_ECX);
nr_bits = this_cpu_property(X86_PROPERTY_PMU_NR_FIXED_COUNTERS);
- return nr_bits > feature.f.bit || this_cpu_has(feature.f);
+ pmu_version = this_cpu_property(X86_PROPERTY_PMU_VERSION);
+
+ return (pmu_version < 5) ? nr_bits > feature.f.bit :
+ nr_bits > feature.f.bit || this_cpu_has(feature.f);
}
static __always_inline u64 this_cpu_supported_xcr0(void)
diff --git a/tools/testing/selftests/kvm/x86/pmu_counters_test.c b/tools/testing/selftests/kvm/x86/pmu_counters_test.c
index c3e784e16348..21bd8ec90c98 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/bitmap.h>
#include "pmu.h"
#include "processor.h"
@@ -631,23 +632,36 @@ static void __test_fixed_counters(struct kvm_vcpu *vcpu, u8 nr_fixed_counters,
static void test_fixed_counters(u8 pmu_version, u64 perf_capabilities)
{
u8 nr_fixed_counters = kvm_cpu_property(X86_PROPERTY_PMU_NR_FIXED_COUNTERS);
+ unsigned long fixed_subset;
struct kvm_vcpu **vcpus;
struct kvm_vm *vm;
+ u32 fixed_bitmap;
int i = 0;
- u32 k;
- u8 j;
pr_info("Testing %u fixed counters, PMU version %u, perf_caps = %lx\n",
nr_fixed_counters, pmu_version, perf_capabilities);
+ fixed_bitmap = BIT_ULL(nr_fixed_counters) - 1;
+ if (pmu_version >= 5)
+ fixed_bitmap |= kvm_cpu_property(X86_PROPERTY_PMU_FIXED_COUNTERS_BITMASK);
- vm = pmu_vm_create_with_vcpus((nr_fixed_counters + 1) * BIT(nr_fixed_counters),
+ vm = pmu_vm_create_with_vcpus((1 << __builtin_popcount(fixed_bitmap)),
guest_test_fixed_counters,
pmu_version, perf_capabilities, &vcpus);
- for (j = 0; j <= nr_fixed_counters; j++) {
- for (k = 0; k <= (BIT(nr_fixed_counters) - 1); k++)
- __test_fixed_counters(vcpus[i++], j, k);
+ for (fixed_subset = 0; fixed_subset <= fixed_bitmap; fixed_subset++) {
+ u32 nr_contiguous;
+
+ /*
+ * The loop walks all values from 0 to fixed_bitmap, so skip any
+ * value that is not a subset of fixed_bitmap.
+ */
+ if (fixed_subset & ~fixed_bitmap)
+ continue;
+
+ nr_contiguous = find_first_zero_bit(&fixed_subset,
+ MAX_NR_FIXED_COUNTERS);
+ __test_fixed_counters(vcpus[i++], nr_contiguous, fixed_subset);
}
pmu_vm_free(vm, vcpus);
--
2.55.0
^ permalink raw reply related [flat|nested] 21+ messages in thread