genirq: Handle force threading of irqs with primary and thread handler
[linux-drm-fsl-dcu.git] / kernel / irq / manage.c
index f9744853b656976bc4fdc49e006464a08a7d5395..0a63c2b20bdd00d5e7e34a865286bea49668b6f4 100644 (file)
@@ -115,6 +115,14 @@ EXPORT_SYMBOL(synchronize_irq);
 #ifdef CONFIG_SMP
 cpumask_var_t irq_default_affinity;
 
+static int __irq_can_set_affinity(struct irq_desc *desc)
+{
+       if (!desc || !irqd_can_balance(&desc->irq_data) ||
+           !desc->irq_data.chip || !desc->irq_data.chip->irq_set_affinity)
+               return 0;
+       return 1;
+}
+
 /**
  *     irq_can_set_affinity - Check if the affinity of a given irq can be set
  *     @irq:           Interrupt to check
@@ -122,13 +130,7 @@ cpumask_var_t irq_default_affinity;
  */
 int irq_can_set_affinity(unsigned int irq)
 {
-       struct irq_desc *desc = irq_to_desc(irq);
-
-       if (!desc || !irqd_can_balance(&desc->irq_data) ||
-           !desc->irq_data.chip || !desc->irq_data.chip->irq_set_affinity)
-               return 0;
-
-       return 1;
+       return __irq_can_set_affinity(irq_to_desc(irq));
 }
 
 /**
@@ -190,7 +192,7 @@ int irq_do_set_affinity(struct irq_data *data, const struct cpumask *mask,
        switch (ret) {
        case IRQ_SET_MASK_OK:
        case IRQ_SET_MASK_OK_DONE:
-               cpumask_copy(data->affinity, mask);
+               cpumask_copy(desc->irq_common_data.affinity, mask);
        case IRQ_SET_MASK_OK_NOCOPY:
                irq_set_thread_affinity(desc);
                ret = 0;
@@ -302,7 +304,7 @@ static void irq_affinity_notify(struct work_struct *work)
        if (irq_move_pending(&desc->irq_data))
                irq_get_pending(cpumask, desc);
        else
-               cpumask_copy(cpumask, desc->irq_data.affinity);
+               cpumask_copy(cpumask, desc->irq_common_data.affinity);
        raw_spin_unlock_irqrestore(&desc->lock, flags);
 
        notify->notify(notify, cpumask);
@@ -359,14 +361,13 @@ EXPORT_SYMBOL_GPL(irq_set_affinity_notifier);
 /*
  * Generic version of the affinity autoselector.
  */
-static int
-setup_affinity(unsigned int irq, struct irq_desc *desc, struct cpumask *mask)
+static int setup_affinity(struct irq_desc *desc, struct cpumask *mask)
 {
        struct cpumask *set = irq_default_affinity;
        int node = irq_desc_get_node(desc);
 
        /* Excludes PER_CPU and NO_BALANCE interrupts */
-       if (!irq_can_set_affinity(irq))
+       if (!__irq_can_set_affinity(desc))
                return 0;
 
        /*
@@ -374,9 +375,9 @@ setup_affinity(unsigned int irq, struct irq_desc *desc, struct cpumask *mask)
         * one of the targets is online.
         */
        if (irqd_has_set(&desc->irq_data, IRQD_AFFINITY_SET)) {
-               if (cpumask_intersects(desc->irq_data.affinity,
+               if (cpumask_intersects(desc->irq_common_data.affinity,
                                       cpu_online_mask))
-                       set = desc->irq_data.affinity;
+                       set = desc->irq_common_data.affinity;
                else
                        irqd_clear(&desc->irq_data, IRQD_AFFINITY_SET);
        }
@@ -393,10 +394,10 @@ setup_affinity(unsigned int irq, struct irq_desc *desc, struct cpumask *mask)
        return 0;
 }
 #else
-static inline int
-setup_affinity(unsigned int irq, struct irq_desc *d, struct cpumask *mask)
+/* Wrapper for ALPHA specific affinity selector magic */
+static inline int setup_affinity(struct irq_desc *d, struct cpumask *mask)
 {
-       return irq_select_affinity(irq);
+       return irq_select_affinity(irq_desc_get_irq(d));
 }
 #endif
 
@@ -410,20 +411,20 @@ int irq_select_affinity_usr(unsigned int irq, struct cpumask *mask)
        int ret;
 
        raw_spin_lock_irqsave(&desc->lock, flags);
-       ret = setup_affinity(irq, desc, mask);
+       ret = setup_affinity(desc, mask);
        raw_spin_unlock_irqrestore(&desc->lock, flags);
        return ret;
 }
 
 #else
 static inline int
-setup_affinity(unsigned int irq, struct irq_desc *desc, struct cpumask *mask)
+setup_affinity(struct irq_desc *desc, struct cpumask *mask)
 {
        return 0;
 }
 #endif
 
-void __disable_irq(struct irq_desc *desc, unsigned int irq)
+void __disable_irq(struct irq_desc *desc)
 {
        if (!desc->depth++)
                irq_disable(desc);
@@ -436,7 +437,7 @@ static int __disable_irq_nosync(unsigned int irq)
 
        if (!desc)
                return -EINVAL;
-       __disable_irq(desc, irq);
+       __disable_irq(desc);
        irq_put_desc_busunlock(desc, flags);
        return 0;
 }
@@ -503,12 +504,13 @@ bool disable_hardirq(unsigned int irq)
 }
 EXPORT_SYMBOL_GPL(disable_hardirq);
 
