Commit d329e071 authored by Jouni Malinen's avatar Jouni Malinen Committed by Greg Kroah-Hartman

um: Fix IRQ controller regression on console read

[ Upstream commit bebe4681 ]

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: ff6a1798 ("Epoll based IRQ controller")
Signed-off-by: default avatarJouni Malinen <j@w1.fi>
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
Signed-off-by: default avatarRichard Weinberger <richard@nod.at>
Signed-off-by: default avatarSasha Levin <sashal@kernel.org>
parent e793ac76
...@@ -171,19 +171,55 @@ int enable_chan(struct line *line) ...@@ -171,19 +171,55 @@ int enable_chan(struct line *line)
return err; return err;
} }
/* Items are added in IRQ context, when free_irq can't be called, and
* removed in process context, when it can.
* This handles interrupt sources which disappear, and which need to
* be permanently disabled. This is discovered in IRQ context, but
* the freeing of the IRQ must be done later.
*/
static DEFINE_SPINLOCK(irqs_to_free_lock);
static LIST_HEAD(irqs_to_free);
void free_irqs(void)
{
struct chan *chan;
LIST_HEAD(list);
struct list_head *ele;
unsigned long flags;
spin_lock_irqsave(&irqs_to_free_lock, flags);
list_splice_init(&irqs_to_free, &list);
spin_unlock_irqrestore(&irqs_to_free_lock, flags);
list_for_each(ele, &list) {
chan = list_entry(ele, struct chan, free_list);
if (chan->input && chan->enabled)
um_free_irq(chan->line->driver->read_irq, chan);
if (chan->output && chan->enabled)
um_free_irq(chan->line->driver->write_irq, chan);
chan->enabled = 0;
}
}
static void close_one_chan(struct chan *chan, int delay_free_irq) static void close_one_chan(struct chan *chan, int delay_free_irq)
{ {
unsigned long flags;
if (!chan->opened) if (!chan->opened)
return; return;
/* we can safely call free now - it will be marked if (delay_free_irq) {
* as free and freed once the IRQ stopped processing spin_lock_irqsave(&irqs_to_free_lock, flags);
*/ list_add(&chan->free_list, &irqs_to_free);
if (chan->input && chan->enabled) spin_unlock_irqrestore(&irqs_to_free_lock, flags);
um_free_irq(chan->line->driver->read_irq, chan); } else {
if (chan->output && chan->enabled) if (chan->input && chan->enabled)
um_free_irq(chan->line->driver->write_irq, chan); um_free_irq(chan->line->driver->read_irq, chan);
chan->enabled = 0; if (chan->output && chan->enabled)
um_free_irq(chan->line->driver->write_irq, chan);
chan->enabled = 0;
}
if (chan->ops->close != NULL) if (chan->ops->close != NULL)
(*chan->ops->close)(chan->fd, chan->data); (*chan->ops->close)(chan->fd, chan->data);
......
...@@ -21,6 +21,8 @@ ...@@ -21,6 +21,8 @@
#include <irq_user.h> #include <irq_user.h>
extern void free_irqs(void);
/* When epoll triggers we do not know why it did so /* When epoll triggers we do not know why it did so
* we can also have different IRQs for read and write. * we can also have different IRQs for read and write.
* This is why we keep a small irq_fd array for each fd - * This is why we keep a small irq_fd array for each fd -
...@@ -100,6 +102,8 @@ void sigio_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs) ...@@ -100,6 +102,8 @@ void sigio_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs)
} }
} }
} }
free_irqs();
} }
static int assign_epoll_events_to_irq(struct irq_entry *irq_entry) static int assign_epoll_events_to_irq(struct irq_entry *irq_entry)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment