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

Configure Feed

Select the types of activity you want to include in your feed.

KVM: arm64: GICv3: Add SPI tracking to handle asymmetric deactivation

SPIs are specially annpying, as they can be activated on a CPU and
deactivated on another. WHich means that when an SPI is in flight
anywhere, all CPUs need to have their TDIR trap bit set.

This translates into broadcasting an IPI across all CPUs to make sure
they set their trap bit, The number of in-flight SPIs is kept in
an atomic variable so that CPUs can turn the trap bit off as soon
as possible.

Tested-by: Fuad Tabba <tabba@google.com>
Signed-off-by: Marc Zyngier <maz@kernel.org>
Tested-by: Mark Brown <broonie@kernel.org>
Link: https://msgid.link/20251120172540.2267180-32-maz@kernel.org
Signed-off-by: Oliver Upton <oupton@kernel.org>

authored by

Marc Zyngier and committed by
Oliver Upton
1c3b3cad 70fd60bd

+42 -8
+1
arch/arm64/kvm/vgic/vgic-init.c
··· 188 188 struct kvm_vcpu *vcpu0 = kvm_get_vcpu(kvm, 0); 189 189 int i; 190 190 191 + dist->active_spis = (atomic_t)ATOMIC_INIT(0); 191 192 dist->spis = kcalloc(nr_spis, sizeof(struct vgic_irq), GFP_KERNEL_ACCOUNT); 192 193 if (!dist->spis) 193 194 return -ENOMEM;
+15 -6
arch/arm64/kvm/vgic/vgic-v3.c
··· 47 47 ICH_HCR_EL2_VGrp1DIE : ICH_HCR_EL2_VGrp1EIE; 48 48 49 49 /* 50 + * Dealing with EOImode=1 is a massive source of headache. Not 51 + * only do we need to track that we have active interrupts 52 + * outside of the LRs and force DIR to be trapped, we also 53 + * need to deal with SPIs that can be deactivated on another 54 + * CPU. 55 + * 50 56 * Note that we set the trap irrespective of EOIMode, as that 51 57 * can change behind our back without any warning... 52 58 */ 53 - if (irqs_active_outside_lrs(als)) 59 + if (irqs_active_outside_lrs(als) || 60 + atomic_read(&vcpu->kvm->arch.vgic.active_spis)) 54 61 cpuif->vgic_hcr |= ICH_HCR_EL2_TDIR; 55 62 } 56 63 ··· 84 77 irq = vgic_get_vcpu_irq(vcpu, intid); 85 78 if (!irq) /* An LPI could have been unmapped. */ 86 79 return; 87 - 88 - /* Notify fds when the guest EOI'ed a level-triggered IRQ */ 89 - if (lr_signals_eoi_mi(val) && vgic_valid_spi(vcpu->kvm, intid)) 90 - kvm_notify_acked_irq(vcpu->kvm, 0, 91 - intid - VGIC_NR_PRIVATE_IRQS); 92 80 93 81 scoped_guard(raw_spinlock, &irq->irq_lock) { 94 82 /* Always preserve the active bit for !LPIs, note deactivation */ ··· 117 115 vgic_irq_handle_resampling(irq, deactivated, val & ICH_LR_PENDING_BIT); 118 116 119 117 irq->on_lr = false; 118 + } 119 + 120 + /* Notify fds when the guest EOI'ed a level-triggered SPI, and drop the refcount */ 121 + if (deactivated && lr_signals_eoi_mi(val) && vgic_valid_spi(vcpu->kvm, intid)) { 122 + kvm_notify_acked_irq(vcpu->kvm, 0, 123 + intid - VGIC_NR_PRIVATE_IRQS); 124 + atomic_dec_if_positive(&vcpu->kvm->arch.vgic.active_spis); 120 125 } 121 126 122 127 vgic_put_irq(vcpu->kvm, irq);
+23 -2
arch/arm64/kvm/vgic/vgic.c
··· 367 367 return false; 368 368 } 369 369 370 + static bool vgic_model_needs_bcst_kick(struct kvm *kvm) 371 + { 372 + /* 373 + * A GICv3 (or GICv3-like) system exposing a GICv3 to the 374 + * guest needs a broadcast kick to set TDIR globally, even if 375 + * the bit doesn't really exist (we still need to check for 376 + * the shadow bit in the DIR emulation fast-path). 377 + */ 378 + return (kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3); 379 + } 380 + 370 381 /* 371 382 * Check whether an IRQ needs to (and can) be queued to a VCPU's ap list. 372 383 * Do the queuing if necessary, taking the right locks in the right order. ··· 390 379 unsigned long flags) __releases(&irq->irq_lock) 391 380 { 392 381 struct kvm_vcpu *vcpu; 382 + bool bcast; 393 383 394 384 lockdep_assert_held(&irq->irq_lock); 395 385 ··· 465 453 list_add_tail(&irq->ap_list, &vcpu->arch.vgic_cpu.ap_list_head); 466 454 irq->vcpu = vcpu; 467 455 456 + /* A new SPI may result in deactivation trapping on all vcpus */ 457 + bcast = (vgic_model_needs_bcst_kick(vcpu->kvm) && 458 + vgic_valid_spi(vcpu->kvm, irq->intid) && 459 + atomic_fetch_inc(&vcpu->kvm->arch.vgic.active_spis) == 0); 460 + 468 461 raw_spin_unlock(&irq->irq_lock); 469 462 raw_spin_unlock_irqrestore(&vcpu->arch.vgic_cpu.ap_list_lock, flags); 470 463 471 - kvm_make_request(KVM_REQ_IRQ_PENDING, vcpu); 472 - kvm_vcpu_kick(vcpu); 464 + if (!bcast) { 465 + kvm_make_request(KVM_REQ_IRQ_PENDING, vcpu); 466 + kvm_vcpu_kick(vcpu); 467 + } else { 468 + kvm_make_all_cpus_request(vcpu->kvm, KVM_REQ_IRQ_PENDING); 469 + } 473 470 474 471 return true; 475 472 }
+3
include/kvm/arm_vgic.h
··· 263 263 /* The GIC maintenance IRQ for nested hypervisors. */ 264 264 u32 mi_intid; 265 265 266 + /* Track the number of in-flight active SPIs */ 267 + atomic_t active_spis; 268 + 266 269 /* base addresses in guest physical address space: */ 267 270 gpa_t vgic_dist_base; /* distributor */ 268 271 union {