TESTED: netdde works on smp with this patch
On 7/18/25 9:13 PM, Damien Zammit wrote:
> We now have a different strategy for EOI depending on trigger mode:
> For edge triggered, the eoi comes before the handler so we don't
> miss interrupts. For level triggered, the eoi comes after the handler
> since a high interrupt line doesn't keep triggering until it has
> been eoi'd so makes sense to handle those first.
>
> To facilitate this, we store a helper struct irqinfo that
> contains the vector and the trigger mode per GSI, so we can look up
> in O(1) from memory instead of asking hardware for the trigger mode
> and vector every time we need it.
>
> TESTED: On SMP this stops failure in xhci from hanging rumpnet.
>
> When a second usb driver is executed, the second one detects a problem
> and disables the uhub0 port, which breaks the first usb driver.
> But this does not block rumpnet from serving packets, sharing irq 11.
> The usb stack can be recovered by killing both usb drivers and
> restarting one. Before this change, the behaviour was that irq 11
> became stuck and unusable for any device.
>
> Parallel compiling speed seems improved with this commit on SMP.
>
> UP+apic still works as per usual.
>
> TODO: We still need to work out a strategy to have interrupts enabled
> during the handler, so that nested interrupts that occur via code that
> is executed inside the irq handler to make the device raise a new
> interrupt, are triggered. Currently these do not work properly.
> I believe this is the remaining bug with interrupts.
>
> ---
> i386/i386/apic.h | 5 +++++
> i386/i386at/interrupt.S | 49 +++++++++++++++++++++++++++--------------
> i386/i386at/ioapic.c | 15 +++++++++++--
> x86_64/interrupt.S | 46 +++++++++++++++++++++++++-------------
> 4 files changed, 82 insertions(+), 33 deletions(-)
>
> diff --git a/i386/i386/apic.h b/i386/i386/apic.h
> index 92fb900a..a91fc9ac 100644
> --- a/i386/i386/apic.h
> +++ b/i386/i386/apic.h
> @@ -235,6 +235,11 @@ typedef struct ApicInfo {
> struct IrqOverrideData irq_override_list[MAX_IRQ_OVERRIDE];
> } ApicInfo;
>
> +struct irqinfo {
> + uint8_t trigger;
> + uint8_t vector;
> +};
> +
> int apic_data_init(void);
> void apic_add_cpu(uint16_t apic_id);
> void apic_lapic_init(ApicLocalUnit* lapic_ptr);
> diff --git a/i386/i386at/interrupt.S b/i386/i386at/interrupt.S
> index 77424b43..4662d024 100644
> --- a/i386/i386at/interrupt.S
> +++ b/i386/i386at/interrupt.S
> @@ -61,6 +61,35 @@ ENTRY(interrupt)
> je _call_local_ast
> #endif
>
> + movb %cs:EXT(irqinfo)(%ecx),%al /* look up
> irq_info[irq].trigger */
> + testb $1,%al /* was this a level triggered
> interrupt? */
> + jz _eoi /* no, edge: eoi before handling */
> +
> +_call_handler:
> +
> + movl S_IPL,%eax
> + movl %eax,4(%esp) /* previous ipl as 2nd arg */
> +
> + movl S_RET,%eax
> + movl %eax,8(%esp) /* return address as 3rd arg */
> +
> + movl S_REGS,%eax
> + movl %eax,12(%esp) /* address of interrupted registers as
> 4th arg */
> +
> + movl S_IRQ,%eax /* copy irq number */
> +
> + shll $2,%eax /* irq * 4 */
> + movl EXT(iunit)(%eax),%edx /* get device unit number */
> + movl %edx,(%esp) /* unit number as 1st arg */
> +
> + call *EXT(ivect)(%eax) /* call interrupt handler */
> +
> + movl S_IRQ,%ecx /* restore irq number */
> + movb %cs:EXT(irqinfo)(%ecx),%al /* look up
> irq_info[irq].trigger */
> + testb $1,%al /* was this a level triggered
> interrupt? */
> + jz _completed /* no, edge: we are done */
> +
> +_eoi:
> #ifndef APIC
> movl $1,%eax
> shll %cl,%eax /* get corresponding IRQ mask */
> @@ -102,22 +131,10 @@ ENTRY(interrupt)
> call EXT(ioapic_irq_eoi) /* ioapic irq specific EOI */
> #endif
>
> - movl S_IPL,%eax
> - movl %eax,4(%esp) /* previous ipl as 2nd arg */
> -
> - movl S_RET,%eax
> - movl %eax,8(%esp) /* return address as 3rd arg */
> -
> - movl S_REGS,%eax
> - movl %eax,12(%esp) /* address of interrupted registers as
> 4th arg */
> -
> - movl S_IRQ,%eax /* copy irq number */
> -
> - shll $2,%eax /* irq * 4 */
> - movl EXT(iunit)(%eax),%edx /* get device unit number */
> - movl %edx,(%esp) /* unit number as 1st arg */
> -
> - call *EXT(ivect)(%eax) /* call interrupt handler */
> + movl S_IRQ,%ecx /* restore irq number */
> + movb %cs:EXT(irqinfo)(%ecx),%al /* look up
> irq_info[irq].trigger */
> + testb $1,%al /* was this a level triggered
> interrupt? */
> + jz _call_handler /* no, edge: call handler */
>
> _completed:
> movl S_IPL,%eax /* restore previous ipl */
> diff --git a/i386/i386at/ioapic.c b/i386/i386at/ioapic.c
> index a6c0fd6a..5dc7321c 100644
> --- a/i386/i386at/ioapic.c
> +++ b/i386/i386at/ioapic.c
> @@ -128,6 +128,9 @@ interrupt_handler_fn ivect[NINTR] = {
> /* 63 */ intnull,
> };
>
> +struct irqinfo irqinfo[NINTR];
> +struct irqinfo *irq_info = &irqinfo[0];
> +
> void
> picdisable(void)
> {
> @@ -329,8 +332,7 @@ ioapic_irq_eoi(int pin)
> //}
> } else {
> volatile ApicIoUnit *ioapic = apic_get_ioapic(apic)->ioapic;
> - ioapic_read_entry(apic, pin, &entry.both);
> - ioapic->eoi.r = entry.both.vector;
> + ioapic->eoi.r = irq_info[pin].vector;
> }
>
> simple_unlock_irq(s, &ioapic_lock);
> @@ -420,6 +422,9 @@ ioapic_configure(void)
> entry.both.vector = IOAPIC_INT_BASE + gsi;
> ioapic_write_entry(apic, pin, entry.both);
>
> + irq_info[pin].vector = entry.both.vector;
> + irq_info[pin].trigger = entry.both.trigger;
> +
> /* Set initial state to masked */
> mask_irq(pin);
>
> @@ -454,6 +459,9 @@ ioapic_configure(void)
> entry.both.vector = IOAPIC_INT_BASE + gsi;
> ioapic_write_entry(apic, pin, entry.both);
>
> + irq_info[pin].vector = entry.both.vector;
> + irq_info[pin].trigger = entry.both.trigger;
> +
> /* Set initial state to masked */
> mask_irq(pin);
> }
> @@ -478,6 +486,9 @@ ioapic_configure(void)
> entry.both.vector = IOAPIC_INT_BASE + gsi;
> ioapic_write_entry(apic, pin, entry.both);
>
> + irq_info[pin + ngsis].vector = entry.both.vector;
> + irq_info[pin + ngsis].trigger = entry.both.trigger;
> +
> /* Set initial state to masked */
> mask_irq(pin + ngsis);
> }
> diff --git a/x86_64/interrupt.S b/x86_64/interrupt.S
> index 6fb77727..533b0df5 100644
> --- a/x86_64/interrupt.S
> +++ b/x86_64/interrupt.S
> @@ -61,6 +61,33 @@ ENTRY(interrupt)
> je _call_local_ast
> #endif
>
> + movb %cs:EXT(irqinfo)(%ecx),%al /* look up
> irq_info[irq].trigger */
> + testb $1,%al /* was this a level triggered
> interrupt? */
> + jz _eoi /* no, edge: eoi before handling */
> +
> +_call_handler:
> + ;
> + movq S_IPL,S_ARG1 /* previous ipl as 2nd arg */
> +
> + ;
> + movq S_RET,S_ARG2 /* return address as 3th arg */
> +
> + ;
> + movq S_REGS,S_ARG3 /* address of interrupted registers as
> 4th arg */
> +
> + movl S_IRQ,%eax /* copy irq number */
> + shll $2,%eax /* irq * 4 */
> + movl EXT(iunit)(%rax),%edi /* get device unit number as 1st arg */
> +
> + shll $1,%eax /* irq * 8 */
> + call *EXT(ivect)(%rax) /* call interrupt handler */
> +
> + movl S_IRQ,%ecx /* restore irq number */
> + movb %cs:EXT(irqinfo)(%ecx),%al /* look up
> irq_info[irq].trigger */
> + testb $1,%al /* was this a level triggered
> interrupt? */
> + jz _completed /* no, edge: we are done */
> +
> +_eoi:
> #ifndef APIC
> movl $1,%eax
> shll %cl,%eax /* get corresponding IRQ mask */
> @@ -102,21 +129,10 @@ ENTRY(interrupt)
> call EXT(ioapic_irq_eoi) /* ioapic irq specific EOI */
> #endif
>
> - ;
> - movq S_IPL,S_ARG1 /* previous ipl as 2nd arg */
> -
> - ;
> - movq S_RET,S_ARG2 /* return address as 3th arg */
> -
> - ;
> - movq S_REGS,S_ARG3 /* address of interrupted registers as
> 4th arg */
> -
> - movl S_IRQ,%eax /* copy irq number */
> - shll $2,%eax /* irq * 4 */
> - movl EXT(iunit)(%rax),%edi /* get device unit number as 1st arg */
> -
> - shll $1,%eax /* irq * 8 */
> - call *EXT(ivect)(%rax) /* call interrupt handler */
> + movl S_IRQ,%ecx /* restore irq number */
> + movb %cs:EXT(irqinfo)(%ecx),%al /* look up
> irq_info[irq].trigger */
> + testb $1,%al /* was this a level triggered
> interrupt? */
> + jz _call_handler /* no, edge: call handler after eoi */
>
> _completed:
> movl S_IPL,%edi /* restore previous ipl */
>