[PATCH v11 10/21] KVM: arm64: PMU: Recreate events after PMCR_EL0 changes
From: Akihiko Odaki
Date: Sun Sep 20 2026 - 07:19:47 EST
PMCR_EL0.LC and PMCR_EL0.LP control whether cycle and event counters
overflow at 32 or 64 bits. They therefore determine the sample period
of each backing perf event.
Guest and userspace writes do not recreate existing events. Changing
either bit after an event has been created leaves its old sample period
in place, resulting in an incorrect overflow point.
Request event recreation when either bit changes. Preserve the existing
reload behavior for PMCR_EL0.E and other userspace writes.
Fixes: c82d28cbf1d4 ("KVM: arm64: PMU: Distinguish between 64bit counter and 64bit overflow")
Fixes: 11af4c37165e ("KVM: arm64: PMU: Implement PMUv3p5 long counter support")
Closes: https://sashiko.dev/#/patchset/20260720-hybrid-v9-0-2b713ca1b5dc@xxxxxxxxxxxxxxxxxxxxxx?part=6
Assisted-by: Codex:gpt-5.6-sol
Signed-off-by: Akihiko Odaki <odaki@xxxxxxxxxxxxxxxxxxxxxx>
---
arch/arm64/kvm/pmu-emul.c | 18 ++++++++++++++----
arch/arm64/kvm/sys_regs.c | 3 ++-
include/kvm/arm_pmu.h | 2 ++
3 files changed, 18 insertions(+), 5 deletions(-)
diff --git a/arch/arm64/kvm/pmu-emul.c b/arch/arm64/kvm/pmu-emul.c
index a8449e533070..86a02fd4cfb8 100644
--- a/arch/arm64/kvm/pmu-emul.c
+++ b/arch/arm64/kvm/pmu-emul.c
@@ -591,6 +591,17 @@ void kvm_pmu_request_recreate(struct kvm_vcpu *vcpu)
kvm_make_request(KVM_REQ_RELOAD_PMU, vcpu);
}
+void kvm_pmu_apply_pmcr(struct kvm_vcpu *vcpu, u64 old, u64 val, bool force_reload)
+{
+ u64 changed = old ^ val;
+
+ /* Reload the PMU if the write affects the backing perf events. */
+ if (changed & (ARMV8_PMU_PMCR_LC | ARMV8_PMU_PMCR_LP))
+ kvm_pmu_request_recreate(vcpu);
+ else if (force_reload || (changed & ARMV8_PMU_PMCR_E))
+ kvm_make_request(KVM_REQ_RELOAD_PMU, vcpu);
+}
+
/**
* kvm_pmu_handle_pmcr - handle PMCR register
* @vcpu: The vcpu pointer
@@ -598,19 +609,18 @@ void kvm_pmu_request_recreate(struct kvm_vcpu *vcpu)
*/
void kvm_pmu_handle_pmcr(struct kvm_vcpu *vcpu, u64 val)
{
+ u64 old = __vcpu_sys_reg(vcpu, PMCR_EL0);
int i;
/* Fixup PMCR_EL0 to reconcile the PMU version and the LP bit */
if (!kvm_has_feat(vcpu->kvm, ID_AA64DFR0_EL1, PMUVer, V3P5))
val &= ~ARMV8_PMU_PMCR_LP;
- /* Request a reload of the PMU to enable/disable affected counters */
- if ((__vcpu_sys_reg(vcpu, PMCR_EL0) ^ val) & ARMV8_PMU_PMCR_E)
- kvm_make_request(KVM_REQ_RELOAD_PMU, vcpu);
-
/* The reset bits don't indicate any state, and shouldn't be saved. */
__vcpu_assign_sys_reg(vcpu, PMCR_EL0, (val & ~(ARMV8_PMU_PMCR_C | ARMV8_PMU_PMCR_P)));
+ kvm_pmu_apply_pmcr(vcpu, old, __vcpu_sys_reg(vcpu, PMCR_EL0), false);
+
if (val & ARMV8_PMU_PMCR_C)
kvm_pmu_set_counter_value(vcpu, ARMV8_PMU_CYCLE_IDX, 0);
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 7dd2cafce247..6786311e9b7d 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -1517,6 +1517,7 @@ static int set_pmcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r,
{
u8 new_n = FIELD_GET(ARMV8_PMU_PMCR_N, val);
struct kvm *kvm = vcpu->kvm;
+ u64 old = __vcpu_sys_reg(vcpu, r->reg);
mutex_lock(&kvm->arch.config_lock);
@@ -1549,7 +1550,7 @@ static int set_pmcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r,
val |= ARMV8_PMU_PMCR_LC;
__vcpu_assign_sys_reg(vcpu, r->reg, val);
- kvm_make_request(KVM_REQ_RELOAD_PMU, vcpu);
+ kvm_pmu_apply_pmcr(vcpu, old, val, true);
return 0;
}
diff --git a/include/kvm/arm_pmu.h b/include/kvm/arm_pmu.h
index 2fe73ad50e4e..fca00f7f4c0a 100644
--- a/include/kvm/arm_pmu.h
+++ b/include/kvm/arm_pmu.h
@@ -58,6 +58,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_request_recreate(struct kvm_vcpu *vcpu);
+void kvm_pmu_apply_pmcr(struct kvm_vcpu *vcpu, u64 old, u64 val, bool force_reload);
void kvm_pmu_handle_pmcr(struct kvm_vcpu *vcpu, u64 val);
void kvm_pmu_apply_mdcr(struct kvm_vcpu *vcpu, u64 old, u64 val);
void kvm_pmu_set_counter_event_type(struct kvm_vcpu *vcpu, u64 data,
@@ -141,6 +142,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_request_recreate(struct kvm_vcpu *vcpu) {}
+static inline void kvm_pmu_apply_pmcr(struct kvm_vcpu *vcpu, u64 old, u64 val, bool force_reload) {}
static inline void kvm_pmu_handle_pmcr(struct kvm_vcpu *vcpu, u64 val) {}
static inline void kvm_pmu_apply_mdcr(struct kvm_vcpu *vcpu, u64 old, u64 val) {}
static inline void kvm_pmu_set_counter_event_type(struct kvm_vcpu *vcpu,
--
2.55.0