diff options
author | Samuel Thibault <samuel.thibault@ens-lyon.org> | 2019-11-12 22:01:13 +0100 |
---|---|---|
committer | Samuel Thibault <samuel.thibault@ens-lyon.org> | 2019-11-12 22:01:13 +0100 |
commit | 259f75fa47ae7bc03c91cc102dc9480f14cd7266 (patch) | |
tree | 53e722f82cf69bda0980f4924ceb8efdd3a2f7e9 | |
parent | db1ae5fb55cc1a46c7d2e5803ba892403c1dd0c1 (diff) |
Fix hang on netdde crash with a nacked interrupt
If netdde crashed without acknowledging the interrupt, the hardware
might be stuck in a situation where it keeps the interrupt raised, and
thus linux_intr keeps getting called. linux_intr should detect that we
have removed handlers and then just disable the interrupt. Hopefully
when the driver restarts it will reset the card, and thus the interrupt
will stop before it enables it again.
-rw-r--r-- | linux/dev/arch/i386/kernel/irq.c | 93 |
1 files changed, 51 insertions, 42 deletions
diff --git a/linux/dev/arch/i386/kernel/irq.c b/linux/dev/arch/i386/kernel/irq.c index 39f88b6b..a3b34956 100644 --- a/linux/dev/arch/i386/kernel/irq.c +++ b/linux/dev/arch/i386/kernel/irq.c @@ -90,6 +90,49 @@ static struct linux_action *irq_action[16] = }; /* + * Mask an IRQ. + */ +static inline void +mask_irq (unsigned int irq_nr) +{ + int new_pic_mask = curr_pic_mask | 1 << irq_nr; + + if (curr_pic_mask != new_pic_mask) + { + curr_pic_mask = new_pic_mask; + if (irq_nr < 8) + outb (curr_pic_mask & 0xff, PIC_MASTER_OCW); + else + outb (curr_pic_mask >> 8, PIC_SLAVE_OCW); + } +} + +/* + * Unmask an IRQ. + */ +static inline void +unmask_irq (unsigned int irq_nr) +{ + int mask; + int new_pic_mask; + + mask = 1 << irq_nr; + if (irq_nr >= 8) + mask |= 1 << 2; + + new_pic_mask = curr_pic_mask & ~mask; + + if (curr_pic_mask != new_pic_mask) + { + curr_pic_mask = new_pic_mask; + if (irq_nr < 8) + outb (curr_pic_mask & 0xff, PIC_MASTER_OCW); + else + outb (curr_pic_mask >> 8, PIC_SLAVE_OCW); + } +} + +/* * Generic interrupt handler for Linux devices. * Set up a fake `struct pt_regs' then call the real handler. */ @@ -119,6 +162,7 @@ linux_intr (int irq) *prev = action->next; linux_kfree(action); action = *prev; + continue; } } else if (action->handler) @@ -127,52 +171,17 @@ linux_intr (int irq) action = action->next; } - restore_flags (flags); - - intr_count--; -} - -/* - * Mask an IRQ. - */ -static inline void -mask_irq (unsigned int irq_nr) -{ - int new_pic_mask = curr_pic_mask | 1 << irq_nr; - - if (curr_pic_mask != new_pic_mask) + if (!irq_action[irq]) { - curr_pic_mask = new_pic_mask; - if (irq_nr < 8) - outb (curr_pic_mask & 0xff, PIC_MASTER_OCW); - else - outb (curr_pic_mask >> 8, PIC_SLAVE_OCW); + /* No handler any more, disable interrupt */ + mask_irq (irq); + ivect[irq] = intnull; + iunit[irq] = irq; } -} - -/* - * Unmask an IRQ. - */ -static inline void -unmask_irq (unsigned int irq_nr) -{ - int mask; - int new_pic_mask; - - mask = 1 << irq_nr; - if (irq_nr >= 8) - mask |= 1 << 2; - new_pic_mask = curr_pic_mask & ~mask; + restore_flags (flags); - if (curr_pic_mask != new_pic_mask) - { - curr_pic_mask = new_pic_mask; - if (irq_nr < 8) - outb (curr_pic_mask & 0xff, PIC_MASTER_OCW); - else - outb (curr_pic_mask >> 8, PIC_SLAVE_OCW); - } + intr_count--; } /* Count how many subsystems requested to disable each IRQ */ |