diff options
author | Jouni Malinen <j@w1.fi> | 2019-05-06 08:39:35 -0400 |
---|---|---|
committer | Richard Weinberger <richard@nod.at> | 2019-07-02 17:26:52 -0400 |
commit | bebe4681d0e7e1be2608282dc86645728bc7f623 (patch) | |
tree | e4324cb2fdcc0aedd27fe3bd0ec7e1881fb9869b /arch/um/drivers | |
parent | 4b972a01a7da614b4796475f933094751a295a2f (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.c | 52 |
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 | */ | ||
180 | static DEFINE_SPINLOCK(irqs_to_free_lock); | ||
181 | static LIST_HEAD(irqs_to_free); | ||
182 | |||
183 | void 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 | |||
174 | static void close_one_chan(struct chan *chan, int delay_free_irq) | 205 | static 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 | ||