Re: [PATCH v6 7/8] KVM: x86/pmu: Emulate RDPMC on performance metrics

From: Chen, Zide

Date: Thu Jul 23 2026 - 15:13:52 EST




On 7/22/2026 10:10 PM, Jim Mattson wrote:
> On Wed, Jul 22, 2026 at 7:53 PM Mi, Dapeng <dapeng1.mi@xxxxxxxxxxxxxxx> wrote:
>>
>>
>> On 7/23/2026 10:25 AM, Jim Mattson wrote:
>>> On Wed, Jul 22, 2026 at 6:44 PM Mi, Dapeng <dapeng1.mi@xxxxxxxxxxxxxxx> wrote:
>>>>
>>>> On 7/23/2026 8:02 AM, Jim Mattson wrote:
>>>>> On Mon, Jun 29, 2026 at 4:29 PM Zide Chen <zide.chen@xxxxxxxxx> wrote:
>>>>>> If the host has the PERF_METRICS capability but it's not present on
>>>>>> the guest, RDPMC interception must be enabled and KVM should inject
>>>>>> an #GP when the guest attempts a PERF_METRICS RDPMC.
>>>>>>
>>>>>> If the guest has PERF_METRICS but RDPMC interception is enabled for
>>>>>> other reasons, KVM needs to emulate RDPMC with type 2000H.
>>>>>>
>>>>>> For simplicity, Metrics Clear Mode is not supported.
>>>>>>
>>>>>> Signed-off-by: Zide Chen <zide.chen@xxxxxxxxx>
>>>>>> ---
>>>>>> v6:
>>>>>> - Merge kvm_pmu_rdpmc_metrics() into intel_emulate_rdpmc().
>>>>>> - Reject non-zero index.
>>>>>> v5:
>>>>>> - new patch.
>>>>>> ---
>>>>>> arch/x86/kvm/pmu.c | 7 +++++++
>>>>>> arch/x86/kvm/vmx/pmu_intel.c | 14 ++++++++++++++
>>>>>> 2 files changed, 21 insertions(+)
>>>>>>
>>>>>> diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c
>>>>>> index 8ef2d4761790..04b9c840f218 100644
>>>>>> --- a/arch/x86/kvm/pmu.c
>>>>>> +++ b/arch/x86/kvm/pmu.c
>>>>>> @@ -806,6 +806,12 @@ bool kvm_need_perf_global_ctrl_intercept(struct kvm_vcpu *vcpu)
>>>>>> }
>>>>>> EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_need_perf_global_ctrl_intercept);
>>>>>>
>>>>>> +static bool kvm_need_perf_metrics_intercept(struct kvm_vcpu *vcpu)
>>>>>> +{
>>>>>> + return (kvm_host.perf_capabilities & PERF_CAP_PERF_METRICS) &&
>>>>>> + !kvm_vcpu_has_perf_metrics(vcpu);
>>>>>> +}
>>>>>> +
>>>>>> bool kvm_need_rdpmc_intercept(struct kvm_vcpu *vcpu)
>>>>>> {
>>>>>> struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
>>>>>> @@ -818,6 +824,7 @@ bool kvm_need_rdpmc_intercept(struct kvm_vcpu *vcpu)
>>>>>> return true;
>>>>> I know we have a strong disagreement here, but I really feel that this
>>>>> must be secure and future-proof out of the box.
>>>>> What if we added a module parameter, enable_rdpmc_passthrough, which
>>>>> defaults to false?
>>>>>
>>>>> Then:
>>>>>
>>>>> if (!enable_rdpmc_passthrough)
>>>>> return true;
>>>>>
>>>>> Is that a reasonable compromise?
>>>> Yes, this is an option. Another option is to add a helper to explicitly
>>>> tell which platforms are secure and can pass-through rdpmc. Maybe like this,
>>>>
>>>> static bool intel_pmu_can_passthrough_rdpmc()
>>>> {
>>>> switch (boot_cpu_data.x86_vfm) {
>>>> case INTEL_ICELAKE_X:
>>>> case INTEL_SAPPHIRERAPIDS_X:
>>>> case INTEL_EMERALDRAPIDS_X:
>>>> case INTEL_GRANITERAPIDS_X:
>>>> ... ...
>>>> return true;
>>>> default:
>>>> return false;
>>>> }
>>>>
>>>> return false;
>>>> }
>>>>
>>>> Then rdpmc can be passed through by default on these known secure
>>>> platforms. For any new platforms, the rdpmc would be intercepted out of
>>>> box until we confirm it's secured and explicitly support them.
>>>>
>>>> How about this? At least for me, it looks like an overkill to disable rdpmc
>>>> passthrough unconditionally on these known secure platforms.
>>> The F/M match would only apply on bare metal, since you can't trust F/M in a VM.
>>>
>>> What if we combine this with the module parameter, but now the module
>>> parameter defaults to true? Somewhere in module load, we have:
>>>
>>> if (boot_cpu_has(X86_FEATURE_HYPERVISOR) || !intel_pmu_can_passthrough_rdpmc())
>>> enable_rdpmc_passthrough = false;
>>
>> It looks good to me. :)
>
> On second thought, it doesn't really matter if L0 has lied to L1 about
> F/M. If the hardware supports more RDPMC types than the L1 virtual CPU
> does, L0 must intercept and emulate RDPMC for vmcs01. Even if L1
> decides to pass through RDPMC to L2, L0 will still intercept and
> emulate RDPMC for vmcs02.
>
> So, we can drop the module parameter and just use
> intel_pmu_can_passthrough_rdpmc().
>
> To keep the list short, I assume that any CPU with only three fixed
> counters will return true.

How about this to keep the list short? To be conservative, don't assume
future PMUv6 CPUs won't introduce new RDPMC types.

diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c
index 9dc453598d06..b2351b764788 100644
--- a/arch/x86/kvm/pmu.c
+++ b/arch/x86/kvm/pmu.c
@@ -817,6 +817,31 @@ static bool kvm_need_perf_metrics_intercept(struct
kvm_vcpu *vcpu)
!kvm_vcpu_has_perf_metrics(vcpu);
}

+static bool kvm_rdpmc_encoding_supported(void)
+{
+ /* KVM understands all RDPMC encodings prior to PMUv6. */
+ if (kvm_host_pmu.version < 6)
+ return true;
+
+ /*
+ * Future PMUv6 implementations and future PMU versions require
RDPMC
+ * interception until their RDPMC encodings are audited and
supported
+ * by KVM.
+ */
+ switch (boot_cpu_data.x86_vfm) {
+ case INTEL_ATOM_DARKMONT_X:
+ case INTEL_NOVALAKE:
+ case INTEL_NOVALAKE_L:
+ case INTEL_PANTHERLAKE_L:
+ case INTEL_PANTHERLAKE_R:
+ case INTEL_WILDCATLAKE_L:
+ case INTEL_LUNARLAKE_M:
+ return true;
+ default:
+ return false;
+ }
+}
+
bool kvm_need_rdpmc_intercept(struct kvm_vcpu *vcpu)
{
struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
@@ -828,6 +853,9 @@ bool kvm_need_rdpmc_intercept(struct kvm_vcpu *vcpu)
if (enable_vmware_backdoor)
return true;

+ if (!kvm_rdpmc_encoding_supported())
+ return true;
+
return kvm_need_any_pmc_intercept(vcpu) ||
kvm_need_perf_metrics_intercept(vcpu) ||
pmu->counter_bitmask[KVM_PMC_GP] !=
(BIT_ULL(kvm_host_pmu.bit_width_gp) - 1) ||