summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDamien Zammit <damien@zamaudio.com>2025-07-19 07:46:11 +0000
committerSamuel Thibault <samuel.thibault@ens-lyon.org>2025-07-19 10:01:50 +0200
commit40a60caa1136c7333c05e733518eaa83fa209f58 (patch)
tree50a8a9466fc83c6ad4df672bfade6e1bee0f8edf
parent8b9765332e5c30690619a314fd780cb8ca3f0169 (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.c14
-rw-r--r--i386/i386at/interrupt.S16
-rw-r--r--x86_64/interrupt.S16
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 */