diff options
Diffstat (limited to 'kernel/irq/manage.c')
| -rw-r--r-- | kernel/irq/manage.c | 60 | 
1 files changed, 32 insertions, 28 deletions
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 1786cf7dac5..453feedbb39 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -759,6 +759,13 @@ static irqreturn_t irq_thread_fn(struct irq_desc *desc,  	return ret;  } +static void wake_threads_waitq(struct irq_desc *desc) +{ +	if (atomic_dec_and_test(&desc->threads_active) && +	    waitqueue_active(&desc->wait_for_threads)) +		wake_up(&desc->wait_for_threads); +} +  /*   * Interrupt handler thread   */ @@ -771,7 +778,6 @@ static int irq_thread(void *data)  	struct irq_desc *desc = irq_to_desc(action->irq);  	irqreturn_t (*handler_fn)(struct irq_desc *desc,  			struct irqaction *action); -	int wake;  	if (force_irqthreads && test_bit(IRQTF_FORCED_THREAD,  					&action->thread_flags)) @@ -783,39 +789,30 @@ static int irq_thread(void *data)  	current->irq_thread = 1;  	while (!irq_wait_for_interrupt(action)) { +		irqreturn_t action_ret;  		irq_thread_check_affinity(desc, action); -		atomic_inc(&desc->threads_active); - -		raw_spin_lock_irq(&desc->lock); -		if (unlikely(irqd_irq_disabled(&desc->irq_data))) { -			/* -			 * CHECKME: We might need a dedicated -			 * IRQ_THREAD_PENDING flag here, which -			 * retriggers the thread in check_irq_resend() -			 * but AFAICT IRQS_PENDING should be fine as it -			 * retriggers the interrupt itself --- tglx -			 */ -			desc->istate |= IRQS_PENDING; -			raw_spin_unlock_irq(&desc->lock); -		} else { -			irqreturn_t action_ret; - -			raw_spin_unlock_irq(&desc->lock); -			action_ret = handler_fn(desc, action); -			if (!noirqdebug) -				note_interrupt(action->irq, desc, action_ret); -		} - -		wake = atomic_dec_and_test(&desc->threads_active); +		action_ret = handler_fn(desc, action); +		if (!noirqdebug) +			note_interrupt(action->irq, desc, action_ret); -		if (wake && waitqueue_active(&desc->wait_for_threads)) -			wake_up(&desc->wait_for_threads); +		wake_threads_waitq(desc);  	} -	/* Prevent a stale desc->threads_oneshot */ -	irq_finalize_oneshot(desc, action, true); +	/* +	 * This is the regular exit path. __free_irq() is stopping the +	 * thread via kthread_stop() after calling +	 * synchronize_irq(). So neither IRQTF_RUNTHREAD nor the +	 * oneshot mask bit should be set. +	 * +	 * Verify that this is true. +	 */ +	if (WARN_ON(test_and_clear_bit(IRQTF_RUNTHREAD, &action->thread_flags))) +		wake_threads_waitq(desc); + +	if (WARN_ON(desc->threads_oneshot & action->thread_mask)) +		irq_finalize_oneshot(desc, action, true);  	/*  	 * Clear irq_thread. Otherwise exit_irq_thread() would make @@ -845,6 +842,13 @@ void exit_irq_thread(void)  	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, true);  }  |