Merge to Fedora kernel-2.6.17-1.2142_FC4 patched with stable patch-2.6.17.4-vs2.0...
[linux-2.6.git] / kernel / irq / manage.c
index 47678cd..1279e34 100644 (file)
@@ -6,6 +6,7 @@
  * This file contains driver APIs to the irq subsystem.
  */
 
+#include <linux/config.h>
 #include <linux/irq.h>
 #include <linux/module.h>
 #include <linux/random.h>
 
 cpumask_t irq_affinity[NR_IRQS] = { [0 ... NR_IRQS-1] = CPU_MASK_ALL };
 
+#if defined (CONFIG_GENERIC_PENDING_IRQ) || defined (CONFIG_IRQBALANCE)
+cpumask_t __cacheline_aligned pending_irq_cpumask[NR_IRQS];
+#endif
+
 /**
  *     synchronize_irq - wait for pending IRQ handlers (on other CPUs)
+ *     @irq: interrupt number to wait for
  *
  *     This function waits for any pending IRQ handlers for this interrupt
  *     to complete before returning. If you use this function while
@@ -30,6 +36,9 @@ void synchronize_irq(unsigned int irq)
 {
        struct irq_desc *desc = irq_desc + irq;
 
+       if (irq >= NR_IRQS)
+               return;
+
        while (desc->status & IRQ_INPROGRESS)
                cpu_relax();
 }
@@ -54,6 +63,9 @@ void disable_irq_nosync(unsigned int irq)
        irq_desc_t *desc = irq_desc + irq;
        unsigned long flags;
 
+       if (irq >= NR_IRQS)
+               return;
+
        spin_lock_irqsave(&desc->lock, flags);
        if (!desc->depth++) {
                desc->status |= IRQ_DISABLED;
@@ -80,6 +92,9 @@ void disable_irq(unsigned int irq)
 {
        irq_desc_t *desc = irq_desc + irq;
 
+       if (irq >= NR_IRQS)
+               return;
+
        disable_irq_nosync(irq);
        if (desc->action)
                synchronize_irq(irq);
@@ -102,6 +117,9 @@ void enable_irq(unsigned int irq)
        irq_desc_t *desc = irq_desc + irq;
        unsigned long flags;
 
+       if (irq >= NR_IRQS)
+               return;
+
        spin_lock_irqsave(&desc->lock, flags);
        switch (desc->depth) {
        case 0:
@@ -146,14 +164,9 @@ int can_request_irq(unsigned int irq, unsigned long irqflags)
        return !action;
 }
 
-/**
- *     setup_irq - register an irqaction structure
- *     @irq: Interrupt to register
- *     @irqaction: The irqaction structure to be registered
- *
- *     Normally called by request_irq, this function can be used
- *     directly to allocate special interrupts that are part of the
- *     architecture.
+/*
+ * Internal function to register an irqaction - typically used to
+ * allocate special interrupts that are part of the architecture.
  */
 int setup_irq(unsigned int irq, struct irqaction * new)
 {
@@ -162,6 +175,9 @@ int setup_irq(unsigned int irq, struct irqaction * new)
        unsigned long flags;
        int shared = 0;
 
+       if (irq >= NR_IRQS)
+               return -EINVAL;
+
        if (desc->handler == &no_irq_type)
                return -ENOSYS;
        /*
@@ -188,10 +204,14 @@ int setup_irq(unsigned int irq, struct irqaction * new)
        p = &desc->action;
        if ((old = *p) != NULL) {
                /* Can't share interrupts unless both agree to */
-               if (!(old->flags & new->flags & SA_SHIRQ)) {
-                       spin_unlock_irqrestore(&desc->lock,flags);
-                       return -EBUSY;
-               }
+               if (!(old->flags & new->flags & SA_SHIRQ))
+                       goto mismatch;
+
+#if defined(ARCH_HAS_IRQ_PER_CPU) && defined(SA_PERCPU_IRQ)
+               /* All handlers must agree on per-cpuness */
+               if ((old->flags & IRQ_PER_CPU) != (new->flags & IRQ_PER_CPU))
+                       goto mismatch;
+#endif
 
                /* add new interrupt at end of irq queue */
                do {
@@ -202,7 +222,10 @@ int setup_irq(unsigned int irq, struct irqaction * new)
        }
 
        *p = new;
-
+#if defined(ARCH_HAS_IRQ_PER_CPU) && defined(SA_PERCPU_IRQ)
+       if (new->flags & SA_PERCPU_IRQ)
+               desc->status |= IRQ_PER_CPU;
+#endif
        if (!shared) {
                desc->depth = 0;
                desc->status &= ~(IRQ_DISABLED | IRQ_AUTODETECT |
@@ -220,29 +243,39 @@ int setup_irq(unsigned int irq, struct irqaction * new)
        register_handler_proc(irq, new);
 
        return 0;
+
+mismatch:
+       spin_unlock_irqrestore(&desc->lock, flags);
+       if (!(new->flags & SA_PROBEIRQ)) {
+               printk(KERN_ERR "%s: irq handler mismatch\n", __FUNCTION__);
+               dump_stack();
+       }
+       return -EBUSY;
 }
 
-/*
- *     teardown_irq - unregister an irqaction
- *     @irq: Interrupt line being freed
- *     @old: Pointer to the irqaction that is to be unregistered
+/**
+ *     free_irq - free an interrupt
+ *     @irq: Interrupt line to free
+ *     @dev_id: Device identity to free
  *
- *     This function is called by free_irq and does the actual
- *     business of unregistering the handler. It exists as a 
- *     seperate function to enable handlers to be unregistered 
- *     for irqactions that have been allocated statically at 
- *     boot time.
+ *     Remove an interrupt handler. The handler is removed and if the
+ *     interrupt line is no longer in use by any driver it is disabled.
+ *     On a shared IRQ the caller must ensure the interrupt is disabled
+ *     on the card it drives before calling this function. The function
+ *     does not return until any executing interrupts for this IRQ
+ *     have completed.
  *
  *     This function must not be called from interrupt context.
  */
-int teardown_irq(unsigned int irq, struct irqaction * old)
+void free_irq(unsigned int irq, void *dev_id)
 {
        struct irq_desc *desc;
        struct irqaction **p;
        unsigned long flags;
 
+       WARN_ON(in_interrupt());
        if (irq >= NR_IRQS)
-               return -ENOENT;
+               return;
 
        desc = irq_desc + irq;
        spin_lock_irqsave(&desc->lock,flags);
@@ -254,11 +287,18 @@ int teardown_irq(unsigned int irq, struct irqaction * old)
                        struct irqaction **pp = p;
 
                        p = &action->next;
-                       if (action != old)
+                       if (action->dev_id != dev_id)
                                continue;
 
                        /* Found it - now remove it from the list of entries */
                        *pp = action->next;
+
+                       /* Currently used only by UML, might disappear one day.*/
+#ifdef CONFIG_IRQ_RELEASE_METHOD
+                       if (desc->handler->release)
+                               desc->handler->release(irq, dev_id);
+#endif
+
                        if (!desc->action) {
                                desc->status |= IRQ_DISABLED;
                                if (desc->handler->shutdown)
@@ -271,52 +311,13 @@ int teardown_irq(unsigned int irq, struct irqaction * old)
 
                        /* Make sure it's not being used on another CPU */
                        synchronize_irq(irq);
-                       return 0;
+                       kfree(action);
+                       return;
                }
-               printk(KERN_ERR "Trying to teardown free IRQ%d\n",irq);
-               spin_unlock_irqrestore(&desc->lock,flags);
-               return -ENOENT;
-       }
-}
-
-/**
- *     free_irq - free an interrupt
- *     @irq: Interrupt line to free
- *     @dev_id: Device identity to free
- *
- *     Remove an interrupt handler. The handler is removed and if the
- *     interrupt line is no longer in use by any driver it is disabled.
- *     On a shared IRQ the caller must ensure the interrupt is disabled
- *     on the card it drives before calling this function. The function
- *     does not return until any executing interrupts for this IRQ
- *     have completed.
- *
- *     This function must not be called from interrupt context.
- */
-void free_irq(unsigned int irq, void *dev_id)
-{
-       struct irq_desc *desc;
-       struct irqaction *action;
-       unsigned long flags;
-
-       if (irq >= NR_IRQS)
-               return;
-
-       desc = irq_desc + irq;
-       spin_lock_irqsave(&desc->lock,flags);
-       for (action = desc->action; action != NULL; action = action->next) {
-               if (action->dev_id != dev_id)
-                       continue;
-
+               printk(KERN_ERR "Trying to free free IRQ%d\n",irq);
                spin_unlock_irqrestore(&desc->lock,flags);
-
-               if (teardown_irq(irq, action) == 0)
-                       kfree(action);
                return;
        }
-       printk(KERN_ERR "Trying to free free IRQ%d\n",irq);
-       spin_unlock_irqrestore(&desc->lock,flags);
-       return;
 }
 
 EXPORT_SYMBOL(free_irq);
@@ -381,6 +382,8 @@ int request_irq(unsigned int irq,
        action->next = NULL;
        action->dev_id = dev_id;
 
+       select_smp_affinity(irq);
+
        retval = setup_irq(irq, action);
        if (retval)
                kfree(action);