Commit 2a1d3ab8 authored by Thomas Gleixner's avatar Thomas Gleixner

genirq: Handle force threading of irqs with primary and thread handler

Force threading of interrupts does not really deal with interrupts
which are requested with a primary and a threaded handler. The current
policy is to leave them alone and let the primary handler run in
interrupt context, but we set the ONESHOT flag for those interrupts as
well.

Kohji Okuno debugged a problem with the SDHCI driver where the
interrupt thread waits for a hardware interrupt to trigger, which can't
work well because the hardware interrupt is masked due to the ONESHOT
flag being set. He proposed to set the ONESHOT flag only if the
interrupt does not provide a thread handler.

Though that does not work either because these interrupts can be
shared. So the other interrupt would rightfully get the ONESHOT flag
set and therefor the same situation would happen again.

To deal with this proper, we need to force thread the primary handler
of such interrupts as well. That means that the primary interrupt
handler is treated as any other primary interrupt handler which is not
marked IRQF_NO_THREAD. The threaded handler becomes a separate thread
so the SDHCI flow logic can be handled gracefully.

The same issue was reported against 4.1-rt.
Reported-and-tested-by: default avatarKohji Okuno <okuno.kohji@jp.panasonic.com>
Reported-By: default avatarMichal Smucr <msmucr@gmail.com>
Reported-and-tested-by: default avatarNathan Sullivan <nathan.sullivan@ni.com>
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
Cc: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Link: http://lkml.kernel.org/r/alpine.DEB.2.11.1509211058080.5606@nanosSigned-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
parent 1f93e4a9
...@@ -102,6 +102,7 @@ typedef irqreturn_t (*irq_handler_t)(int, void *); ...@@ -102,6 +102,7 @@ typedef irqreturn_t (*irq_handler_t)(int, void *);
* @flags: flags (see IRQF_* above) * @flags: flags (see IRQF_* above)
* @thread_fn: interrupt handler function for threaded interrupts * @thread_fn: interrupt handler function for threaded interrupts
* @thread: thread pointer for threaded interrupts * @thread: thread pointer for threaded interrupts
* @secondary: pointer to secondary irqaction (force threading)
* @thread_flags: flags related to @thread * @thread_flags: flags related to @thread
* @thread_mask: bitmask for keeping track of @thread activity * @thread_mask: bitmask for keeping track of @thread activity
* @dir: pointer to the proc/irq/NN/name entry * @dir: pointer to the proc/irq/NN/name entry
...@@ -113,6 +114,7 @@ struct irqaction { ...@@ -113,6 +114,7 @@ struct irqaction {
struct irqaction *next; struct irqaction *next;
irq_handler_t thread_fn; irq_handler_t thread_fn;
struct task_struct *thread; struct task_struct *thread;
struct irqaction *secondary;
unsigned int irq; unsigned int irq;
unsigned int flags; unsigned int flags;
unsigned long thread_flags; unsigned long thread_flags;
......
...@@ -730,6 +730,12 @@ static irqreturn_t irq_nested_primary_handler(int irq, void *dev_id) ...@@ -730,6 +730,12 @@ static irqreturn_t irq_nested_primary_handler(int irq, void *dev_id)
return IRQ_NONE; return IRQ_NONE;
} }
static irqreturn_t irq_forced_secondary_handler(int irq, void *dev_id)
{
WARN(1, "Secondary action handler called for irq %d\n", irq);
return IRQ_NONE;
}
static int irq_wait_for_interrupt(struct irqaction *action) static int irq_wait_for_interrupt(struct irqaction *action)
{ {
set_current_state(TASK_INTERRUPTIBLE); set_current_state(TASK_INTERRUPTIBLE);
...@@ -756,7 +762,8 @@ static int irq_wait_for_interrupt(struct irqaction *action) ...@@ -756,7 +762,8 @@ static int irq_wait_for_interrupt(struct irqaction *action)
static void irq_finalize_oneshot(struct irq_desc *desc, static void irq_finalize_oneshot(struct irq_desc *desc,
struct irqaction *action) struct irqaction *action)
{ {
if (!(desc->istate & IRQS_ONESHOT)) if (!(desc->istate & IRQS_ONESHOT) ||
action->handler == irq_forced_secondary_handler)
return; return;
again: again:
chip_bus_lock(desc); chip_bus_lock(desc);
...@@ -910,6 +917,18 @@ static void irq_thread_dtor(struct callback_head *unused) ...@@ -910,6 +917,18 @@ static void irq_thread_dtor(struct callback_head *unused)
irq_finalize_oneshot(desc, action); irq_finalize_oneshot(desc, action);
} }
static void irq_wake_secondary(struct irq_desc *desc, struct irqaction *action)
{
struct irqaction *secondary = action->secondary;
if (WARN_ON_ONCE(!secondary))
return;
raw_spin_lock_irq(&desc->lock);
__irq_wake_thread(desc, secondary);
raw_spin_unlock_irq(&desc->lock);
}
/* /*
* Interrupt handler thread * Interrupt handler thread
*/ */
...@@ -940,6 +959,8 @@ static int irq_thread(void *data) ...@@ -940,6 +959,8 @@ static int irq_thread(void *data)
action_ret = handler_fn(desc, action); action_ret = handler_fn(desc, action);
if (action_ret == IRQ_HANDLED) if (action_ret == IRQ_HANDLED)
atomic_inc(&desc->threads_handled); atomic_inc(&desc->threads_handled);
if (action_ret == IRQ_WAKE_THREAD)
irq_wake_secondary(desc, action);
wake_threads_waitq(desc); wake_threads_waitq(desc);
} }
...@@ -984,20 +1005,36 @@ void irq_wake_thread(unsigned int irq, void *dev_id) ...@@ -984,20 +1005,36 @@ void irq_wake_thread(unsigned int irq, void *dev_id)
} }
EXPORT_SYMBOL_GPL(irq_wake_thread); EXPORT_SYMBOL_GPL(irq_wake_thread);
static void irq_setup_forced_threading(struct irqaction *new) static int irq_setup_forced_threading(struct irqaction *new)
{ {
if (!force_irqthreads) if (!force_irqthreads)
return; return 0;
if (new->flags & (IRQF_NO_THREAD | IRQF_PERCPU | IRQF_ONESHOT)) if (new->flags & (IRQF_NO_THREAD | IRQF_PERCPU | IRQF_ONESHOT))
return; return 0;
new->flags |= IRQF_ONESHOT; new->flags |= IRQF_ONESHOT;
if (!new->thread_fn) { /*
* Handle the case where we have a real primary handler and a
* thread handler. We force thread them as well by creating a
* secondary action.
*/
if (new->handler != irq_default_primary_handler && new->thread_fn) {
/* Allocate the secondary action */
new->secondary = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
if (!new->secondary)
return -ENOMEM;
new->secondary->handler = irq_forced_secondary_handler;
new->secondary->thread_fn = new->thread_fn;
new->secondary->dev_id = new->dev_id;
new->secondary->irq = new->irq;
new->secondary->name = new->name;
}
/* Deal with the primary handler */
set_bit(IRQTF_FORCED_THREAD, &new->thread_flags); set_bit(IRQTF_FORCED_THREAD, &new->thread_flags);
new->thread_fn = new->handler; new->thread_fn = new->handler;
new->handler = irq_default_primary_handler; new->handler = irq_default_primary_handler;
} return 0;
} }
static int irq_request_resources(struct irq_desc *desc) static int irq_request_resources(struct irq_desc *desc)
...@@ -1017,6 +1054,48 @@ static void irq_release_resources(struct irq_desc *desc) ...@@ -1017,6 +1054,48 @@ static void irq_release_resources(struct irq_desc *desc)
c->irq_release_resources(d); c->irq_release_resources(d);
} }
static int
setup_irq_thread(struct irqaction *new, unsigned int irq, bool secondary)
{
struct task_struct *t;
struct sched_param param = {
.sched_priority = MAX_USER_RT_PRIO/2,
};
if (!secondary) {
t = kthread_create(irq_thread, new, "irq/%d-%s", irq,
new->name);
} else {
t = kthread_create(irq_thread, new, "irq/%d-s-%s", irq,
new->name);
param.sched_priority -= 1;
}
if (IS_ERR(t))
return PTR_ERR(t);
sched_setscheduler_nocheck(t, SCHED_FIFO, &param);
/*
* We keep the reference to the task struct even if
* the thread dies to avoid that the interrupt code
* references an already freed task_struct.
*/
get_task_struct(t);
new->thread = t;
/*
* Tell the thread to set its affinity. This is
* important for shared interrupt handlers as we do
* not invoke setup_affinity() for the secondary
* handlers as everything is already set up. Even for
* interrupts marked with IRQF_NO_BALANCE this is
* correct as we want the thread to move to the cpu(s)
* on which the requesting code placed the interrupt.
*/
set_bit(IRQTF_AFFINITY, &new->thread_flags);
return 0;
}
/* /*
* Internal function to register an irqaction - typically used to * Internal function to register an irqaction - typically used to
* allocate special interrupts that are part of the architecture. * allocate special interrupts that are part of the architecture.
...@@ -1037,6 +1116,8 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) ...@@ -1037,6 +1116,8 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
if (!try_module_get(desc->owner)) if (!try_module_get(desc->owner))
return -ENODEV; return -ENODEV;
new->irq = irq;
/* /*
* Check whether the interrupt nests into another interrupt * Check whether the interrupt nests into another interrupt
* thread. * thread.
...@@ -1054,8 +1135,11 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) ...@@ -1054,8 +1135,11 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
*/ */
new->handler = irq_nested_primary_handler; new->handler = irq_nested_primary_handler;
} else { } else {
if (irq_settings_can_thread(desc)) if (irq_settings_can_thread(desc)) {
irq_setup_forced_threading(new); ret = irq_setup_forced_threading(new);
if (ret)
goto out_mput;
}
} }
/* /*
...@@ -1064,37 +1148,14 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) ...@@ -1064,37 +1148,14 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
* thread. * thread.
*/ */
if (new->thread_fn && !nested) { if (new->thread_fn && !nested) {
struct task_struct *t; ret = setup_irq_thread(new, irq, false);
static const struct sched_param param = { if (ret)
.sched_priority = MAX_USER_RT_PRIO/2,
};
t = kthread_create(irq_thread, new, "irq/%d-%s", irq,
new->name);
if (IS_ERR(t)) {
ret = PTR_ERR(t);
goto out_mput; goto out_mput;
if (new->secondary) {
ret = setup_irq_thread(new->secondary, irq, true);
if (ret)
goto out_thread;
} }
sched_setscheduler_nocheck(t, SCHED_FIFO, &param);
/*
* We keep the reference to the task struct even if
* the thread dies to avoid that the interrupt code
* references an already freed task_struct.
*/
get_task_struct(t);
new->thread = t;
/*
* Tell the thread to set its affinity. This is
* important for shared interrupt handlers as we do
* not invoke setup_affinity() for the secondary
* handlers as everything is already set up. Even for
* interrupts marked with IRQF_NO_BALANCE this is
* correct as we want the thread to move to the cpu(s)
* on which the requesting code placed the interrupt.
*/
set_bit(IRQTF_AFFINITY, &new->thread_flags);
} }
if (!alloc_cpumask_var(&mask, GFP_KERNEL)) { if (!alloc_cpumask_var(&mask, GFP_KERNEL)) {
...@@ -1267,7 +1328,6 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) ...@@ -1267,7 +1328,6 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
irq, nmsk, omsk); irq, nmsk, omsk);
} }
new->irq = irq;
*old_ptr = new; *old_ptr = new;
irq_pm_install_action(desc, new); irq_pm_install_action(desc, new);
...@@ -1293,6 +1353,8 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) ...@@ -1293,6 +1353,8 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
*/ */
if (new->thread) if (new->thread)
wake_up_process(new->thread); wake_up_process(new->thread);
if (new->secondary)
wake_up_process(new->secondary->thread);
register_irq_proc(irq, desc); register_irq_proc(irq, desc);
new->dir = NULL; new->dir = NULL;
...@@ -1323,6 +1385,13 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) ...@@ -1323,6 +1385,13 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
kthread_stop(t); kthread_stop(t);
put_task_struct(t); put_task_struct(t);
} }
if (new->secondary && new->secondary->thread) {
struct task_struct *t = new->secondary->thread;
new->secondary->thread = NULL;
kthread_stop(t);
put_task_struct(t);
}
out_mput: out_mput:
module_put(desc->owner); module_put(desc->owner);
return ret; return ret;
...@@ -1430,9 +1499,14 @@ static struct irqaction *__free_irq(unsigned int irq, void *dev_id) ...@@ -1430,9 +1499,14 @@ static struct irqaction *__free_irq(unsigned int irq, void *dev_id)
if (action->thread) { if (action->thread) {
kthread_stop(action->thread); kthread_stop(action->thread);
put_task_struct(action->thread); put_task_struct(action->thread);
if (action->secondary && action->secondary->thread) {
kthread_stop(action->secondary->thread);
put_task_struct(action->secondary->thread);
}
} }
module_put(desc->owner); module_put(desc->owner);
kfree(action->secondary);
return action; return action;
} }
...@@ -1576,8 +1650,10 @@ int request_threaded_irq(unsigned int irq, irq_handler_t handler, ...@@ -1576,8 +1650,10 @@ int request_threaded_irq(unsigned int irq, irq_handler_t handler,
retval = __setup_irq(irq, desc, action); retval = __setup_irq(irq, desc, action);
chip_bus_sync_unlock(desc); chip_bus_sync_unlock(desc);
if (retval) if (retval) {
kfree(action->secondary);
kfree(action); kfree(action);
}
#ifdef CONFIG_DEBUG_SHIRQ_FIXME #ifdef CONFIG_DEBUG_SHIRQ_FIXME
if (!retval && (irqflags & IRQF_SHARED)) { if (!retval && (irqflags & IRQF_SHARED)) {
......
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