-void __enable_irq(struct irq_desc *desc, unsigned int irq)
+void __enable_irq(struct irq_desc *desc)
 {
        switch (desc->depth) {
        case 0:
  err_out:
-               WARN(1, KERN_WARNING "Unbalanced enable for IRQ %d\n", irq);
+               WARN(1, KERN_WARNING "Unbalanced enable for IRQ %d\n",
+                    irq_desc_get_irq(desc));
                break;
        case 1: {
                if (desc->istate & IRQS_SUSPENDED)
@@ -516,7 +518,7 @@ void __enable_irq(struct irq_desc *desc, unsigned int irq)
                /* Prevent probing on this irq: */
                irq_settings_set_noprobe(desc);
                irq_enable(desc);
-               check_irq_resend(desc, irq);
+               check_irq_resend(desc);
                /* fall-through */
        }
        default:
@@ -546,7 +548,7 @@ void enable_irq(unsigned int irq)
                 KERN_ERR "enable_irq before setup/request_irq: irq %u\n", irq))
                goto out;
 
-       __enable_irq(desc, irq);
+       __enable_irq(desc);
 out:
        irq_put_desc_busunlock(desc, flags);
 }
@@ -637,8 +639,7 @@ int can_request_irq(unsigned int irq, unsigned long irqflags)
        return canrequest;
 }
 
-int __irq_set_trigger(struct irq_desc *desc, unsigned int irq,
-                     unsigned long flags)
+int __irq_set_trigger(struct irq_desc *desc, unsigned long flags)
 {
        struct irq_chip *chip = desc->irq_data.chip;
        int ret, unmask = 0;
@@ -648,7 +649,8 @@ int __irq_set_trigger(struct irq_desc *desc, unsigned int irq,
                 * IRQF_TRIGGER_* but the PIC does not support multiple
                 * flow-types?
                 */
-               pr_debug("No set_type function for IRQ %d (%s)\n", irq,
+               pr_debug("No set_type function for IRQ %d (%s)\n",
+                        irq_desc_get_irq(desc),
                         chip ? (chip->name ? : "unknown") : "unknown");
                return 0;
        }
@@ -685,7 +687,7 @@ int __irq_set_trigger(struct irq_desc *desc, unsigned int irq,
                break;
        default:
                pr_err("Setting trigger mode %lu for irq %u failed (%pF)\n",
-                      flags, irq, chip->irq_set_type);
+                      flags, irq_desc_get_irq(desc), chip->irq_set_type);
        }
        if (unmask)
                unmask_irq(desc);
@@ -728,6 +730,12 @@ static irqreturn_t irq_nested_primary_handler(int irq, void *dev_id)
        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)
 {
        set_current_state(TASK_INTERRUPTIBLE);
@@ -754,7 +762,8 @@ static int irq_wait_for_interrupt(struct irqaction *action)
 static void irq_finalize_oneshot(struct irq_desc *desc,
                                 struct irqaction *action)
 {
-       if (!(desc->istate & IRQS_ONESHOT))
+       if (!(desc->istate & IRQS_ONESHOT) ||
+           action->handler == irq_forced_secondary_handler)
                return;
 again:
        chip_bus_lock(desc);
@@ -827,8 +836,8 @@ irq_thread_check_affinity(struct irq_desc *desc, struct irqaction *action)
         * This code is triggered unconditionally. Check the affinity
         * mask pointer. For CPU_MASK_OFFSTACK=n this is optimized out.
         */
-       if (desc->irq_data.affinity)
-               cpumask_copy(mask, desc->irq_data.affinity);
+       if (desc->irq_common_data.affinity)
+               cpumask_copy(mask, desc->irq_common_data.affinity);
        else
                valid = false;
        raw_spin_unlock_irq(&desc->lock);
@@ -908,6 +917,18 @@ static void irq_thread_dtor(struct callback_head *unused)
        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
  */
@@ -938,6 +959,8 @@ static int irq_thread(void *data)
                action_ret = handler_fn(desc, action);
                if (action_ret == IRQ_HANDLED)
                        atomic_inc(&desc->threads_handled);
+               if (action_ret == IRQ_WAKE_THREAD)
+                       irq_wake_secondary(desc, action);
 
                wake_threads_waitq(desc);
        }
@@ -982,20 +1005,36 @@ void irq_wake_thread(unsigned int irq, void *dev_id)
 }
 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)
-               return;
+               return 0;
        if (new->flags & (IRQF_NO_THREAD | IRQF_PERCPU | IRQF_ONESHOT))
-               return;
+               return 0;
 
        new->flags |= IRQF_ONESHOT;
 
-       if (!new->thread_fn) {
-               set_bit(IRQTF_FORCED_THREAD, &new->thread_flags);
-               new->thread_fn = new->handler;
-               new->handler = irq_default_primary_handler;
+       /*
+        * 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);
+       new->thread_fn = new->handler;
+       new->handler = irq_default_primary_handler;
+       return 0;
 }
 
 static int irq_request_resources(struct irq_desc *desc)
@@ -1015,6 +1054,48 @@ static void irq_release_resources(struct irq_desc *desc)
                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
  * allocate special interrupts that are part of the architecture.
@@ -1035,6 +1116,8 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
        if (!try_module_get(desc->owner))
                return -ENODEV;
 
+       new->irq = irq;
+
        /*
         * Check whether the interrupt nests into another interrupt
         * thread.
@@ -1052,8 +1135,11 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
                 */
                new->handler = irq_nested_primary_handler;
        } else {
-               if (irq_settings_can_thread(desc))
-                       irq_setup_forced_threading(new);
+               if (irq_settings_can_thread(desc)) {
+                       ret = irq_setup_forced_threading(new);
+                       if (ret)
+                               goto out_mput;
+               }
        }
 
        /*
@@ -1062,37 +1148,14 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
         * thread.
         */
        if (new->thread_fn && !nested) {
-               struct task_struct *t;
-               static const struct sched_param param = {
-                       .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);
+               ret = setup_irq_thread(new, irq, false);
+               if (ret)
                        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)) {
@@ -1221,8 +1284,8 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
 
                /* Setup the type (level, edge polarity) if configured: */
                if (new->flags & IRQF_TRIGGER_MASK) {
-                       ret = __irq_set_trigger(desc, irq,
-                                       new->flags & IRQF_TRIGGER_MASK);
+                       ret = __irq_set_trigger(desc,
+                                               new->flags & IRQF_TRIGGER_MASK);
 
                        if (ret)
                                goto out_mask;
@@ -1253,7 +1316,7 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
                }
 
                /* Set default affinity mask once everything is setup */
-               setup_affinity(irq, desc, mask);
+               setup_affinity(desc, mask);
 
        } else if (new->flags & IRQF_TRIGGER_MASK) {
                unsigned int nmsk = new->flags & IRQF_TRIGGER_MASK;
@@ -1265,7 +1328,6 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
                                   irq, nmsk, omsk);
        }
 
-       new->irq = irq;
        *old_ptr = new;
 
        irq_pm_install_action(desc, new);
@@ -1280,7 +1342,7 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
         */
        if (shared && (desc->istate & IRQS_SPURIOUS_DISABLED)) {
                desc->istate &= ~IRQS_SPURIOUS_DISABLED;
-               __enable_irq(desc, irq);
+               __enable_irq(desc);
        }
 
        raw_spin_unlock_irqrestore(&desc->lock, flags);
@@ -1291,6 +1353,8 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
         */
        if (new->thread)
                wake_up_process(new->thread);
+       if (new->secondary)
+               wake_up_process(new->secondary->thread);
 
        register_irq_proc(irq, desc);
        new->dir = NULL;
@@ -1321,6 +1385,13 @@ out_thread:
                kthread_stop(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:
        module_put(desc->owner);
        return ret;
@@ -1428,9 +1499,14 @@ static struct irqaction *__free_irq(unsigned int irq, void *dev_id)
        if (action->thread) {
                kthread_stop(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);
+       kfree(action->secondary);
        return action;
 }
 
@@ -1574,8 +1650,10 @@ int request_threaded_irq(unsigned int irq, irq_handler_t handler,
        retval = __setup_irq(irq, desc, action);
        chip_bus_sync_unlock(desc);
 
-       if (retval)
+       if (retval) {
+               kfree(action->secondary);
                kfree(action);
+       }
 
 #ifdef CONFIG_DEBUG_SHIRQ_FIXME
        if (!retval && (irqflags & IRQF_SHARED)) {
@@ -1650,7 +1728,7 @@ void enable_percpu_irq(unsigned int irq, unsigned int type)
        if (type != IRQ_TYPE_NONE) {
                int ret;
 
-               ret = __irq_set_trigger(desc, irq, type);
+               ret = __irq_set_trigger(desc, type);
 
                if (ret) {
                        WARN(1, "failed to set type for IRQ%d\n", irq);
@@ -1875,6 +1953,7 @@ int irq_get_irqchip_state(unsigned int irq, enum irqchip_irq_state which,
        irq_put_desc_busunlock(desc, flags);
        return err;
 }
+EXPORT_SYMBOL_GPL(irq_get_irqchip_state);
 
 /**
  *     irq_set_irqchip_state - set the state of a forwarded interrupt.
@@ -1920,3 +1999,4 @@ int irq_set_irqchip_state(unsigned int irq, enum irqchip_irq_state which,
        irq_put_desc_busunlock(desc, flags);
        return err;
 }
+EXPORT_SYMBOL_GPL(irq_set_irqchip_state);