Skip to content

Commit b1f778a

Browse files
author
Marc Zyngier
committed
KVM: arm64: pmu: Resync EL0 state on counter rotation
Huang Shijie reports that, when profiling a guest from the host with a number of events that exceeds the number of available counters, the reported counts are wildly inaccurate. Without the counter oversubscription, the reported counts are correct. Their investigation indicates that upon counter rotation (which takes place on the back of a timer interrupt), we fail to re-apply the guest EL0 enabling, leading to the counting of host events instead of guest events. In order to solve this, add yet another hook between the host PMU driver and KVM, re-applying the guest EL0 configuration if the right conditions apply (the host is VHE, we are in interrupt context, and we interrupted a running vcpu). This triggers a new vcpu request which will apply the correct configuration on guest reentry. With this, we have the correct counts, even when the counters are oversubscribed. Reported-by: Huang Shijie <shijie@os.amperecomputing.com> Suggested-by: Oliver Upton <oliver.upton@linux.dev> Tested_by: Huang Shijie <shijie@os.amperecomputing.com> Signed-off-by: Marc Zyngier <maz@kernel.org> Cc: Leo Yan <leo.yan@linaro.org> Cc: Mark Rutland <mark.rutland@arm.com> Cc: Will Deacon <will@kernel.org> Link: https://lore.kernel.org/r/20230809013953.7692-1-shijie@os.amperecomputing.com Acked-by: Mark Rutland <mark.rutland@arm.com> Link: https://lore.kernel.org/r/20230820090108.177817-1-maz@kernel.org
1 parent 64b8100 commit b1f778a

File tree

6 files changed

+28
-0
lines changed

6 files changed

+28
-0
lines changed

arch/arm/include/asm/arm_pmuv3.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,8 @@ static inline bool kvm_set_pmuserenr(u64 val)
227227
return false;
228228
}
229229

230+
static inline void kvm_vcpu_pmu_resync_el0(void) {}
231+
230232
/* PMU Version in DFR Register */
231233
#define ARMV8_PMU_DFR_VER_NI 0
232234
#define ARMV8_PMU_DFR_VER_V3P4 0x5

arch/arm64/include/asm/kvm_host.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
#define KVM_REQ_RELOAD_GICv4 KVM_ARCH_REQ(4)
5050
#define KVM_REQ_RELOAD_PMU KVM_ARCH_REQ(5)
5151
#define KVM_REQ_SUSPEND KVM_ARCH_REQ(6)
52+
#define KVM_REQ_RESYNC_PMU_EL0 KVM_ARCH_REQ(7)
5253

5354
#define KVM_DIRTY_LOG_MANUAL_CAPS (KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE | \
5455
KVM_DIRTY_LOG_INITIALLY_SET)

arch/arm64/kvm/arm.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -803,6 +803,9 @@ static int check_vcpu_requests(struct kvm_vcpu *vcpu)
803803
kvm_pmu_handle_pmcr(vcpu,
804804
__vcpu_sys_reg(vcpu, PMCR_EL0));
805805

806+
if (kvm_check_request(KVM_REQ_RESYNC_PMU_EL0, vcpu))
807+
kvm_vcpu_pmu_restore_guest(vcpu);
808+
806809
if (kvm_check_request(KVM_REQ_SUSPEND, vcpu))
807810
return kvm_vcpu_suspend(vcpu);
808811

arch/arm64/kvm/pmu.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,3 +236,21 @@ bool kvm_set_pmuserenr(u64 val)
236236
ctxt_sys_reg(hctxt, PMUSERENR_EL0) = val;
237237
return true;
238238
}
239+
240+
/*
241+
* If we interrupted the guest to update the host PMU context, make
242+
* sure we re-apply the guest EL0 state.
243+
*/
244+
void kvm_vcpu_pmu_resync_el0(void)
245+
{
246+
struct kvm_vcpu *vcpu;
247+
248+
if (!has_vhe() || !in_interrupt())
249+
return;
250+
251+
vcpu = kvm_get_running_vcpu();
252+
if (!vcpu)
253+
return;
254+
255+
kvm_make_request(KVM_REQ_RESYNC_PMU_EL0, vcpu);
256+
}

drivers/perf/arm_pmuv3.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -772,6 +772,8 @@ static void armv8pmu_start(struct arm_pmu *cpu_pmu)
772772

773773
/* Enable all counters */
774774
armv8pmu_pmcr_write(armv8pmu_pmcr_read() | ARMV8_PMU_PMCR_E);
775+
776+
kvm_vcpu_pmu_resync_el0();
775777
}
776778

777779
static void armv8pmu_stop(struct arm_pmu *cpu_pmu)

include/kvm/arm_pmu.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ int kvm_arm_pmu_v3_enable(struct kvm_vcpu *vcpu);
7474
struct kvm_pmu_events *kvm_get_pmu_events(void);
7575
void kvm_vcpu_pmu_restore_guest(struct kvm_vcpu *vcpu);
7676
void kvm_vcpu_pmu_restore_host(struct kvm_vcpu *vcpu);
77+
void kvm_vcpu_pmu_resync_el0(void);
7778

7879
#define kvm_vcpu_has_pmu(vcpu) \
7980
(test_bit(KVM_ARM_VCPU_PMU_V3, (vcpu)->arch.features))
@@ -171,6 +172,7 @@ static inline u8 kvm_arm_pmu_get_pmuver_limit(void)
171172
{
172173
return 0;
173174
}
175+
static inline void kvm_vcpu_pmu_resync_el0(void) {}
174176

175177
#endif
176178

0 commit comments

Comments
 (0)