git: 5ea878684f6c - main - bhyve/ioapic: improve the tracking of IRR bit

Roger Pau Monné royger at FreeBSD.org
Tue Feb 2 08:49:03 UTC 2021


The branch main has been updated by royger:

URL: https://cgit.FreeBSD.org/src/commit/?id=5ea878684f6cfff4ad05186346ff3a4828d980ca

commit 5ea878684f6cfff4ad05186346ff3a4828d980ca
Author:     Roger Pau Monné <royger at FreeBSD.org>
AuthorDate: 2021-01-19 11:52:28 +0000
Commit:     Roger Pau Monné <royger at FreeBSD.org>
CommitDate: 2021-02-02 08:47:00 +0000

    bhyve/ioapic: improve the tracking of IRR bit
    
    One common method of EOI'ing an interrupt at the IO-APIC level is to
    switch the pin to edge triggering mode and then back into level mode.
    That would cause the IRR bit to be cleared and thus further interrupts
    to be injected. FreeBSD does indeed use that method if the IO-APIC EOI
    register is not supported.
    
    The bhyve IO-APIC emulation code didn't clear the IRR bit when doing
    that switch, and was also missing acknowledging the IRR state when
    trying to inject an interrupt in vioapic_send_intr.
    
    Reviewed by:            grehan
    Differential revision:  https://reviews.freebsd.org/D28238
---
 sys/amd64/vmm/io/vioapic.c | 22 ++++++++++++++++++----
 1 file changed, 18 insertions(+), 4 deletions(-)

diff --git a/sys/amd64/vmm/io/vioapic.c b/sys/amd64/vmm/io/vioapic.c
index 941f7c7364bc..639c1b07eb08 100644
--- a/sys/amd64/vmm/io/vioapic.c
+++ b/sys/amd64/vmm/io/vioapic.c
@@ -122,8 +122,14 @@ vioapic_send_intr(struct vioapic *vioapic, int pin)
 	phys = ((low & IOART_DESTMOD) == IOART_DESTPHY);
 	delmode = low & IOART_DELMOD;
 	level = low & IOART_TRGRLVL ? true : false;
-	if (level)
+	if (level) {
+		if ((low & IOART_REM_IRR) != 0) {
+			VIOAPIC_CTR1(vioapic, "ioapic pin%d: irr pending",
+			    pin);
+			return;
+		}
 		vioapic->rtbl[pin].reg |= IOART_REM_IRR;
+	}
 
 	vector = low & IOART_INTVEC;
 	dest = high >> APIC_ID_SHIFT;
@@ -342,6 +348,16 @@ vioapic_write(struct vioapic *vioapic, int vcpuid, uint32_t addr, uint32_t data)
 		vioapic->rtbl[pin].reg &= ~mask64 | RTBL_RO_BITS;
 		vioapic->rtbl[pin].reg |= data64 & ~RTBL_RO_BITS;
 
+		/*
+		 * Switching from level to edge triggering will clear the IRR
+		 * bit. This is what FreeBSD will do in order to EOI an
+		 * interrupt when the IO-APIC doesn't support targeted EOI (see
+		 * _ioapic_eoi_source).
+		 */
+		if ((vioapic->rtbl[pin].reg & IOART_TRGRMOD) == IOART_TRGREDG &&
+		    (vioapic->rtbl[pin].reg & IOART_REM_IRR) != 0)
+			vioapic->rtbl[pin].reg &= ~IOART_REM_IRR;
+
 		VIOAPIC_CTR2(vioapic, "ioapic pin%d: redir table entry %#lx",
 		    pin, vioapic->rtbl[pin].reg);
 
@@ -363,12 +379,10 @@ vioapic_write(struct vioapic *vioapic, int vcpuid, uint32_t addr, uint32_t data)
 
 		/*
 		 * Generate an interrupt if the following conditions are met:
-		 * - previous interrupt has been EOIed
 		 * - pin trigger mode is level
 		 * - pin level is asserted
 		 */
-		if ((vioapic->rtbl[pin].reg & IOART_REM_IRR) == 0 &&
-		    (vioapic->rtbl[pin].reg & IOART_TRGRMOD) == IOART_TRGRLVL &&
+		if ((vioapic->rtbl[pin].reg & IOART_TRGRMOD) == IOART_TRGRLVL &&
 		    (vioapic->rtbl[pin].acnt > 0)) {
 			VIOAPIC_CTR2(vioapic, "ioapic pin%d: asserted at rtbl "
 			    "write, acnt %d", pin, vioapic->rtbl[pin].acnt);


More information about the dev-commits-src-main mailing list