Commit bebe4681 authored by Jouni Malinen's avatar Jouni Malinen Committed by Richard Weinberger
Browse files

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: 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>
parent 4b972a01
Loading
Loading
Loading
Loading
+44 −8
Original line number Diff line number Diff line
@@ -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 (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);

+4 −0
Original line number Diff line number Diff line
@@ -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 -
@@ -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)