linux/kernel/irq
Thomas Gleixner 0b1adaa031 genirq: Prevent oneshot irq thread race
Lars-Peter pointed out that the oneshot threaded interrupt handler
code has the following race:

 CPU0                            CPU1
 hande_level_irq(irq X)
   mask_ack_irq(irq X)
   handle_IRQ_event(irq X)
     wake_up(thread_handler)
                                 thread handler(irq X) runs
                                 finalize_oneshot(irq X)
				  does not unmask due to 
				  !(desc->status & IRQ_MASKED)

 return from irq
 does not unmask due to
 (desc->status & IRQ_ONESHOT)
  				  
This leaves the interrupt line masked forever. 

The reason for this is the inconsistent handling of the IRQ_MASKED
flag. Instead of setting it in the mask function the oneshot support
sets the flag after waking up the irq thread.

The solution for this is to set/clear the IRQ_MASKED status whenever
we mask/unmask an interrupt line. That's the easy part, but that
cleanup opens another race:

 CPU0                            CPU1
 hande_level_irq(irq)
   mask_ack_irq(irq)
   handle_IRQ_event(irq)
     wake_up(thread_handler)
                                 thread handler(irq) runs
                                 finalize_oneshot_irq(irq)
				  unmask(irq)
     irq triggers again
     handle_level_irq(irq)
       mask_ack_irq(irq)
     return from irq due to IRQ_INPROGRESS				  

 return from irq
 does not unmask due to
 (desc->status & IRQ_ONESHOT)

This requires that we synchronize finalize_oneshot_irq() with the
primary handler. If IRQ_INPROGESS is set we wait until the primary
handler on the other CPU has returned before unmasking the interrupt
line again.

We probably have never seen that problem because it does not happen on
UP and on SMP the irqbalancer protects us by pinning the primary
handler and the thread to the same CPU.

Reported-by: Lars-Peter Clausen <lars@metafoo.de>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Cc: stable@kernel.org
2010-03-10 17:45:14 +01:00
..
autoprobe.c genirq: Convert irq_desc.lock to raw_spinlock 2009-12-14 23:55:33 +01:00
chip.c genirq: Prevent oneshot irq thread race 2010-03-10 17:45:14 +01:00
devres.c genirq: add support for threaded interrupts to devres 2009-03-24 12:15:23 +01:00
handle.c sparseirq: Use radix_tree instead of ptrs array 2010-02-17 17:27:20 -08:00
internals.h sparseirq: Change irq_desc_ptrs to static 2010-02-17 17:27:03 -08:00
Makefile x86/irq: use move_irq_desc() in create_irq_nr() 2009-05-01 19:01:12 +02:00
manage.c genirq: Prevent oneshot irq thread race 2010-03-10 17:45:14 +01:00
migration.c genirq: Convert irq_desc.lock to raw_spinlock 2009-12-14 23:55:33 +01:00
numa_migrate.c sparseirq: Change irq_desc_ptrs to static 2010-02-17 17:27:03 -08:00
pm.c genirq: Convert irq_desc.lock to raw_spinlock 2009-12-14 23:55:33 +01:00
proc.c genirq: Convert irq_desc.lock to raw_spinlock 2009-12-14 23:55:33 +01:00
resend.c irq: Remove superfluous NULL pointer check in check_irq_resend() 2009-08-09 12:44:29 +02:00
spurious.c genirq: Convert irq_desc.lock to raw_spinlock 2009-12-14 23:55:33 +01:00