Skip to content

Commit 16ca6a6

Browse files
author
Marc Zyngier
committed
KVM: arm/arm64: vgic: Don't populate multiple LRs with the same vintid
The vgic code is trying to be clever when injecting GICv2 SGIs, and will happily populate LRs with the same interrupt number if they come from multiple vcpus (after all, they are distinct interrupt sources). Unfortunately, this is against the letter of the architecture, and the GICv2 architecture spec says "Each valid interrupt stored in the List registers must have a unique VirtualID for that virtual CPU interface.". GICv3 has similar (although slightly ambiguous) restrictions. This results in guests locking up when using GICv2-on-GICv3, for example. The obvious fix is to stop trying so hard, and inject a single vcpu per SGI per guest entry. After all, pending SGIs with multiple source vcpus are pretty rare, and are mostly seen in scenario where the physical CPUs are severely overcomitted. But as we now only inject a single instance of a multi-source SGI per vcpu entry, we may delay those interrupts for longer than strictly necessary, and run the risk of injecting lower priority interrupts in the meantime. In order to address this, we adopt a three stage strategy: - If we encounter a multi-source SGI in the AP list while computing its depth, we force the list to be sorted - When populating the LRs, we prevent the injection of any interrupt of lower priority than that of the first multi-source SGI we've injected. - Finally, the injection of a multi-source SGI triggers the request of a maintenance interrupt when there will be no pending interrupt in the LRs (HCR_NPIE). At the point where the last pending interrupt in the LRs switches from Pending to Active, the maintenance interrupt will be delivered, allowing us to add the remaining SGIs using the same process. Cc: [email protected] Fixes: 0919e84 ("KVM: arm/arm64: vgic-new: Add IRQ sync/flush framework") Acked-by: Christoffer Dall <[email protected]> Signed-off-by: Marc Zyngier <[email protected]>
1 parent 7660042 commit 16ca6a6

File tree

6 files changed

+67
-16
lines changed

6 files changed

+67
-16
lines changed

include/linux/irqchip/arm-gic-v3.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,7 @@
503503

504504
#define ICH_HCR_EN (1 << 0)
505505
#define ICH_HCR_UIE (1 << 1)
506+
#define ICH_HCR_NPIE (1 << 3)
506507
#define ICH_HCR_TC (1 << 10)
507508
#define ICH_HCR_TALL0 (1 << 11)
508509
#define ICH_HCR_TALL1 (1 << 12)

include/linux/irqchip/arm-gic.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@
8484

8585
#define GICH_HCR_EN (1 << 0)
8686
#define GICH_HCR_UIE (1 << 1)
87+
#define GICH_HCR_NPIE (1 << 3)
8788

8889
#define GICH_LR_VIRTUALID (0x3ff << 0)
8990
#define GICH_LR_PHYSID_CPUID_SHIFT (10)

virt/kvm/arm/vgic/vgic-v2.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,13 @@ void vgic_v2_init_lrs(void)
3737
vgic_v2_write_lr(i, 0);
3838
}
3939

