Re: [PATCH RT 02/12] genirq: Handle force threading of interrupts with primary and thread handler

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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 <rostedt@xxxxxxxxxxx> wrote:

> 3.18.27-rt26-rc1 stable review patch.
> If anyone has any objections, please let me know.
> 
> ------------------
> 
> From: Thomas Gleixner <tglx@xxxxxxxxxxxxx>
> 
> 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.kohji@xxxxxxxxxxxxxxxx>
> Reported-By: Michal Å mucr <msmucr@xxxxxxxxx>
> Reported-and-tested-by: Nathan Sullivan <nathan.sullivan@xxxxxx>
> Signed-off-by: Thomas Gleixner <tglx@xxxxxxxxxxxxx>
> Cc: Sebastian Andrzej Siewior <bigeasy@xxxxxxxxxxxxx>
> Cc: Steven Rostedt <rostedt@xxxxxxxxxxx>
> Cc: stable-rt@xxxxxxxxxxxxxxx
> Signed-off-by: Steven Rostedt <rostedt@xxxxxxxxxxx>
> ---
>  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)) {

--
To unsubscribe from this list: send the line "unsubscribe stable-rt" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux USB Development]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux