diff options
author | Gleb Natapov <gleb@redhat.com> | 2009-07-05 11:48:11 -0400 |
---|---|---|
committer | Avi Kivity <avi@redhat.com> | 2009-08-09 04:45:49 -0400 |
commit | b4a2f5e723e4f7df46731106faf9e2405673c073 (patch) | |
tree | 98766bcb94182957b27b9bdd371c742d927da951 /virt/kvm | |
parent | 53a27b39ff4d2492f84b1fdc2f0047175f0b0b93 (diff) |
KVM: Avoid redelivery of edge interrupt before next edge
The check for an edge is broken in current ioapic code. ioapic->irr is
cleared on each edge interrupt by ioapic_service() and this makes
old_irr != ioapic->irr condition in kvm_ioapic_set_irq() to be always
true. The patch fixes the code to properly recognise edge.
Some HW emulation calls set_irq() without level change. If each such
call is propagated to an OS it may confuse a device driver. This is the
case with keyboard device emulation and Windows XP x64 installer on SMP VM.
Each keystroke produce two interrupts (down/up) one interrupt is
submitted to CPU0 and another to CPU1. This confuses Windows somehow
and it ignores keystrokes.
Signed-off-by: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Avi Kivity <avi@redhat.com>
Diffstat (limited to 'virt/kvm')
-rw-r--r-- | virt/kvm/ioapic.c | 10 |
1 files changed, 5 insertions, 5 deletions
diff --git a/virt/kvm/ioapic.c b/virt/kvm/ioapic.c index 1eddae94bab3..1150c6d5c7b8 100644 --- a/virt/kvm/ioapic.c +++ b/virt/kvm/ioapic.c | |||
@@ -95,8 +95,6 @@ static int ioapic_service(struct kvm_ioapic *ioapic, unsigned int idx) | |||
95 | if (injected && pent->fields.trig_mode == IOAPIC_LEVEL_TRIG) | 95 | if (injected && pent->fields.trig_mode == IOAPIC_LEVEL_TRIG) |
96 | pent->fields.remote_irr = 1; | 96 | pent->fields.remote_irr = 1; |
97 | } | 97 | } |
98 | if (!pent->fields.trig_mode) | ||
99 | ioapic->irr &= ~(1 << idx); | ||
100 | 98 | ||
101 | return injected; | 99 | return injected; |
102 | } | 100 | } |
@@ -136,7 +134,8 @@ static void ioapic_write_indirect(struct kvm_ioapic *ioapic, u32 val) | |||
136 | mask_after = ioapic->redirtbl[index].fields.mask; | 134 | mask_after = ioapic->redirtbl[index].fields.mask; |
137 | if (mask_before != mask_after) | 135 | if (mask_before != mask_after) |
138 | kvm_fire_mask_notifiers(ioapic->kvm, index, mask_after); | 136 | kvm_fire_mask_notifiers(ioapic->kvm, index, mask_after); |
139 | if (ioapic->irr & (1 << index)) | 137 | if (ioapic->redirtbl[index].fields.trig_mode == IOAPIC_LEVEL_TRIG |
138 | && ioapic->irr & (1 << index)) | ||
140 | ioapic_service(ioapic, index); | 139 | ioapic_service(ioapic, index); |
141 | break; | 140 | break; |
142 | } | 141 | } |
@@ -184,9 +183,10 @@ int kvm_ioapic_set_irq(struct kvm_ioapic *ioapic, int irq, int level) | |||
184 | if (!level) | 183 | if (!level) |
185 | ioapic->irr &= ~mask; | 184 | ioapic->irr &= ~mask; |
186 | else { | 185 | else { |
186 | int edge = (entry.fields.trig_mode == IOAPIC_EDGE_TRIG); | ||
187 | ioapic->irr |= mask; | 187 | ioapic->irr |= mask; |
188 | if ((!entry.fields.trig_mode && old_irr != ioapic->irr) | 188 | if ((edge && old_irr != ioapic->irr) || |
189 | || !entry.fields.remote_irr) | 189 | (!edge && !entry.fields.remote_irr)) |
190 | ret = ioapic_service(ioapic, irq); | 190 | ret = ioapic_service(ioapic, irq); |
191 | } | 191 | } |
192 | } | 192 | } |