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>
This commit is contained in:
parent
4b972a01a7
commit
bebe4681d0
@ -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 @@
|
|||||||
#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)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user