[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH gnumach] Fix interrupt handling
From: |
Damien Zammit |
Subject: |
[PATCH gnumach] Fix interrupt handling |
Date: |
Mon, 02 Oct 2023 03:39:13 +0000 |
Logic for interrupts:
- 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 registers, queue_intr masks the irq.
- interrupt.S lowers spl with splx_cli, thus IF still cleared
- iret, that clears the IF
- later on, userland acks the IRQ, that unmasks the irq
The key to this change is that all interrupts, including IPIs, are
treated the same way. Eg. the spl level is now raised before an IPI and
restored after. Also, EOI is not needed inside irq_acknowledge.
With this change and the experimental change not to dispatch threads
direct to idle processors in the scheduler, I no longer observe kernel
faults, but an occasional hang does occur.
---
device/intr.c | 16 ++++++++++++++--
i386/i386at/interrupt.S | 28 +++++++++++++---------------
x86_64/interrupt.S | 28 +++++++++++++---------------
3 files changed, 40 insertions(+), 32 deletions(-)
diff --git a/device/intr.c b/device/intr.c
index 15029440..97cfa377 100644
--- a/device/intr.c
+++ b/device/intr.c
@@ -50,6 +50,20 @@ search_intr (struct irqdev *dev, ipc_port_t dst_port)
return NULL;
}
+
+/*
+ * Interrupt handling logic:
+ *
+ * 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.
+ * interrupt.S lowers spl with splx_cli, thus IF still cleared
+ * iret, that also clears the IF
+ *
+ * later on, (irq_acknowledge), userland acks the IRQ, that unmasks the irq
+ */
kern_return_t
irq_acknowledge (ipc_port_t receive_port)
{
@@ -76,8 +90,6 @@ irq_acknowledge (ipc_port_t receive_port)
if (ret)
return ret;
- (*(irqtab.irqdev_ack)) (&irqtab, e->id);
-
__enable_irq (irqtab.irq[e->id]);
return D_SUCCESS;
diff --git a/i386/i386at/interrupt.S b/i386/i386at/interrupt.S
index 8ae6b97c..77424b43 100644
--- a/i386/i386at/interrupt.S
+++ b/i386/i386at/interrupt.S
@@ -45,14 +45,6 @@ ENTRY(interrupt)
ret /* if so, just return */
1:
#endif
-#if NCPUS > 1
- cmpl $CALL_PMAP_UPDATE,%eax /* was this a SMP pmap_update request?
*/
- je _call_single
-
- cmpl $CALL_AST_CHECK,%eax /* was this a SMP remote -> local ast
request? */
- je _call_local_ast
-#endif
-
subl $24,%esp /* Two local variables + 4 parameters */
movl %eax,S_IRQ /* save irq number */
@@ -61,6 +53,14 @@ ENTRY(interrupt)
movl S_IRQ,%ecx /* restore irq number */
+#if NCPUS > 1
+ cmpl $CALL_PMAP_UPDATE,%ecx /* was this a SMP pmap_update request?
*/
+ je _call_single
+
+ cmpl $CALL_AST_CHECK,%ecx /* was this a SMP remote -> local ast
request? */
+ je _call_local_ast
+#endif
+
#ifndef APIC
movl $1,%eax
shll %cl,%eax /* get corresponding IRQ mask */
@@ -98,11 +98,8 @@ ENTRY(interrupt)
outb %al,$(PIC_MASTER_OCW) /* unmask master */
2:
#else
- cmpl $16,%ecx /* was this a low ISA intr? */
- jge _no_eoi /* no, must be PCI (let irq_ack handle
EOI) */
movl %ecx,(%esp) /* load irq number as 1st arg */
call EXT(ioapic_irq_eoi) /* ioapic irq specific EOI */
-_no_eoi:
#endif
movl S_IPL,%eax
@@ -122,6 +119,7 @@ _no_eoi:
call *EXT(ivect)(%eax) /* call interrupt handler */
+_completed:
movl S_IPL,%eax /* restore previous ipl */
movl %eax,(%esp)
call splx_cli /* restore previous ipl */
@@ -131,14 +129,14 @@ _no_eoi:
#if NCPUS > 1
_call_single:
- cli /* no nested interrupts */
call EXT(lapic_eoi) /* lapic EOI before the handler to
allow extra update */
call EXT(pmap_update_interrupt)
- ret
+ jmp _completed
_call_local_ast:
- call EXT(ast_check) /* AST check on this cpu */
call EXT(lapic_eoi) /* lapic EOI */
- ret
+ call EXT(ast_check) /* AST check on this cpu */
+ jmp _completed
+
#endif
END(interrupt)
diff --git a/x86_64/interrupt.S b/x86_64/interrupt.S
index fcd5a032..6fb77727 100644
--- a/x86_64/interrupt.S
+++ b/x86_64/interrupt.S
@@ -45,14 +45,6 @@ ENTRY(interrupt)
ret /* if so, just return */
1:
#endif
-#if NCPUS > 1
- cmpl $CALL_PMAP_UPDATE,%eax /* was this a SMP pmap_update request?
*/
- je _call_single
-
- cmpl $CALL_AST_CHECK,%eax /* was this a SMP remote -> local ast
request? */
- je _call_local_ast
-#endif
-
subq $16,%rsp /* Two local variables */
movl %eax,S_IRQ /* save irq number */
@@ -61,6 +53,14 @@ ENTRY(interrupt)
movl S_IRQ,%ecx /* restore irq number */
+#if NCPUS > 1
+ cmpl $CALL_PMAP_UPDATE,%ecx /* was this a SMP pmap_update request?
*/
+ je _call_single
+
+ cmpl $CALL_AST_CHECK,%ecx /* was this a SMP remote -> local ast
request? */
+ je _call_local_ast
+#endif
+
#ifndef APIC
movl $1,%eax
shll %cl,%eax /* get corresponding IRQ mask */
@@ -98,11 +98,8 @@ ENTRY(interrupt)
outb %al,$(PIC_MASTER_OCW) /* unmask master */
2:
#else
- cmpl $16,%ecx /* was this a low ISA intr? */
- jge _no_eoi /* no, must be PCI (let irq_ack handle
EOI) */
movl %ecx,%edi /* load irq number as 1st arg */
call EXT(ioapic_irq_eoi) /* ioapic irq specific EOI */
-_no_eoi:
#endif
;
@@ -121,6 +118,7 @@ _no_eoi:
shll $1,%eax /* irq * 8 */
call *EXT(ivect)(%rax) /* call interrupt handler */
+_completed:
movl S_IPL,%edi /* restore previous ipl */
call splx_cli /* restore previous ipl */
@@ -129,14 +127,14 @@ _no_eoi:
#if NCPUS > 1
_call_single:
- cli /* no nested interrupts */
call EXT(lapic_eoi) /* lapic EOI before the handler to
allow extra update */
call EXT(pmap_update_interrupt)
- ret
+ jmp _completed
_call_local_ast:
- call EXT(ast_check) /* AST check on this cpu */
call EXT(lapic_eoi) /* lapic EOI */
- ret
+ call EXT(ast_check) /* AST check on this cpu */
+ jmp _completed
+
#endif
END(interrupt)
--
2.40.1
- [PATCH gnumach] Fix interrupt handling,
Damien Zammit <=