KVM: fix race with level interrupts
When more than 1 source id is in use for the same GSI, we have the following race related to handling irq_states race: CPU 0 clears bit 0. CPU 0 read irq_state as 0. CPU 1 sets level to 1. CPU 1 calls kvm_ioapic_set_irq(1). CPU 0 calls kvm_ioapic_set_irq(0). Now ioapic thinks the level is 0 but irq_state is not 0. Fix by performing all irq_states bitmap handling under pic/ioapic lock. This also removes the need for atomics with irq_states handling. Reported-by: Gleb Natapov <gleb@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com>
This commit is contained in:

committed by
Marcelo Tosatti

parent
d63d3e6217
commit
1a577b7247
@@ -188,14 +188,15 @@ void kvm_pic_update_irq(struct kvm_pic *s)
|
||||
pic_unlock(s);
|
||||
}
|
||||
|
||||
int kvm_pic_set_irq(void *opaque, int irq, int level)
|
||||
int kvm_pic_set_irq(struct kvm_pic *s, int irq, int irq_source_id, int level)
|
||||
{
|
||||
struct kvm_pic *s = opaque;
|
||||
int ret = -1;
|
||||
|
||||
pic_lock(s);
|
||||
if (irq >= 0 && irq < PIC_NUM_PINS) {
|
||||
ret = pic_set_irq1(&s->pics[irq >> 3], irq & 7, level);
|
||||
int irq_level = __kvm_irq_line_state(&s->irq_states[irq],
|
||||
irq_source_id, level);
|
||||
ret = pic_set_irq1(&s->pics[irq >> 3], irq & 7, irq_level);
|
||||
pic_update_irq(s);
|
||||
trace_kvm_pic_set_irq(irq >> 3, irq & 7, s->pics[irq >> 3].elcr,
|
||||
s->pics[irq >> 3].imr, ret == 0);
|
||||
@@ -205,6 +206,16 @@ int kvm_pic_set_irq(void *opaque, int irq, int level)
|
||||
return ret;
|
||||
}
|
||||
|
||||
void kvm_pic_clear_all(struct kvm_pic *s, int irq_source_id)
|
||||
{
|
||||
int i;
|
||||
|
||||
pic_lock(s);
|
||||
for (i = 0; i < PIC_NUM_PINS; i++)
|
||||
__clear_bit(irq_source_id, &s->irq_states[i]);
|
||||
pic_unlock(s);
|
||||
}
|
||||
|
||||
/*
|
||||
* acknowledge interrupt 'irq'
|
||||
*/
|
||||
|
Reference in New Issue
Block a user