Index: sys/x86/include/apicvar.h =================================================================== --- sys/x86/include/apicvar.h +++ sys/x86/include/apicvar.h @@ -426,6 +426,7 @@ void xen_intr_handle_upcall(struct trapframe *frame); extern int x2apic_mode; +extern int lapic_eoi_suppression; #ifdef _SYS_SYSCTL_H_ SYSCTL_DECL(_hw_apic); Index: sys/x86/x86/io_apic.c =================================================================== --- sys/x86/x86/io_apic.c +++ sys/x86/x86/io_apic.c @@ -97,6 +97,7 @@ u_int io_apic_id:4; u_int io_intbase:8; /* System Interrupt base */ u_int io_numintr:8; + u_int io_haseoi:1; volatile ioapic_t *io_addr; /* XXX: should use bus_space */ vm_paddr_t io_paddr; STAILQ_ENTRY(ioapic) io_next; @@ -134,10 +135,53 @@ SYSCTL_INT(_hw_apic, OID_AUTO, enable_extint, CTLFLAG_RDTUN, &enable_extint, 0, "Enable the ExtINT pin in the first I/O APIC"); -static __inline void -_ioapic_eoi_source(struct intsrc *isrc) +static void +_ioapic_eoi_source(struct intsrc *isrc, int locked) { + struct ioapic_intsrc *src; + struct ioapic *io; + volatile uint32_t *apic_eoi; + uint32_t low1; + lapic_eoi(); + if (!lapic_eoi_suppression) + return; + src = (struct ioapic_intsrc *)isrc; + if (src->io_edgetrigger) + return; + io = (struct ioapic *)isrc->is_pic; + + /* + * Handle targeted EOI for level-triggered pins, if broadcast + * EOI suppression is supported by LAPICs. + */ + if (io->io_haseoi) { + /* + * If IOAPIC has EOI Register, simply write vector + * number into the reg. + */ + apic_eoi = (volatile uint32_t *)((volatile char *) + io->io_addr + IOAPIC_EOIR); + *apic_eoi = src->io_vector; + } else { + /* + * Otherwise, if IO-APIC is too old to provide EOIR, + * do what Intel did for the Linux kernel. Temporary + * switch the pin to edge-trigger and back, masking + * the pin during the trick. + */ + if (!locked) + mtx_lock_spin(&icu_lock); + low1 = src->io_lowreg; + low1 &= ~IOART_TRGRLVL; + low1 |= IOART_TRGREDG | IOART_INTMSET; + ioapic_write(io->io_addr, IOAPIC_REDTBL_LO(src->io_intpin), + low1); + ioapic_write(io->io_addr, IOAPIC_REDTBL_LO(src->io_intpin), + src->io_lowreg); + if (!locked) + mtx_unlock_spin(&icu_lock); + } } static u_int @@ -230,7 +274,7 @@ } if (eoi == PIC_EOI) - _ioapic_eoi_source(isrc); + _ioapic_eoi_source(isrc, 1); mtx_unlock_spin(&icu_lock); } @@ -239,7 +283,7 @@ ioapic_eoi_source(struct intsrc *isrc) { - _ioapic_eoi_source(isrc); + _ioapic_eoi_source(isrc, 0); } /* @@ -545,6 +589,22 @@ io->io_addr = apic; io->io_paddr = addr; + if (bootverbose) { + printf("ioapic%u: ver 0x%02x maxredir 0x%02x\n", io->io_id, + (value & IOART_VER_VERSION), (value & IOART_VER_MAXREDIR) + >> MAXREDIRSHIFT); + } + /* + * The summary information about IO-APIC versions is taken from + * the Linux kernel source: + * 0Xh 82489DX + * 1Xh I/OAPIC or I/O(x)APIC which are not PCI 2.2 Compliant + * 2Xh I/O(x)APIC which is PCI 2.2 Compliant + * 30h-FFh Reserved + * IO-APICs with version >= 0x20 have working EOIR register. + */ + io->io_haseoi = (value & IOART_VER_VERSION) >= 0x20; + /* * Initialize pins. Start off with interrupts disabled. Default * to active-hi and edge-triggered for ISA interrupts and active-lo Index: sys/x86/x86/local_apic.c =================================================================== --- sys/x86/x86/local_apic.c +++ sys/x86/x86/local_apic.c @@ -159,11 +159,14 @@ volatile char *lapic_map; vm_paddr_t lapic_paddr; int x2apic_mode; +int lapic_eoi_suppression; static u_long lapic_timer_divisor; static struct eventtimer lapic_et; SYSCTL_NODE(_hw, OID_AUTO, apic, CTLFLAG_RD, 0, "APIC options"); SYSCTL_INT(_hw_apic, OID_AUTO, x2apic_mode, CTLFLAG_RD, &x2apic_mode, 0, ""); +SYSCTL_INT(_hw_apic, OID_AUTO, eoi_suppression, CTLFLAG_RD, + &lapic_eoi_suppression, 0, ""); static uint32_t lapic_read32(enum LAPIC_REGISTERS reg) @@ -380,6 +383,7 @@ static void native_lapic_init(vm_paddr_t addr) { + uint32_t ver; u_int regs[4]; int i, arat; @@ -443,6 +447,20 @@ lapic_et.et_priv = NULL; et_register(&lapic_et); } + + /* + * Set lapic_eoi_suppression after lapic_enable(), to not + * enable suppression in the hardware prematurely. Note that + * we by default enable suppression even when system only has + * one IO-APIC, since EOI is broadcasted to all APIC agents, + * including CPUs, otherwise. + */ + ver = lapic_read32(LAPIC_VERSION); + if ((ver & APIC_VER_EOI_SUPPRESSION) != 0) { + lapic_eoi_suppression = 1; + TUNABLE_INT_FETCH("hw.lapic_eoi_suppression", + &lapic_eoi_suppression); + } } /* @@ -755,6 +773,8 @@ value = lapic_read32(LAPIC_SVR); value &= ~(APIC_SVR_VECTOR | APIC_SVR_FOCUS); value |= APIC_SVR_FEN | APIC_SVR_SWEN | APIC_SPURIOUS_INT; + if (lapic_eoi_suppression) + value |= APIC_SVR_EOI_SUPPRESSION; lapic_write32(LAPIC_SVR, value); } @@ -1562,8 +1582,10 @@ return; #endif /* - * Finish setting up the local APIC on the BSP once we know how to - * properly program the LINT pins. + * Finish setting up the local APIC on the BSP once we know + * how to properly program the LINT pins. In particular, this + * enables the EOI suppression mode, if LAPIC support it and + * user did not disabled the mode. */ lapic_setup(1); if (bootverbose)