[RFC PATCH 28/41] KVM: x86/pmu: Switch IA32_PERF_GLOBAL_CTRL at VM boundary

From: Xiong Zhang
Date: Fri Jan 26 2024 - 04:43:50 EST


From: Xiong Zhang <xiong.y.zhang@xxxxxxxxx>

In PMU passthrough mode, use global_ctrl field in struct kvm_pmu as the
cached value. This is convenient for KVM to set and get the value from the
host side. In addition, load and save the value across VM enter/exit
boundary in the following way:

- At VM exit, if processor supports
GUEST_VM_EXIT_SAVE_IA32_PERF_GLOBAL_CTRL, read guest
IA32_PERF_GLOBAL_CTRL GUEST_IA32_PERF_GLOBAL_CTRL VMCS field, else read
it from VM-exit MSR-stroe array in VMCS. The value is then assigned to
global_ctrl.

- At VM Entry, if processor supports
GUEST_VM_ENTRY_LOAD_IA32_PERF_GLOBAL_CTRL, read guest
IA32_PERF_GLOBAL_CTRL from GUEST_IA32_PERF_GLOBAL_CTRL VMCS field, else
read it from VM-entry MSR-load array in VMCS. The value is then
assigned to global ctrl.

Implement the above logic into two helper functions and invoke them around
VM Enter/exit boundary.

Co-developed-by: Mingwei Zhang <mizhang@xxxxxxxxxx>
Signed-off-by: Mingwei Zhang <mizhang@xxxxxxxxxx>
Signed-off-by: Xiong Zhang <xiong.y.zhang@xxxxxxxxx>
---
arch/x86/kvm/vmx/vmx.c | 51 +++++++++++++++++++++++++++++++++++++++++-
1 file changed, 50 insertions(+), 1 deletion(-)

diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index 50100954cd92..a9623351eafe 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -7193,7 +7193,7 @@ static void vmx_cancel_injection(struct kvm_vcpu *vcpu)
vmcs_write32(VM_ENTRY_INTR_INFO_FIELD, 0);
}

-static void atomic_switch_perf_msrs(struct vcpu_vmx *vmx)
+static void __atomic_switch_perf_msrs(struct vcpu_vmx *vmx)
{
int i, nr_msrs;
struct perf_guest_switch_msr *msrs;
@@ -7216,6 +7216,52 @@ static void atomic_switch_perf_msrs(struct vcpu_vmx *vmx)
msrs[i].host, false);
}

+static void save_perf_global_ctrl_in_passthrough_pmu(struct vcpu_vmx *vmx)
+{
+ struct kvm_pmu *pmu = vcpu_to_pmu(&vmx->vcpu);
+ int i;
+
+ if (vm_exit_controls_get(vmx) & VM_EXIT_SAVE_IA32_PERF_GLOBAL_CTRL) {
+ pmu->global_ctrl = vmcs_read64(GUEST_IA32_PERF_GLOBAL_CTRL);
+ } else {
+ i = vmx_find_loadstore_msr_slot(&vmx->msr_autostore.guest,
+ MSR_CORE_PERF_GLOBAL_CTRL);
+ if (i < 0)
+ return;
+ pmu->global_ctrl = vmx->msr_autostore.guest.val[i].value;
+ }
+}
+
+static void load_perf_global_ctrl_in_passthrough_pmu(struct vcpu_vmx *vmx)
+{
+ u64 global_ctrl = vcpu_to_pmu(&vmx->vcpu)->global_ctrl;
+ int i;
+
+ if (vm_entry_controls_get(vmx) & VM_ENTRY_LOAD_IA32_PERF_GLOBAL_CTRL) {
+ vmcs_write64(GUEST_IA32_PERF_GLOBAL_CTRL, global_ctrl);
+ } else {
+ i = vmx_find_loadstore_msr_slot(&vmx->msr_autoload.guest,
+ MSR_CORE_PERF_GLOBAL_CTRL);
+ if (i < 0)
+ return;
+
+ vmx->msr_autoload.guest.val[i].value = global_ctrl;
+ }
+}
+
+static void __atomic_switch_perf_msrs_in_passthrough_pmu(struct vcpu_vmx *vmx)
+{
+ load_perf_global_ctrl_in_passthrough_pmu(vmx);
+}
+
+static void atomic_switch_perf_msrs(struct vcpu_vmx *vmx)
+{
+ if (is_passthrough_pmu_enabled(&vmx->vcpu))
+ __atomic_switch_perf_msrs_in_passthrough_pmu(vmx);
+ else
+ __atomic_switch_perf_msrs(vmx);
+}
+
static void vmx_update_hv_timer(struct kvm_vcpu *vcpu)
{
struct vcpu_vmx *vmx = to_vmx(vcpu);
@@ -7314,6 +7360,9 @@ static noinstr void vmx_vcpu_enter_exit(struct kvm_vcpu *vcpu,
vcpu->arch.cr2 = native_read_cr2();
vcpu->arch.regs_avail &= ~VMX_REGS_LAZY_LOAD_SET;

+ if (is_passthrough_pmu_enabled(vcpu))
+ save_perf_global_ctrl_in_passthrough_pmu(vmx);
+
vmx->idt_vectoring_info = 0;

vmx_enable_fb_clear(vmx);
--
2.34.1