aboutsummaryrefslogtreecommitdiffstats
path: root/arch/um/drivers
diff options
context:
space:
mode:
authorJouni Malinen <j@w1.fi>2019-05-06 08:39:35 -0400
committerRichard Weinberger <richard@nod.at>2019-07-02 17:26:52 -0400
commitbebe4681d0e7e1be2608282dc86645728bc7f623 (patch)
treee4324cb2fdcc0aedd27fe3bd0ec7e1881fb9869b /arch/um/drivers
parent4b972a01a7da614b4796475f933094751a295a2f (diff)
um: Fix IRQ controller regression on console read
The conversion of UML to use epoll based IRQ controller claimed that clone_one_chan() can safely call um_free_irq() while starting to ignore the delay_free_irq parameter that explicitly noted that the IRQ cannot be freed because this is being called from chan_interrupt(). This resulted in free_irq() getting called in interrupt context ("Trying to free IRQ 6 from IRQ context!"). Fix this by restoring previously used delay_free_irq processing. Fixes: ff6a17989c08 ("Epoll based IRQ controller") Signed-off-by: Jouni Malinen <j@w1.fi> Signed-off-by: Johannes Berg <johannes.berg@intel.com> Signed-off-by: Richard Weinberger <richard@nod.at>
Diffstat (limited to 'arch/um/drivers')
-rw-r--r--arch/um/drivers/chan_kern.c52
1 files changed, 44 insertions, 8 deletions
diff --git a/arch/um/drivers/chan_kern.c b/arch/um/drivers/chan_kern.c
index a4e64edb8f38..749d2bf59599 100644
--- a/arch/um/drivers/chan_kern.c
+++ b/arch/um/drivers/chan_kern.c
@@ -171,19 +171,55 @@ int enable_chan(struct line *line)
171 return err; 171 return err;
172} 172}
173 173
174/* Items are added in IRQ context, when free_irq can't be called, and
175 * removed in process context, when it can.
176 * This handles interrupt sources which disappear, and which need to
177 * be permanently disabled. This is discovered in IRQ context, but
178 * the freeing of the IRQ must be done later.
179 */
180static DEFINE_SPINLOCK(irqs_to_free_lock);
181static LIST_HEAD(irqs_to_free);
182
183void free_irqs(void)
184{
185 struct chan *chan;
186 LIST_HEAD(list);
187 struct list_head *ele;
188 unsigned long flags;
189
190 spin_lock_irqsave(&irqs_to_free_lock, flags);
191 list_splice_init(&irqs_to_free, &list);
192 spin_unlock_irqrestore(&irqs_to_free_lock, flags);
193
194 list_for_each(ele, &list) {
195 chan = list_entry(ele, struct chan, free_list);
196
197 if (chan->input && chan->enabled)
198 um_free_irq(chan->line->driver->read_irq, chan);
199 if (chan->output && chan->enabled)
200 um_free_irq(chan->line->driver->write_irq, chan);
201 chan->enabled = 0;
202 }
203}
204
174static void close_one_chan(struct chan *chan, int delay_free_irq) 205static void close_one_chan(struct chan *chan, int delay_free_irq)
175{ 206{
207 unsigned long flags;
208
176 if (!chan->opened) 209 if (!chan->opened)
177 return; 210 return;
178 211
179 /* we can safely call free now - it will be marked 212 if (delay_free_irq) {
180 * as free and freed once the IRQ stopped processing 213 spin_lock_irqsave(&irqs_to_free_lock, flags);
181 */ 214 list_add(&chan->free_list, &irqs_to_free);
182 if (chan->input && chan->enabled) 215 spin_unlock_irqrestore(&irqs_to_free_lock, flags);
183 um_free_irq(chan->line->driver->read_irq, chan); 216 } else {
184 if (chan->output && chan->enabled) 217 if (chan->input && chan->enabled)
185 um_free_irq(chan->line->driver->write_irq, chan); 218 um_free_irq(chan->line->driver->read_irq, chan);
186 chan->enabled = 0; 219 if (chan->output && chan->enabled)
220 um_free_irq(chan->line->driver->write_irq, chan);
221 chan->enabled = 0;
222 }
187 if (chan->ops->close != NULL) 223 if (chan->ops->close != NULL)
188 (*chan->ops->close)(chan->fd, chan->data); 224 (*chan->ops->close)(chan->fd, chan->data);
189 225