[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