summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSamuel Thibault <samuel.thibault@ens-lyon.org>2019-11-12 22:01:13 +0100
committerSamuel Thibault <samuel.thibault@ens-lyon.org>2019-11-12 22:01:13 +0100
commit259f75fa47ae7bc03c91cc102dc9480f14cd7266 (patch)
tree53e722f82cf69bda0980f4924ceb8efdd3a2f7e9
parentdb1ae5fb55cc1a46c7d2e5803ba892403c1dd0c1 (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.c93
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 */