Skip to content

Commit

Permalink
um: Fix IRQ controller regression on console read
Browse files Browse the repository at this point in the history
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: ff6a179 ("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>
  • Loading branch information
Jouni Malinen authored and Richard Weinberger committed Jul 2, 2019
1 parent 4b972a0 commit bebe468
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 8 deletions.
52 changes: 44 additions & 8 deletions arch/um/drivers/chan_kern.c
Original file line number Diff line number Diff line change
Expand Up @@ -171,19 +171,55 @@ int enable_chan(struct line *line)
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)
{
unsigned long flags;

if (!chan->opened)
return;

/* we can safely call free now - it will be marked
* as free and freed once the IRQ stopped processing
*/
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;
if (delay_free_irq) {
spin_lock_irqsave(&irqs_to_free_lock, flags);
list_add(&chan->free_list, &irqs_to_free);
spin_unlock_irqrestore(&irqs_to_free_lock, flags);
} else {
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;
}
if (chan->ops->close != NULL)
(*chan->ops->close)(chan->fd, chan->data);

Expand Down
4 changes: 4 additions & 0 deletions arch/um/kernel/irq.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
#include <irq_user.h>


extern void free_irqs(void);

/* When epoll triggers we do not know why it did so
* we can also have different IRQs for read and write.
* This is why we keep a small irq_fd array for each fd -
Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit bebe468

Please sign in to comment.