The hardware perf event driver and oprofile interpret the global cp0_perfcount_irq differently: in the hardware perf event driver it is an offset from MIPS_CPU_IRQ_BASE and in oprofile it is the actual IRQ number. This still works most of the time since MIPS_CPU_IRQ_BASE is usually 0, but is clearly wrong. Since the performance counter interrupt may vary from platform to platform like the C0 timer interrupt, add the optional get_c0_perfcount_int hook which returns the IRQ number of the performance counter. The hook should return < 0 if the performance counter interrupt is shared with the timer. If the hook is not present, the CPU vector reported in C0_IntCtl (cp0_perfcount_irq) is used. Signed-off-by: Andrew Bresticker <abrestic@xxxxxxxxxxxx> Reviewed-by: Qais Yousef <qais.yousef@xxxxxxxxxx> Tested-by: Qais Yousef <qais.yousef@xxxxxxxxxx> --- No changes from v1. --- arch/mips/ath79/irq.c | 1 - arch/mips/ath79/setup.c | 5 +++++ arch/mips/include/asm/time.h | 1 + arch/mips/kernel/perf_event_mipsxx.c | 23 +++++++---------------- arch/mips/lantiq/irq.c | 8 +++++++- arch/mips/mti-malta/malta-time.c | 16 ++++++---------- arch/mips/mti-sead3/sead3-time.c | 7 ++++--- arch/mips/oprofile/op_model_mipsxx.c | 18 ++++++++++++++---- arch/mips/ralink/irq.c | 8 +++++++- 9 files changed, 51 insertions(+), 36 deletions(-) diff --git a/arch/mips/ath79/irq.c b/arch/mips/ath79/irq.c index 9c0e176..6adae36 100644 --- a/arch/mips/ath79/irq.c +++ b/arch/mips/ath79/irq.c @@ -359,7 +359,6 @@ void __init arch_init_irq(void) BUG(); } - cp0_perfcount_irq = ATH79_MISC_IRQ(5); mips_cpu_irq_init(); ath79_misc_irq_init(); diff --git a/arch/mips/ath79/setup.c b/arch/mips/ath79/setup.c index 64807a4..a73c93c 100644 --- a/arch/mips/ath79/setup.c +++ b/arch/mips/ath79/setup.c @@ -182,6 +182,11 @@ const char *get_system_type(void) return ath79_sys_type; } +int get_c0_perfcount_int(void) +{ + return ATH79_MISC_IRQ(5); +} + unsigned int get_c0_compare_int(void) { return CP0_LEGACY_COMPARE_IRQ; diff --git a/arch/mips/include/asm/time.h b/arch/mips/include/asm/time.h index 8f3047d..7969933 100644 --- a/arch/mips/include/asm/time.h +++ b/arch/mips/include/asm/time.h @@ -46,6 +46,7 @@ extern unsigned int mips_hpt_frequency; * so it lives here. */ extern int (*perf_irq)(void); +extern int __weak get_c0_perfcount_int(void); /* * Initialize the calling CPU's compare interrupt as clockevent device diff --git a/arch/mips/kernel/perf_event_mipsxx.c b/arch/mips/kernel/perf_event_mipsxx.c index b63f248..3a25096 100644 --- a/arch/mips/kernel/perf_event_mipsxx.c +++ b/arch/mips/kernel/perf_event_mipsxx.c @@ -1614,22 +1614,13 @@ init_hw_perf_events(void) counters = counters_total_to_per_cpu(counters); #endif -#ifdef MSC01E_INT_BASE - if (cpu_has_veic) { - /* - * Using platform specific interrupt controller defines. - */ - irq = MSC01E_INT_BASE + MSC01E_INT_PERFCTR; - } else { -#endif - if ((cp0_perfcount_irq >= 0) && - (cp0_compare_irq != cp0_perfcount_irq)) - irq = MIPS_CPU_IRQ_BASE + cp0_perfcount_irq; - else - irq = -1; -#ifdef MSC01E_INT_BASE - } -#endif + if (get_c0_perfcount_int) + irq = get_c0_perfcount_int(); + else if ((cp0_perfcount_irq >= 0) && + (cp0_compare_irq != cp0_perfcount_irq)) + irq = MIPS_CPU_IRQ_BASE + cp0_perfcount_irq; + else + irq = -1; mipspmu.map_raw_event = mipsxx_pmu_map_raw_event; diff --git a/arch/mips/lantiq/irq.c b/arch/mips/lantiq/irq.c index 030568a..21c38ee 100644 --- a/arch/mips/lantiq/irq.c +++ b/arch/mips/lantiq/irq.c @@ -70,6 +70,7 @@ static struct resource ltq_eiu_irq[MAX_EIU]; static void __iomem *ltq_icu_membase[MAX_IM]; static void __iomem *ltq_eiu_membase; static struct irq_domain *ltq_domain; +static int ltq_perfcount_irq; int ltq_eiu_get_irq(int exin) { @@ -449,7 +450,7 @@ int __init icu_of_init(struct device_node *node, struct device_node *parent) #endif /* tell oprofile which irq to use */ - cp0_perfcount_irq = irq_create_mapping(ltq_domain, LTQ_PERF_IRQ); + ltq_perfcount_irq = irq_create_mapping(ltq_domain, LTQ_PERF_IRQ); /* * if the timer irq is not one of the mips irqs we need to @@ -461,6 +462,11 @@ int __init icu_of_init(struct device_node *node, struct device_node *parent) return 0; } +int get_c0_perfcount_int(void) +{ + return ltq_perfcount_irq; +} + unsigned int get_c0_compare_int(void) { return MIPS_CPU_TIMER_IRQ; diff --git a/arch/mips/mti-malta/malta-time.c b/arch/mips/mti-malta/malta-time.c index 3778a35..a4e035c 100644 --- a/arch/mips/mti-malta/malta-time.c +++ b/arch/mips/mti-malta/malta-time.c @@ -121,22 +121,20 @@ void read_persistent_clock(struct timespec *ts) ts->tv_nsec = 0; } -static void __init plat_perf_setup(void) +int get_c0_perfcount_int(void) { -#ifdef MSC01E_INT_BASE if (cpu_has_veic) { set_vi_handler(MSC01E_INT_PERFCTR, mips_perf_dispatch); mips_cpu_perf_irq = MSC01E_INT_BASE + MSC01E_INT_PERFCTR; - } else -#endif - if (cp0_perfcount_irq >= 0) { + } else if (cp0_perfcount_irq >= 0) { if (cpu_has_vint) set_vi_handler(cp0_perfcount_irq, mips_perf_dispatch); mips_cpu_perf_irq = MIPS_CPU_IRQ_BASE + cp0_perfcount_irq; -#ifdef CONFIG_SMP - irq_set_handler(mips_cpu_perf_irq, handle_percpu_irq); -#endif + } else { + mips_cpu_perf_irq = -1; } + + return mips_cpu_perf_irq; } unsigned int get_c0_compare_int(void) @@ -201,6 +199,4 @@ void __init plat_time_init(void) #endif } #endif - - plat_perf_setup(); } diff --git a/arch/mips/mti-sead3/sead3-time.c b/arch/mips/mti-sead3/sead3-time.c index 678d03d..f090c51 100644 --- a/arch/mips/mti-sead3/sead3-time.c +++ b/arch/mips/mti-sead3/sead3-time.c @@ -81,13 +81,16 @@ void read_persistent_clock(struct timespec *ts) ts->tv_nsec = 0; } -static void __init plat_perf_setup(void) +int get_c0_perfcount_int(void) { if (cp0_perfcount_irq >= 0) { if (cpu_has_vint) set_vi_handler(cp0_perfcount_irq, mips_perf_dispatch); mips_cpu_perf_irq = MIPS_CPU_IRQ_BASE + cp0_perfcount_irq; + } else { + mips_cpu_perf_irq = -1; } + return mips_cpu_perf_irq; } unsigned int get_c0_compare_int(void) @@ -108,6 +111,4 @@ void __init plat_time_init(void) (est_freq % 1000000) * 100 / 1000000); mips_scroll_message(); - - plat_perf_setup(); } diff --git a/arch/mips/oprofile/op_model_mipsxx.c b/arch/mips/oprofile/op_model_mipsxx.c index 42821ae..01f721a 100644 --- a/arch/mips/oprofile/op_model_mipsxx.c +++ b/arch/mips/oprofile/op_model_mipsxx.c @@ -11,6 +11,7 @@ #include <linux/interrupt.h> #include <linux/smp.h> #include <asm/irq_regs.h> +#include <asm/time.h> #include "op_impl.h" @@ -35,6 +36,7 @@ #define M_PERFCTL_COUNT_ALL_THREADS (1UL << 13) static int (*save_perf_irq)(void); +static int perfcount_irq; /* * XLR has only one set of counters per core. Designate the @@ -431,8 +433,16 @@ static int __init mipsxx_init(void) save_perf_irq = perf_irq; perf_irq = mipsxx_perfcount_handler; - if ((cp0_perfcount_irq >= 0) && (cp0_compare_irq != cp0_perfcount_irq)) - return request_irq(cp0_perfcount_irq, mipsxx_perfcount_int, + if (get_c0_perfcount_int) + perfcount_irq = get_c0_perfcount_int(); + else if ((cp0_perfcount_irq >= 0) && + (cp0_compare_irq != cp0_perfcount_irq)) + perfcount_irq = MIPS_CPU_IRQ_BASE + cp0_perfcount_irq; + else + perfcount_irq = -1; + + if (perfcount_irq >= 0) + return request_irq(perfcount_irq, mipsxx_perfcount_int, 0, "Perfcounter", save_perf_irq); return 0; @@ -442,8 +452,8 @@ static void mipsxx_exit(void) { int counters = op_model_mipsxx_ops.num_counters; - if ((cp0_perfcount_irq >= 0) && (cp0_compare_irq != cp0_perfcount_irq)) - free_irq(cp0_perfcount_irq, save_perf_irq); + if (perfcount_irq >= 0) + free_irq(perfcount_irq, save_perf_irq); counters = counters_per_cpu_to_total(counters); on_each_cpu(reset_counters, (void *)(long)counters, 1); diff --git a/arch/mips/ralink/irq.c b/arch/mips/ralink/irq.c index 0495011..7634dcd 100644 --- a/arch/mips/ralink/irq.c +++ b/arch/mips/ralink/irq.c @@ -45,6 +45,7 @@ #define RALINK_INTC_IRQ_PERFC (RALINK_INTC_IRQ_BASE + 9) static void __iomem *rt_intc_membase; +static int rt_perfcount_irq; static inline void rt_intc_w32(u32 val, unsigned reg) { @@ -73,6 +74,11 @@ static struct irq_chip ralink_intc_irq_chip = { .irq_mask_ack = ralink_intc_irq_mask, }; +int get_c0_perfcount_int(void) +{ + return rt_perfcount_irq; +} + unsigned int get_c0_compare_int(void) { return CP0_LEGACY_COMPARE_IRQ; @@ -167,7 +173,7 @@ static int __init intc_of_init(struct device_node *node, irq_set_handler_data(irq, domain); /* tell the kernel which irq is used for performance monitoring */ - cp0_perfcount_irq = irq_create_mapping(domain, 9); + rt_perfcount_irq = irq_create_mapping(domain, 9); return 0; } -- 2.1.0.rc2.206.gedb03e5