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