[syslinux:lwip] pxe: Add code to detect a struck interrupt line and disable (go to poll)

syslinux-bot for H. Peter Anvin hpa at linux.intel.com
Tue May 29 16:09:05 PDT 2012


Commit-ID:  624432a3429aa6c57eb8d2b03faf64c4edfdf0cc
Gitweb:     http://www.syslinux.org/commit/624432a3429aa6c57eb8d2b03faf64c4edfdf0cc
Author:     H. Peter Anvin <hpa at linux.intel.com>
AuthorDate: Tue, 29 May 2012 16:06:23 -0700
Committer:  H. Peter Anvin <hpa at linux.intel.com>
CommitDate: Tue, 29 May 2012 16:06:23 -0700

pxe: Add code to detect a struck interrupt line and disable (go to poll)

Add code to detect a stuck interrupt line and disable it; at that
point we will go to polling mode.  Currently we only give ourselves 3
timer ticks after initialization to detect this, but this is good
enough to deal with gPXE on virtio.

Signed-off-by: H. Peter Anvin <hpa at linux.intel.com>

---
 core/fs/pxe/isr.c |   42 ++++++++++++++++++++++++++++++++----------
 core/pxeisr.inc   |   42 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 74 insertions(+), 10 deletions(-)

diff --git a/core/fs/pxe/isr.c b/core/fs/pxe/isr.c
index c073e03..e2bf2f6 100644
--- a/core/fs/pxe/isr.c
+++ b/core/fs/pxe/isr.c
@@ -13,15 +13,19 @@
 #include <sys/io.h>
 
 extern uint8_t pxe_irq_pending;
+extern volatile uint8_t pxe_irq_timeout;
 static DECLARE_INIT_SEMAPHORE(pxe_receive_thread_sem, 0);
 static struct thread *pxe_thread, *poll_thread;
 
+/*
+ * Note: this *must* be called with interrupts enabled.
+ */
 static bool install_irq_vector(uint8_t irq, void (*isr)(void), far_ptr_t *old)
 {
     far_ptr_t *entry;
     unsigned int vec;
     uint8_t mask, mymask;
-    irq_state_t irqstate;
+    uint32_t now;
 
     if (irq < 8)
 	vec = irq + 0x08;
@@ -30,7 +34,7 @@ static bool install_irq_vector(uint8_t irq, void (*isr)(void), far_ptr_t *old)
     else
 	return false;
 
-    irqstate = irq_save();
+    cli();
 
     entry = (far_ptr_t *)(vec << 2);
     *old = *entry;
@@ -51,18 +55,28 @@ static bool install_irq_vector(uint8_t irq, void (*isr)(void), far_ptr_t *old)
 	outb(mask, 0x21);
     }
 
-    irq_restore(irqstate);
+    sti();
+
+    now = jiffies();
+
+    /* Some time to watch for stuck interrupts */
+    while (jiffies() - now < 4 && !pxe_irq_timeout)
+	hlt();
+
+    if (pxe_irq_timeout)
+	*entry = *old;		/* Restore the old vector */
 
     printf("UNDI: IRQ %d(0x%02x): %04x:%04x -> %04x:%04x\n", irq, vec,
 	   old->seg, old->offs, entry->seg, entry->offs);
 
-    return true;
+    return !pxe_irq_timeout;
 }
 
 static bool uninstall_irq_vector(uint8_t irq, void (*isr), far_ptr_t *old)
 {
     far_ptr_t *entry;
     unsigned int vec;
+    bool rv;
 
     if (!irq)
 	return true;		/* Nothing to uninstall */
@@ -74,13 +88,19 @@ static bool uninstall_irq_vector(uint8_t irq, void (*isr), far_ptr_t *old)
     else
 	return false;
 
+    cli();
+
     entry = (far_ptr_t *)(vec << 2);
 
-    if (entry->ptr != (uint32_t)isr)
-	return false;
+    if (entry->ptr != (uint32_t)isr) {
+	rv = false;
+    } else {
+	*entry = *old;
+	rv = true;
+    }
 
-    *entry = *old;
-    return true;
+    sti();
+    return rv;
 }
 
 static void pxe_poll_wakeups(void)
