Skip to content

Commit 388d435

Browse files
Andre-ARMbonzini
authored andcommitted
KVM: arm/arm64: Properly protect VGIC locks from IRQs
As Jan reported [1], lockdep complains about the VGIC not being bullet proof. This seems to be due to two issues: - When commit 006df0f ("KVM: arm/arm64: Support calling vgic_update_irq_pending from irq context") promoted irq_lock and ap_list_lock to _irqsave, we forgot two instances of irq_lock. lockdeps seems to pick those up. - If a lock is _irqsave, any other locks we take inside them should be _irqsafe as well. So the lpi_list_lock needs to be promoted also. This fixes both issues by simply making the remaining instances of those locks _irqsave. One irq_lock is addressed in a separate patch, to simplify backporting. [1] http://lists.infradead.org/pipermail/linux-arm-kernel/2018-May/575718.html Cc: [email protected] Fixes: 006df0f ("KVM: arm/arm64: Support calling vgic_update_irq_pending from irq context") Reported-by: Jan Glauber <[email protected]> Acked-by: Christoffer Dall <[email protected]> Signed-off-by: Andre Przywara <[email protected]> Signed-off-by: Paolo Bonzini <[email protected]>
1 parent 4c27625 commit 388d435

File tree

3 files changed

+23
-14
lines changed

3 files changed

+23
-14
lines changed

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ static int vgic_debug_show(struct seq_file *s, void *v)
211211
struct vgic_state_iter *iter = (struct vgic_state_iter *)v;
212212
struct vgic_irq *irq;
213213
struct kvm_vcpu *vcpu = NULL;
214+
unsigned long flags;
214215

215216
if (iter->dist_id == 0) {
216217
print_dist_state(s, &kvm->arch.vgic);
@@ -227,9 +228,9 @@ static int vgic_debug_show(struct seq_file *s, void *v)
227228
irq = &kvm->arch.vgic.spis[iter->intid - VGIC_NR_PRIVATE_IRQS];
228229
}
229230

230-
spin_lock(&irq->irq_lock);
231+
spin_lock_irqsave(&irq->irq_lock, flags);
231232
print_irq_state(s, irq, vcpu);
232-
spin_unlock(&irq->irq_lock);
233+
spin_unlock_irqrestore(&irq->irq_lock, flags);
233234

