[Devel] [PATCH RHEL8 COMMIT] KVM: x86/vPMU: Ignore access to LBR-related MSRs

Konstantin Khorenko khorenko at virtuozzo.com
Fri Apr 30 13:21:23 MSK 2021


The commit is pushed to "branch-rh8-4.18.0-240.1.1.vz8.5.x-ovz" and will appear at https://src.openvz.org/scm/ovz/vzkernel.git
after rh8-4.18.0-240.1.1.vz8.5.23
------>
commit 1fb36a7c3b577381714bd6bcee5f629839dfad45
Author: Jan Dakinevich <jan.dakinevich at virtuozzo.com>
Date:   Fri Apr 30 13:21:23 2021 +0300

    KVM: x86/vPMU: Ignore access to LBR-related MSRs
    
    Windows Server 2016 Essentials (for yet unknown reason) attempts to
    access MSR_LBR_TOS and other LBR-related registers at startup.  These
    are not currently hadled by KVM so the guest gets #GP and crashes.
    
    To prevent that, identify LBR-related MSRs pertinent to the CPU model
    exposed to the guest, and dummy handle them (ignore writes and return
    zero on reads).
    
    https://jira.sw.ru/browse/PSBM-75679
    Signed-off-by: Jan Dakinevich <jan.dakinevich at virtuozzo.com>
    
    Commit 29ae48aecec8 in the kernels from VZ7.
    
    Done in the scope of https://jira.sw.ru/browse/PSBM-127794.
    
    Note. It looks like, the recent mainline kernels (5.12-rcN+) added support
    for passthrough of LBR registers to the guest systems. It should probably
    fix the issue described in PSBM-75679 but I haven't tried that.
    Details: https://lore.kernel.org/kvm/20210201051039.255478-1-like.xu@linux.intel.com
    
    That patchset has more prerequisites and some follow-up fixes. It is likely
    it has not been tested very thoroughly in production yet, esp. with Windows
    guests. The kernels 4.18.0-240.* from RHEL8 do not have it yet, by the way.
    I'd stick with the VZ-specific patches for now, they are less invasive.
    
    Signed-off-by: Evgenii Shatokhin <eshatokhin at virtuozzo.com>
    
    =====================
    Patchset description:
    
    KVM: x86/vPMU: ignore access to LBR-related MSRs (PSBM-75679)
    
    This is the part of patchset from PSBM-75679 which is not present in
    mainline or RHEL kernels.
    
    I have adapted it to VZ8, added handling of new CPU models and simplified
    the code a bit more.
    
    Apart from that the patches have been renamed. The first two were named
    "perf/x86/intel: make reusable LBR initialization code, part 1/2" and
    "perf/x86/intel: make reusable LBR initialization code, part 2/2" in
    VZ7. This confused the tools like 'git format-patch' which stripped the
    last parts of the names, making them identical.
    
    "ms/" prefix was also removed: the patches did not make it into the
    mainline kernel.
    
    They are probably not needed there: It looks like, the recent mainline kernels
    (5.12-rcN+) added support for passthrough of LBR registers to the guest systems.
    It should probably fix the issue described in PSBM-75679 but I haven't tried
    that.
    Details: https://lore.kernel.org/kvm/20210201051039.255478-1-like.xu@linux.intel.com
    
    That mainline patchset has more prerequisites and some follow-up fixes.
    It is likely, it has not been tested very thoroughly in production yet, esp.
    with Windows guests. The kernels 4.18.0-240.* from RHEL8 do not have it yet,
    by the way.
    I'd stick with the VZ-specific patches for now, they are less invasive.
    
    https://jira.sw.ru/browse/PSBM-75679
    Done in the scope of https://jira.sw.ru/browse/PSBM-127794
    
    Signed-off-by: Evgenii Shatokhin <eshatokhin at virtuozzo.com>
---
 arch/x86/include/asm/kvm_host.h |  3 +++
 arch/x86/kvm/vmx/pmu_intel.c    | 34 ++++++++++++++++++++++++++++++++++
 2 files changed, 37 insertions(+)

diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 66b04b85ec51..65df58b61830 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -37,6 +37,7 @@
 #include <asm/kvm_page_track.h>
 #include <asm/kvm_vcpu_regs.h>
 #include <asm/hyperv-tlfs.h>
+#include <asm/perf_event.h>
 
 #define __KVM_HAVE_ARCH_VCPU_DEBUGFS
 
@@ -506,6 +507,8 @@ struct kvm_pmu {
 	 * redundant check before cleanup if guest don't use vPMU at all.
 	 */
 	u8 event_count;
+
+	struct x86_pmu_lbr lbr;
 };
 
 struct kvm_pmu_ops;
diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c
index e1a303fefc16..0df709f5b856 100644
--- a/arch/x86/kvm/vmx/pmu_intel.c
+++ b/arch/x86/kvm/vmx/pmu_intel.c
@@ -12,6 +12,7 @@
 #include <linux/kvm_host.h>
 #include <linux/perf_event.h>
 #include <asm/perf_event.h>
+#include <asm/cpu.h>
 #include "x86.h"
 #include "cpuid.h"
 #include "lapic.h"
@@ -150,6 +151,24 @@ static struct kvm_pmc *intel_rdpmc_ecx_to_pmc(struct kvm_vcpu *vcpu,
 	return &counters[array_index_nospec(idx, num_counters)];
 }
 
+static bool intel_is_lbr_msr(struct kvm_vcpu *vcpu, u32 msr)
+{
+	struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
+	struct x86_pmu_lbr *lbr = &pmu->lbr;
+
+	if (!lbr->nr)
+		return false;
+
+	if (msr == lbr->tos)
+		return true;
+	if (msr >= lbr->from && msr < lbr->from + lbr->nr)
+		return true;
+	if (msr >= lbr->to && msr < lbr->to + lbr->nr)
+		return true;
+
+	return false;
+}
+
 static bool intel_is_valid_msr(struct kvm_vcpu *vcpu, u32 msr)
 {
 	struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
@@ -163,6 +182,10 @@ static bool intel_is_valid_msr(struct kvm_vcpu *vcpu, u32 msr)
 		ret = pmu->version > 1;
 		break;
 	default:
+		if (intel_is_lbr_msr(vcpu, msr)) {
+			ret = true;
+			break;
+		}
 		ret = get_gp_pmc(pmu, msr, MSR_IA32_PERFCTR0) ||
 			get_gp_pmc(pmu, msr, MSR_P6_EVNTSEL0) ||
 			get_fixed_pmc(pmu, msr);
@@ -204,6 +227,10 @@ static int intel_pmu_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
 		msr_info->data = pmu->global_ovf_ctrl;
 		return 0;
 	default:
+		if (intel_is_lbr_msr(vcpu, msr)) {
+			msr_info->data = 0;
+			return 0;
+		}
 		if ((pmc = get_gp_pmc(pmu, msr, MSR_IA32_PERFCTR0))) {
 			u64 val = pmc_read_counter(pmc);
 			msr_info->data =
@@ -262,6 +289,8 @@ static int intel_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
 		}
 		break;
 	default:
+		if (intel_is_lbr_msr(vcpu, msr))
+			return 0;
 		if ((pmc = get_gp_pmc(pmu, msr, MSR_IA32_PERFCTR0))) {
 			if (!msr_info->host_initiated)
 				data = (s64)(s32)data;
@@ -354,6 +383,11 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu)
 		INTEL_PMC_MAX_GENERIC, pmu->nr_arch_fixed_counters);
 
 	nested_vmx_pmu_entry_exit_ctls_update(vcpu);
+
+	entry = kvm_find_cpuid_entry(vcpu, 1, 0);
+	if (entry)
+		intel_pmu_lbr_fill(&pmu->lbr,
+			x86_family(entry->eax), x86_model(entry->eax));
 }
 
 static void intel_pmu_init(struct kvm_vcpu *vcpu)


More information about the Devel mailing list