40+
void vgic_v2_set_npie(struct kvm_vcpu *vcpu)
41+
{
42+
struct vgic_v2_cpu_if *cpuif = &vcpu->arch.vgic_cpu.vgic_v2;
43+
44+
cpuif->vgic_hcr |= GICH_HCR_NPIE;
45+
}
46+
4047
void vgic_v2_set_underflow(struct kvm_vcpu *vcpu)
4148
{
4249
struct vgic_v2_cpu_if *cpuif = &vcpu->arch.vgic_cpu.vgic_v2;
@@ -64,7 +71,7 @@ void vgic_v2_fold_lr_state(struct kvm_vcpu *vcpu)
6471
int lr;
6572
unsigned long flags;
6673

67-
cpuif->vgic_hcr &= ~GICH_HCR_UIE;
74+
cpuif->vgic_hcr &= ~(GICH_HCR_UIE | GICH_HCR_NPIE);
6875

6976
for (lr = 0; lr < vgic_cpu->used_lrs; lr++) {
7077
u32 val = cpuif->vgic_lr[lr];

virt/kvm/arm/vgic/vgic-v3.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,13 @@ static bool group1_trap;
2626
static bool common_trap;
2727
static bool gicv4_enable;
2828

29+
void vgic_v3_set_npie(struct kvm_vcpu *vcpu)
30+
{
31+
struct vgic_v3_cpu_if *cpuif = &vcpu->arch.vgic_cpu.vgic_v3;
32+
33+
cpuif->vgic_hcr |= ICH_HCR_NPIE;
34+
}
35+
2936
void vgic_v3_set_underflow(struct kvm_vcpu *vcpu)
3037
{
3138
struct vgic_v3_cpu_if *cpuif = &vcpu->arch.vgic_cpu.vgic_v3;
@@ -47,7 +54,7 @@ void vgic_v3_fold_lr_state(struct kvm_vcpu *vcpu)
4754
int lr;
4855
unsigned long flags;
4956

50-
cpuif->vgic_hcr &= ~ICH_HCR_UIE;
57+
cpuif->vgic_hcr &= ~(ICH_HCR_UIE | ICH_HCR_NPIE);
5158

5259
for (lr = 0; lr < vgic_cpu->used_lrs; lr++) {
5360
u64 val = cpuif->vgic_lr[lr];

virt/kvm/arm/vgic/vgic.c

Lines changed: 47 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -710,22 +710,37 @@ static inline void vgic_set_underflow(struct kvm_vcpu *vcpu)
710710
vgic_v3_set_underflow(vcpu);
711711
}
712712

713+
static inline void vgic_set_npie(struct kvm_vcpu *vcpu)
714+
{
715+
if (kvm_vgic_global_state.type == VGIC_V2)
716+
vgic_v2_set_npie(vcpu);
717+
else
718+
vgic_v3_set_npie(vcpu);
719+
}
720+
713721
/* Requires the ap_list_lock to be held. */
714-
static int compute_ap_list_depth(struct kvm_vcpu *vcpu)
722+
static int compute_ap_list_depth(struct kvm_vcpu *vcpu,
723+
bool *multi_sgi)
715724
{
716725
struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
717726
struct vgic_irq *irq;
718727
int count = 0;
719728

729+
*multi_sgi = false;
730+
720731
DEBUG_SPINLOCK_BUG_ON(!spin_is_locked(&vgic_cpu->ap_list_lock));
721732

722733
list_for_each_entry(irq, &vgic_cpu->ap_list_head, ap_list) {
723734
spin_lock(&irq->irq_lock);
724735
/* GICv2 SGIs can count for more than one... */
725-
if (vgic_irq_is_sgi(irq->intid) && irq->source)
726-
count += hweight8(irq->source);
727-
else
736+
if (vgic_irq_is_sgi(irq->intid) && irq->source) {
737+
int w = hweight8(irq->source);
738+
739+
count += w;
740+
*multi_sgi |= (w > 1);
741+
} else {
728742
count++;
743+
}
729744
spin_unlock(&irq->irq_lock);
730745
}
731746
return count;
@@ -736,28 +751,43 @@ static void vgic_flush_lr_state(struct kvm_vcpu *vcpu)
736751
{
737752
struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
738753
struct vgic_irq *irq;
739-
int count = 0;
754+
int count;
755+
bool npie = false;
756+
bool multi_sgi;
757+
u8 prio = 0xff;
740758

741759
DEBUG_SPINLOCK_BUG_ON(!spin_is_locked(&vgic_cpu->ap_list_lock));
742760

743-
if (compute_ap_list_depth(vcpu) > kvm_vgic_global_state.nr_lr)
761+
count = compute_ap_list_depth(vcpu, &multi_sgi);
762+
if (count > kvm_vgic_global_state.nr_lr || multi_sgi)
744763
vgic_sort_ap_list(vcpu);
745764

765+
count = 0;
766+
746767
list_for_each_entry(irq, &vgic_cpu->ap_list_head, ap_list) {
747768
spin_lock(&irq->irq_lock);
748769

749-
if (unlikely(vgic_target_oracle(irq) != vcpu))
750-
goto next;
751-
752770
/*
753-
* If we get an SGI with multiple sources, try to get
754-
* them in all at once.
771+
* If we have multi-SGIs in the pipeline, we need to
772+
* guarantee that they are all seen before any IRQ of
773+
* lower priority. In that case, we need to filter out
774+
* these interrupts by exiting early. This is easy as
775+
* the AP list has been sorted already.
755776
*/
756-
do {
777+
if (multi_sgi && irq->priority > prio) {
778+
spin_unlock(&irq->irq_lock);
779+
break;
780+
}
781+
782+
if (likely(vgic_target_oracle(irq) == vcpu)) {
757783
vgic_populate_lr(vcpu, irq, count++);
758-
} while (irq->source && count < kvm_vgic_global_state.nr_lr);
759784

760-
next:
785+
if (irq->source) {
786+
npie = true;
787+
prio = irq->priority;
788+
}
789+
}
790+
761791
spin_unlock(&irq->irq_lock);
762792

763793
if (count == kvm_vgic_global_state.nr_lr) {
@@ -768,6 +798,9 @@ static void vgic_flush_lr_state(struct kvm_vcpu *vcpu)
768798
}
769799
}
770800

801+
if (npie)
802+
vgic_set_npie(vcpu);
803+
771804
vcpu->arch.vgic_cpu.used_lrs = count;
772805

773806
/* Nuke remaining LRs */

virt/kvm/arm/vgic/vgic.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ void vgic_v2_fold_lr_state(struct kvm_vcpu *vcpu);
160160
void vgic_v2_populate_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq, int lr);
161161
void vgic_v2_clear_lr(struct kvm_vcpu *vcpu, int lr);
162162
void vgic_v2_set_underflow(struct kvm_vcpu *vcpu);
163+
void vgic_v2_set_npie(struct kvm_vcpu *vcpu);
163164
int vgic_v2_has_attr_regs(struct kvm_device *dev, struct kvm_device_attr *attr);
164165
int vgic_v2_dist_uaccess(struct kvm_vcpu *vcpu, bool is_write,
165166
int offset, u32 *val);
@@ -189,6 +190,7 @@ void vgic_v3_fold_lr_state(struct kvm_vcpu *vcpu);
189190
void vgic_v3_populate_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq, int lr);
190191
void vgic_v3_clear_lr(struct kvm_vcpu *vcpu, int lr);
191192
void vgic_v3_set_underflow(struct kvm_vcpu *vcpu);
193+
void vgic_v3_set_npie(struct kvm_vcpu *vcpu);
192194
void vgic_v3_set_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr);
193195
void vgic_v3_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr);
194196
void vgic_v3_enable(struct kvm_vcpu *vcpu);

0 commit comments

Comments
 (0)