234235
return 0;
235236
}

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

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ static struct vgic_irq *vgic_add_lpi(struct kvm *kvm, u32 intid,
5252
{
5353
struct vgic_dist *dist = &kvm->arch.vgic;
5454
struct vgic_irq *irq = vgic_get_irq(kvm, NULL, intid), *oldirq;
55+
unsigned long flags;
5556
int ret;
5657

5758
/* In this case there is no put, since we keep the reference. */
@@ -71,7 +72,7 @@ static struct vgic_irq *vgic_add_lpi(struct kvm *kvm, u32 intid,
7172
irq->intid = intid;
7273
irq->target_vcpu = vcpu;
7374

74-
spin_lock(&dist->lpi_list_lock);
75+
spin_lock_irqsave(&dist->lpi_list_lock, flags);
7576

7677
/*
7778
* There could be a race with another vgic_add_lpi(), so we need to
@@ -99,7 +100,7 @@ static struct vgic_irq *vgic_add_lpi(struct kvm *kvm, u32 intid,
99100
dist->lpi_list_count++;
100101

101102
out_unlock:
102-
spin_unlock(&dist->lpi_list_lock);
103+
spin_unlock_irqrestore(&dist->lpi_list_lock, flags);
103104

104105
/*
105106
* We "cache" the configuration table entries in our struct vgic_irq's.
@@ -315,6 +316,7 @@ static int vgic_copy_lpi_list(struct kvm_vcpu *vcpu, u32 **intid_ptr)
315316
{
316317
struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
317318
struct vgic_irq *irq;
319+
unsigned long flags;
318320
u32 *intids;
319321
int irq_count, i = 0;
320322

@@ -330,7 +332,7 @@ static int vgic_copy_lpi_list(struct kvm_vcpu *vcpu, u32 **intid_ptr)
330332
if (!intids)
331333
return -ENOMEM;
332334

333-
spin_lock(&dist->lpi_list_lock);
335+
spin_lock_irqsave(&dist->lpi_list_lock, flags);
334336
list_for_each_entry(irq, &dist->lpi_list_head, lpi_list) {
335337
if (i == irq_count)
336338
break;
@@ -339,7 +341,7 @@ static int vgic_copy_lpi_list(struct kvm_vcpu *vcpu, u32 **intid_ptr)
339341
continue;
340342
intids[i++] = irq->intid;
341343
}
342-
spin_unlock(&dist->lpi_list_lock);
344+
spin_unlock_irqrestore(&dist->lpi_list_lock, flags);
343345

344346
*intid_ptr = intids;
345347
return i;

virt/kvm/arm/vgic/vgic.c

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,13 @@ struct vgic_global kvm_vgic_global_state __ro_after_init = {
4343
* kvm->lock (mutex)
4444
* its->cmd_lock (mutex)
4545
* its->its_lock (mutex)
46-
* vgic_cpu->ap_list_lock
47-
* kvm->lpi_list_lock
48-
* vgic_irq->irq_lock
46+
* vgic_cpu->ap_list_lock must be taken with IRQs disabled
47+
* kvm->lpi_list_lock must be taken with IRQs disabled
48+
* vgic_irq->irq_lock must be taken with IRQs disabled
49+
*
50+
* As the ap_list_lock might be taken from the timer interrupt handler,
51+
* we have to disable IRQs before taking this lock and everything lower
52+
* than it.
4953
*
5054
* If you need to take multiple locks, always take the upper lock first,
5155
* then the lower ones, e.g. first take the its_lock, then the irq_lock.
@@ -72,8 +76,9 @@ static struct vgic_irq *vgic_get_lpi(struct kvm *kvm, u32 intid)
7276
{
7377
struct vgic_dist *dist = &kvm->arch.vgic;
7478
struct vgic_irq *irq = NULL;
79+
unsigned long flags;
7580

76-
spin_lock(&dist->lpi_list_lock);
81+
spin_lock_irqsave(&dist->lpi_list_lock, flags);
7782

7883
list_for_each_entry(irq, &dist->lpi_list_head, lpi_list) {
7984
if (irq->intid != intid)
@@ -89,7 +94,7 @@ static struct vgic_irq *vgic_get_lpi(struct kvm *kvm, u32 intid)
8994
irq = NULL;
9095

9196
out_unlock:
92-
spin_unlock(&dist->lpi_list_lock);
97+
spin_unlock_irqrestore(&dist->lpi_list_lock, flags);
9398

9499
return irq;
95100
}
@@ -134,19 +139,20 @@ static void vgic_irq_release(struct kref *ref)
134139
void vgic_put_irq(struct kvm *kvm, struct vgic_irq *irq)
135140
{
136141
struct vgic_dist *dist = &kvm->arch.vgic;
142+
unsigned long flags;
137143

138144
if (irq->intid < VGIC_MIN_LPI)
139145
return;
140146

141-
spin_lock(&dist->lpi_list_lock);
147+
spin_lock_irqsave(&dist->lpi_list_lock, flags);
142148
if (!kref_put(&irq->refcount, vgic_irq_release)) {
143-
spin_unlock(&dist->lpi_list_lock);
149+
spin_unlock_irqrestore(&dist->lpi_list_lock, flags);
144150
return;
145151
};
146152

147153
list_del(&irq->lpi_list);
148154
dist->lpi_list_count--;
149-
spin_unlock(&dist->lpi_list_lock);
155+
spin_unlock_irqrestore(&dist->lpi_list_lock, flags);
150156

151157
kfree(irq);
152158
}

0 commit comments

Comments
 (0)