Only allow writing to event selector if event is allowed in filter. Since passthrough PMU implementation does the PMU context switch at VM Enter/Exit boudary, even if the value of event selector passes the checking, it cannot be written directly to HW since PMU HW is owned by the host PMU at the moment. Because of that, introduce eventsel_hw to cache that value which will be assigned into HW just before VM entry. Note that regardless of whether an event value is allowed, the value will be cached in pmc->eventsel and guest VM can always read the cached value back. This implementation is consistent with the HW CPU design. Signed-off-by: Mingwei Zhang <mizhang@xxxxxxxxxx> Signed-off-by: Dapeng Mi <dapeng1.mi@xxxxxxxxxxxxxxx> --- arch/x86/include/asm/kvm_host.h | 1 + arch/x86/kvm/pmu.c | 5 ++--- arch/x86/kvm/vmx/pmu_intel.c | 13 ++++++++++++- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 8b4ea9bdcc74..b396000b9440 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -519,6 +519,7 @@ struct kvm_pmc { */ u64 emulated_counter; u64 eventsel; + u64 eventsel_hw; u64 msr_counter; u64 msr_eventsel; struct perf_event *perf_event; diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c index 0f587651a49e..2ad71020a2c0 100644 --- a/arch/x86/kvm/pmu.c +++ b/arch/x86/kvm/pmu.c @@ -1085,10 +1085,9 @@ void kvm_pmu_save_pmu_context(struct kvm_vcpu *vcpu) for (i = 0; i < pmu->nr_arch_gp_counters; i++) { pmc = &pmu->gp_counters[i]; rdmsrl(pmc->msr_counter, pmc->counter); - rdmsrl(pmc->msr_eventsel, pmc->eventsel); if (pmc->counter) wrmsrl(pmc->msr_counter, 0); - if (pmc->eventsel) + if (pmc->eventsel_hw) wrmsrl(pmc->msr_eventsel, 0); } @@ -1118,7 +1117,7 @@ void kvm_pmu_restore_pmu_context(struct kvm_vcpu *vcpu) for (i = 0; i < pmu->nr_arch_gp_counters; i++) { pmc = &pmu->gp_counters[i]; wrmsrl(pmc->msr_counter, pmc->counter); - wrmsrl(pmc->msr_eventsel, pmu->gp_counters[i].eventsel); + wrmsrl(pmc->msr_eventsel, pmu->gp_counters[i].eventsel_hw); } for (i = 0; i < pmu->nr_arch_fixed_counters; i++) { diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c index a23cf9ca224e..e706d107ff28 100644 --- a/arch/x86/kvm/vmx/pmu_intel.c +++ b/arch/x86/kvm/vmx/pmu_intel.c @@ -399,7 +399,18 @@ static int intel_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) if (data & reserved_bits) return 1; - if (data != pmc->eventsel) { + if (is_passthrough_pmu_enabled(vcpu)) { + pmc->eventsel = data; + if (!check_pmu_event_filter(pmc)) { + if (pmc->eventsel_hw & + ARCH_PERFMON_EVENTSEL_ENABLE) { + pmc->eventsel_hw &= ~ARCH_PERFMON_EVENTSEL_ENABLE; + pmc->counter = 0; + } + return 0; + } + pmc->eventsel_hw = data; + } else if (data != pmc->eventsel) { pmc->eventsel = data; kvm_pmu_request_counter_reprogram(pmc); } -- 2.45.0.rc1.225.g2a3ae87e7f-goog