KVM: x86/pmu: Support full width counting
PATCH RESEND 1/1 KVM: x86/pmu: Support full width counting
Hi Jim & Sean,
Do you mind helping review this little feature for vPMU?
The related specification in the Intel SDM is "18.2.6 Full-Width Writes to Performance Counter Registers" and related kernel commit is 069e0c3c40581.
If there is anything needs to be improved, please let me know.
Thanks,
Like Xu
----
Intel CPUs have a new alternative MSR range (starting from MSR_IA32_PMC0) for GP counters that allows writing the full counter width. Enable this range from a new capability bit (IA32_PERF_CAPABILITIES.FW_WRITE[bit 13]).
The guest would query CPUID to get the counter width, and sign extends the counter values as needed. The traditional MSRs always limit to 32bit, even though the counter internally is larger (usually 48 bits).
When the new capability is set, use the alternative range which do not have these restrictions. This lowers the overhead of perf stat slightly because it has to do less interrupts to accumulate the counter value.
--- arch/x86/include/asm/kvm_host.h | 1 + arch/x86/kvm/vmx/capabilities.h | 15 +++++++++++++++ arch/x86/kvm/vmx/pmu_intel.c | 32 ++++++++++++++++++++++++++++---- arch/x86/kvm/vmx/vmx.c | 2 ++ arch/x86/kvm/x86.c | 8 ++++++++ 5 files changed, 54 insertions(+), 4 deletions(-)
arch/x86/include/asm/kvm_host.h +1 -0
@@ -486,6 +486,7 @@ struct kvm_pmu {u64 counter_bitmask[2 ];u64 global_ctrl_mask;u64 global_ovf_ctrl_mask; +u64 perf_capabilities;u64 reserved_bits;u8 version;struct kvm_pmc gp_counters[INTEL_PMC_MAX_GENERIC ];
arch/x86/kvm/vmx/capabilities.h +15 -0
@@ -367,4 +367,19 @@ static inline bool vmx_pt_mode_is_host_guest(void) return pt_mode == PT_MODE_HOST_GUEST; } +#define PMU_CAP_FW_WRITES (1ULL << 13) + +static inline u64 vmx_supported_perf_capabilities (void ) +{ +u64 perf_cap =0 ; + +if (boot_cpu_has (X86_FEATURE_PDCM )) +rdmsrl (MSR_IA32_PERF_CAPABILITIES , perf_cap); + +/* Currently, KVM only supports Full-Width Writes. */ + perf_cap &=PMU_CAP_FW_WRITES ; + +return perf_cap; +} +#endif /* __KVM_X86_VMX_CAPS_H */
arch/x86/kvm/vmx/pmu_intel.c +28 -4
@@ -150,6 +150,14 @@ static struct kvm_pmc *intel_rdpmc_ecx_to_pmc(struct kvm_vcpu *vcpu,return &counters[array_index_nospec (idx, num_counters)]; } +static inline bool fw_writes_is_enabled (struct kvm_pmu *pmu) +{ +if (!guest_cpuid_has (pmu_to_vcpu (pmu),X86_FEATURE_PDCM )) +return false; + +return pmu->perf_capabilities &PMU_CAP_FW_WRITES ; +} +static bool intel_is_valid_msr (struct kvm_vcpu *vcpu,u32 msr) {struct kvm_pmu *pmu =vcpu_to_pmu (vcpu);
@@ -162,10 +170,15 @@ static bool intel_is_valid_msr(struct kvm_vcpu *vcpu, u32 msr)case MSR_CORE_PERF_GLOBAL_OVF_CTRL : ret = pmu->version >1 ;break ; +case MSR_IA32_PERF_CAPABILITIES : + ret =guest_cpuid_has (vcpu,X86_FEATURE_PDCM ); +break ;default : ret =get_gp_pmc (pmu, msr,MSR_IA32_PERFCTR0 ) ||get_gp_pmc (pmu, msr,MSR_P6_EVNTSEL0 ) || -get_fixed_pmc (pmu, msr); +get_fixed_pmc (pmu, msr) || + (fw_writes_is_enabled (pmu) && +get_gp_pmc (pmu, msr,MSR_IA32_PMC0 ));break ; }
@@ -202,8 +215,12 @@ static int intel_pmu_get_msr(struct kvm_vcpu *vcpu, u32 msr, u64 *data)case MSR_CORE_PERF_GLOBAL_OVF_CTRL : *data = pmu->global_ovf_ctrl;return 0 ; +case MSR_IA32_PERF_CAPABILITIES : + *data = pmu->perf_capabilities; +return 0 ;default : -if ((pmc =get_gp_pmc (pmu, msr,MSR_IA32_PERFCTR0 ))) { +if ((pmc =get_gp_pmc (pmu, msr,MSR_IA32_PERFCTR0 )) || + (pmc =get_gp_pmc (pmu, msr,MSR_IA32_PMC0 ))) {u64 val =pmc_read_counter (pmc); *data = val & pmu->counter_bitmask[KVM_PMC_GP ];return 0 ;
@@ -258,9 +275,13 @@ static int intel_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)return 0 ; }break ; +case MSR_IA32_PERF_CAPABILITIES : +return 1 ;/* RO MSR */ default : -if ((pmc =get_gp_pmc (pmu, msr,MSR_IA32_PERFCTR0 ))) { -if (!msr_info->host_initiated) +if ((pmc =get_gp_pmc (pmu, msr,MSR_IA32_PERFCTR0 )) || + (pmc =get_gp_pmc (pmu, msr,MSR_IA32_PMC0 ))) { +if (!msr_info->host_initiated && + !fw_writes_is_enabled (pmu)) data = (s64 )(s32 )data; pmc->counter += data -pmc_read_counter (pmc);if (pmc->perf_event)
@@ -300,6 +321,7 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu) pmu->counter_bitmask[KVM_PMC_FIXED ] =0 ; pmu->version =0 ; pmu->reserved_bits =0xffffffff00200000ull ; + pmu->perf_capabilities =0 ; entry =kvm_find_cpuid_entry (vcpu,0xa ,0 );if (!entry)
@@ -312,6 +334,8 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu)return ;perf_get_x86_pmu_capability (&x86_pmu); +if (guest_cpuid_has (vcpu,X86_FEATURE_PDCM )) + pmu->perf_capabilities =vmx_supported_perf_capabilities (); pmu->nr_arch_gp_counters =min_t (int, eax.split.num_counters, x86_pmu.num_counters_gp);
arch/x86/kvm/vmx/vmx.c +2 -0
@@ -7222,6 +7222,8 @@ static __init void vmx_set_cpu_caps(void)kvm_cpu_cap_check_and_set (X86_FEATURE_INVPCID );if (vmx_pt_mode_is_host_guest ())kvm_cpu_cap_check_and_set (X86_FEATURE_INTEL_PT ); +if (vmx_supported_perf_capabilities ()) +kvm_cpu_cap_check_and_set (X86_FEATURE_PDCM );/* PKU is not yet implemented for shadow paging. */ if (enable_ept &&boot_cpu_has (X86_FEATURE_OSPKE ))
arch/x86/kvm/x86.c +8 -0
@@ -1229,6 +1229,14 @@ static const u32 msrs_to_save_all[] = {MSR_ARCH_PERFMON_EVENTSEL0 +12 ,MSR_ARCH_PERFMON_EVENTSEL0 +13 ,MSR_ARCH_PERFMON_EVENTSEL0 +14 ,MSR_ARCH_PERFMON_EVENTSEL0 +15 ,MSR_ARCH_PERFMON_EVENTSEL0 +16 ,MSR_ARCH_PERFMON_EVENTSEL0 +17 , + +MSR_IA32_PERF_CAPABILITIES , +MSR_IA32_PMC0 ,MSR_IA32_PMC0 +1 ,MSR_IA32_PMC0 +2 , +MSR_IA32_PMC0 +3 ,MSR_IA32_PMC0 +4 ,MSR_IA32_PMC0 +5 , +MSR_IA32_PMC0 +6 ,MSR_IA32_PMC0 +7 ,MSR_IA32_PMC0 +8 , +MSR_IA32_PMC0 +9 ,MSR_IA32_PMC0 +10 ,MSR_IA32_PMC0 +11 , +MSR_IA32_PMC0 +12 ,MSR_IA32_PMC0 +13 ,MSR_IA32_PMC0 +14 , + MSR_IA32_PMC0 + 15, MSR_IA32_PMC0 + 16, MSR_IA32_PMC0 + 17, };static u32 msrs_to_save[ARRAY_SIZE (msrs_to_save_all)];
Signature · 1 line
2.21.1
Paolo Bonzini
maintainerarch/x86/kvm/vmx/vmx.c static __init void vmx_set_cpu_caps(void) · line 7226
Quoted 2 lines · Like Xu
arch/x86/kvm/vmx/vmx.c +2
-0+if (vmx_supported_perf_capabilities ()) +kvm_cpu_cap_check_and_set (X86_FEATURE_PDCM );
I think we can always set it, worst case it will be zero.
However, blocking intel_pmu_set_msr altogether is incorrect. Instead, you need to:
- list the MSR in msr_based_features_all so that it appears in KVM_GET_MSR_FEATURE_INDEX_LIST
- return the supported bits in vmx_get_msr_feature
- allow host-initiated writes (as long as they only set supported bits) of the MSR in intel_pmu_set_msr.
Thanks,
Paolo
Hi Paolo,
Thanks for your comments!
Quoted 4 lines · Paolo Bonzini
arch/x86/kvm/vmx/vmx.c static __init void vmx_set_cpu_caps(void) · line 7226
Quoted 2 lines · Like Xu
arch/x86/kvm/vmx/vmx.c +2
-0+if (vmx_supported_perf_capabilities ()) +kvm_cpu_cap_check_and_set (X86_FEATURE_PDCM );I think we can always set it, worst case it will be zero.
Sure, we could set it for x86.
Quoted 11 lines
However, blocking
intel_pmu_set_msraltogether is incorrect. Instead, you need to:- list the MSR in
msr_based_features_allso that it appears inKVM_GET_MSR_FEATURE_INDEX_LIST- return the supported bits in
vmx_get_msr_feature- allow host-initiated writes (as long as they only set supported bits) of the MSR in
intel_pmu_set_msr.
Please review the v2 patch for this feature,
https://lore.kernel.org/kvm/[email protected]/
Thanks,
Like Xu
Quoted 5 lines
Thanks,
Paolo