diff options
author | Thomas Gleixner <tglx@linutronix.de> | 2011-02-07 03:05:05 -0500 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2011-02-19 06:58:08 -0500 |
commit | 1082687e8d6292a61759eb83358e7db39fed1bf4 (patch) | |
tree | 2a6ba19073820f116050b1730f3e08f97187ddb2 /kernel/irq/spurious.c | |
parent | 2b879eaf095878430c38cbd95e5c0fc4ce65ad8e (diff) |
genirq: Plug race in report_bad_irq()
We cannot walk the action chain unlocked. Even if IRQ_INPROGRESS is
set an action can be removed and we follow a null pointer. It's safe
to take the lock there, because the code which removes the action will
call synchronize_irq() which waits unlocked for IRQ_INPROGRESS going
away.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'kernel/irq/spurious.c')
-rw-r--r-- | kernel/irq/spurious.c | 12 |
1 files changed, 9 insertions, 3 deletions
diff --git a/kernel/irq/spurious.c b/kernel/irq/spurious.c index 3089d3b9d5f3..2fbfda2716e1 100644 --- a/kernel/irq/spurious.c +++ b/kernel/irq/spurious.c | |||
@@ -139,15 +139,13 @@ static void poll_spurious_irqs(unsigned long dummy) | |||
139 | * | 139 | * |
140 | * (The other 100-of-100,000 interrupts may have been a correctly | 140 | * (The other 100-of-100,000 interrupts may have been a correctly |
141 | * functioning device sharing an IRQ with the failing one) | 141 | * functioning device sharing an IRQ with the failing one) |
142 | * | ||
143 | * Called under desc->lock | ||
144 | */ | 142 | */ |
145 | |||
146 | static void | 143 | static void |
147 | __report_bad_irq(unsigned int irq, struct irq_desc *desc, | 144 | __report_bad_irq(unsigned int irq, struct irq_desc *desc, |
148 | irqreturn_t action_ret) | 145 | irqreturn_t action_ret) |
149 | { | 146 | { |
150 | struct irqaction *action; | 147 | struct irqaction *action; |
148 | unsigned long flags; | ||
151 | 149 | ||
152 | if (action_ret != IRQ_HANDLED && action_ret != IRQ_NONE) { | 150 | if (action_ret != IRQ_HANDLED && action_ret != IRQ_NONE) { |
153 | printk(KERN_ERR "irq event %d: bogus return value %x\n", | 151 | printk(KERN_ERR "irq event %d: bogus return value %x\n", |
@@ -159,6 +157,13 @@ __report_bad_irq(unsigned int irq, struct irq_desc *desc, | |||
159 | dump_stack(); | 157 | dump_stack(); |
160 | printk(KERN_ERR "handlers:\n"); | 158 | printk(KERN_ERR "handlers:\n"); |
161 | 159 | ||
160 | /* | ||
161 | * We need to take desc->lock here. note_interrupt() is called | ||
162 | * w/o desc->lock held, but IRQ_PROGRESS set. We might race | ||
163 | * with something else removing an action. It's ok to take | ||
164 | * desc->lock here. See synchronize_irq(). | ||
165 | */ | ||
166 | raw_spin_lock_irqsave(&desc->lock, flags); | ||
162 | action = desc->action; | 167 | action = desc->action; |
163 | while (action) { | 168 | while (action) { |
164 | printk(KERN_ERR "[<%p>]", action->handler); | 169 | printk(KERN_ERR "[<%p>]", action->handler); |
@@ -167,6 +172,7 @@ __report_bad_irq(unsigned int irq, struct irq_desc *desc, | |||
167 | printk("\n"); | 172 | printk("\n"); |
168 | action = action->next; | 173 | action = action->next; |
169 | } | 174 | } |
175 | raw_spin_unlock_irqrestore(&desc->lock, flags); | ||
170 | } | 176 | } |
171 | 177 | ||
172 | static void | 178 | static void |