diff options
author | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-05-24 11:37:14 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-05-24 11:37:14 -0400 |
commit | 92ea77275b5345c1300433f28689493dc4163f24 (patch) | |
tree | 8813e2453b081d700ae32b7dc6f056f2eba8ebe7 /kernel | |
parent | db2668fdbeb2e3c95ebadf95856c9e31a8a8d569 (diff) |
Fix crash with irqpoll due to the IRQF_IRQPOLL flag testing
With irqpoll enabled, trying to test the IRQF_IRQPOLL flag in the
actions would cause a NULL pointer dereference if no action was
installed (for example, the driver might have been unloaded with
interrupts still pending).
So be a bit more careful about testing the flag by making sure to test
for that case.
(The actual _change_ is trivial, the patch is more than a one-liner
because I rewrote the testing to also be much more readable.
Original (discarded) bugfix by Bernhard Walle.
Cc: Bernhard Walle <bwalle@suse.de>
Tested-by: Vivek Goyal <vgoyal@in.ibm.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/irq/spurious.c | 46 |
1 files changed, 37 insertions, 9 deletions
diff --git a/kernel/irq/spurious.c b/kernel/irq/spurious.c index b0d81aae472f..bd9e272d55e9 100644 --- a/kernel/irq/spurious.c +++ b/kernel/irq/spurious.c | |||
@@ -135,6 +135,39 @@ report_bad_irq(unsigned int irq, struct irq_desc *desc, irqreturn_t action_ret) | |||
135 | } | 135 | } |
136 | } | 136 | } |
137 | 137 | ||
138 | static inline int try_misrouted_irq(unsigned int irq, struct irq_desc *desc, irqreturn_t action_ret) | ||
139 | { | ||
140 | struct irqaction *action; | ||
141 | |||
142 | if (!irqfixup) | ||
143 | return 0; | ||
144 | |||
145 | /* We didn't actually handle the IRQ - see if it was misrouted? */ | ||
146 | if (action_ret == IRQ_NONE) | ||
147 | return 1; | ||
148 | |||
149 | /* | ||
150 | * But for 'irqfixup == 2' we also do it for handled interrupts if | ||
151 | * they are marked as IRQF_IRQPOLL (or for irq zero, which is the | ||
152 | * traditional PC timer interrupt.. Legacy) | ||
153 | */ | ||
154 | if (irqfixup < 2) | ||
155 | return 0; | ||
156 | |||
157 | if (!irq) | ||
158 | return 1; | ||
159 | |||
160 | /* | ||
161 | * Since we don't get the descriptor lock, "action" can | ||
162 | * change under us. We don't really care, but we don't | ||
163 | * want to follow a NULL pointer. So tell the compiler to | ||
164 | * just load it once by using a barrier. | ||
165 | */ | ||
166 | action = desc->action; | ||
167 | barrier(); | ||
168 | return action && (action->flags & IRQF_IRQPOLL); | ||
169 | } | ||
170 | |||
138 | void note_interrupt(unsigned int irq, struct irq_desc *desc, | 171 | void note_interrupt(unsigned int irq, struct irq_desc *desc, |
139 | irqreturn_t action_ret) | 172 | irqreturn_t action_ret) |
140 | { | 173 | { |
@@ -144,15 +177,10 @@ void note_interrupt(unsigned int irq, struct irq_desc *desc, | |||
144 | report_bad_irq(irq, desc, action_ret); | 177 | report_bad_irq(irq, desc, action_ret); |
145 | } | 178 | } |
146 | 179 | ||
147 | if (unlikely(irqfixup)) { | 180 | if (unlikely(try_misrouted_irq(irq, desc, action_ret))) { |
148 | /* Don't punish working computers */ | 181 | int ok = misrouted_irq(irq); |
149 | if ((irqfixup == 2 && ((irq == 0) || | 182 | if (action_ret == IRQ_NONE) |
150 | (desc->action->flags & IRQF_IRQPOLL))) || | 183 | desc->irqs_unhandled -= ok; |
151 | action_ret == IRQ_NONE) { | ||
152 | int ok = misrouted_irq(irq); | ||
153 | if (action_ret == IRQ_NONE) | ||
154 | desc->irqs_unhandled -= ok; | ||
155 | } | ||
156 | } | 184 | } |
157 | 185 | ||
158 | desc->irq_count++; | 186 | desc->irq_count++; |