MIPS: Oprofile: Fixup the loose ends in the plumbing.
Ralf Baechle [Fri, 9 Dec 2005 12:29:38 +0000 (12:29 +0000)]
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>

arch/mips/kernel/time.c
arch/mips/mips-boards/generic/time.c
arch/mips/oprofile/op_impl.h
arch/mips/oprofile/op_model_mipsxx.c

index 07e125c..7050b4f 100644 (file)
@@ -507,14 +507,38 @@ irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
        return IRQ_HANDLED;
 }
 
+int null_perf_irq(struct pt_regs *regs)
+{
+       return 0;
+}
+
+int (*perf_irq)(struct pt_regs *regs) = null_perf_irq;
+
+EXPORT_SYMBOL(null_perf_irq);
+EXPORT_SYMBOL(perf_irq);
+
 asmlinkage void ll_timer_interrupt(int irq, struct pt_regs *regs)
 {
+       int r2 = cpu_has_mips_r2;
+
        irq_enter();
        kstat_this_cpu.irqs[irq]++;
 
+       /*
+        * Suckage alert:
+        * Before R2 of the architecture there was no way to see if a
+        * performance counter interrupt was pending, so we have to run the
+        * performance counter interrupt handler anyway.
+        */
+       if (!r2 || (read_c0_cause() & (1 << 26)))
+               if (perf_irq(regs))
+                       goto out;
+
        /* we keep interrupt disabled all the time */
-       timer_interrupt(irq, NULL, regs);
+       if (!r2 || (read_c0_cause() & (1 << 30)))
+               timer_interrupt(irq, NULL, regs);
 
+out:
        irq_exit();
 }
 
index 2830f65..93f3bf2 100644 (file)
@@ -75,16 +75,29 @@ static void mips_timer_dispatch (struct pt_regs *regs)
        do_IRQ (mips_cpu_timer_irq, regs);
 }
 
+extern int null_perf_irq(struct pt_regs *regs);
+
+extern int (*perf_irq)(struct pt_regs *regs);
+
 irqreturn_t mips_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
+       int r2 = cpu_has_mips_r2;
        int cpu = smp_processor_id();
 
        if (cpu == 0) {
                /*
-                * CPU 0 handles the global timer interrupt job and process accounting
-                * resets count/compare registers to trigger next timer int.
+                * CPU 0 handles the global timer interrupt job and process
+                * accounting resets count/compare registers to trigger next
+                * timer int.
                 */
-               timer_interrupt(irq, dev_id, regs);
+               if (!r2 || (read_c0_cause() & (1 << 26)))
+                       if (perf_irq(regs))
+                               goto out;
+
+               /* we keep interrupt disabled all the time */
+               if (!r2 || (read_c0_cause() & (1 << 30)))
+                       timer_interrupt(irq, NULL, regs);
+
                scroll_display_message();
        } else {
                /* Everyone else needs to reset the timer int here as
@@ -101,6 +114,7 @@ irqreturn_t mips_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
                local_timer_interrupt (irq, dev_id, regs);
        }
 
+out:
        return IRQ_HANDLED;
 }
 
index f012155..5cfce7d 100644 (file)
@@ -12,8 +12,8 @@
 
 struct pt_regs;
 
-extern void null_perf_irq(struct pt_regs *regs);
-extern void (*perf_irq)(struct pt_regs *regs);
+extern int null_perf_irq(struct pt_regs *regs);
+extern int (*perf_irq)(struct pt_regs *regs);
 
 /* Per-counter configuration as set via oprofilefs.  */
 struct op_counter_config {
index d36b64d..a4a4aa9 100644 (file)
@@ -114,11 +114,12 @@ static void mipsxx_cpu_stop(void *args)
        }
 }
 
-static void mipsxx_perfcount_handler(struct pt_regs *regs)
+static int mipsxx_perfcount_handler(struct pt_regs *regs)
 {
        unsigned int counters = op_model_mipsxx.num_counters;
        unsigned int control;
        unsigned int counter;
+       int handled = 0;
 
        switch (counters) {
 #define HANDLE_COUNTER(n)                                              \
@@ -129,12 +130,15 @@ static void mipsxx_perfcount_handler(struct pt_regs *regs)
                    (counter & M_COUNTER_OVERFLOW)) {                   \
                        oprofile_add_sample(regs, n);                   \
                        write_c0_perfcntr ## n(reg.counter[n]);         \
+                       handled = 1;                                    \
                }
        HANDLE_COUNTER(3)
        HANDLE_COUNTER(2)
        HANDLE_COUNTER(1)
        HANDLE_COUNTER(0)
        }
+
+       return handled;
 }
 
 #define M_CONFIG1_PC   (1 << 4)