On Mon, 20 Jul 2026 at 06:44, Akihiko Odaki <[email protected]> wrote: > > MDCR_EL2.HPMN changes which counters are reserved for EL2 and thus which > enable control, event filter, and overflow width apply. HPMD changes EL2 > filtering, while HLP changes the overflow width and sample period. > > The existing guest MDCR_EL2 handling only requests a PMU reload for > HPME. Reloading enables or disables existing perf events, but does not > rebuild events whose attributes have become stale. Generic userspace > writes through KVM_SET_ONE_REG do not request a reload at all. > > Route guest writes, userspace writes, and reset through a common helper. > Mark the vCPU's perf events for recreation when HPMN, HPMD, or HLP > changes. Consume the marker on the vCPU thread before reprogramming the > counters, which also preserves counter values and avoids creating events > on an arbitrary ioctl thread. > > Use a setter-only accessor so register restore gains these side effects > without changing generic reads or rejecting register values. > > Fixes: fe827f916662 ("KVM: arm64: nv: Honor MDCR_EL2.HPME") > Fixes: 8a34979030f6 ("KVM: arm64: nv: Apply EL2 event filtering when in hyp > context") > Fixes: 16535d55e91f ("KVM: arm64: nv: Honor MDCR_EL2.HLP") > Assisted-by: Codex:gpt-5.6-sol > Signed-off-by: Akihiko Odaki <[email protected]>
Sashiko's comment seems to be worth pursuing, but that's a separate fix. Reviewed-by: Fuad Tabba <[email protected]> Cheers, /fuad > --- > arch/arm64/kvm/pmu-emul.c | 27 +++++++++++++++++++++++++++ > arch/arm64/kvm/sys_regs.c | 31 +++++++++++++++++++++---------- > include/kvm/arm_pmu.h | 3 +++ > 3 files changed, 51 insertions(+), 10 deletions(-) > > diff --git a/arch/arm64/kvm/pmu-emul.c b/arch/arm64/kvm/pmu-emul.c > index 5281d37634a0..9ac5f307ea31 100644 > --- a/arch/arm64/kvm/pmu-emul.c > +++ b/arch/arm64/kvm/pmu-emul.c > @@ -17,6 +17,9 @@ > > #define PERF_ATTR_CFG1_COUNTER_64BIT BIT(0) > > +#define MDCR_EL2_PMU_RECREATE_MASK (MDCR_EL2_HPMN | MDCR_EL2_HPMD | \ > + MDCR_EL2_HLP) > + > static LIST_HEAD(arm_pmus); > static DEFINE_MUTEX(arm_pmus_lock); > > @@ -606,6 +609,21 @@ void kvm_pmu_handle_pmcr(struct kvm_vcpu *vcpu, u64 val) > } > } > > +void kvm_pmu_handle_mdcr(struct kvm_vcpu *vcpu, u64 old, u64 val) > +{ > + u64 changed = old ^ val; > + > + /* > + * HPMN determines which counters HPMD and HLP apply to. Changes to > + * these fields require new perf event filters and sample periods. > + */ > + if (changed & MDCR_EL2_PMU_RECREATE_MASK) > + vcpu->arch.pmu.events_need_recreate = true; > + > + if (changed & (MDCR_EL2_HPME | MDCR_EL2_PMU_RECREATE_MASK)) > + kvm_make_request(KVM_REQ_RELOAD_PMU, vcpu); > +} > + > static bool kvm_pmu_counter_is_enabled(struct kvm_pmc *pmc) > { > struct kvm_vcpu *vcpu = kvm_pmc_to_vcpu(pmc); > @@ -899,7 +917,16 @@ u64 kvm_pmu_get_pmceid(struct kvm_vcpu *vcpu, bool > pmceid1) > > void kvm_vcpu_reload_pmu(struct kvm_vcpu *vcpu) > { > + struct kvm_pmu *pmu = &vcpu->arch.pmu; > u64 mask = kvm_pmu_implemented_counter_mask(vcpu); > + int i; > + > + if (pmu->events_need_recreate) { > + for (i = 0; i < KVM_ARMV8_PMU_MAX_COUNTERS; i++) > + kvm_pmu_stop_counter(kvm_vcpu_idx_to_pmc(vcpu, i)); > + > + pmu->events_need_recreate = false; > + } > > __vcpu_rmw_sys_reg(vcpu, PMOVSSET_EL0, &=, mask); > __vcpu_rmw_sys_reg(vcpu, PMINTENSET_EL1, &=, mask); > diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c > index 5d5c579d4579..9e905d9be9f6 100644 > --- a/arch/arm64/kvm/sys_regs.c > +++ b/arch/arm64/kvm/sys_regs.c > @@ -3020,17 +3020,22 @@ static bool access_mdcr(struct kvm_vcpu *vcpu, > } > > __vcpu_assign_sys_reg(vcpu, MDCR_EL2, val); > - > - /* > - * Request a reload of the PMU to enable/disable the counters > - * affected by HPME. > - */ > - if ((old ^ val) & MDCR_EL2_HPME) > - kvm_make_request(KVM_REQ_RELOAD_PMU, vcpu); > + kvm_pmu_handle_mdcr(vcpu, old, val); > > return true; > } > > +static int set_mdcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd, > + u64 val) > +{ > + u64 old = __vcpu_sys_reg(vcpu, MDCR_EL2); > + > + __vcpu_assign_sys_reg(vcpu, MDCR_EL2, val); > + kvm_pmu_handle_mdcr(vcpu, old, val); > + > + return 0; > +} > + > static bool access_ras(struct kvm_vcpu *vcpu, > struct sys_reg_params *p, > const struct sys_reg_desc *r) > @@ -3177,8 +3182,13 @@ static int set_imp_id_reg(struct kvm_vcpu *vcpu, const > struct sys_reg_desc *r, > > static u64 reset_mdcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r) > { > - __vcpu_assign_sys_reg(vcpu, r->reg, vcpu->kvm->arch.nr_pmu_counters); > - return vcpu->kvm->arch.nr_pmu_counters; > + u64 old = __vcpu_sys_reg(vcpu, r->reg); > + u64 val = vcpu->kvm->arch.nr_pmu_counters; > + > + __vcpu_assign_sys_reg(vcpu, r->reg, val); > + kvm_pmu_handle_mdcr(vcpu, old, val); > + > + return val; > } > > /* > @@ -3741,7 +3751,8 @@ static const struct sys_reg_desc sys_reg_descs[] = { > EL2_REG_FILTERED(SCTLR2_EL2, access_vm_reg, reset_val, 0, > sctlr2_el2_visibility), > EL2_REG_VNCR(HCR_EL2, reset_hcr, 0), > - EL2_REG(MDCR_EL2, access_mdcr, reset_mdcr, 0), > + SYS_REG_USER_FILTER(MDCR_EL2, access_mdcr, reset_mdcr, 0, > + NULL, set_mdcr, el2_visibility), > EL2_REG(CPTR_EL2, access_rw, reset_val, CPTR_NVHE_EL2_RES1), > EL2_REG_VNCR(HSTR_EL2, reset_val, 0), > EL2_REG_VNCR_FILT(HFGRTR_EL2, fgt_visibility), > diff --git a/include/kvm/arm_pmu.h b/include/kvm/arm_pmu.h > index b5e5942204fc..1971f8794c9d 100644 > --- a/include/kvm/arm_pmu.h > +++ b/include/kvm/arm_pmu.h > @@ -32,6 +32,7 @@ struct kvm_pmu { > struct kvm_pmc pmc[KVM_ARMV8_PMU_MAX_COUNTERS]; > int irq_num; > bool created; > + bool events_need_recreate; > }; > > struct arm_pmu_entry { > @@ -56,6 +57,7 @@ bool kvm_pmu_should_notify_user(struct kvm_vcpu *vcpu); > bool kvm_pmu_update_run(struct kvm_vcpu *vcpu); > void kvm_pmu_software_increment(struct kvm_vcpu *vcpu, u64 val); > void kvm_pmu_handle_pmcr(struct kvm_vcpu *vcpu, u64 val); > +void kvm_pmu_handle_mdcr(struct kvm_vcpu *vcpu, u64 old, u64 val); > void kvm_pmu_set_counter_event_type(struct kvm_vcpu *vcpu, u64 data, > u64 select_idx); > void kvm_vcpu_reload_pmu(struct kvm_vcpu *vcpu); > @@ -133,6 +135,7 @@ static inline bool kvm_pmu_should_notify_user(struct > kvm_vcpu *vcpu) > static inline bool kvm_pmu_update_run(struct kvm_vcpu *vcpu) { return false; > } > static inline void kvm_pmu_software_increment(struct kvm_vcpu *vcpu, u64 > val) {} > static inline void kvm_pmu_handle_pmcr(struct kvm_vcpu *vcpu, u64 val) {} > +static inline void kvm_pmu_handle_mdcr(struct kvm_vcpu *vcpu, u64 old, u64 > val) {} > static inline void kvm_pmu_set_counter_event_type(struct kvm_vcpu *vcpu, > u64 data, u64 select_idx) {} > static inline int kvm_arm_pmu_v3_set_attr(struct kvm_vcpu *vcpu, > > -- > 2.55.0 > >

