Hardware interrupts usually communicate with a tasklet or softirq. Frequently this involves putting work in a queue, which the softirq will take out.
      If a hardware irq handler shares data with a softirq, you have
      two concerns.  Firstly, the softirq processing can be
      interrupted by a hardware interrupt, and secondly, the
      critical region could be entered by a hardware interrupt on
      another CPU.  This is where spin_lock_irq() is 
      used.  It is defined to disable interrupts on that cpu, then grab 
      the lock. spin_unlock_irq() does the reverse.
    
      The irq handler does not to use
      spin_lock_irq(), because the softirq cannot
      run while the irq handler is running: it can use
      spin_lock(), which is slightly faster.  The
      only exception would be if a different hardware irq handler uses
      the same lock: spin_lock_irq() will stop
      that from interrupting us.
    
      This works perfectly for UP as well: the spin lock vanishes,
      and this macro simply becomes local_irq_disable()
      (include/asm/smp.h), which 
      protects you from the softirq/tasklet/BH being run.
    
      spin_lock_irqsave() 
      (include/linux/spinlock.h) is a variant
      which saves whether interrupts were on or off in a flags word,
      which is passed to spin_unlock_irqrestore().  This
      means that the same code can be used inside an hard irq handler (where
      interrupts are already off) and in softirqs (where the irq
      disabling is required).
    
      Note that softirqs (and hence tasklets and timers) are run on
      return from hardware interrupts, so
      spin_lock_irq() also stops these.  In that
      sense, spin_lock_irqsave() is the most
      general and powerful locking function.