diff options
Diffstat (limited to 'kernel/irq/manage.c')
| -rw-r--r-- | kernel/irq/manage.c | 22 | 
1 files changed, 22 insertions, 0 deletions
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index eb6078ca60c..398fda155f6 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -382,6 +382,7 @@ int can_request_irq(unsigned int irq, unsigned long irqflags)  {  	struct irq_desc *desc = irq_to_desc(irq);  	struct irqaction *action; +	unsigned long flags;  	if (!desc)  		return 0; @@ -389,11 +390,14 @@ int can_request_irq(unsigned int irq, unsigned long irqflags)  	if (desc->status & IRQ_NOREQUEST)  		return 0; +	raw_spin_lock_irqsave(&desc->lock, flags);  	action = desc->action;  	if (action)  		if (irqflags & action->flags & IRQF_SHARED)  			action = NULL; +	raw_spin_unlock_irqrestore(&desc->lock, flags); +  	return !action;  } @@ -483,8 +487,26 @@ static int irq_wait_for_interrupt(struct irqaction *action)   */  static void irq_finalize_oneshot(unsigned int irq, struct irq_desc *desc)  { +again:  	chip_bus_lock(irq, desc);  	raw_spin_lock_irq(&desc->lock); + +	/* +	 * Implausible though it may be we need to protect us against +	 * the following scenario: +	 * +	 * The thread is faster done than the hard interrupt handler +	 * on the other CPU. If we unmask the irq line then the +	 * interrupt can come in again and masks the line, leaves due +	 * to IRQ_INPROGRESS and the irq line is masked forever. +	 */ +	if (unlikely(desc->status & IRQ_INPROGRESS)) { +		raw_spin_unlock_irq(&desc->lock); +		chip_bus_sync_unlock(irq, desc); +		cpu_relax(); +		goto again; +	} +  	if (!(desc->status & IRQ_DISABLED) && (desc->status & IRQ_MASKED)) {  		desc->status &= ~IRQ_MASKED;  		desc->chip->unmask(irq);  |