Merge branch 'linus' into cont_syslog
[safe/jmp/linux-2.6] / arch / powerpc / kernel / irq.c
index 4efb886..30817d9 100644 (file)
 #include <asm/firmware.h>
 #include <asm/lv1call.h>
 #endif
+#define CREATE_TRACE_POINTS
+#include <asm/trace.h>
+
+DEFINE_PER_CPU_SHARED_ALIGNED(irq_cpustat_t, irq_stat);
+EXPORT_PER_CPU_SYMBOL(irq_stat);
 
 int __irq_offset_value;
-static int ppc_spurious_interrupts;
 
 #ifdef CONFIG_PPC32
 EXPORT_SYMBOL(__irq_offset_value);
@@ -84,7 +88,10 @@ extern int tau_interrupts(int);
 #endif /* CONFIG_PPC32 */
 
 #ifdef CONFIG_PPC64
+
+#ifndef CONFIG_SPARSE_IRQ
 EXPORT_SYMBOL(irq_desc);
+#endif
 
 int distribute_irqs = 1;
 
@@ -104,13 +111,6 @@ static inline notrace void set_soft_enabled(unsigned long enable)
        : : "r" (enable), "i" (offsetof(struct paca_struct, soft_enabled)));
 }
 
-#ifdef CONFIG_PERF_COUNTERS
-notrace void __weak perf_counter_do_pending(void)
-{
-       set_perf_counter_pending(0);
-}
-#endif
-
 notrace void raw_local_irq_restore(unsigned long en)
 {
        /*
@@ -124,6 +124,7 @@ notrace void raw_local_irq_restore(unsigned long en)
        if (!en)
                return;
 
+#ifdef CONFIG_PPC_STD_MMU_64
        if (firmware_has_feature(FW_FEATURE_ISERIES)) {
                /*
                 * Do we need to disable preemption here?  Not really: in the
@@ -141,9 +142,7 @@ notrace void raw_local_irq_restore(unsigned long en)
                if (local_paca->lppaca_ptr->int_dword.any_int)
                        iseries_handle_interrupts();
        }
-
-       if (get_perf_counter_pending())
-               perf_counter_do_pending();
+#endif /* CONFIG_PPC_STD_MMU_64 */
 
        /*
         * if (get_paca()->hard_enabled) return;
@@ -177,160 +176,228 @@ notrace void raw_local_irq_restore(unsigned long en)
 EXPORT_SYMBOL(raw_local_irq_restore);
 #endif /* CONFIG_PPC64 */
 
+static int show_other_interrupts(struct seq_file *p, int prec)
+{
+       int j;
+
+#if defined(CONFIG_PPC32) && defined(CONFIG_TAU_INT)
+       if (tau_initialized) {
+               seq_printf(p, "%*s: ", prec, "TAU");
+               for_each_online_cpu(j)
+                       seq_printf(p, "%10u ", tau_interrupts(j));
+               seq_puts(p, "  PowerPC             Thermal Assist (cpu temp)\n");
+       }
+#endif /* CONFIG_PPC32 && CONFIG_TAU_INT */
+
+       seq_printf(p, "%*s: ", prec, "LOC");
+       for_each_online_cpu(j)
+               seq_printf(p, "%10u ", per_cpu(irq_stat, j).timer_irqs);
+        seq_printf(p, "  Local timer interrupts\n");
+
+       seq_printf(p, "%*s: ", prec, "SPU");
+       for_each_online_cpu(j)
+               seq_printf(p, "%10u ", per_cpu(irq_stat, j).spurious_irqs);
+       seq_printf(p, "  Spurious interrupts\n");
+
+       seq_printf(p, "%*s: ", prec, "CNT");
+       for_each_online_cpu(j)
+               seq_printf(p, "%10u ", per_cpu(irq_stat, j).pmu_irqs);
+       seq_printf(p, "  Performance monitoring interrupts\n");
+
+       seq_printf(p, "%*s: ", prec, "MCE");
+       for_each_online_cpu(j)
+               seq_printf(p, "%10u ", per_cpu(irq_stat, j).mce_exceptions);
+       seq_printf(p, "  Machine check exceptions\n");
+
+       return 0;
+}
+
 int show_interrupts(struct seq_file *p, void *v)
 {
-       int i = *(loff_t *)v, j;
+       unsigned long flags, any_count = 0;
+       int i = *(loff_t *) v, j, prec;
        struct irqaction *action;
-       irq_desc_t *desc;
-       unsigned long flags;
+       struct irq_desc *desc;
+
+       if (i > nr_irqs)
+               return 0;
+
+       for (prec = 3, j = 1000; prec < 10 && j <= nr_irqs; ++prec)
+               j *= 10;
+
+       if (i == nr_irqs)
+               return show_other_interrupts(p, prec);
 
+       /* print header */
        if (i == 0) {
-               seq_puts(p, "           ");
+               seq_printf(p, "%*s", prec + 8, "");
                for_each_online_cpu(j)
-                       seq_printf(p, "CPU%d       ", j);
+                       seq_printf(p, "CPU%-8d", j);
                seq_putc(p, '\n');
        }
 
-       if (i < NR_IRQS) {
-               desc = get_irq_desc(i);
-               spin_lock_irqsave(&desc->lock, flags);
-               action = desc->action;
-               if (!action || !action->handler)
-                       goto skip;
-               seq_printf(p, "%3d: ", i);
-#ifdef CONFIG_SMP
-               for_each_online_cpu(j)
-                       seq_printf(p, "%10u ", kstat_cpu(j).irqs[i]);
-#else
-               seq_printf(p, "%10u ", kstat_irqs(i));
-#endif /* CONFIG_SMP */
-               if (desc->chip)
-                       seq_printf(p, " %s ", desc->chip->typename);
-               else
-                       seq_puts(p, "  None      ");
-               seq_printf(p, "%s", (desc->status & IRQ_LEVEL) ? "Level " : "Edge  ");
-               seq_printf(p, "    %s", action->name);
-               for (action = action->next; action; action = action->next)
+       desc = irq_to_desc(i);
+       if (!desc)
+               return 0;
+
+       raw_spin_lock_irqsave(&desc->lock, flags);
+       for_each_online_cpu(j)
+               any_count |= kstat_irqs_cpu(i, j);
+       action = desc->action;
+       if (!action && !any_count)
+               goto out;
+
+       seq_printf(p, "%*d: ", prec, i);
+       for_each_online_cpu(j)
+               seq_printf(p, "%10u ", kstat_irqs_cpu(i, j));
+
+       if (desc->chip)
+               seq_printf(p, "  %-16s", desc->chip->name);
+       else
+               seq_printf(p, "  %-16s", "None");
+       seq_printf(p, " %-8s", (desc->status & IRQ_LEVEL) ? "Level" : "Edge");
+
+       if (action) {
+               seq_printf(p, "     %s", action->name);
+               while ((action = action->next) != NULL)
                        seq_printf(p, ", %s", action->name);
-               seq_putc(p, '\n');
-skip:
-               spin_unlock_irqrestore(&desc->lock, flags);
-       } else if (i == NR_IRQS) {
-#if defined(CONFIG_PPC32) && defined(CONFIG_TAU_INT)
-               if (tau_initialized){
-                       seq_puts(p, "TAU: ");
-                       for_each_online_cpu(j)
-                               seq_printf(p, "%10u ", tau_interrupts(j));
-                       seq_puts(p, "  PowerPC             Thermal Assist (cpu temp)\n");
-               }
-#endif /* CONFIG_PPC32 && CONFIG_TAU_INT*/
-               seq_printf(p, "BAD: %10u\n", ppc_spurious_interrupts);
        }
+
+       seq_putc(p, '\n');
+out:
+       raw_spin_unlock_irqrestore(&desc->lock, flags);
        return 0;
 }
 
+/*
+ * /proc/stat helpers
+ */
+u64 arch_irq_stat_cpu(unsigned int cpu)
+{
+       u64 sum = per_cpu(irq_stat, cpu).timer_irqs;
+
+       sum += per_cpu(irq_stat, cpu).pmu_irqs;
+       sum += per_cpu(irq_stat, cpu).mce_exceptions;
+       sum += per_cpu(irq_stat, cpu).spurious_irqs;
+
+       return sum;
+}
+
 #ifdef CONFIG_HOTPLUG_CPU
-void fixup_irqs(cpumask_t map)
+void fixup_irqs(const struct cpumask *map)
 {
+       struct irq_desc *desc;
        unsigned int irq;
        static int warned;
+       cpumask_var_t mask;
 
-       for_each_irq(irq) {
-               cpumask_t mask;
+       alloc_cpumask_var(&mask, GFP_KERNEL);
 
-               if (irq_desc[irq].status & IRQ_PER_CPU)
+       for_each_irq(irq) {
+               desc = irq_to_desc(irq);
+               if (desc && desc->status & IRQ_PER_CPU)
                        continue;
 
-               cpus_and(mask, irq_desc[irq].affinity, map);
-               if (any_online_cpu(mask) == NR_CPUS) {
+               cpumask_and(mask, desc->affinity, map);
+               if (cpumask_any(mask) >= nr_cpu_ids) {
                        printk("Breaking affinity for irq %i\n", irq);
-                       mask = map;
+                       cpumask_copy(mask, map);
                }
-               if (irq_desc[irq].chip->set_affinity)
-                       irq_desc[irq].chip->set_affinity(irq, mask);
-               else if (irq_desc[irq].action && !(warned++))
+               if (desc->chip->set_affinity)
+                       desc->chip->set_affinity(irq, mask);
+               else if (desc->action && !(warned++))
                        printk("Cannot set affinity for irq %i\n", irq);
        }
 
+       free_cpumask_var(mask);
+
        local_irq_enable();
        mdelay(1);
        local_irq_disable();
 }
 #endif
 
-void do_IRQ(struct pt_regs *regs)
-{
-       struct pt_regs *old_regs = set_irq_regs(regs);
-       unsigned int irq;
 #ifdef CONFIG_IRQSTACKS
+static inline void handle_one_irq(unsigned int irq)
+{
        struct thread_info *curtp, *irqtp;
-#endif
+       unsigned long saved_sp_limit;
+       struct irq_desc *desc;
 
-       irq_enter();
+       /* Switch to the irq stack to handle this */
+       curtp = current_thread_info();
+       irqtp = hardirq_ctx[smp_processor_id()];
 
+       if (curtp == irqtp) {
+               /* We're already on the irq stack, just handle it */
+               generic_handle_irq(irq);
+               return;
+       }
+
+       desc = irq_to_desc(irq);
+       saved_sp_limit = current->thread.ksp_limit;
+
+       irqtp->task = curtp->task;
+       irqtp->flags = 0;
+
+       /* Copy the softirq bits in preempt_count so that the
+        * softirq checks work in the hardirq context. */
+       irqtp->preempt_count = (irqtp->preempt_count & ~SOFTIRQ_MASK) |
+                              (curtp->preempt_count & SOFTIRQ_MASK);
+
+       current->thread.ksp_limit = (unsigned long)irqtp +
+               _ALIGN_UP(sizeof(struct thread_info), 16);
+
+       call_handle_irq(irq, desc, irqtp, desc->handle_irq);
+       current->thread.ksp_limit = saved_sp_limit;
+       irqtp->task = NULL;
+
+       /* Set any flag that may have been set on the
+        * alternate stack
+        */
+       if (irqtp->flags)
+               set_bits(irqtp->flags, &curtp->flags);
+}
+#else
+static inline void handle_one_irq(unsigned int irq)
+{
+       generic_handle_irq(irq);
+}
+#endif
+
+static inline void check_stack_overflow(void)
+{
 #ifdef CONFIG_DEBUG_STACKOVERFLOW
-       /* Debugging check for stack overflow: is there less than 2KB free? */
-       {
-               long sp;
+       long sp;
 
-               sp = __get_SP() & (THREAD_SIZE-1);
+       sp = __get_SP() & (THREAD_SIZE-1);
 
-               if (unlikely(sp < (sizeof(struct thread_info) + 2048))) {
-                       printk("do_IRQ: stack overflow: %ld\n",
-                               sp - sizeof(struct thread_info));
-                       dump_stack();
-               }
+       /* check for stack overflow: is there less than 2KB free? */
+       if (unlikely(sp < (sizeof(struct thread_info) + 2048))) {
+               printk("do_IRQ: stack overflow: %ld\n",
+                       sp - sizeof(struct thread_info));
+               dump_stack();
        }
 #endif
+}
 
-       /*
-        * Every platform is required to implement ppc_md.get_irq.
-        * This function will either return an irq number or NO_IRQ to
-        * indicate there are no more pending.
-        * The value NO_IRQ_IGNORE is for buggy hardware and means that this
-        * IRQ has already been handled. -- Tom
-        */
-       irq = ppc_md.get_irq();
+void do_IRQ(struct pt_regs *regs)
+{
+       struct pt_regs *old_regs = set_irq_regs(regs);
+       unsigned int irq;
 
-       if (irq != NO_IRQ && irq != NO_IRQ_IGNORE) {
-#ifdef CONFIG_IRQSTACKS
-               /* Switch to the irq stack to handle this */
-               curtp = current_thread_info();
-               irqtp = hardirq_ctx[smp_processor_id()];
-               if (curtp != irqtp) {
-                       struct irq_desc *desc = irq_desc + irq;
-                       void *handler = desc->handle_irq;
-                       unsigned long saved_sp_limit = current->thread.ksp_limit;
-                       if (handler == NULL)
-                               handler = &__do_IRQ;
-                       irqtp->task = curtp->task;
-                       irqtp->flags = 0;
-
-                       /* Copy the softirq bits in preempt_count so that the
-                        * softirq checks work in the hardirq context.
-                        */
-                       irqtp->preempt_count =
-                               (irqtp->preempt_count & ~SOFTIRQ_MASK) |
-                               (curtp->preempt_count & SOFTIRQ_MASK);
+       trace_irq_entry(regs);
 
-                       current->thread.ksp_limit = (unsigned long)irqtp +
-                               _ALIGN_UP(sizeof(struct thread_info), 16);
-                       call_handle_irq(irq, desc, irqtp, handler);
-                       current->thread.ksp_limit = saved_sp_limit;
-                       irqtp->task = NULL;
+       irq_enter();
 
+       check_stack_overflow();
 
-                       /* Set any flag that may have been set on the
-                        * alternate stack
-                        */
-                       if (irqtp->flags)
-                               set_bits(irqtp->flags, &curtp->flags);
-               } else
-#endif
-                       generic_handle_irq(irq);
-       } else if (irq != NO_IRQ_IGNORE)
-               /* That's not SMP safe ... but who cares ? */
-               ppc_spurious_interrupts++;
+       irq = ppc_md.get_irq();
+
+       if (irq != NO_IRQ && irq != NO_IRQ_IGNORE)
+               handle_one_irq(irq);
+       else if (irq != NO_IRQ_IGNORE)
+               __get_cpu_var(irq_stat).spurious_irqs++;
 
        irq_exit();
        set_irq_regs(old_regs);
@@ -343,6 +410,8 @@ void do_IRQ(struct pt_regs *regs)
                timer_interrupt(regs);
        }
 #endif
+
+       trace_irq_exit(regs);
 }
 
 void __init init_IRQ(void)
@@ -448,7 +517,7 @@ void do_softirq(void)
  */
 
 static LIST_HEAD(irq_hosts);
-static DEFINE_SPINLOCK(irq_big_lock);
+static DEFINE_RAW_SPINLOCK(irq_big_lock);
 static unsigned int revmap_trees_allocated;
 static DEFINE_MUTEX(revmap_trees_mutex);
 struct irq_map_entry irq_map[NR_IRQS];
@@ -494,14 +563,14 @@ struct irq_host *irq_alloc_host(struct device_node *of_node,
        if (host->ops->match == NULL)
                host->ops->match = default_irq_host_match;
 
-       spin_lock_irqsave(&irq_big_lock, flags);
+       raw_spin_lock_irqsave(&irq_big_lock, flags);
 
        /* If it's a legacy controller, check for duplicates and
         * mark it as allocated (we use irq 0 host pointer for that
         */
        if (revmap_type == IRQ_HOST_MAP_LEGACY) {
                if (irq_map[0].host != NULL) {
-                       spin_unlock_irqrestore(&irq_big_lock, flags);
+                       raw_spin_unlock_irqrestore(&irq_big_lock, flags);
                        /* If we are early boot, we can't free the structure,
                         * too bad...
                         * this will be fixed once slab is made available early
@@ -515,7 +584,7 @@ struct irq_host *irq_alloc_host(struct device_node *of_node,
        }
 
        list_add(&host->link, &irq_hosts);
-       spin_unlock_irqrestore(&irq_big_lock, flags);
+       raw_spin_unlock_irqrestore(&irq_big_lock, flags);
 
        /* Additional setups per revmap type */
        switch(revmap_type) {
@@ -530,7 +599,7 @@ struct irq_host *irq_alloc_host(struct device_node *of_node,
                        smp_wmb();
 
                        /* Clear norequest flags */
-                       get_irq_desc(i)->status &= ~IRQ_NOREQUEST;
+                       irq_to_desc(i)->status &= ~IRQ_NOREQUEST;
 
                        /* Legacy flags are left to default at this point,
                         * one can then use irq_create_mapping() to
@@ -566,13 +635,13 @@ struct irq_host *irq_find_host(struct device_node *node)
         * the absence of a device node. This isn't a problem so far
         * yet though...
         */
-       spin_lock_irqsave(&irq_big_lock, flags);
+       raw_spin_lock_irqsave(&irq_big_lock, flags);
        list_for_each_entry(h, &irq_hosts, link)
                if (h->ops->match(h, node)) {
                        found = h;
                        break;
                }
-       spin_unlock_irqrestore(&irq_big_lock, flags);
+       raw_spin_unlock_irqrestore(&irq_big_lock, flags);
        return found;
 }
 EXPORT_SYMBOL_GPL(irq_find_host);
@@ -596,8 +665,16 @@ void irq_set_virq_count(unsigned int count)
 static int irq_setup_virq(struct irq_host *host, unsigned int virq,
                            irq_hw_number_t hwirq)
 {
+       struct irq_desc *desc;
+
+       desc = irq_to_desc_alloc_node(virq, 0);
+       if (!desc) {
+               pr_debug("irq: -> allocating desc failed\n");
+               goto error;
+       }
+
        /* Clear IRQ_NOREQUEST flag */
-       get_irq_desc(virq)->status &= ~IRQ_NOREQUEST;
+       desc->status &= ~IRQ_NOREQUEST;
 
        /* map it */
        smp_wmb();
@@ -606,11 +683,14 @@ static int irq_setup_virq(struct irq_host *host, unsigned int virq,
 
        if (host->ops->map(host, virq, hwirq)) {
                pr_debug("irq: -> mapping failed, freeing\n");
-               irq_free_virt(virq, 1);
-               return -1;
+               goto error;
        }
 
        return 0;
+
+error:
+       irq_free_virt(virq, 1);
+       return -1;
 }
 
 unsigned int irq_create_direct_mapping(struct irq_host *host)
@@ -682,17 +762,19 @@ unsigned int irq_create_mapping(struct irq_host *host,
                        return NO_IRQ;
                }
        }
-       pr_debug("irq: -> obtained virq %d\n", virq);
 
        if (irq_setup_virq(host, virq, hwirq))
                return NO_IRQ;
 
+       printk(KERN_DEBUG "irq: irq %lu on host %s mapped to virtual irq %u\n",
+               hwirq, host->of_node ? host->of_node->full_name : "null", virq);
+
        return virq;
 }
 EXPORT_SYMBOL_GPL(irq_create_mapping);
 
 unsigned int irq_create_of_mapping(struct device_node *controller,
-                                  u32 *intspec, unsigned int intsize)
+                                  const u32 *intspec, unsigned int intsize)
 {
        struct irq_host *host;
        irq_hw_number_t hwirq;
@@ -725,7 +807,7 @@ unsigned int irq_create_of_mapping(struct device_node *controller,
 
        /* Set type if specified and different than the current one */
        if (type != IRQ_TYPE_NONE &&
-           type != (get_irq_desc(virq)->status & IRQF_TRIGGER_MASK))
+           type != (irq_to_desc(virq)->status & IRQF_TRIGGER_MASK))
                set_irq_type(virq, type);
        return virq;
 }
@@ -797,7 +879,7 @@ void irq_dispose_mapping(unsigned int virq)
        irq_map[virq].hwirq = host->inval_irq;
 
        /* Set some flags */
-       get_irq_desc(virq)->status |= IRQ_NOREQUEST;
+       irq_to_desc(virq)->status |= IRQ_NOREQUEST;
 
        /* Free it */
        irq_free_virt(virq, 1);
@@ -928,7 +1010,7 @@ unsigned int irq_alloc_virt(struct irq_host *host,
        if (count == 0 || count > (irq_virq_count - NUM_ISA_INTERRUPTS))
                return NO_IRQ;
 
-       spin_lock_irqsave(&irq_big_lock, flags);
+       raw_spin_lock_irqsave(&irq_big_lock, flags);
 
        /* Use hint for 1 interrupt if any */
        if (count == 1 && hint >= NUM_ISA_INTERRUPTS &&
@@ -952,7 +1034,7 @@ unsigned int irq_alloc_virt(struct irq_host *host,
                }
        }
        if (found == NO_IRQ) {
-               spin_unlock_irqrestore(&irq_big_lock, flags);
+               raw_spin_unlock_irqrestore(&irq_big_lock, flags);
                return NO_IRQ;
        }
  hint_found:
@@ -961,7 +1043,7 @@ unsigned int irq_alloc_virt(struct irq_host *host,
                smp_wmb();
                irq_map[i].host = host;
        }
-       spin_unlock_irqrestore(&irq_big_lock, flags);
+       raw_spin_unlock_irqrestore(&irq_big_lock, flags);
        return found;
 }
 
@@ -973,7 +1055,7 @@ void irq_free_virt(unsigned int virq, unsigned int count)
        WARN_ON (virq < NUM_ISA_INTERRUPTS);
        WARN_ON (count == 0 || (virq + count) > irq_virq_count);
 
-       spin_lock_irqsave(&irq_big_lock, flags);
+       raw_spin_lock_irqsave(&irq_big_lock, flags);
        for (i = virq; i < (virq + count); i++) {
                struct irq_host *host;
 
@@ -986,15 +1068,27 @@ void irq_free_virt(unsigned int virq, unsigned int count)
                smp_wmb();
                irq_map[i].host = NULL;
        }
-       spin_unlock_irqrestore(&irq_big_lock, flags);
+       raw_spin_unlock_irqrestore(&irq_big_lock, flags);
 }
 
-void irq_early_init(void)
+int arch_early_irq_init(void)
 {
-       unsigned int i;
+       struct irq_desc *desc;
+       int i;
 
-       for (i = 0; i < NR_IRQS; i++)
-               get_irq_desc(i)->status |= IRQ_NOREQUEST;
+       for (i = 0; i < NR_IRQS; i++) {
+               desc = irq_to_desc(i);
+               if (desc)
+                       desc->status |= IRQ_NOREQUEST;
+       }
+
+       return 0;
+}
+
+int arch_init_chip_data(struct irq_desc *desc, int node)
+{
+       desc->status |= IRQ_NOREQUEST;
+       return 0;
 }
 
 /* We need to create the radix trees late */
@@ -1048,7 +1142,7 @@ arch_initcall(irq_late_init);
 static int virq_debug_show(struct seq_file *m, void *private)
 {
        unsigned long flags;
-       irq_desc_t *desc;
+       struct irq_desc *desc;
        const char *p;
        char none[] = "none";
        int i;
@@ -1056,16 +1150,19 @@ static int virq_debug_show(struct seq_file *m, void *private)
        seq_printf(m, "%-5s  %-7s  %-15s  %s\n", "virq", "hwirq",
                      "chip name", "host name");
 
-       for (i = 1; i < NR_IRQS; i++) {
-               desc = get_irq_desc(i);
-               spin_lock_irqsave(&desc->lock, flags);
+       for (i = 1; i < nr_irqs; i++) {
+               desc = irq_to_desc(i);
+               if (!desc)
+                       continue;
+
+               raw_spin_lock_irqsave(&desc->lock, flags);
 
                if (desc->action && desc->action->handler) {
                        seq_printf(m, "%5d  ", i);
                        seq_printf(m, "0x%05lx  ", virq_to_hw(i));
 
-                       if (desc->chip && desc->chip->typename)
-                               p = desc->chip->typename;
+                       if (desc->chip && desc->chip->name)
+                               p = desc->chip->name;
                        else
                                p = none;
                        seq_printf(m, "%-15s  ", p);
@@ -1077,7 +1174,7 @@ static int virq_debug_show(struct seq_file *m, void *private)
                        seq_printf(m, "%s\n", p);
                }
 
-               spin_unlock_irqrestore(&desc->lock, flags);
+               raw_spin_unlock_irqrestore(&desc->lock, flags);
        }
 
        return 0;