Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

KVM: arm/arm64: PMU: Fix overflow interrupt injection

kvm_pmu_overflow_set() is called from perf's interrupt handler,
making the call of kvm_vgic_inject_irq() from it introduced with
"KVM: arm/arm64: PMU: remove request-less vcpu kick" a really bad
idea, as it's quite easy to try and retake a lock that the
interrupted context is already holding. The fix is to use a vcpu
kick, leaving the interrupt injection to kvm_pmu_sync_hwstate(),
like it was doing before the refactoring. We don't just revert,
though, because before the kick was request-less, leaving the vcpu
exposed to the request-less vcpu kick race, and also because the
kick was used unnecessarily from register access handlers.

Reviewed-by: Christoffer Dall <cdall@linaro.org>
Signed-off-by: Andrew Jones <drjones@redhat.com>
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>

authored by

Andrew Jones and committed by
Marc Zyngier
d9f89b4e 79962a5c

+16 -31
+1 -1
arch/arm64/kvm/sys_regs.c
··· 764 764 if (p->is_write) { 765 765 if (r->CRm & 0x2) 766 766 /* accessing PMOVSSET_EL0 */ 767 - kvm_pmu_overflow_set(vcpu, p->regval & mask); 767 + vcpu_sys_reg(vcpu, PMOVSSET_EL0) |= (p->regval & mask); 768 768 else 769 769 /* accessing PMOVSCLR_EL0 */ 770 770 vcpu_sys_reg(vcpu, PMOVSSET_EL0) &= ~(p->regval & mask);
-2
include/kvm/arm_pmu.h
··· 48 48 void kvm_pmu_vcpu_destroy(struct kvm_vcpu *vcpu); 49 49 void kvm_pmu_disable_counter(struct kvm_vcpu *vcpu, u64 val); 50 50 void kvm_pmu_enable_counter(struct kvm_vcpu *vcpu, u64 val); 51 - void kvm_pmu_overflow_set(struct kvm_vcpu *vcpu, u64 val); 52 51 void kvm_pmu_flush_hwstate(struct kvm_vcpu *vcpu); 53 52 void kvm_pmu_sync_hwstate(struct kvm_vcpu *vcpu); 54 53 bool kvm_pmu_should_notify_user(struct kvm_vcpu *vcpu); ··· 85 86 static inline void kvm_pmu_vcpu_destroy(struct kvm_vcpu *vcpu) {} 86 87 static inline void kvm_pmu_disable_counter(struct kvm_vcpu *vcpu, u64 val) {} 87 88 static inline void kvm_pmu_enable_counter(struct kvm_vcpu *vcpu, u64 val) {} 88 - static inline void kvm_pmu_overflow_set(struct kvm_vcpu *vcpu, u64 val) {} 89 89 static inline void kvm_pmu_flush_hwstate(struct kvm_vcpu *vcpu) {} 90 90 static inline void kvm_pmu_sync_hwstate(struct kvm_vcpu *vcpu) {} 91 91 static inline bool kvm_pmu_should_notify_user(struct kvm_vcpu *vcpu)
+15 -28
virt/kvm/arm/pmu.c
··· 203 203 return reg; 204 204 } 205 205 206 - static void kvm_pmu_check_overflow(struct kvm_vcpu *vcpu) 206 + static void kvm_pmu_update_state(struct kvm_vcpu *vcpu) 207 207 { 208 208 struct kvm_pmu *pmu = &vcpu->arch.pmu; 209 - bool overflow = !!kvm_pmu_overflow_status(vcpu); 209 + bool overflow; 210 210 211 + if (!kvm_arm_pmu_v3_ready(vcpu)) 212 + return; 213 + 214 + overflow = !!kvm_pmu_overflow_status(vcpu); 211 215 if (pmu->irq_level == overflow) 212 216 return; 213 217 ··· 219 215 220 216 if (likely(irqchip_in_kernel(vcpu->kvm))) { 221 217 int ret = kvm_vgic_inject_irq(vcpu->kvm, vcpu->vcpu_id, 222 - pmu->irq_num, overflow, 223 - &vcpu->arch.pmu); 218 + pmu->irq_num, overflow, pmu); 224 219 WARN_ON(ret); 225 220 } 226 - } 227 - 228 - /** 229 - * kvm_pmu_overflow_set - set PMU overflow interrupt 230 - * @vcpu: The vcpu pointer 231 - * @val: the value guest writes to PMOVSSET register 232 - */ 233 - void kvm_pmu_overflow_set(struct kvm_vcpu *vcpu, u64 val) 234 - { 235 - if (val == 0) 236 - return; 237 - 238 - vcpu_sys_reg(vcpu, PMOVSSET_EL0) |= val; 239 - kvm_pmu_check_overflow(vcpu); 240 - } 241 - 242 - static void kvm_pmu_update_state(struct kvm_vcpu *vcpu) 243 - { 244 - if (!kvm_arm_pmu_v3_ready(vcpu)) 245 - return; 246 - kvm_pmu_check_overflow(vcpu); 247 221 } 248 222 249 223 bool kvm_pmu_should_notify_user(struct kvm_vcpu *vcpu) ··· 285 303 } 286 304 287 305 /** 288 - * When perf event overflows, call kvm_pmu_overflow_set to set overflow status. 306 + * When the perf event overflows, set the overflow status and inform the vcpu. 289 307 */ 290 308 static void kvm_pmu_perf_overflow(struct perf_event *perf_event, 291 309 struct perf_sample_data *data, ··· 295 313 struct kvm_vcpu *vcpu = kvm_pmc_to_vcpu(pmc); 296 314 int idx = pmc->idx; 297 315 298 - kvm_pmu_overflow_set(vcpu, BIT(idx)); 316 + vcpu_sys_reg(vcpu, PMOVSSET_EL0) |= BIT(idx); 317 + 318 + if (kvm_pmu_overflow_status(vcpu)) { 319 + kvm_make_request(KVM_REQ_IRQ_PENDING, vcpu); 320 + kvm_vcpu_kick(vcpu); 321 + } 299 322 } 300 323 301 324 /** ··· 328 341 reg = lower_32_bits(reg); 329 342 vcpu_sys_reg(vcpu, PMEVCNTR0_EL0 + i) = reg; 330 343 if (!reg) 331 - kvm_pmu_overflow_set(vcpu, BIT(i)); 344 + vcpu_sys_reg(vcpu, PMOVSSET_EL0) |= BIT(i); 332 345 } 333 346 } 334 347 }