kvm: ioapic: Lazy update IOAPIC EOI

In-kernel IOAPIC does not receive EOI with AMD SVM AVIC
since the processor accelerate write to APIC EOI register and
does not trap if the interrupt is edge-triggered.

Workaround this by lazy check for pending APIC EOI at the time when
setting new IOPIC irq, and update IOAPIC EOI if no pending APIC EOI.

Signed-off-by: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
Suravee Suthikulpanit 2019-11-14 14:15:19 -06:00 committed by Paolo Bonzini
parent 1ec2405c7c
commit f458d039db
1 changed files with 39 additions and 0 deletions

View File

@ -49,6 +49,11 @@
static int ioapic_service(struct kvm_ioapic *vioapic, int irq,
bool line_status);
static void kvm_ioapic_update_eoi_one(struct kvm_vcpu *vcpu,
struct kvm_ioapic *ioapic,
int trigger_mode,
int pin);
static unsigned long ioapic_read_indirect(struct kvm_ioapic *ioapic,
unsigned long addr,
unsigned long length)
@ -177,6 +182,31 @@ static bool rtc_irq_check_coalesced(struct kvm_ioapic *ioapic)
return false;
}
static void ioapic_lazy_update_eoi(struct kvm_ioapic *ioapic, int irq)
{
int i;
struct kvm_vcpu *vcpu;
union kvm_ioapic_redirect_entry *entry = &ioapic->redirtbl[irq];
kvm_for_each_vcpu(i, vcpu, ioapic->kvm) {
if (!kvm_apic_match_dest(vcpu, NULL, APIC_DEST_NOSHORT,
entry->fields.dest_id,
entry->fields.dest_mode) ||
kvm_apic_pending_eoi(vcpu, entry->fields.vector))
continue;
/*
* If no longer has pending EOI in LAPICs, update
* EOI for this vetor.
*/
rtc_irq_eoi(ioapic, vcpu, entry->fields.vector);
kvm_ioapic_update_eoi_one(vcpu, ioapic,
entry->fields.trig_mode,
irq);
break;
}
}
static int ioapic_set_irq(struct kvm_ioapic *ioapic, unsigned int irq,
int irq_level, bool line_status)
{
@ -194,6 +224,15 @@ static int ioapic_set_irq(struct kvm_ioapic *ioapic, unsigned int irq,
goto out;
}
/*
* AMD SVM AVIC accelerate EOI write and do not trap,
* in-kernel IOAPIC will not be able to receive the EOI.
* In this case, we do lazy update of the pending EOI when
* trying to set IOAPIC irq.
*/
if (kvm_apicv_activated(ioapic->kvm))
ioapic_lazy_update_eoi(ioapic, irq);
/*
* Return 0 for coalesced interrupts; for edge-triggered interrupts,
* this only happens if a previous edge has not been delivered due