diff options
author | Damien Zammit <damien@zamaudio.com> | 2025-07-19 07:46:11 +0000 |
---|---|---|
committer | Samuel Thibault <samuel.thibault@ens-lyon.org> | 2025-07-19 10:01:50 +0200 |
commit | 40a60caa1136c7333c05e733518eaa83fa209f58 (patch) | |
tree | 50a8a9466fc83c6ad4df672bfade6e1bee0f8edf | |
parent | 8b9765332e5c30690619a314fd780cb8ca3f0169 (diff) |
interrupt.S: Change interrupt EOI strategy
We now have a different strategy for EOI depending on trigger mode:
For edge triggered, the behaviour is unchanged; the eoi comes
before the handler so we don't miss interrupts.
For level triggered, the eoi comes after the handler since the high
interrupt line will trigger the interrupt again and stack up, before we mask
it in queue_intr (and we don't risk missing interrupts).
Message-ID: <20250719074547.288203-1-damien@zamaudio.com>
-rw-r--r-- | device/intr.c | 14 | ||||
-rw-r--r-- | i386/i386at/interrupt.S | 16 | ||||
-rw-r--r-- | x86_64/interrupt.S | 16 |
3 files changed, 42 insertions, 4 deletions
diff --git a/device/intr.c b/device/intr.c index 9529492b..07d8acd8 100644 --- a/device/intr.c +++ b/device/intr.c @@ -55,12 +55,18 @@ search_intr (struct irqdev *dev, ipc_port_t dst_port) /* * Interrupt handling logic: + * PCI interrupts are raised on master processor only. * * interrupt.S raises spl (thus IF cleared) - * interrupt.S EOI - * interrupt.S calls the handler - * - for pure in-kernel handlers, they do whatever they want with IF cleared. - * - when a userland handler is registered, queue_intr masks the irq. + * For edge triggered interrupts (to ensure none are missed): + * - interrupt.S EOI + * - interrupt.S calls the handler + * For level triggered interrupts (to prevent stacking): + * - interrupt.S calls the handler + * - interrupt.S EOI + * + * For pure in-kernel handlers, they do whatever they want with IF cleared. + * When a userland handler is registered, queue_intr masks the irq. * interrupt.S lowers spl with splx_cli, thus IF still cleared * iret, that also sets IF * diff --git a/i386/i386at/interrupt.S b/i386/i386at/interrupt.S index 77424b43..ac71473b 100644 --- a/i386/i386at/interrupt.S +++ b/i386/i386at/interrupt.S @@ -61,6 +61,11 @@ ENTRY(interrupt) je _call_local_ast #endif + movb EXT(irqinfo)(,%ecx,2),%al /* look up irq_info[irq].trigger */ + testb $1,%al /* was this a level triggered interrupt? */ + jnz _call_handler /* yes: handle before eoi */ + +_eoi: #ifndef APIC movl $1,%eax shll %cl,%eax /* get corresponding IRQ mask */ @@ -102,6 +107,12 @@ ENTRY(interrupt) call EXT(ioapic_irq_eoi) /* ioapic irq specific EOI */ #endif + movl S_IRQ,%ecx /* restore irq number */ + movb EXT(irqinfo)(,%ecx,2),%al /* look up irq_info[irq].trigger */ + testb $1,%al /* was this a level triggered interrupt? */ + jnz _completed /* yes: we are done */ + +_call_handler: movl S_IPL,%eax movl %eax,4(%esp) /* previous ipl as 2nd arg */ @@ -119,6 +130,11 @@ ENTRY(interrupt) call *EXT(ivect)(%eax) /* call interrupt handler */ + movl S_IRQ,%ecx /* restore irq number */ + movb EXT(irqinfo)(,%ecx,2),%al /* look up irq_info[irq].trigger */ + testb $1,%al /* was this a level triggered interrupt? */ + jnz _eoi /* yes: eoi */ + _completed: movl S_IPL,%eax /* restore previous ipl */ movl %eax,(%esp) diff --git a/x86_64/interrupt.S b/x86_64/interrupt.S index 6fb77727..55fa993a 100644 --- a/x86_64/interrupt.S +++ b/x86_64/interrupt.S @@ -61,6 +61,11 @@ ENTRY(interrupt) je _call_local_ast #endif + movb EXT(irqinfo)(,%ecx,2),%al /* look up irq_info[irq].trigger */ + testb $1,%al /* was this a level triggered interrupt? */ + jnz _call_handler /* yes: call handler before eoi */ + +_eoi: #ifndef APIC movl $1,%eax shll %cl,%eax /* get corresponding IRQ mask */ @@ -102,6 +107,12 @@ ENTRY(interrupt) call EXT(ioapic_irq_eoi) /* ioapic irq specific EOI */ #endif + movl S_IRQ,%ecx + movb EXT(irqinfo)(,%ecx,2),%al /* look up irq_info[irq].trigger */ + testb $1,%al /* was this a level triggered interrupt? */ + jnz _completed /* yes: we are done */ + +_call_handler: ; movq S_IPL,S_ARG1 /* previous ipl as 2nd arg */ @@ -118,6 +129,11 @@ ENTRY(interrupt) shll $1,%eax /* irq * 8 */ call *EXT(ivect)(%rax) /* call interrupt handler */ + movl S_IRQ,%ecx + movb EXT(irqinfo)(,%ecx,2),%al /* look up irq_info[irq].trigger */ + testb $1,%al /* was this a level triggered interrupt? */ + jnz _eoi /* yes: eoi */ + _completed: movl S_IPL,%edi /* restore previous ipl */ call splx_cli /* restore previous ipl */ |