@@ -205,8 +225,10 @@ void pxe_start_isr(void)
 
     pxe_irq_vector = irq;
 
-    if (irq)
-	install_irq_vector(irq, pxe_isr, &pxe_irq_chain);
+    if (irq) {
+	if (!install_irq_vector(irq, pxe_isr, &pxe_irq_chain))
+	    irq = 0;		/* Install failed or stuck interrupt */
+    }
     
     if (!irq ||	!(pxe_undi_iface.ServiceFlags & PXE_UNDI_IFACE_FLAG_IRQ))
 	poll_thread = start_thread("pxe poll", 4096, POLL_THREAD_PRIORITY,
diff --git a/core/pxeisr.inc b/core/pxeisr.inc
index a1db334..b6434f3 100644
--- a/core/pxeisr.inc
+++ b/core/pxeisr.inc
@@ -3,6 +3,8 @@
 ;
 		section .text16
 
+PXEIRQ_MAX	equ 1000	; Max spurious interrupts in a timer tick
+
 		global pxe_isr
 pxe_isr:
 		cld
@@ -27,6 +29,7 @@ pxe_isr:
 		mov byte [pxenv_undi_isr_buf.funcflag],PXENV_UNDI_ISR_IN_START
 
 		call pxenv
+		mov ax,[__jiffies]
 		jc .notus
 
 		cmp word [pxenv_undi_isr_buf.funcflag],PXENV_UNDI_ISR_OUT_OURS
@@ -45,6 +48,10 @@ pxe_isr:
 .pri_pic:
 		out 0x20,al		; Primary PIC
 
+		mov [pxeirq_last],ax
+		mov word [pxeirq_count],PXEIRQ_MAX
+
+.exit:
 		pop gs
 		pop fs
 		pop es
@@ -53,6 +60,12 @@ pxe_isr:
 		iret
 
 .notus:
+		cmp ax,[pxeirq_last]
+		jne .reset_timeout
+		dec word [pxeirq_count]
+		jz .timeout
+
+.chain:
 		pop gs
 		pop fs
 		pop es
@@ -62,6 +75,29 @@ pxe_isr:
 		global pxe_irq_chain
 pxe_irq_chain	equ $-4
 
+.reset_timeout:
+		mov [pxeirq_last],ax
+		mov word [pxeirq_count],PXEIRQ_MAX
+		jmp .chain
+
+		; Too many spurious interrupts, shut off the interrupts
+		; and go to polling mode
+.timeout:
+		mov al,[pxe_irq_vector]
+		mov dx,21h
+		movzx cx,al
+		shl cx,7-3
+		add dx,cx
+		and al,7
+		xchg ax,cx
+		mov ch,1
+		shl ch,cl
+		in al,dx
+		or al,ch
+		out dx,al
+		mov byte [pxe_irq_timeout],1
+		jmp .exit
+
 
 ; Emulate a PXE interrupt from the polling thread
 		global pxe_poll
@@ -118,9 +154,15 @@ pxenv_undi_isr_buf:
 .pkttype:	resb 1
 .size		equ $-pxenv_undi_isr_buf
 
+		alignb 2
+pxeirq_last	resw 1
+pxeirq_count	resw 1
+
 		global pxe_irq_vector
 pxe_irq_vector	resb 1			; PXE IRQ vector
 		global pxe_irq_pending
 pxe_irq_pending	resb 1			; IRQ pending flag
+		global pxe_irq_timeout
+pxe_irq_timeout	resb 1			; Stuck IRQs, disabled timeout
 
 		section .text16


More information about the Syslinux-commits mailing list