diff options
Diffstat (limited to 'kernel/irq/manage.c')
| -rw-r--r-- | kernel/irq/manage.c | 68 | 
1 files changed, 33 insertions, 35 deletions
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 7c475cd3f6e..ea0c6c2ae6f 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -16,6 +16,7 @@  #include <linux/interrupt.h>  #include <linux/slab.h>  #include <linux/sched.h> +#include <linux/task_work.h>  #include "internals.h" @@ -775,11 +776,39 @@ static void wake_threads_waitq(struct irq_desc *desc)  		wake_up(&desc->wait_for_threads);  } +static void irq_thread_dtor(struct task_work *unused) +{ +	struct task_struct *tsk = current; +	struct irq_desc *desc; +	struct irqaction *action; + +	if (WARN_ON_ONCE(!(current->flags & PF_EXITING))) +		return; + +	action = kthread_data(tsk); + +	pr_err("exiting task \"%s\" (%d) is an active IRQ thread (irq %d)\n", +	       tsk->comm ? tsk->comm : "", tsk->pid, action->irq); + + +	desc = irq_to_desc(action->irq); +	/* +	 * If IRQTF_RUNTHREAD is set, we need to decrement +	 * desc->threads_active and wake possible waiters. +	 */ +	if (test_and_clear_bit(IRQTF_RUNTHREAD, &action->thread_flags)) +		wake_threads_waitq(desc); + +	/* Prevent a stale desc->threads_oneshot */ +	irq_finalize_oneshot(desc, action); +} +  /*   * Interrupt handler thread   */  static int irq_thread(void *data)  { +	struct task_work on_exit_work;  	static const struct sched_param param = {  		.sched_priority = MAX_USER_RT_PRIO/2,  	}; @@ -795,7 +824,9 @@ static int irq_thread(void *data)  		handler_fn = irq_thread_fn;  	sched_setscheduler(current, SCHED_FIFO, ¶m); -	current->irq_thread = 1; + +	init_task_work(&on_exit_work, irq_thread_dtor, NULL); +	task_work_add(current, &on_exit_work, false);  	while (!irq_wait_for_interrupt(action)) {  		irqreturn_t action_ret; @@ -817,44 +848,11 @@ static int irq_thread(void *data)  	 * cannot touch the oneshot mask at this point anymore as  	 * __setup_irq() might have given out currents thread_mask  	 * again. -	 * -	 * Clear irq_thread. Otherwise exit_irq_thread() would make -	 * fuzz about an active irq thread going into nirvana.  	 */ -	current->irq_thread = 0; +	task_work_cancel(current, irq_thread_dtor);  	return 0;  } -/* - * Called from do_exit() - */ -void exit_irq_thread(void) -{ -	struct task_struct *tsk = current; -	struct irq_desc *desc; -	struct irqaction *action; - -	if (!tsk->irq_thread) -		return; - -	action = kthread_data(tsk); - -	pr_err("exiting task \"%s\" (%d) is an active IRQ thread (irq %d)\n", -	       tsk->comm ? tsk->comm : "", tsk->pid, action->irq); - -	desc = irq_to_desc(action->irq); - -	/* -	 * If IRQTF_RUNTHREAD is set, we need to decrement -	 * desc->threads_active and wake possible waiters. -	 */ -	if (test_and_clear_bit(IRQTF_RUNTHREAD, &action->thread_flags)) -		wake_threads_waitq(desc); - -	/* Prevent a stale desc->threads_oneshot */ -	irq_finalize_oneshot(desc, action); -} -  static void irq_setup_forced_threading(struct irqaction *new)  {  	if (!force_irqthreads)  |