Re: [PATCH v2 11/16] KVM: x86/pmu: Emulate the GLOBAL_STATUS_SET and GLOBAL_INUSE MSRs
From: Chen, Zide
Date: Tue Sep 01 2026 - 10:21:43 EST
On 9/1/2026 1:45 AM, Mi, Dapeng wrote:
>
> On 8/28/2026 6:37 AM, Zide Chen wrote:
>> 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@xxxxxxxxx>
>> Signed-off-by: Zide Chen <zide.chen@xxxxxxxxx>
>> ---
>> 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)
>
> Why to check ARCH_PERFMON_EVENTSEL_EVENT instead of
> ARCH_PERFMON_EVENTSEL_ENABLE here? Suppose only
> ARCH_PERFMON_EVENTSEL_ENABLE is set, then the counter is in use.
This implementation strictly follows the SDM, which checks the event
bits, not the enable bit. This is also verified on bare metal.
"IA32_PERF_GLOBAL_INUSE.PERFEVTSEL0_InUse[bit 0]: This bit reflects the
logical state of (IA32_PERFEVTSEL0[7:0] != 0)."
That's why, in the reply above to Sashiko, I noted that on bare metal
without a vCPU scheduled in, after a perf event is closed, the counter
could still show as in use. perf/x86 clears the enable bit while keeps
the event bits set.