Message ID | 20250324173121.1275209-28-mizhang@google.com |
---|---|
State | New |
Headers | show |
Series | [v4,01/38] perf: Support get/put mediated PMU interfaces | expand |
On Fri, May 16, 2025, Dapeng Mi wrote: > On 3/25/2025 1:31 AM, Mingwei Zhang wrote: > > + if (kvm_mediated_pmu_enabled(pmu_to_vcpu(pmu))) { > > + bool allowed = check_pmu_event_filter(pmc); > > + > > + if (pmc_is_gp(pmc)) { > > + if (allowed) > > + pmc->eventsel_hw |= pmc->eventsel & > > + ARCH_PERFMON_EVENTSEL_ENABLE; > > + else > > + pmc->eventsel_hw &= ~ARCH_PERFMON_EVENTSEL_ENABLE; > > + } else { > > + int idx = pmc->idx - KVM_FIXED_PMC_BASE_IDX; > > + > > + if (allowed) > > + pmu->fixed_ctr_ctrl_hw = pmu->fixed_ctr_ctrl; > > Sean, just found there is a potential bug here. The > "pmu->fixed_ctr_ctrl_hw" should not be assigned to "pmu->fixed_ctr_ctrl" > here, otherwise the other filtered fixed counter (not this allowed fixed > counter) could be enabled accidentally. > > diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c > index ba9d336f1d1d..f32e5f66f73b 100644 > --- a/arch/x86/kvm/pmu.c > +++ b/arch/x86/kvm/pmu.c > @@ -473,7 +473,8 @@ static int reprogram_counter(struct kvm_pmc *pmc) > int idx = pmc->idx - KVM_FIXED_PMC_BASE_IDX; > > if (allowed) > - pmu->fixed_ctr_ctrl_hw = pmu->fixed_ctr_ctrl; > + pmu->fixed_ctr_ctrl_hw |= pmu->fixed_ctr_ctrl & > + > intel_fixed_bits_by_idx(idx, 0xf); Hmm, I think that's fine, since pmu->fixed_ctr_ctrl should have changed? But I'd rather play it safe and do (completely untested): if (pmc_is_gp(pmc)) { pmc->eventsel_hw &= ~ARCH_PERFMON_EVENTSEL_ENABLE; if (allowed) pmc->eventsel_hw |= pmc->eventsel & ARCH_PERFMON_EVENTSEL_ENABLE; } else { u64 mask = intel_fixed_bits_by_idx(pmc->idx - KVM_FIXED_PMC_BASE_IDX, 0xf); pmu->fixed_ctr_ctrl_hw &= ~mask; if (allowed) pmu->fixed_ctr_ctrl_hw |= pmu->fixed_ctr_ctrl & mask; }
On 5/17/2025 4:54 AM, Sean Christopherson wrote: > On Fri, May 16, 2025, Dapeng Mi wrote: >> On 3/25/2025 1:31 AM, Mingwei Zhang wrote: >>> + if (kvm_mediated_pmu_enabled(pmu_to_vcpu(pmu))) { >>> + bool allowed = check_pmu_event_filter(pmc); >>> + >>> + if (pmc_is_gp(pmc)) { >>> + if (allowed) >>> + pmc->eventsel_hw |= pmc->eventsel & >>> + ARCH_PERFMON_EVENTSEL_ENABLE; >>> + else >>> + pmc->eventsel_hw &= ~ARCH_PERFMON_EVENTSEL_ENABLE; >>> + } else { >>> + int idx = pmc->idx - KVM_FIXED_PMC_BASE_IDX; >>> + >>> + if (allowed) >>> + pmu->fixed_ctr_ctrl_hw = pmu->fixed_ctr_ctrl; >> Sean, just found there is a potential bug here. The >> "pmu->fixed_ctr_ctrl_hw" should not be assigned to "pmu->fixed_ctr_ctrl" >> here, otherwise the other filtered fixed counter (not this allowed fixed >> counter) could be enabled accidentally. >> >> diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c >> index ba9d336f1d1d..f32e5f66f73b 100644 >> --- a/arch/x86/kvm/pmu.c >> +++ b/arch/x86/kvm/pmu.c >> @@ -473,7 +473,8 @@ static int reprogram_counter(struct kvm_pmc *pmc) >> int idx = pmc->idx - KVM_FIXED_PMC_BASE_IDX; >> >> if (allowed) >> - pmu->fixed_ctr_ctrl_hw = pmu->fixed_ctr_ctrl; >> + pmu->fixed_ctr_ctrl_hw |= pmu->fixed_ctr_ctrl & >> + >> intel_fixed_bits_by_idx(idx, 0xf); > Hmm, I think that's fine, since pmu->fixed_ctr_ctrl should have changed? But I'd Not exactly. Assume guest enables the fixed counter 0 and 1, then pmu->fixed_ctr_ctrl would be 0xbb. At the first time, user disables the fixed counter 0 by KVM event filter, then pmu->fixed_ctr_ctrl_hw would be 0xb0, secondly user disables the fixed counter 1 as well, so pmu->fixed_ctr_ctrl_hw is 0x0. At the third time, user re-enables fixed counter 1, so pmu->fixed_ctr_ctrl_hw is expected to be 0xb0, but it's not actually. Although the 1st calling (for fixed counter 0) to reprogram_counter() would disable it, but the 2nd calling (for fixed counter 1) to reprogram_counter() accidentally enables it (pmu->fixed_ctr_ctrl_hw becomes 0xbb eventually). This is incorrect. > rather play it safe and do (completely untested): > > if (pmc_is_gp(pmc)) { > pmc->eventsel_hw &= ~ARCH_PERFMON_EVENTSEL_ENABLE; > if (allowed) > pmc->eventsel_hw |= pmc->eventsel & > ARCH_PERFMON_EVENTSEL_ENABLE; > } else { > u64 mask = intel_fixed_bits_by_idx(pmc->idx - KVM_FIXED_PMC_BASE_IDX, 0xf); > > pmu->fixed_ctr_ctrl_hw &= ~mask; > if (allowed) > pmu->fixed_ctr_ctrl_hw |= pmu->fixed_ctr_ctrl & mask; > } The code looks good.
diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c index 63143eeb5c44..e9100dc49fdc 100644 --- a/arch/x86/kvm/pmu.c +++ b/arch/x86/kvm/pmu.c @@ -305,6 +305,11 @@ static void pmc_update_sample_period(struct kvm_pmc *pmc) void pmc_write_counter(struct kvm_pmc *pmc, u64 val) { + if (kvm_mediated_pmu_enabled(pmc->vcpu)) { + pmc->counter = val & pmc_bitmask(pmc); + return; + } + /* * Drop any unconsumed accumulated counts, the WRMSR is a write, not a * read-modify-write. Adjust the counter value so that its value is @@ -455,6 +460,28 @@ static int reprogram_counter(struct kvm_pmc *pmc) bool emulate_overflow; u8 fixed_ctr_ctrl; + if (kvm_mediated_pmu_enabled(pmu_to_vcpu(pmu))) { + bool allowed = check_pmu_event_filter(pmc); + + if (pmc_is_gp(pmc)) { + if (allowed) + pmc->eventsel_hw |= pmc->eventsel & + ARCH_PERFMON_EVENTSEL_ENABLE; + else + pmc->eventsel_hw &= ~ARCH_PERFMON_EVENTSEL_ENABLE; + } else { + int idx = pmc->idx - KVM_FIXED_PMC_BASE_IDX; + + if (allowed) + pmu->fixed_ctr_ctrl_hw = pmu->fixed_ctr_ctrl; + else + pmu->fixed_ctr_ctrl_hw &= + ~intel_fixed_bits_by_idx(idx, 0xf); + } + + return 0; + } + emulate_overflow = pmc_pause_counter(pmc); if (!pmc_event_is_allowed(pmc)) diff --git a/arch/x86/kvm/pmu.h b/arch/x86/kvm/pmu.h index 509c995b7871..6289f523d893 100644 --- a/arch/x86/kvm/pmu.h +++ b/arch/x86/kvm/pmu.h @@ -113,6 +113,9 @@ static inline u64 pmc_read_counter(struct kvm_pmc *pmc) { u64 counter, enabled, running; + if (kvm_mediated_pmu_enabled(pmc->vcpu)) + return pmc->counter & pmc_bitmask(pmc); + counter = pmc->counter + pmc->emulated_counter; if (pmc->perf_event && !pmc->is_paused)