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 a high interrupt line doesn't keep triggering until it has
been eoi'd so we should handle it first to prevent possibility of
stacking if IF is set during handling (we may want do this in future).
---
 device/intr.c           | 15 +++++++++++----
 i386/i386at/interrupt.S | 16 ++++++++++++++++
 x86_64/interrupt.S      | 16 ++++++++++++++++
 3 files changed, 43 insertions(+), 4 deletions(-)

diff --git a/device/intr.c b/device/intr.c
index 27dfa8ec..6a9970fc 100644
--- a/device/intr.c
+++ b/device/intr.c
@@ -55,12 +55,19 @@ 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
+ *      (there is a window here where we could miss new interrupts!)
+ *   - 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 */
-- 
2.45.2



Reply via email to