[RFC PATCH 39/41] KVM: x86/pmu: Implement emulated counter increment for passthrough PMU

From: Xiong Zhang
Date: Fri Jan 26 2024 - 04:48:40 EST


From: Mingwei Zhang <mizhang@xxxxxxxxxx>

Implement emulated counter increment for passthrough PMU under KVM_REQ_PMU.
Defer the counter increment to KVM_REQ_PMU handler because counter
increment requests come from kvm_pmu_trigger_event() which can be triggered
within the KVM_RUN inner loop or outside of the inner loop. This means the
counter increment could happen before or after PMU context switch.

So process counter increment in one place makes the implementation simple.

Signed-off-by: Mingwei Zhang <mizhang@xxxxxxxxxx>
---
arch/x86/include/asm/kvm_host.h | 2 ++
arch/x86/kvm/pmu.c | 52 ++++++++++++++++++++++++++++++++-
arch/x86/kvm/pmu.h | 1 +
arch/x86/kvm/x86.c | 8 +++--
4 files changed, 60 insertions(+), 3 deletions(-)

diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 869de0d81055..9080319751de 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -532,6 +532,7 @@ struct kvm_pmu {
u64 fixed_ctr_ctrl_mask;
u64 global_ctrl;
u64 global_status;
+ u64 synthesized_overflow;
u64 counter_bitmask[2];
u64 global_ctrl_mask;
u64 global_status_mask;
@@ -550,6 +551,7 @@ struct kvm_pmu {
atomic64_t __reprogram_pmi;
};
DECLARE_BITMAP(all_valid_pmc_idx, X86_PMC_IDX_MAX);
+ DECLARE_BITMAP(incremented_pmc_idx, X86_PMC_IDX_MAX);
DECLARE_BITMAP(pmc_in_use, X86_PMC_IDX_MAX);

u64 ds_area;
diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c
index 7b0bac1ac4bf..9e62e96fe48a 100644
--- a/arch/x86/kvm/pmu.c
+++ b/arch/x86/kvm/pmu.c
@@ -449,6 +449,26 @@ static bool kvm_passthrough_pmu_incr_counter(struct kvm_pmc *pmc)
return false;
}

+void kvm_passthrough_pmu_handle_event(struct kvm_vcpu *vcpu)
+{
+ struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
+ int bit;
+
+ for_each_set_bit(bit, pmu->incremented_pmc_idx, X86_PMC_IDX_MAX) {
+ struct kvm_pmc *pmc = static_call(kvm_x86_pmu_pmc_idx_to_pmc)(pmu, bit);
+
+ if (kvm_passthrough_pmu_incr_counter(pmc)) {
+ __set_bit(pmc->idx, (unsigned long *)&pmc_to_pmu(pmc)->synthesized_overflow);
+
+ if (pmc->eventsel & ARCH_PERFMON_EVENTSEL_INT)
+ kvm_make_request(KVM_REQ_PMI, vcpu);
+ }
+ }
+ bitmap_zero(pmu->incremented_pmc_idx, X86_PMC_IDX_MAX);
+ pmu->global_status |= pmu->synthesized_overflow;
+ pmu->synthesized_overflow = 0;
+}
+
void kvm_pmu_handle_event(struct kvm_vcpu *vcpu)
{
struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
@@ -748,7 +768,29 @@ static inline bool cpl_is_matched(struct kvm_pmc *pmc)
return (static_call(kvm_x86_get_cpl)(pmc->vcpu) == 0) ? select_os : select_user;
}

-void kvm_pmu_trigger_event(struct kvm_vcpu *vcpu, u64 perf_hw_id)
+static void __kvm_passthrough_pmu_trigger_event(struct kvm_vcpu *vcpu, u64 perf_hw_id)
+{
+ struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
+ struct kvm_pmc *pmc;
+ int i;
+
+ for_each_set_bit(i, pmu->all_valid_pmc_idx, X86_PMC_IDX_MAX) {
+ pmc = static_call(kvm_x86_pmu_pmc_idx_to_pmc)(pmu, i);
+
+ if (!pmc || !pmc_speculative_in_use(pmc) ||
+ !check_pmu_event_filter(pmc))
+ continue;
+
+ /* Ignore checks for edge detect, pin control, invert and CMASK bits */
+ if (eventsel_match_perf_hw_id(pmc, perf_hw_id) && cpl_is_matched(pmc)) {
+ pmc->emulated_counter += 1;
+ __set_bit(pmc->idx, pmu->incremented_pmc_idx);
+ kvm_make_request(KVM_REQ_PMU, vcpu);
+ }
+ }
+}
+
+static void __kvm_pmu_trigger_event(struct kvm_vcpu *vcpu, u64 perf_hw_id)
{
struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
struct kvm_pmc *pmc;
@@ -765,6 +807,14 @@ void kvm_pmu_trigger_event(struct kvm_vcpu *vcpu, u64 perf_hw_id)
kvm_pmu_incr_counter(pmc);
}
}
+
+void kvm_pmu_trigger_event(struct kvm_vcpu *vcpu, u64 perf_hw_id)
+{
+ if (is_passthrough_pmu_enabled(vcpu))
+ __kvm_passthrough_pmu_trigger_event(vcpu, perf_hw_id);
+ else
+ __kvm_pmu_trigger_event(vcpu, perf_hw_id);
+}
EXPORT_SYMBOL_GPL(kvm_pmu_trigger_event);

static bool is_masked_filter_valid(const struct kvm_x86_pmu_event_filter *filter)
diff --git a/arch/x86/kvm/pmu.h b/arch/x86/kvm/pmu.h
index 6f44fe056368..0fc37a06fe48 100644
--- a/arch/x86/kvm/pmu.h
+++ b/arch/x86/kvm/pmu.h
@@ -277,6 +277,7 @@ static inline bool is_passthrough_pmu_enabled(struct kvm_vcpu *vcpu)

void kvm_pmu_deliver_pmi(struct kvm_vcpu *vcpu);
void kvm_pmu_handle_event(struct kvm_vcpu *vcpu);
+void kvm_passthrough_pmu_handle_event(struct kvm_vcpu *vcpu);
int kvm_pmu_rdpmc(struct kvm_vcpu *vcpu, unsigned pmc, u64 *data);
bool kvm_pmu_is_valid_rdpmc_ecx(struct kvm_vcpu *vcpu, unsigned int idx);
bool kvm_pmu_is_valid_msr(struct kvm_vcpu *vcpu, u32 msr);
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index fe7da1a16c3b..1bbf312cbd73 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -10726,8 +10726,12 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
}
if (kvm_check_request(KVM_REQ_STEAL_UPDATE, vcpu))
record_steal_time(vcpu);
- if (kvm_check_request(KVM_REQ_PMU, vcpu))
- kvm_pmu_handle_event(vcpu);
+ if (kvm_check_request(KVM_REQ_PMU, vcpu)) {
+ if (is_passthrough_pmu_enabled(vcpu))
+ kvm_passthrough_pmu_handle_event(vcpu);
+ else
+ kvm_pmu_handle_event(vcpu);
+ }
if (kvm_check_request(KVM_REQ_PMI, vcpu))
kvm_pmu_deliver_pmi(vcpu);
#ifdef CONFIG_KVM_SMM
--
2.34.1