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/[email protected]?part=6
Assisted-by: Codex:gpt-5.6-sol
Signed-off-by: Akihiko Odaki <[email protected]>
---
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 953255111779..e24ad6c72937 100644
--- a/arch/arm64/kvm/pmu-emul.c
+++ b/arch/arm64/kvm/pmu-emul.c
@@ -577,6 +577,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
@@ -584,19 +595,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 bae1b69a5ab9..2754b743da01 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 f12c916c04d5..29bcc09da93e 100644
--- a/include/kvm/arm_pmu.h
+++ b/include/kvm/arm_pmu.h
@@ -57,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_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,
@@ -139,6 +140,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