This avoids race condition with multiple devices raising interrupts simultaneously on the same IRQ and causing mask to fail. --- i386/i386/irq.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-)
diff --git a/i386/i386/irq.c b/i386/i386/irq.c index 91318f67..d91b3856 100644 --- a/i386/i386/irq.c +++ b/i386/i386/irq.c @@ -41,11 +41,19 @@ __disable_irq (irq_t irq_nr) { assert (irq_nr < NINTR); + /* The spl wrapping protects the same cpu + * from being interrupted while updating this */ spl_t s = splhigh(); - ndisabled_irq[irq_nr]++; + + /* masking the irq unconditionally prevents + * other cpus being interrupted on this irq */ + mask_irq (irq_nr); + + /* we can atomically increment the nesting counter now */ + __atomic_add_fetch(&ndisabled_irq[irq_nr], 1, __ATOMIC_RELAXED); + assert (ndisabled_irq[irq_nr] > 0); - if (ndisabled_irq[irq_nr] == 1) - mask_irq (irq_nr); + splx(s); } @@ -54,11 +62,17 @@ __enable_irq (irq_t irq_nr) { assert (irq_nr < NINTR); + /* The spl wrapping protects the same cpu + * from being interrupted while updating this */ spl_t s = splhigh(); - assert (ndisabled_irq[irq_nr] > 0); - ndisabled_irq[irq_nr]--; - if (ndisabled_irq[irq_nr] == 0) + + /* This could be racy if the irq was already unmasked, + * and the irq is not guaranteed to be masked at this point if more + * than one device has called us almost simultaneously on this irq. + * Therefore it is only safe to decrement the counter here atomically inside the check */ + if (__atomic_add_fetch(&ndisabled_irq[irq_nr], -1, __ATOMIC_RELAXED) == 0) unmask_irq (irq_nr); + splx(s); } -- 2.45.2