This avoids race condition with multiple devices raising interrupts
simultaneously on the same IRQ and causing mask to fail.

TESTED: on SMP and UP+apic gnumach, boots to console and receives
a large file over rumpnet on debian hurd-i386.

---
 i386/i386/irq.c         | 49 ++++++++++++++++++++++++++++++++---------
 i386/i386/irq.h         |  1 +
 i386/i386at/model_dep.c |  1 +
 3 files changed, 40 insertions(+), 11 deletions(-)

diff --git a/i386/i386/irq.c b/i386/i386/irq.c
index 91318f67..6268db18 100644
--- a/i386/i386/irq.c
+++ b/i386/i386/irq.c
@@ -34,32 +34,59 @@ irq_eoi (struct irqdev *dev, int id)
 #endif
 }
 
-static unsigned int ndisabled_irq[NINTR];
+/* Each array elem fits in a cache line */
+struct nested_irq {
+  simple_lock_irq_data_t irq_lock;
+  int32_t ndisabled;
+  uint32_t unused[14];
+} __attribute__((packed)) nested_irqs[NINTR];
+
+void
+init_irqs (void)
+{
+  int i;
+
+  for (i = 0; i < NINTR; i++)
+    {
+      simple_lock_init_irq(&nested_irqs[i].irq_lock);
+      nested_irqs[i].ndisabled = 0;
+    }
+}
 
 void
 __disable_irq (irq_t irq_nr)
 {
+  spl_t s;
+  struct nested_irq *nirq = &nested_irqs[irq_nr];
+
   assert (irq_nr < NINTR);
 
-  spl_t s = splhigh();
-  ndisabled_irq[irq_nr]++;
-  assert (ndisabled_irq[irq_nr] > 0);
-  if (ndisabled_irq[irq_nr] == 1)
+  s = simple_lock_irq(&nirq->irq_lock);
+
+  nirq->ndisabled++;
+  assert (nirq->ndisabled > 0);
+  if (nirq->ndisabled == 1)
     mask_irq (irq_nr);
-  splx(s);
+
+  simple_unlock_irq(s, &nirq->irq_lock);
 }
 
 void
 __enable_irq (irq_t irq_nr)
 {
+  spl_t s;
+  struct nested_irq *nirq = &nested_irqs[irq_nr];
+
   assert (irq_nr < NINTR);
 
-  spl_t s = splhigh();
-  assert (ndisabled_irq[irq_nr] > 0);
-  ndisabled_irq[irq_nr]--;
-  if (ndisabled_irq[irq_nr] == 0)
+  s = simple_lock_irq(&nirq->irq_lock);
+
+  assert (nirq->ndisabled > 0);
+  nirq->ndisabled--;
+  if (nirq->ndisabled == 0)
     unmask_irq (irq_nr);
-  splx(s);
+
+  simple_unlock_irq(s, &nirq->irq_lock);
 }
 
 struct irqdev irqtab = {
diff --git a/i386/i386/irq.h b/i386/i386/irq.h
index 72bbe57b..e1f41045 100644
--- a/i386/i386/irq.h
+++ b/i386/i386/irq.h
@@ -23,6 +23,7 @@
 
 typedef unsigned int irq_t;
 
+void init_irqs (void);
 void __enable_irq (irq_t irq);
 void __disable_irq (irq_t irq);
 
diff --git a/i386/i386at/model_dep.c b/i386/i386at/model_dep.c
index 42dadeb8..afb4fadd 100644
--- a/i386/i386at/model_dep.c
+++ b/i386/i386at/model_dep.c
@@ -176,6 +176,7 @@ void machine_init(void)
 #if defined(APIC)
        ioapic_configure();
 #endif
+       init_irqs();
        clkstart();
 
        /*
-- 
2.45.2



Reply via email to