Commit ff4b9df8 authored by Marcelo Tosatti's avatar Marcelo Tosatti Committed by Avi Kivity

KVM: IOAPIC: only set remote_irr if interrupt was injected

There's a bug in the IOAPIC code for level-triggered interrupts. Its
relatively easy to trigger by sharing (virtio-blk + usbtablet was the
testcase, initially reported by Gerd von Egidy).

The "remote_irr" variable is used to indicate accepted but not yet acked
interrupts. Its cleared from the EOI handler.

Problem is that the EOI handler clears remote_irr unconditionally, even
if it reinjected another pending interrupt.

In that case, kvm_ioapic_set_irq() proceeds to ioapic_service() which
sets remote_irr even if it failed to inject (since the IRR was high due
to EOI reinjection).

Since the TMR bit has been cleared by the first EOI, the second one
fails to clear remote_irr.

End result is interrupt line dead.

Fix it by setting remote_irr only if a new pending interrupt has been
generated (and the TMR bit for vector in question set).
Signed-off-by: default avatarMarcelo Tosatti <mtosatti@redhat.com>
Signed-off-by: default avatarAvi Kivity <avi@qumranet.com>
parent 8d2d73b9
...@@ -45,7 +45,7 @@ ...@@ -45,7 +45,7 @@
#else #else
#define ioapic_debug(fmt, arg...) #define ioapic_debug(fmt, arg...)
#endif #endif
static void ioapic_deliver(struct kvm_ioapic *vioapic, int irq); static int ioapic_deliver(struct kvm_ioapic *vioapic, int irq);
static unsigned long ioapic_read_indirect(struct kvm_ioapic *ioapic, static unsigned long ioapic_read_indirect(struct kvm_ioapic *ioapic,
unsigned long addr, unsigned long addr,
...@@ -89,8 +89,8 @@ static void ioapic_service(struct kvm_ioapic *ioapic, unsigned int idx) ...@@ -89,8 +89,8 @@ static void ioapic_service(struct kvm_ioapic *ioapic, unsigned int idx)
pent = &ioapic->redirtbl[idx]; pent = &ioapic->redirtbl[idx];
if (!pent->fields.mask) { if (!pent->fields.mask) {
ioapic_deliver(ioapic, idx); int injected = ioapic_deliver(ioapic, idx);
if (pent->fields.trig_mode == IOAPIC_LEVEL_TRIG) if (injected && pent->fields.trig_mode == IOAPIC_LEVEL_TRIG)
pent->fields.remote_irr = 1; pent->fields.remote_irr = 1;
} }
if (!pent->fields.trig_mode) if (!pent->fields.trig_mode)
...@@ -133,7 +133,7 @@ static void ioapic_write_indirect(struct kvm_ioapic *ioapic, u32 val) ...@@ -133,7 +133,7 @@ static void ioapic_write_indirect(struct kvm_ioapic *ioapic, u32 val)
} }
} }
static void ioapic_inj_irq(struct kvm_ioapic *ioapic, static int ioapic_inj_irq(struct kvm_ioapic *ioapic,
struct kvm_vcpu *vcpu, struct kvm_vcpu *vcpu,
u8 vector, u8 trig_mode, u8 delivery_mode) u8 vector, u8 trig_mode, u8 delivery_mode)
{ {
...@@ -143,7 +143,7 @@ static void ioapic_inj_irq(struct kvm_ioapic *ioapic, ...@@ -143,7 +143,7 @@ static void ioapic_inj_irq(struct kvm_ioapic *ioapic,
ASSERT((delivery_mode == IOAPIC_FIXED) || ASSERT((delivery_mode == IOAPIC_FIXED) ||
(delivery_mode == IOAPIC_LOWEST_PRIORITY)); (delivery_mode == IOAPIC_LOWEST_PRIORITY));
kvm_apic_set_irq(vcpu, vector, trig_mode); return kvm_apic_set_irq(vcpu, vector, trig_mode);
} }
static u32 ioapic_get_delivery_bitmask(struct kvm_ioapic *ioapic, u8 dest, static u32 ioapic_get_delivery_bitmask(struct kvm_ioapic *ioapic, u8 dest,
...@@ -186,7 +186,7 @@ static u32 ioapic_get_delivery_bitmask(struct kvm_ioapic *ioapic, u8 dest, ...@@ -186,7 +186,7 @@ static u32 ioapic_get_delivery_bitmask(struct kvm_ioapic *ioapic, u8 dest,
return mask; return mask;
} }
static void ioapic_deliver(struct kvm_ioapic *ioapic, int irq) static int ioapic_deliver(struct kvm_ioapic *ioapic, int irq)
{ {
u8 dest = ioapic->redirtbl[irq].fields.dest_id; u8 dest = ioapic->redirtbl[irq].fields.dest_id;
u8 dest_mode = ioapic->redirtbl[irq].fields.dest_mode; u8 dest_mode = ioapic->redirtbl[irq].fields.dest_mode;
...@@ -195,7 +195,7 @@ static void ioapic_deliver(struct kvm_ioapic *ioapic, int irq) ...@@ -195,7 +195,7 @@ static void ioapic_deliver(struct kvm_ioapic *ioapic, int irq)
u8 trig_mode = ioapic->redirtbl[irq].fields.trig_mode; u8 trig_mode = ioapic->redirtbl[irq].fields.trig_mode;
u32 deliver_bitmask; u32 deliver_bitmask;
struct kvm_vcpu *vcpu; struct kvm_vcpu *vcpu;
int vcpu_id; int vcpu_id, r = 0;
ioapic_debug("dest=%x dest_mode=%x delivery_mode=%x " ioapic_debug("dest=%x dest_mode=%x delivery_mode=%x "
"vector=%x trig_mode=%x\n", "vector=%x trig_mode=%x\n",
...@@ -204,7 +204,7 @@ static void ioapic_deliver(struct kvm_ioapic *ioapic, int irq) ...@@ -204,7 +204,7 @@ static void ioapic_deliver(struct kvm_ioapic *ioapic, int irq)
deliver_bitmask = ioapic_get_delivery_bitmask(ioapic, dest, dest_mode); deliver_bitmask = ioapic_get_delivery_bitmask(ioapic, dest, dest_mode);
if (!deliver_bitmask) { if (!deliver_bitmask) {
ioapic_debug("no target on destination\n"); ioapic_debug("no target on destination\n");
return; return 0;
} }
switch (delivery_mode) { switch (delivery_mode) {
...@@ -216,7 +216,7 @@ static void ioapic_deliver(struct kvm_ioapic *ioapic, int irq) ...@@ -216,7 +216,7 @@ static void ioapic_deliver(struct kvm_ioapic *ioapic, int irq)
vcpu = ioapic->kvm->vcpus[0]; vcpu = ioapic->kvm->vcpus[0];
#endif #endif
if (vcpu != NULL) if (vcpu != NULL)
ioapic_inj_irq(ioapic, vcpu, vector, r = ioapic_inj_irq(ioapic, vcpu, vector,
trig_mode, delivery_mode); trig_mode, delivery_mode);
else else
ioapic_debug("null lowest prio vcpu: " ioapic_debug("null lowest prio vcpu: "
...@@ -234,7 +234,7 @@ static void ioapic_deliver(struct kvm_ioapic *ioapic, int irq) ...@@ -234,7 +234,7 @@ static void ioapic_deliver(struct kvm_ioapic *ioapic, int irq)
deliver_bitmask &= ~(1 << vcpu_id); deliver_bitmask &= ~(1 << vcpu_id);
vcpu = ioapic->kvm->vcpus[vcpu_id]; vcpu = ioapic->kvm->vcpus[vcpu_id];
if (vcpu) { if (vcpu) {
ioapic_inj_irq(ioapic, vcpu, vector, r = ioapic_inj_irq(ioapic, vcpu, vector,
trig_mode, delivery_mode); trig_mode, delivery_mode);
} }
} }
...@@ -246,6 +246,7 @@ static void ioapic_deliver(struct kvm_ioapic *ioapic, int irq) ...@@ -246,6 +246,7 @@ static void ioapic_deliver(struct kvm_ioapic *ioapic, int irq)
delivery_mode); delivery_mode);
break; break;
} }
return r;
} }
void kvm_ioapic_set_irq(struct kvm_ioapic *ioapic, int irq, int level) void kvm_ioapic_set_irq(struct kvm_ioapic *ioapic, int irq, int level)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment