Quilt mail doesn't seem to handle Å well, and vger.kernel.org blocked
it.

-- Steve


On Fri, 26 Feb 2016 16:32:37 -0500
Steven Rostedt <rost...@goodmis.org> wrote:

> 3.18.27-rt26-rc1 stable review patch.
> If anyone has any objections, please let me know.
> 
> ------------------
> 
> From: Thomas Gleixner <t...@linutronix.de>
> 
> Force threading of interrupts does not 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 cant
> 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-by: Kohji Okuno <okuno.ko...@jp.panasonic.com>
> Reported-By: Michal Å mucr <msm...@gmail.com>
> Reported-and-tested-by: Nathan Sullivan <nathan.sulli...@ni.com>
> Signed-off-by: Thomas Gleixner <t...@linutronix.de>
> Cc: Sebastian Andrzej Siewior <bige...@linutronix.de>
> Cc: Steven Rostedt <rost...@goodmis.org>
> Cc: stable...@vger.kernel.org
> Signed-off-by: Steven Rostedt <rost...@goodmis.org>
> ---
>  include/linux/interrupt.h |   2 +
>  kernel/irq/manage.c       | 158 
> ++++++++++++++++++++++++++++++++++------------
>  2 files changed, 119 insertions(+), 41 deletions(-)
> 
> diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h
> index 33cfbc085a94..86628c733be7 100644
> --- a/include/linux/interrupt.h
> +++ b/include/linux/interrupt.h
> @@ -100,6 +100,7 @@ typedef irqreturn_t (*irq_handler_t)(int, void *);
>   * @flags:   flags (see IRQF_* above)
>   * @thread_fn:       interrupt handler function for threaded interrupts
>   * @thread:  thread pointer for threaded interrupts
> + * @secondary:       pointer to secondary irqaction (force threading)
>   * @thread_flags:    flags related to @thread
>   * @thread_mask:     bitmask for keeping track of @thread activity
>   * @dir:     pointer to the proc/irq/NN/name entry
> @@ -111,6 +112,7 @@ struct irqaction {
>       struct irqaction        *next;
>       irq_handler_t           thread_fn;
>       struct task_struct      *thread;
> +     struct irqaction        *secondary;
>       unsigned int            irq;
>       unsigned int            flags;
>       unsigned long           thread_flags;
> diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c
> index 382cbe57abf3..70f59992c201 100644
> --- a/kernel/irq/manage.c
> +++ b/kernel/irq/manage.c
> @@ -735,6 +735,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);
> @@ -761,7 +767,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);
> @@ -923,6 +930,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
>   */
> @@ -953,6 +972,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);
>  
>  #ifdef CONFIG_PREEMPT_RT_FULL
>               migrate_disable();
> @@ -1003,20 +1024,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)
> @@ -1036,6 +1073,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.
> @@ -1056,6 +1135,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.
> @@ -1073,8 +1154,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;
> +             }
>       }
>  
>       /*
> @@ -1083,37 +1167,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)) {
> @@ -1289,7 +1350,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);
> @@ -1315,6 +1375,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;
> @@ -1345,6 +1407,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;
> @@ -1452,9 +1521,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;
>  }
>  
> @@ -1593,8 +1667,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)) {

Reply via email to