|
@@ -14,6 +14,7 @@
|
|
|
#include <linux/interrupt.h>
|
|
|
#include <linux/slab.h>
|
|
|
#include <linux/sched.h>
|
|
|
+#include <linux/task_work.h>
|
|
|
|
|
|
#include "internals.h"
|
|
|
|
|
@@ -773,11 +774,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("genirq: 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,
|
|
|
};
|
|
@@ -793,7 +822,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;
|
|
@@ -815,44 +846,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("genirq: 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)
|