All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v6 0/7] MIPS performance event support v6
@ 2010-06-09  4:35 Deng-Cheng Zhu
  2010-06-09  4:35 ` [PATCH v6 1/7] MIPS/Oprofile: extract PMU defines/helper functions for sharing Deng-Cheng Zhu
                   ` (6 more replies)
  0 siblings, 7 replies; 17+ messages in thread
From: Deng-Cheng Zhu @ 2010-06-09  4:35 UTC (permalink / raw)
  To: linux-mips, ralf
  Cc: a.p.zijlstra, paulus, mingo, acme, jamie.iles, Deng-Cheng Zhu

This patchset implements the low-level logic for the Linux performance
counter subsystem (Perf-events) on MIPS, which enables the collection of
all sorts of HW/SW performance events based on per-CPU or per-task.
For more information, please refer to tools/perf/Documentation/perf.txt.

Currently in the Oprofile module, MIPS PMUs are mainly categorized into 3
(mipsxx, loongson2 and rm9000). This patchset provides the skeleton code
together with the mipsxx specific code. The other 2 (and maybe more) can
be supported by adding new C files like the current perf_event_mipsxx.c.

To support mipsxx CPUs other than the implemented 24K/34K/74K/1004K, the
corresponding general event map, cache event map and raw event macros need
to be added. Please refer to init_hw_perf_events() for more info.

Tests were carried out on the Malta-R board. 24K/34K/74K/1004K cores and
SMVP/UP kernels have been basically tested. Any more tests are welcome.

To do this, you may want to compile the tool "perf" for your MIPS
platform. You can refer to the following URL:
http://www.linux-mips.org/archives/linux-mips/2010-04/msg00158.html

Please note: Before that patch is accepted, you can choose a "specific"
rmb() which is suitable for your platform -- an example is provided in
the description of that patch.

You also need to customize the CFLAGS and LDFLAGS in tools/perf/Makefile
for your libs, includes, etc.

In case you encounter the boot failure in SMVP kernel on multi-threading
CPUs, you may take a look at:
http://www.linux-mips.org/git?p=linux-mti.git;a=commitdiff;h=5460815027d802697b879644c74f0e8365254020

CHANGES:
-----------------------------
v6 - v5:
o leaving the Oprofile framework changes to another patchset.
o changing the callchain support.
o re-defining local_xchg() to pass the build (see patch #7).
o following some of David Daney's suggestions:
  * CPU #if's removed from pmu.h
  * Copyright info modified
  * Removing the perf_irq redefinition
  * 64bit counters considered
  * Read/write funcs and register defs separated from event defs
  * Raw event support merged into the main part
v5 - v4:
o splitting up Perf-events patches (v4 patches 1~4 are now splitted into
v5 patches 1~7) for easier review.
o adding more info into the description of patches (especially how to test
this patchset).
o simplifying the 2 small functions mipspmu_get_pmu_id() and
mipspmu_get_max_events().
v4 - v3:
o placing the variable save_perf_irq properly (issue found by Wu Zhangjin)
o the patches 5~9 make Oprofile use Perf-events framework as backend.
v3 - v2:
o adding 1004K core support.
o slightly adjusting the code structure.
o adding more comments in the code.
o fixing some small coding style issues.
v2 - v1:
o Adjusting code structure as suggested by Wu Zhangjin. With this change,
hardware performance event support for loongson2 and rm9000 can be
conveniently implemented by adding and including new files like
perf_event_loongson2.c; Oprofile and Perf for MIPS are now sharing pmu.h;
Naming changes were made to some functions.
o Fixing the generic atomic64 issue reported by David Daney. Currently,
32bit kernel is using the generic version from lib. When Ralf Baechle's
common atomic64 version is ready, this may change.
o Adding raw event support. For more details, please refer to the code
comments for mipsxx_pmu_map_raw_event().
o Adding new software events - PERF_COUNT_SW_ALIGNMENT_FAULTS and
PERF_COUNT_SW_EMULATION_FAULTS.
o Fixing some small bugs.
o Adding new comments for the code.
o Making some code style changes.
v1:
o Using generic atomic64 operations from lib.
o SMVP/UP kernels are supported (not for SMTC).
o 24K/34K/74K cores are implemented.
o Currently working when Oprofile is _not_ available.
o Minimal software perf events are supported.
-----------------------------

Deng-Cheng Zhu (7):
  MIPS/Oprofile: extract PMU defines/helper functions for sharing
  MIPS: use generic atomic64 in non-64bit kernels
  MIPS: add support for software performance events
  MIPS: add support for hardware performance events (skeleton)
  MIPS/Perf-events: add callchain support
  MIPS: add support for hardware performance events (mipsxx)
  MIPS: define local_xchg from xchg_local to atomic_long_xchg

 arch/mips/Kconfig                       |   11 +
 arch/mips/include/asm/atomic.h          |    4 +
 arch/mips/include/asm/local.h           |    2 +-
 arch/mips/include/asm/perf_event.h      |   26 +
 arch/mips/include/asm/pmu.h             |  299 ++++++++++++
 arch/mips/kernel/Makefile               |    2 +
 arch/mips/kernel/perf_event.c           |  623 ++++++++++++++++++++++++
 arch/mips/kernel/perf_event_mipsxx.c    |  802 +++++++++++++++++++++++++++++++
 arch/mips/kernel/traps.c                |   18 +-
 arch/mips/kernel/unaligned.c            |    5 +
 arch/mips/math-emu/cp1emu.c             |    3 +
 arch/mips/mm/fault.c                    |   11 +-
 arch/mips/oprofile/op_model_loongson2.c |   18 +-
 arch/mips/oprofile/op_model_mipsxx.c    |  147 +------
 arch/mips/oprofile/op_model_rm9000.c    |   16 +-
 15 files changed, 1805 insertions(+), 182 deletions(-)
 create mode 100644 arch/mips/include/asm/perf_event.h
 create mode 100644 arch/mips/include/asm/pmu.h
 create mode 100644 arch/mips/kernel/perf_event.c
 create mode 100644 arch/mips/kernel/perf_event_mipsxx.c

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH v6 1/7] MIPS/Oprofile: extract PMU defines/helper functions for sharing
  2010-06-09  4:35 [PATCH v6 0/7] MIPS performance event support v6 Deng-Cheng Zhu
@ 2010-06-09  4:35 ` Deng-Cheng Zhu
  2010-06-09  4:35 ` [PATCH v6 2/7] MIPS: use generic atomic64 in non-64bit kernels Deng-Cheng Zhu
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 17+ messages in thread
From: Deng-Cheng Zhu @ 2010-06-09  4:35 UTC (permalink / raw)
  To: linux-mips, ralf
  Cc: a.p.zijlstra, paulus, mingo, acme, jamie.iles, Deng-Cheng Zhu

Moving performance counter/control defines/helper functions into a single
header file, so that software using the MIPS PMU can share the code.

Signed-off-by: Deng-Cheng Zhu <dengcheng.zhu@gmail.com>
---
 arch/mips/include/asm/pmu.h             |  212 +++++++++++++++++++++++++++++++
 arch/mips/oprofile/op_model_loongson2.c |   18 +---
 arch/mips/oprofile/op_model_mipsxx.c    |  147 +---------------------
 arch/mips/oprofile/op_model_rm9000.c    |   16 +--
 4 files changed, 217 insertions(+), 176 deletions(-)
 create mode 100644 arch/mips/include/asm/pmu.h

diff --git a/arch/mips/include/asm/pmu.h b/arch/mips/include/asm/pmu.h
new file mode 100644
index 0000000..c84d70c
--- /dev/null
+++ b/arch/mips/include/asm/pmu.h
@@ -0,0 +1,212 @@
+/*
+ * linux/arch/mips/include/asm/pmu.h
+ *
+ * Copyright (C) 2004, 05, 06 by Ralf Baechle
+ * Copyright (C) 2005 by MIPS Technologies, Inc.
+ * Copyright (C) 2009 Lemote Inc.
+ * Author: Yanhua <yanh@lemote.com>
+ * Author: Wu Zhangjin <wuzhangjin@gmail.com>
+ * Copyright (C) 2010 MIPS Technologies, Inc.
+ * Author: Deng-Cheng Zhu (move things from Oprofile to common place)
+ *
+ * This file is shared by Oprofile and Perf. It is also shared across the
+ * Oprofile implementation for different MIPS CPUs.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __MIPS_PMU_H__
+#define __MIPS_PMU_H__
+
+/* Used by Perf-events */
+#define MIPS_MAX_HWEVENTS 4
+
+/* mipsxx */
+#define M_CONFIG1_PC	(1 << 4)
+
+#define M_PERFCTL_EXL			(1UL      <<  0)
+#define M_PERFCTL_KERNEL		(1UL      <<  1)
+#define M_PERFCTL_SUPERVISOR		(1UL      <<  2)
+#define M_PERFCTL_USER			(1UL      <<  3)
+#define M_PERFCTL_INTERRUPT_ENABLE	(1UL      <<  4)
+#define M_PERFCTL_EVENT(event)		(((event) & 0x3ff)  << 5)
+#define M_PERFCTL_VPEID(vpe)		((vpe)    << 16)
+#define M_PERFCTL_MT_EN(filter)		((filter) << 20)
+#define    M_TC_EN_ALL			M_PERFCTL_MT_EN(0)
+#define    M_TC_EN_VPE			M_PERFCTL_MT_EN(1)
+#define    M_TC_EN_TC			M_PERFCTL_MT_EN(2)
+#define M_PERFCTL_TCID(tcid)		((tcid)   << 22)
+#define M_PERFCTL_WIDE			(1UL      << 30)
+#define M_PERFCTL_MORE			(1UL      << 31)
+
+#define M_COUNTER_OVERFLOW		(1UL      << 31)
+
+#ifdef CONFIG_MIPS_MT_SMP
+static int cpu_has_mipsmt_pertccounters;
+#define WHAT		(M_TC_EN_VPE | \
+			M_PERFCTL_VPEID(cpu_data[smp_processor_id()].vpe_id))
+/*
+ * FIXME: For VSMP, vpe_id() is redefined for Perf, because
+ * cpu_data[cpuid].vpe_id reports 0 for _both_ CPUs. WHAT is not
+ * redefined because Perf does not use it.
+ */
+#if defined(CONFIG_HW_PERF_EVENTS)
+#define vpe_id()	(cpu_has_mipsmt_pertccounters ? \
+			0 : smp_processor_id())
+#else
+#define vpe_id()	(cpu_has_mipsmt_pertccounters ? \
+			0 : cpu_data[smp_processor_id()].vpe_id)
+#endif
+/*
+ * The number of bits to shift to convert between counters per core and
+ * counters per VPE.  There is no reasonable interface atm to obtain the
+ * number of VPEs used by Linux and in the 34K this number is fixed to two
+ * anyways so we hardcore a few things here for the moment.  The way it's
+ * done here will ensure that oprofile VSMP kernel will run right on a lesser
+ * core like a 24K also or with maxcpus=1.
+ */
+static inline unsigned int vpe_shift(void)
+{
+	if (num_possible_cpus() > 1)
+		return 1;
+
+	return 0;
+}
+#else
+#define WHAT		0
+#define vpe_id()	0
+static inline unsigned int vpe_shift(void)
+{
+	return 0;
+}
+#endif
+
+static inline unsigned int
+counters_total_to_per_cpu(unsigned int counters)
+{
+	return counters >> vpe_shift();
+}
+
+static inline unsigned int
+counters_per_cpu_to_total(unsigned int counters)
+{
+	return counters << vpe_shift();
+}
+
+#define __define_perf_accessors(r, n, np)				\
+									\
+static inline unsigned int r_c0_ ## r ## n(void)			\
+{									\
+	unsigned int cpu = vpe_id();					\
+									\
+	switch (cpu) {							\
+	case 0:								\
+		return read_c0_ ## r ## n();				\
+	case 1:								\
+		return read_c0_ ## r ## np();				\
+	default:							\
+		BUG();							\
+	}								\
+	return 0;							\
+}									\
+									\
+static inline void w_c0_ ## r ## n(unsigned int value)			\
+{									\
+	unsigned int cpu = vpe_id();					\
+									\
+	switch (cpu) {							\
+	case 0:								\
+		write_c0_ ## r ## n(value);				\
+		return;							\
+	case 1:								\
+		write_c0_ ## r ## np(value);				\
+		return;							\
+	default:							\
+		BUG();							\
+	}								\
+	return;								\
+}									\
+
+__define_perf_accessors(perfcntr, 0, 2)
+__define_perf_accessors(perfcntr, 1, 3)
+__define_perf_accessors(perfcntr, 2, 0)
+__define_perf_accessors(perfcntr, 3, 1)
+
+__define_perf_accessors(perfctrl, 0, 2)
+__define_perf_accessors(perfctrl, 1, 3)
+__define_perf_accessors(perfctrl, 2, 0)
+__define_perf_accessors(perfctrl, 3, 1)
+
+static inline int __n_counters(void)
+{
+	if (!(read_c0_config1() & M_CONFIG1_PC))
+		return 0;
+	if (!(read_c0_perfctrl0() & M_PERFCTL_MORE))
+		return 1;
+	if (!(read_c0_perfctrl1() & M_PERFCTL_MORE))
+		return 2;
+	if (!(read_c0_perfctrl2() & M_PERFCTL_MORE))
+		return 3;
+
+	return 4;
+}
+
+static inline int n_counters(void)
+{
+	int counters;
+
+	switch (current_cpu_type()) {
+	case CPU_R10000:
+		counters = 2;
+		break;
+
+	case CPU_R12000:
+	case CPU_R14000:
+		counters = 4;
+		break;
+
+	default:
+		counters = __n_counters();
+	}
+
+	return counters;
+}
+
+/* rm9000 */
+#define RM9K_COUNTER1_EVENT(event)	((event) << 0)
+#define RM9K_COUNTER1_SUPERVISOR	(1ULL    <<  7)
+#define RM9K_COUNTER1_KERNEL		(1ULL    <<  8)
+#define RM9K_COUNTER1_USER		(1ULL    <<  9)
+#define RM9K_COUNTER1_ENABLE		(1ULL    << 10)
+#define RM9K_COUNTER1_OVERFLOW		(1ULL    << 15)
+
+#define RM9K_COUNTER2_EVENT(event)	((event) << 16)
+#define RM9K_COUNTER2_SUPERVISOR	(1ULL    << 23)
+#define RM9K_COUNTER2_KERNEL		(1ULL    << 24)
+#define RM9K_COUNTER2_USER		(1ULL    << 25)
+#define RM9K_COUNTER2_ENABLE		(1ULL    << 26)
+#define RM9K_COUNTER2_OVERFLOW		(1ULL    << 31)
+
+extern unsigned int rm9000_perfcount_irq;
+
+/* loongson2 */
+#define LOONGSON2_CPU_TYPE	"mips/loongson2"
+
+#define LOONGSON2_PERFCNT_OVERFLOW		(1ULL   << 31)
+
+#define LOONGSON2_PERFCTRL_EXL			(1UL	<<  0)
+#define LOONGSON2_PERFCTRL_KERNEL		(1UL    <<  1)
+#define LOONGSON2_PERFCTRL_SUPERVISOR		(1UL    <<  2)
+#define LOONGSON2_PERFCTRL_USER			(1UL    <<  3)
+#define LOONGSON2_PERFCTRL_ENABLE		(1UL    <<  4)
+#define LOONGSON2_PERFCTRL_EVENT(idx, event) \
+	(((event) & 0x0f) << ((idx) ? 9 : 5))
+
+#define read_c0_perfctrl() __read_64bit_c0_register($24, 0)
+#define write_c0_perfctrl(val) __write_64bit_c0_register($24, 0, val)
+#define read_c0_perfcnt() __read_64bit_c0_register($25, 0)
+#define write_c0_perfcnt(val) __write_64bit_c0_register($25, 0, val)
+
+#endif /* __MIPS_PMU_H__ */
diff --git a/arch/mips/oprofile/op_model_loongson2.c b/arch/mips/oprofile/op_model_loongson2.c
index 60d3ea6..665e994 100644
--- a/arch/mips/oprofile/op_model_loongson2.c
+++ b/arch/mips/oprofile/op_model_loongson2.c
@@ -12,27 +12,11 @@
 #include <linux/init.h>
 #include <linux/oprofile.h>
 #include <linux/interrupt.h>
+#include <asm/pmu.h>
 
 #include <loongson.h>			/* LOONGSON2_PERFCNT_IRQ */
 #include "op_impl.h"
 
-#define LOONGSON2_CPU_TYPE	"mips/loongson2"
-
-#define LOONGSON2_PERFCNT_OVERFLOW		(1ULL   << 31)
-
-#define LOONGSON2_PERFCTRL_EXL			(1UL	<<  0)
-#define LOONGSON2_PERFCTRL_KERNEL		(1UL    <<  1)
-#define LOONGSON2_PERFCTRL_SUPERVISOR		(1UL    <<  2)
-#define LOONGSON2_PERFCTRL_USER			(1UL    <<  3)
-#define LOONGSON2_PERFCTRL_ENABLE		(1UL    <<  4)
-#define LOONGSON2_PERFCTRL_EVENT(idx, event) \
-	(((event) & 0x0f) << ((idx) ? 9 : 5))
-
-#define read_c0_perfctrl() __read_64bit_c0_register($24, 0)
-#define write_c0_perfctrl(val) __write_64bit_c0_register($24, 0, val)
-#define read_c0_perfcnt() __read_64bit_c0_register($25, 0)
-#define write_c0_perfcnt(val) __write_64bit_c0_register($25, 0, val)
-
 static struct loongson2_register_config {
 	unsigned int ctrl;
 	unsigned long long reset_counter1;
diff --git a/arch/mips/oprofile/op_model_mipsxx.c b/arch/mips/oprofile/op_model_mipsxx.c
index 54759f1..38d8a3f 100644
--- a/arch/mips/oprofile/op_model_mipsxx.c
+++ b/arch/mips/oprofile/op_model_mipsxx.c
@@ -11,116 +11,10 @@
 #include <linux/interrupt.h>
 #include <linux/smp.h>
 #include <asm/irq_regs.h>
+#include <asm/pmu.h>
 
 #include "op_impl.h"
 
-#define M_PERFCTL_EXL			(1UL      <<  0)
-#define M_PERFCTL_KERNEL		(1UL      <<  1)
-#define M_PERFCTL_SUPERVISOR		(1UL      <<  2)
-#define M_PERFCTL_USER			(1UL      <<  3)
-#define M_PERFCTL_INTERRUPT_ENABLE	(1UL      <<  4)
-#define M_PERFCTL_EVENT(event)		(((event) & 0x3ff)  << 5)
-#define M_PERFCTL_VPEID(vpe)		((vpe)    << 16)
-#define M_PERFCTL_MT_EN(filter)		((filter) << 20)
-#define    M_TC_EN_ALL			M_PERFCTL_MT_EN(0)
-#define    M_TC_EN_VPE			M_PERFCTL_MT_EN(1)
-#define    M_TC_EN_TC			M_PERFCTL_MT_EN(2)
-#define M_PERFCTL_TCID(tcid)		((tcid)   << 22)
-#define M_PERFCTL_WIDE			(1UL      << 30)
-#define M_PERFCTL_MORE			(1UL      << 31)
-
-#define M_COUNTER_OVERFLOW		(1UL      << 31)
-
-static int (*save_perf_irq)(void);
-
-#ifdef CONFIG_MIPS_MT_SMP
-static int cpu_has_mipsmt_pertccounters;
-#define WHAT		(M_TC_EN_VPE | \
-			 M_PERFCTL_VPEID(cpu_data[smp_processor_id()].vpe_id))
-#define vpe_id()	(cpu_has_mipsmt_pertccounters ? \
-			0 : cpu_data[smp_processor_id()].vpe_id)
-
-/*
- * The number of bits to shift to convert between counters per core and
- * counters per VPE.  There is no reasonable interface atm to obtain the
- * number of VPEs used by Linux and in the 34K this number is fixed to two
- * anyways so we hardcore a few things here for the moment.  The way it's
- * done here will ensure that oprofile VSMP kernel will run right on a lesser
- * core like a 24K also or with maxcpus=1.
- */
-static inline unsigned int vpe_shift(void)
-{
-	if (num_possible_cpus() > 1)
-		return 1;
-
-	return 0;
-}
-
-#else
-
-#define WHAT		0
-#define vpe_id()	0
-
-static inline unsigned int vpe_shift(void)
-{
-	return 0;
-}
-
-#endif
-
-static inline unsigned int counters_total_to_per_cpu(unsigned int counters)
-{
-	return counters >> vpe_shift();
-}
-
-static inline unsigned int counters_per_cpu_to_total(unsigned int counters)
-{
-	return counters << vpe_shift();
-}
-
-#define __define_perf_accessors(r, n, np)				\
-									\
-static inline unsigned int r_c0_ ## r ## n(void)			\
-{									\
-	unsigned int cpu = vpe_id();					\
-									\
-	switch (cpu) {							\
-	case 0:								\
-		return read_c0_ ## r ## n();				\
-	case 1:								\
-		return read_c0_ ## r ## np();				\
-	default:							\
-		BUG();							\
-	}								\
-	return 0;							\
-}									\
-									\
-static inline void w_c0_ ## r ## n(unsigned int value)			\
-{									\
-	unsigned int cpu = vpe_id();					\
-									\
-	switch (cpu) {							\
-	case 0:								\
-		write_c0_ ## r ## n(value);				\
-		return;							\
-	case 1:								\
-		write_c0_ ## r ## np(value);				\
-		return;							\
-	default:							\
-		BUG();							\
-	}								\
-	return;								\
-}									\
-
-__define_perf_accessors(perfcntr, 0, 2)
-__define_perf_accessors(perfcntr, 1, 3)
-__define_perf_accessors(perfcntr, 2, 0)
-__define_perf_accessors(perfcntr, 3, 1)
-
-__define_perf_accessors(perfctrl, 0, 2)
-__define_perf_accessors(perfctrl, 1, 3)
-__define_perf_accessors(perfctrl, 2, 0)
-__define_perf_accessors(perfctrl, 3, 1)
 
 struct op_mips_model op_model_mipsxx_ops;
 
@@ -242,43 +136,6 @@ static int mipsxx_perfcount_handler(void)
 	return handled;
 }
 
-#define M_CONFIG1_PC	(1 << 4)
-
-static inline int __n_counters(void)
-{
-	if (!(read_c0_config1() & M_CONFIG1_PC))
-		return 0;
-	if (!(read_c0_perfctrl0() & M_PERFCTL_MORE))
-		return 1;
-	if (!(read_c0_perfctrl1() & M_PERFCTL_MORE))
-		return 2;
-	if (!(read_c0_perfctrl2() & M_PERFCTL_MORE))
-		return 3;
-
-	return 4;
-}
-
-static inline int n_counters(void)
-{
-	int counters;
-
-	switch (current_cpu_type()) {
-	case CPU_R10000:
-		counters = 2;
-		break;
-
-	case CPU_R12000:
-	case CPU_R14000:
-		counters = 4;
-		break;
-
-	default:
-		counters = __n_counters();
-	}
-
-	return counters;
-}
-
 static void reset_counters(void *arg)
 {
 	int counters = (int)(long)arg;
@@ -298,6 +155,8 @@ static void reset_counters(void *arg)
 	}
 }
 
+static int (*save_perf_irq)(void);
+
 static int __init mipsxx_init(void)
 {
 	int counters;
diff --git a/arch/mips/oprofile/op_model_rm9000.c b/arch/mips/oprofile/op_model_rm9000.c
index 3aa8138..48e7487 100644
--- a/arch/mips/oprofile/op_model_rm9000.c
+++ b/arch/mips/oprofile/op_model_rm9000.c
@@ -9,24 +9,10 @@
 #include <linux/oprofile.h>
 #include <linux/interrupt.h>
 #include <linux/smp.h>
+#include <asm/pmu.h>
 
 #include "op_impl.h"
 
-#define RM9K_COUNTER1_EVENT(event)	((event) << 0)
-#define RM9K_COUNTER1_SUPERVISOR	(1ULL    <<  7)
-#define RM9K_COUNTER1_KERNEL		(1ULL    <<  8)
-#define RM9K_COUNTER1_USER		(1ULL    <<  9)
-#define RM9K_COUNTER1_ENABLE		(1ULL    << 10)
-#define RM9K_COUNTER1_OVERFLOW		(1ULL    << 15)
-
-#define RM9K_COUNTER2_EVENT(event)	((event) << 16)
-#define RM9K_COUNTER2_SUPERVISOR	(1ULL    << 23)
-#define RM9K_COUNTER2_KERNEL		(1ULL    << 24)
-#define RM9K_COUNTER2_USER		(1ULL    << 25)
-#define RM9K_COUNTER2_ENABLE		(1ULL    << 26)
-#define RM9K_COUNTER2_OVERFLOW		(1ULL    << 31)
-
-extern unsigned int rm9000_perfcount_irq;
 
 static struct rm9k_register_config {
 	unsigned int control;
-- 
1.6.3.3

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH v6 2/7] MIPS: use generic atomic64 in non-64bit kernels
  2010-06-09  4:35 [PATCH v6 0/7] MIPS performance event support v6 Deng-Cheng Zhu
  2010-06-09  4:35 ` [PATCH v6 1/7] MIPS/Oprofile: extract PMU defines/helper functions for sharing Deng-Cheng Zhu
@ 2010-06-09  4:35 ` Deng-Cheng Zhu
  2010-06-09 16:58   ` David Daney
  2010-08-06 16:45   ` Ralf Baechle
  2010-06-09  4:35 ` [PATCH v6 3/7] MIPS: add support for software performance events Deng-Cheng Zhu
                   ` (4 subsequent siblings)
  6 siblings, 2 replies; 17+ messages in thread
From: Deng-Cheng Zhu @ 2010-06-09  4:35 UTC (permalink / raw)
  To: linux-mips, ralf
  Cc: a.p.zijlstra, paulus, mingo, acme, jamie.iles, Deng-Cheng Zhu

64bit kernel has already had its atomic64 functions. Except for that, we
use the generic spinlocked version. The atomic64 types and related
functions are needed for the Linux performance counter subsystem.

Signed-off-by: Deng-Cheng Zhu <dengcheng.zhu@gmail.com>
---
 arch/mips/Kconfig              |    1 +
 arch/mips/include/asm/atomic.h |    4 ++++
 2 files changed, 5 insertions(+), 0 deletions(-)

diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index cdaae94..564e30b 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -11,6 +11,7 @@ config MIPS
 	select HAVE_FTRACE_MCOUNT_RECORD
 	select HAVE_FUNCTION_GRAPH_TRACER
 	select RTC_LIB if !MACH_LOONGSON
+	select GENERIC_ATOMIC64 if !64BIT
 
 mainmenu "Linux/MIPS Kernel Configuration"
 
diff --git a/arch/mips/include/asm/atomic.h b/arch/mips/include/asm/atomic.h
index 59dc0c7..485ec36 100644
--- a/arch/mips/include/asm/atomic.h
+++ b/arch/mips/include/asm/atomic.h
@@ -782,6 +782,10 @@ static __inline__ int atomic64_add_unless(atomic64_t *v, long a, long u)
  */
 #define atomic64_add_negative(i, v) (atomic64_add_return(i, (v)) < 0)
 
+#else /* !CONFIG_64BIT */
+
+#include <asm-generic/atomic64.h>
+
 #endif /* CONFIG_64BIT */
 
 /*
-- 
1.6.3.3

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH v6 3/7] MIPS: add support for software performance events
  2010-06-09  4:35 [PATCH v6 0/7] MIPS performance event support v6 Deng-Cheng Zhu
  2010-06-09  4:35 ` [PATCH v6 1/7] MIPS/Oprofile: extract PMU defines/helper functions for sharing Deng-Cheng Zhu
  2010-06-09  4:35 ` [PATCH v6 2/7] MIPS: use generic atomic64 in non-64bit kernels Deng-Cheng Zhu
@ 2010-06-09  4:35 ` Deng-Cheng Zhu
  2010-09-22 11:38   ` Matt Fleming
  2010-06-09  4:35 ` [PATCH v6 4/7] MIPS: add support for hardware performance events (skeleton) Deng-Cheng Zhu
                   ` (3 subsequent siblings)
  6 siblings, 1 reply; 17+ messages in thread
From: Deng-Cheng Zhu @ 2010-06-09  4:35 UTC (permalink / raw)
  To: linux-mips, ralf
  Cc: a.p.zijlstra, paulus, mingo, acme, jamie.iles, Deng-Cheng Zhu

Software events are required as part of the measurable stuff by the
Linux performance counter subsystem. Here is the list of events added by
this patch:
PERF_COUNT_SW_PAGE_FAULTS
PERF_COUNT_SW_PAGE_FAULTS_MIN
PERF_COUNT_SW_PAGE_FAULTS_MAJ
PERF_COUNT_SW_ALIGNMENT_FAULTS
PERF_COUNT_SW_EMULATION_FAULTS

Signed-off-by: Deng-Cheng Zhu <dengcheng.zhu@gmail.com>
---
 arch/mips/Kconfig            |    2 ++
 arch/mips/kernel/traps.c     |   18 +++++++++++++++---
 arch/mips/kernel/unaligned.c |    5 +++++
 arch/mips/math-emu/cp1emu.c  |    3 +++
 arch/mips/mm/fault.c         |   11 +++++++++--
 5 files changed, 34 insertions(+), 5 deletions(-)

diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index 564e30b..378c25d 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -4,6 +4,8 @@ config MIPS
 	select HAVE_GENERIC_DMA_COHERENT
 	select HAVE_IDE
 	select HAVE_OPROFILE
+	select HAVE_PERF_EVENTS
+	select PERF_USE_VMALLOC
 	select HAVE_ARCH_KGDB
 	select HAVE_FUNCTION_TRACER
 	select HAVE_FUNCTION_TRACE_MCOUNT_TEST
diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c
index 8bdd6a6..d16b3fc 100644
--- a/arch/mips/kernel/traps.c
+++ b/arch/mips/kernel/traps.c
@@ -27,6 +27,7 @@
 #include <linux/kdebug.h>
 #include <linux/notifier.h>
 #include <linux/kdb.h>
+#include <linux/perf_event.h>
 
 #include <asm/bootinfo.h>
 #include <asm/branch.h>
@@ -570,10 +571,16 @@ static inline int simulate_sc(struct pt_regs *regs, unsigned int opcode)
  */
 static int simulate_llsc(struct pt_regs *regs, unsigned int opcode)
 {
-	if ((opcode & OPCODE) == LL)
+	if ((opcode & OPCODE) == LL) {
+		perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS,
+				1, 0, regs, 0);
 		return simulate_ll(regs, opcode);
-	if ((opcode & OPCODE) == SC)
+	}
+	if ((opcode & OPCODE) == SC) {
+		perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS,
+				1, 0, regs, 0);
 		return simulate_sc(regs, opcode);
+	}
 
 	return -1;			/* Must be something else ... */
 }
@@ -589,6 +596,8 @@ static int simulate_rdhwr(struct pt_regs *regs, unsigned int opcode)
 	if ((opcode & OPCODE) == SPEC3 && (opcode & FUNC) == RDHWR) {
 		int rd = (opcode & RD) >> 11;
 		int rt = (opcode & RT) >> 16;
+		perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS,
+				1, 0, regs, 0);
 		switch (rd) {
 		case 0:		/* CPU number */
 			regs->regs[rt] = smp_processor_id();
@@ -624,8 +633,11 @@ static int simulate_rdhwr(struct pt_regs *regs, unsigned int opcode)
 
 static int simulate_sync(struct pt_regs *regs, unsigned int opcode)
 {
-	if ((opcode & OPCODE) == SPEC0 && (opcode & FUNC) == SYNC)
+	if ((opcode & OPCODE) == SPEC0 && (opcode & FUNC) == SYNC) {
+		perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS,
+				1, 0, regs, 0);
 		return 0;
+	}
 
 	return -1;			/* Must be something else ... */
 }
diff --git a/arch/mips/kernel/unaligned.c b/arch/mips/kernel/unaligned.c
index 69b039c..a319c7a 100644
--- a/arch/mips/kernel/unaligned.c
+++ b/arch/mips/kernel/unaligned.c
@@ -78,6 +78,7 @@
 #include <linux/smp.h>
 #include <linux/sched.h>
 #include <linux/debugfs.h>
+#include <linux/perf_event.h>
 #include <asm/asm.h>
 #include <asm/branch.h>
 #include <asm/byteorder.h>
@@ -109,6 +110,8 @@ static void emulate_load_store_insn(struct pt_regs *regs,
 	unsigned long value;
 	unsigned int res;
 
+	perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS,
+			1, 0, regs, 0);
 	regs->regs[0] = 0;
 
 	/*
@@ -513,6 +516,8 @@ asmlinkage void do_ade(struct pt_regs *regs)
 	unsigned int __user *pc;
 	mm_segment_t seg;
 
+	perf_sw_event(PERF_COUNT_SW_ALIGNMENT_FAULTS,
+			1, 0, regs, regs->cp0_badvaddr);
 	/*
 	 * Did we catch a fault trying to load an instruction?
 	 * Or are we running in MIPS16 mode?
diff --git a/arch/mips/math-emu/cp1emu.c b/arch/mips/math-emu/cp1emu.c
index 47842b7..fd180ea 100644
--- a/arch/mips/math-emu/cp1emu.c
+++ b/arch/mips/math-emu/cp1emu.c
@@ -37,6 +37,7 @@
 #include <linux/sched.h>
 #include <linux/module.h>
 #include <linux/debugfs.h>
+#include <linux/perf_event.h>
 
 #include <asm/inst.h>
 #include <asm/bootinfo.h>
@@ -259,6 +260,8 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx)
 	}
 
       emul:
+	perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS,
+			1, 0, xcp, 0);
 	MIPS_FPU_EMU_INC_STATS(emulated);
 	switch (MIPSInst_OPCODE(ir)) {
 	case ldc1_op:{
diff --git a/arch/mips/mm/fault.c b/arch/mips/mm/fault.c
index b4aac42..1887ff6 100644
--- a/arch/mips/mm/fault.c
+++ b/arch/mips/mm/fault.c
@@ -17,6 +17,7 @@
 #include <linux/mm.h>
 #include <linux/smp.h>
 #include <linux/module.h>
+#include <linux/perf_event.h>
 
 #include <asm/branch.h>
 #include <asm/mmu_context.h>
@@ -131,6 +132,7 @@ good_area:
 	 * the fault.
 	 */
 	fault = handle_mm_fault(mm, vma, address, write ? FAULT_FLAG_WRITE : 0);
+	perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, 0, regs, address);
 	if (unlikely(fault & VM_FAULT_ERROR)) {
 		if (fault & VM_FAULT_OOM)
 			goto out_of_memory;
@@ -138,10 +140,15 @@ good_area:
 			goto do_sigbus;
 		BUG();
 	}
-	if (fault & VM_FAULT_MAJOR)
+	if (fault & VM_FAULT_MAJOR) {
+		perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ,
+				1, 0, regs, address);
 		tsk->maj_flt++;
-	else
+	} else {
+		perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN,
+				1, 0, regs, address);
 		tsk->min_flt++;
+	}
 
 	up_read(&mm->mmap_sem);
 	return;
-- 
1.6.3.3

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH v6 4/7] MIPS: add support for hardware performance events (skeleton)
  2010-06-09  4:35 [PATCH v6 0/7] MIPS performance event support v6 Deng-Cheng Zhu
                   ` (2 preceding siblings ...)
  2010-06-09  4:35 ` [PATCH v6 3/7] MIPS: add support for software performance events Deng-Cheng Zhu
@ 2010-06-09  4:35 ` Deng-Cheng Zhu
  2010-09-22 12:27   ` Matt Fleming
  2010-06-09  4:35 ` [PATCH v6 5/7] MIPS/Perf-events: add callchain support Deng-Cheng Zhu
                   ` (2 subsequent siblings)
  6 siblings, 1 reply; 17+ messages in thread
From: Deng-Cheng Zhu @ 2010-06-09  4:35 UTC (permalink / raw)
  To: linux-mips, ralf
  Cc: a.p.zijlstra, paulus, mingo, acme, jamie.iles, Deng-Cheng Zhu

This patch provides the skeleton of the HW perf event support. To enable
this feature, we can not choose the SMTC kernel; Oprofile should be
disabled; kernel performance events be selected. Then we can enable it in
Kernel type menu.

Oprofile for MIPS platforms initializes irq at arch init time. Currently
we do not change this logic to allow PMU reservation.

If a platform has EIC, we can use the irq base and perf counter irq
offset defines for the interrupt controller in mipspmu_get_irq().

Based on this skeleton patch, the 3 different kinds of MIPS PMU, namely,
mipsxx/loongson2/rm9000, can be supported by adding corresponding lower
level C files at the bottom. The suggested names of these files are
perf_event_mipsxx.c/perf_event_loongson2.c/perf_event_rm9000.c. So, for
example, we can do this by adding "#include perf_event_mipsxx.c" at the
bottom of perf_event.c.

In addition, PMUs with 64bit counters are also considered in this patch.

Signed-off-by: Deng-Cheng Zhu <dengcheng.zhu@gmail.com>
---
 arch/mips/Kconfig                  |    8 +
 arch/mips/include/asm/perf_event.h |   26 ++
 arch/mips/kernel/Makefile          |    2 +
 arch/mips/kernel/perf_event.c      |  510 ++++++++++++++++++++++++++++++++++++
 4 files changed, 546 insertions(+), 0 deletions(-)
 create mode 100644 arch/mips/include/asm/perf_event.h
 create mode 100644 arch/mips/kernel/perf_event.c

diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index 378c25d..653d574 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -1888,6 +1888,14 @@ config NODES_SHIFT
 	default "6"
 	depends on NEED_MULTIPLE_NODES
 
+config HW_PERF_EVENTS
+	bool "Enable hardware performance counter support for perf events"
+	depends on PERF_EVENTS && !MIPS_MT_SMTC && OPROFILE=n && CPU_MIPS32
+	default y
+	help
+	  Enable hardware performance counter support for perf events. If
+	  disabled, perf events will use software events only.
+
 source "mm/Kconfig"
 
 config SMP
diff --git a/arch/mips/include/asm/perf_event.h b/arch/mips/include/asm/perf_event.h
new file mode 100644
index 0000000..ad8f986
--- /dev/null
+++ b/arch/mips/include/asm/perf_event.h
@@ -0,0 +1,26 @@
+/*
+ * linux/arch/mips/include/asm/perf_event.h
+ *
+ * Copyright (C) 2010 MIPS Technologies, Inc.
+ * Author: Deng-Cheng Zhu
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __MIPS_PERF_EVENT_H__
+#define __MIPS_PERF_EVENT_H__
+
+/*
+ * MIPS performance counters do not raise NMI upon overflow, a regular
+ * interrupt will be signaled. Hence we can do the pending perf event
+ * work at the tail of the irq handler.
+ */
+static inline void
+set_perf_event_pending(void)
+{
+}
+
+#endif /* __MIPS_PERF_EVENT_H__ */
+
diff --git a/arch/mips/kernel/Makefile b/arch/mips/kernel/Makefile
index ff5ec2e..c037121 100644
--- a/arch/mips/kernel/Makefile
+++ b/arch/mips/kernel/Makefile
@@ -101,4 +101,6 @@ obj-$(CONFIG_HAVE_STD_PC_SERIAL_PORT)	+= 8250-platform.o
 
 obj-$(CONFIG_MIPS_CPUFREQ)	+= cpufreq/
 
+obj-$(CONFIG_HW_PERF_EVENTS)	+= perf_event.o
+
 CPPFLAGS_vmlinux.lds		:= $(KBUILD_CFLAGS)
diff --git a/arch/mips/kernel/perf_event.c b/arch/mips/kernel/perf_event.c
new file mode 100644
index 0000000..51e31e2
--- /dev/null
+++ b/arch/mips/kernel/perf_event.c
@@ -0,0 +1,510 @@
+/*
+ * Linux performance counter support for MIPS.
+ *
+ * Copyright (C) 2010 MIPS Technologies, Inc.
+ * Author: Deng-Cheng Zhu
+ *
+ * This code is based on the implementation for ARM, which is in turn
+ * based on the sparc64 perf event code and the x86 code. Performance
+ * counter access is based on the MIPS Oprofile code.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/cpumask.h>
+#include <linux/interrupt.h>
+#include <linux/smp.h>
+#include <linux/kernel.h>
+#include <linux/perf_event.h>
+#include <linux/uaccess.h>
+
+#include <asm/irq.h>
+#include <asm/irq_regs.h>
+#include <asm/stacktrace.h>
+#include <asm/time.h> /* For perf_irq */
+#include <asm/pmu.h>
+
+/* These are for 32bit counters. For 64bit ones, define them accordingly. */
+#define MAX_PERIOD	((1ULL << 32) - 1)
+#define VALID_COUNT	0x7fffffff
+#define TOTAL_BITS	32
+#define HIGHEST_BIT	31
+
+struct cpu_hw_events {
+	/* Array of events on this cpu. */
+	struct perf_event	*events[MIPS_MAX_HWEVENTS];
+
+	/*
+	 * Set the bit (indexed by the counter number) when the counter
+	 * is used for an event.
+	 */
+	unsigned long		used_mask[BITS_TO_LONGS(MIPS_MAX_HWEVENTS)];
+
+	/*
+	 * The borrowed MSB for the performance counter. A MIPS performance
+	 * counter uses its bit 31 (for 32bit counters) or bit 63 (for 64bit
+	 * counters) as a factor of determining whether a counter overflow
+	 * should be signaled. So here we use a separate MSB for each
+	 * counter to make things easy.
+	 */
+	unsigned long		msbs[BITS_TO_LONGS(MIPS_MAX_HWEVENTS)];
+
+	/*
+	 * Software copy of the control register for each performance counter.
+	 * MIPS CPUs vary in performance counters. They use this differently,
+	 * and even may not use it.
+	 */
+	unsigned int		saved_ctrl[MIPS_MAX_HWEVENTS];
+};
+DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events) = {
+	.saved_ctrl = {0},
+};
+
+/* The description of MIPS performance events. */
+struct mips_perf_event {
+	unsigned int event_id;
+	/*
+	 * MIPS performance counters are indexed starting from 0.
+	 * CNTR_EVEN indicates the indexes of the counters to be used are
+	 * even numbers.
+	 */
+	unsigned int cntr_mask;
+	#define CNTR_EVEN	0x55555555
+	#define CNTR_ODD	0xaaaaaaaa
+#ifdef CONFIG_MIPS_MT_SMP
+	enum {
+		T  = 0,
+		V  = 1,
+		P  = 2,
+	} range;
+#else
+	#define T
+	#define V
+	#define P
+#endif
+};
+
+#define UNSUPPORTED_PERF_EVENT_ID 0xffffffff
+#define C(x) PERF_COUNT_HW_CACHE_##x
+
+struct mips_pmu {
+	const char	*name;
+	irqreturn_t	(*handle_irq)(int irq, void *dev);
+	int		(*handle_shared_irq)(void);
+	void		(*start)(void);
+	void		(*stop)(void);
+	int		(*alloc_counter)(struct cpu_hw_events *cpuc,
+					struct hw_perf_event *hwc);
+	u64		(*read_counter)(unsigned int idx);
+	void		(*write_counter)(unsigned int idx, u64 val);
+	void		(*enable_event)(struct hw_perf_event *evt, int idx);
+	void		(*disable_event)(int idx);
+	const struct mips_perf_event (*general_event_map)[PERF_COUNT_HW_MAX];
+	const struct mips_perf_event (*cache_event_map)
+				[PERF_COUNT_HW_CACHE_MAX]
+				[PERF_COUNT_HW_CACHE_OP_MAX]
+				[PERF_COUNT_HW_CACHE_RESULT_MAX];
+	unsigned int	num_counters;
+};
+
+static const struct mips_pmu *mipspmu;
+
+static int
+mipspmu_event_set_period(struct perf_event *event,
+			struct hw_perf_event *hwc,
+			int idx)
+{
+	struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
+	s64 left = atomic64_read(&hwc->period_left);
+	s64 period = hwc->sample_period;
+	int ret = 0;
+	u64 uleft;
+	unsigned long flags;
+
+	if (unlikely(left <= -period)) {
+		left = period;
+		atomic64_set(&hwc->period_left, left);
+		hwc->last_period = period;
+		ret = 1;
+	}
+
+	if (unlikely(left <= 0)) {
+		left += period;
+		atomic64_set(&hwc->period_left, left);
+		hwc->last_period = period;
+		ret = 1;
+	}
+
+	if (left > (s64)MAX_PERIOD)
+		left = MAX_PERIOD;
+
+	atomic64_set(&hwc->prev_count, (u64)-left);
+
+	local_irq_save(flags);
+	uleft = (u64)(-left) & MAX_PERIOD;
+	uleft > VALID_COUNT ?
+		set_bit(idx, cpuc->msbs) : clear_bit(idx, cpuc->msbs);
+	mipspmu->write_counter(idx, (u64)(-left) & VALID_COUNT);
+	local_irq_restore(flags);
+
+	perf_event_update_userpage(event);
+
+	return ret;
+}
+
+static int mipspmu_enable(struct perf_event *event)
+{
+	struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
+	struct hw_perf_event *hwc = &event->hw;
+	int idx;
+	int err = 0;
+
+	/* To look for a free counter for this event. */
+	idx = mipspmu->alloc_counter(cpuc, hwc);
+	if (idx < 0) {
+		err = idx;
+		goto out;
+	}
+
+	/*
+	 * If there is an event in the counter we are going to use then
+	 * make sure it is disabled.
+	 */
+	event->hw.idx = idx;
+	mipspmu->disable_event(idx);
+	cpuc->events[idx] = event;
+
+	/* Set the period for the event. */
+	mipspmu_event_set_period(event, hwc, idx);
+
+	/* Enable the event. */
+	mipspmu->enable_event(hwc, idx);
+
+	/* Propagate our changes to the userspace mapping. */
+	perf_event_update_userpage(event);
+
+out:
+	return err;
+}
+
+static void mipspmu_event_update(struct perf_event *event,
+			struct hw_perf_event *hwc,
+			int idx)
+{
+	struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
+	unsigned long flags;
+	int shift = 64 - TOTAL_BITS;
+	s64 prev_raw_count, new_raw_count;
+	s64 delta;
+
+again:
+	prev_raw_count = atomic64_read(&hwc->prev_count);
+	local_irq_save(flags);
+	/* Make the counter value be a "real" one. */
+	new_raw_count = mipspmu->read_counter(idx);
+	if (new_raw_count & (test_bit(idx, cpuc->msbs) << HIGHEST_BIT)) {
+		new_raw_count &= VALID_COUNT;
+		clear_bit(idx, cpuc->msbs);
+	} else
+		new_raw_count |= (test_bit(idx, cpuc->msbs) << HIGHEST_BIT);
+	local_irq_restore(flags);
+
+	if (atomic64_cmpxchg(&hwc->prev_count, prev_raw_count,
+				new_raw_count) != prev_raw_count)
+		goto again;
+
+	delta = (new_raw_count << shift) - (prev_raw_count << shift);
+	delta >>= shift;
+
+	atomic64_add(delta, &event->count);
+	atomic64_sub(delta, &hwc->period_left);
+
+	return;
+}
+
+static void mipspmu_disable(struct perf_event *event)
+{
+	struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
+	struct hw_perf_event *hwc = &event->hw;
+	int idx = hwc->idx;
+
+
+	WARN_ON(idx < 0 || idx >= mipspmu->num_counters);
+
+	/* We are working on a local event. */
+	mipspmu->disable_event(idx);
+
+	barrier();
+
+	mipspmu_event_update(event, hwc, idx);
+	cpuc->events[idx] = NULL;
+	clear_bit(idx, cpuc->used_mask);
+
+	perf_event_update_userpage(event);
+}
+
+static void mipspmu_unthrottle(struct perf_event *event)
+{
+	struct hw_perf_event *hwc = &event->hw;
+
+	mipspmu->enable_event(hwc, hwc->idx);
+}
+
+static void mipspmu_read(struct perf_event *event)
+{
+	struct hw_perf_event *hwc = &event->hw;
+
+	/* Don't read disabled counters! */
+	if (hwc->idx < 0)
+		return;
+
+	mipspmu_event_update(event, hwc, hwc->idx);
+}
+
+static struct pmu pmu = {
+	.enable		= mipspmu_enable,
+	.disable	= mipspmu_disable,
+	.unthrottle	= mipspmu_unthrottle,
+	.read		= mipspmu_read,
+};
+
+static atomic_t active_events = ATOMIC_INIT(0);
+static DEFINE_MUTEX(pmu_reserve_mutex);
+static int mips_pmu_irq = -1;
+static int (*save_perf_irq)(void);
+
+static int mipspmu_get_irq(void)
+{
+	int err;
+
+	if (cpu_has_veic) {
+		/*
+		 * Using platform specific interrupt controller defines.
+		 */
+#ifdef MSC01E_INT_BASE
+		mips_pmu_irq = MSC01E_INT_BASE + MSC01E_INT_PERFCTR;
+#endif
+	} else if (cp0_perfcount_irq >= 0) {
+		/*
+		 * Some CPUs have explicitly defined their perfcount irq.
+		 */
+#if defined(CONFIG_CPU_RM9000)
+		mips_pmu_irq = rm9000_perfcount_irq;
+#elif defined(CONFIG_CPU_LOONGSON2)
+		mips_pmu_irq = LOONGSON2_PERFCNT_IRQ;
+#else
+		mips_pmu_irq = MIPS_CPU_IRQ_BASE + cp0_perfcount_irq;
+#endif
+	}
+
+	if (mips_pmu_irq >= 0) {
+		/* Request my own irq handler. */
+		err = request_irq(mips_pmu_irq, mipspmu->handle_irq,
+			IRQF_DISABLED | IRQF_NOBALANCING,
+			"mips_perf_pmu", NULL);
+		if (err) {
+			pr_warning("Unable to request IRQ%d for MIPS "
+			   "performance counters!\n", mips_pmu_irq);
+		}
+	} else if (cp0_perfcount_irq < 0) {
+		/*
+		 * We are sharing the irq number with the timer interrupt.
+		 */
+		save_perf_irq = perf_irq;
+		perf_irq = mipspmu->handle_shared_irq;
+		err = 0;
+	} else {
+		pr_warning("The platform hasn't properly defined its "
+			"interrupt controller.\n");
+		err = -ENOENT;
+	}
+
+	return err;
+}
+
+static void mipspmu_free_irq(void)
+{
+	if (mips_pmu_irq >= 0)
+		free_irq(mips_pmu_irq, NULL);
+	else if (cp0_perfcount_irq < 0)
+		perf_irq = save_perf_irq;
+
+	mips_pmu_irq = -1;
+}
+
+static inline unsigned int
+mipspmu_perf_event_encode(const struct mips_perf_event *pev)
+{
+/*
+ * Top 8 bits for range, next 16 bits for cntr_mask, lowest 8 bits for
+ * event_id.
+ */
+#ifdef CONFIG_MIPS_MT_SMP
+	return ((unsigned int)pev->range << 24) |
+		(pev->cntr_mask & 0xffff00) |
+		(pev->event_id & 0xff);
+#else
+	return (pev->cntr_mask & 0xffff00) |
+		(pev->event_id & 0xff);
+#endif
+}
+
+static const struct mips_perf_event *
+mipspmu_map_general_event(int idx)
+{
+	const struct mips_perf_event *pev;
+
+	pev = ((*mipspmu->general_event_map)[idx].event_id ==
+		UNSUPPORTED_PERF_EVENT_ID ? ERR_PTR(-EOPNOTSUPP) :
+		&(*mipspmu->general_event_map)[idx]);
+
+	return pev;
+}
+
+static const struct mips_perf_event *
+mipspmu_map_cache_event(u64 config)
+{
+	unsigned int cache_type, cache_op, cache_result;
+	const struct mips_perf_event *pev;
+
+	cache_type = (config >> 0) & 0xff;
+	if (cache_type >= PERF_COUNT_HW_CACHE_MAX)
+		return ERR_PTR(-EINVAL);
+
+	cache_op = (config >> 8) & 0xff;
+	if (cache_op >= PERF_COUNT_HW_CACHE_OP_MAX)
+		return ERR_PTR(-EINVAL);
+
+	cache_result = (config >> 16) & 0xff;
+	if (cache_result >= PERF_COUNT_HW_CACHE_RESULT_MAX)
+		return ERR_PTR(-EINVAL);
+
+	pev = &((*mipspmu->cache_event_map)
+					[cache_type]
+					[cache_op]
+					[cache_result]);
+
+	if (pev->event_id == UNSUPPORTED_PERF_EVENT_ID)
+		return ERR_PTR(-EOPNOTSUPP);
+
+	return pev;
+
+}
+
+static int validate_event(struct cpu_hw_events *cpuc,
+	       struct perf_event *event)
+{
+	struct hw_perf_event fake_hwc = event->hw;
+
+	if (event->pmu && event->pmu != &pmu)
+		return 0;
+
+	return mipspmu->alloc_counter(cpuc, &fake_hwc) >= 0;
+}
+
+static int validate_group(struct perf_event *event)
+{
+	struct perf_event *sibling, *leader = event->group_leader;
+	struct cpu_hw_events fake_cpuc;
+
+	memset(&fake_cpuc, 0, sizeof(fake_cpuc));
+
+	if (!validate_event(&fake_cpuc, leader))
+		return -ENOSPC;
+
+	list_for_each_entry(sibling, &leader->sibling_list, group_entry) {
+		if (!validate_event(&fake_cpuc, sibling))
+			return -ENOSPC;
+	}
+
+	if (!validate_event(&fake_cpuc, event))
+		return -ENOSPC;
+
+	return 0;
+}
+
+/*
+ * mipsxx/rm9000/loongson2 have different performance counters, they have
+ * specific low-level init routines.
+ */
+static int __hw_perf_event_init(struct perf_event *event);
+
+static void hw_perf_event_destroy(struct perf_event *event)
+{
+	if (atomic_dec_and_mutex_lock(&active_events,
+				&pmu_reserve_mutex)) {
+		/*
+		 * We must not call the destroy function with interrupts
+		 * disabled.
+		 */
+		on_each_cpu(reset_counters,
+			(void *)(long)mipspmu->num_counters, 1);
+		mipspmu_free_irq();
+		mutex_unlock(&pmu_reserve_mutex);
+	}
+}
+
+const struct pmu *hw_perf_event_init(struct perf_event *event)
+{
+	int err = 0;
+
+	if (!mipspmu || event->cpu >= nr_cpumask_bits ||
+		(event->cpu >= 0 && !cpu_online(event->cpu)))
+		return ERR_PTR(-ENODEV);
+
+	if (!atomic_inc_not_zero(&active_events)) {
+		if (atomic_read(&active_events) > MIPS_MAX_HWEVENTS) {
+			atomic_dec(&active_events);
+			return ERR_PTR(-ENOSPC);
+		}
+
+		mutex_lock(&pmu_reserve_mutex);
+		if (atomic_read(&active_events) == 0)
+			err = mipspmu_get_irq();
+
+		if (!err)
+			atomic_inc(&active_events);
+		mutex_unlock(&pmu_reserve_mutex);
+	}
+
+	if (err)
+		return ERR_PTR(err);
+
+	err = __hw_perf_event_init(event);
+	if (err)
+		hw_perf_event_destroy(event);
+
+	return err ? ERR_PTR(err) : &pmu;
+}
+
+void hw_perf_enable(void)
+{
+	if (mipspmu)
+		mipspmu->start();
+}
+
+void hw_perf_disable(void)
+{
+	if (mipspmu)
+		mipspmu->stop();
+}
+
+/* This is needed by specific irq handlers in perf_event_*.c */
+static void
+handle_associated_event(struct cpu_hw_events *cpuc,
+	int idx, struct perf_sample_data *data, struct pt_regs *regs)
+{
+	struct perf_event *event = cpuc->events[idx];
+	struct hw_perf_event *hwc = &event->hw;
+
+	mipspmu_event_update(event, hwc, idx);
+	data->period = event->hw.last_period;
+	if (!mipspmu_event_set_period(event, hwc, idx))
+		return;
+
+	if (perf_event_overflow(event, 0, data, regs))
+		mipspmu->disable_event(idx);
+}
+
-- 
1.6.3.3

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH v6 5/7] MIPS/Perf-events: add callchain support
  2010-06-09  4:35 [PATCH v6 0/7] MIPS performance event support v6 Deng-Cheng Zhu
                   ` (3 preceding siblings ...)
  2010-06-09  4:35 ` [PATCH v6 4/7] MIPS: add support for hardware performance events (skeleton) Deng-Cheng Zhu
@ 2010-06-09  4:35 ` Deng-Cheng Zhu
  2010-06-09  4:35 ` [PATCH v6 6/7] MIPS: add support for hardware performance events (mipsxx) Deng-Cheng Zhu
  2010-06-09  4:35 ` [PATCH v6 7/7] MIPS: define local_xchg from xchg_local to atomic_long_xchg Deng-Cheng Zhu
  6 siblings, 0 replies; 17+ messages in thread
From: Deng-Cheng Zhu @ 2010-06-09  4:35 UTC (permalink / raw)
  To: linux-mips, ralf
  Cc: a.p.zijlstra, paulus, mingo, acme, jamie.iles, Deng-Cheng Zhu

This patch adds callchain support for MIPS Perf-events. For more info on
this feature, please refer to tools/perf/Documentation/perf-report.txt
and tools/perf/design.txt.

Currently userspace callchain data is not recorded, because we do not have
a safe way to do this.

Signed-off-by: Deng-Cheng Zhu <dengcheng.zhu@gmail.com>
---
 arch/mips/kernel/perf_event.c |  108 ++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 107 insertions(+), 1 deletions(-)

diff --git a/arch/mips/kernel/perf_event.c b/arch/mips/kernel/perf_event.c
index 51e31e2..7e371ae 100644
--- a/arch/mips/kernel/perf_event.c
+++ b/arch/mips/kernel/perf_event.c
@@ -6,7 +6,8 @@
  *
  * This code is based on the implementation for ARM, which is in turn
  * based on the sparc64 perf event code and the x86 code. Performance
- * counter access is based on the MIPS Oprofile code.
+ * counter access is based on the MIPS Oprofile code. And the callchain
+ * support references the code of MIPS stacktrace.c.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -508,3 +509,108 @@ handle_associated_event(struct cpu_hw_events *cpuc,
 		mipspmu->disable_event(idx);
 }
 
+/* Callchain handling code. */
+static inline void
+callchain_store(struct perf_callchain_entry *entry,
+		u64 ip)
+{
+	if (entry->nr < PERF_MAX_STACK_DEPTH)
+		entry->ip[entry->nr++] = ip;
+}
+
+/*
+ * Leave userspace callchain empty for now. When we find a way to trace
+ * the user stack callchains, we add here.
+ */
+static void
+perf_callchain_user(struct pt_regs *regs,
+		    struct perf_callchain_entry *entry)
+{
+}
+
+static void save_raw_perf_callchain(struct perf_callchain_entry *entry,
+	unsigned long reg29)
+{
+	unsigned long *sp = (unsigned long *)reg29;
+	unsigned long addr;
+
+	while (!kstack_end(sp)) {
+		addr = *sp++;
+		if (__kernel_text_address(addr)) {
+			callchain_store(entry, addr);
+			if (entry->nr >= PERF_MAX_STACK_DEPTH)
+				break;
+		}
+	}
+}
+
+static void
+perf_callchain_kernel(struct pt_regs *regs,
+		      struct perf_callchain_entry *entry)
+{
+	unsigned long sp = regs->regs[29];
+#ifdef CONFIG_KALLSYMS
+	unsigned long ra = regs->regs[31];
+	unsigned long pc = regs->cp0_epc;
+
+	callchain_store(entry, PERF_CONTEXT_KERNEL);
+	if (raw_show_trace || !__kernel_text_address(pc)) {
+		unsigned long stack_page =
+			(unsigned long)task_stack_page(current);
+		if (stack_page && sp >= stack_page &&
+		    sp <= stack_page + THREAD_SIZE - 32)
+			save_raw_perf_callchain(entry, sp);
+		return;
+	}
+	do {
+		callchain_store(entry, pc);
+		if (entry->nr >= PERF_MAX_STACK_DEPTH)
+			break;
+		pc = unwind_stack(current, &sp, pc, &ra);
+	} while (pc);
+#else
+	callchain_store(entry, PERF_CONTEXT_KERNEL);
+	save_raw_perf_callchain(entry, sp);
+#endif
+}
+
+static void
+perf_do_callchain(struct pt_regs *regs,
+		  struct perf_callchain_entry *entry)
+{
+	int is_user;
+
+	if (!regs)
+		return;
+
+	is_user = user_mode(regs);
+
+	if (!current || !current->pid)
+		return;
+
+	if (is_user && current->state != TASK_RUNNING)
+		return;
+
+	if (!is_user) {
+		perf_callchain_kernel(regs, entry);
+		if (current->mm)
+			regs = task_pt_regs(current);
+		else
+			regs = NULL;
+	}
+	if (regs)
+		perf_callchain_user(regs, entry);
+}
+
+static DEFINE_PER_CPU(struct perf_callchain_entry, pmc_irq_entry);
+
+struct perf_callchain_entry *
+perf_callchain(struct pt_regs *regs)
+{
+	struct perf_callchain_entry *entry = &__get_cpu_var(pmc_irq_entry);
+
+	entry->nr = 0;
+	perf_do_callchain(regs, entry);
+	return entry;
+}
+
-- 
1.6.3.3

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH v6 6/7] MIPS: add support for hardware performance events (mipsxx)
  2010-06-09  4:35 [PATCH v6 0/7] MIPS performance event support v6 Deng-Cheng Zhu
                   ` (4 preceding siblings ...)
  2010-06-09  4:35 ` [PATCH v6 5/7] MIPS/Perf-events: add callchain support Deng-Cheng Zhu
@ 2010-06-09  4:35 ` Deng-Cheng Zhu
  2010-06-09  4:35 ` [PATCH v6 7/7] MIPS: define local_xchg from xchg_local to atomic_long_xchg Deng-Cheng Zhu
  6 siblings, 0 replies; 17+ messages in thread
From: Deng-Cheng Zhu @ 2010-06-09  4:35 UTC (permalink / raw)
  To: linux-mips, ralf
  Cc: a.p.zijlstra, paulus, mingo, acme, jamie.iles, Deng-Cheng Zhu

This patch adds the mipsxx Perf-event support based on the skeleton.
Generic hardware events and cache events are now fully implemented for
the 24K/34K/74K/1004K cores. To support other cores in mipsxx (such as
R10000/SB1), the generic hardware event tables and cache event tables
need to be filled out. To support other CPUs which have different PMU
than mipsxx, such as RM9000 and LOONGSON2, the additional files
perf_event_$cpu.c need to be created.

Raw event is an important part of Perf-events. It helps the user collect
performance data for events that are not listed as the generic hardware
events and cache events but ARE supported by the CPU's PMU.

This patch also adds this feature for mipsxx 24K/34K/74K/1004K. For how to
use it, please refer to processor core software user's manual and the
comments for mipsxx_pmu_map_raw_event() for more details.

Please note that this is a "precise" implementation, which means the
kernel will check whether the requested raw events are supported by this
CPU and which hardware counters can be assigned for them.

To test the functionality of Perf-event, you may want to compile the tool
"perf" for your MIPS platform. You can refer to the following URL:
http://www.linux-mips.org/archives/linux-mips/2010-04/msg00158.html

Please note: Before that patch is accepted, you can choose a "specific"
rmb() which is suitable for your platform -- an example is provided in
the description of that patch.

You also need to customize the CFLAGS and LDFLAGS in tools/perf/Makefile
for your libs, includes, etc.

In case you encounter the boot failure in SMVP kernel on multi-threading
CPUs, you may take a look at:
http://www.linux-mips.org/git?p=linux-mti.git;a=commitdiff;h=5460815027d802697b879644c74f0e8365254020

Signed-off-by: Deng-Cheng Zhu <dengcheng.zhu@gmail.com>
---
 arch/mips/include/asm/pmu.h          |   87 ++++
 arch/mips/kernel/perf_event.c        |    7 +
 arch/mips/kernel/perf_event_mipsxx.c |  802 ++++++++++++++++++++++++++++++++++
 3 files changed, 896 insertions(+), 0 deletions(-)
 create mode 100644 arch/mips/kernel/perf_event_mipsxx.c

diff --git a/arch/mips/include/asm/pmu.h b/arch/mips/include/asm/pmu.h
index c84d70c..be057ca 100644
--- a/arch/mips/include/asm/pmu.h
+++ b/arch/mips/include/asm/pmu.h
@@ -41,6 +41,19 @@
 #define M_PERFCTL_WIDE			(1UL      << 30)
 #define M_PERFCTL_MORE			(1UL      << 31)
 
+#define M_PERFCTL_COUNT_EVENT_WHENEVER	(M_PERFCTL_EXL |		\
+					M_PERFCTL_KERNEL |		\
+					M_PERFCTL_USER |		\
+					M_PERFCTL_SUPERVISOR |		\
+					M_PERFCTL_INTERRUPT_ENABLE)
+
+#ifdef CONFIG_MIPS_MT_SMP
+#define M_PERFCTL_CONFIG_MASK		0x3fff801f
+#else
+#define M_PERFCTL_CONFIG_MASK		0x1f
+#endif
+#define M_PERFCTL_EVENT_MASK		0xfe0
+
 #define M_COUNTER_OVERFLOW		(1UL      << 31)
 
 #ifdef CONFIG_MIPS_MT_SMP
@@ -174,6 +187,80 @@ static inline int n_counters(void)
 	return counters;
 }
 
+static inline u64
+mipsxx_pmu_read_counter(unsigned int idx)
+{
+	switch (idx) {
+	case 0:
+		return r_c0_perfcntr0();
+	case 1:
+		return r_c0_perfcntr1();
+	case 2:
+		return r_c0_perfcntr2();
+	case 3:
+		return r_c0_perfcntr3();
+	default:
+		WARN_ONCE(1, "Invalid performance counter number (%d)\n", idx);
+		return 0;
+	}
+}
+
+static inline void
+mipsxx_pmu_write_counter(unsigned int idx, u64 val)
+{
+	switch (idx) {
+	case 0:
+		w_c0_perfcntr0(val);
+		return;
+	case 1:
+		w_c0_perfcntr1(val);
+		return;
+	case 2:
+		w_c0_perfcntr2(val);
+		return;
+	case 3:
+		w_c0_perfcntr3(val);
+		return;
+	}
+}
+
+static inline unsigned int
+mipsxx_pmu_read_control(unsigned int idx)
+{
+	switch (idx) {
+	case 0:
+		return r_c0_perfctrl0();
+	case 1:
+		return r_c0_perfctrl1();
+	case 2:
+		return r_c0_perfctrl2();
+	case 3:
+		return r_c0_perfctrl3();
+	default:
+		WARN_ONCE(1, "Invalid performance counter number (%d)\n", idx);
+		return 0;
+	}
+}
+
+static inline void
+mipsxx_pmu_write_control(unsigned int idx, unsigned int val)
+{
+	switch (idx) {
+	case 0:
+		w_c0_perfctrl0(val);
+		return;
+	case 1:
+		w_c0_perfctrl1(val);
+		return;
+	case 2:
+		w_c0_perfctrl2(val);
+		return;
+	case 3:
+		w_c0_perfctrl3(val);
+		return;
+	}
+}
+
 /* rm9000 */
 #define RM9K_COUNTER1_EVENT(event)	((event) << 0)
 #define RM9K_COUNTER1_SUPERVISOR	(1ULL    <<  7)
diff --git a/arch/mips/kernel/perf_event.c b/arch/mips/kernel/perf_event.c
index 7e371ae..fb7a040 100644
--- a/arch/mips/kernel/perf_event.c
+++ b/arch/mips/kernel/perf_event.c
@@ -87,6 +87,9 @@ struct mips_perf_event {
 #endif
 };
 
+static struct mips_perf_event raw_event;
+static DEFINE_MUTEX(raw_event_mutex);
+
 #define UNSUPPORTED_PERF_EVENT_ID 0xffffffff
 #define C(x) PERF_COUNT_HW_CACHE_##x
 
@@ -102,6 +105,7 @@ struct mips_pmu {
 	void		(*write_counter)(unsigned int idx, u64 val);
 	void		(*enable_event)(struct hw_perf_event *evt, int idx);
 	void		(*disable_event)(int idx);
+	const struct mips_perf_event *(*map_raw_event)(u64 config);
 	const struct mips_perf_event (*general_event_map)[PERF_COUNT_HW_MAX];
 	const struct mips_perf_event (*cache_event_map)
 				[PERF_COUNT_HW_CACHE_MAX]
@@ -430,6 +434,7 @@ static int validate_group(struct perf_event *event)
  * mipsxx/rm9000/loongson2 have different performance counters, they have
  * specific low-level init routines.
  */
+static void reset_counters(void *arg);
 static int __hw_perf_event_init(struct perf_event *event);
 
 static void hw_perf_event_destroy(struct perf_event *event)
@@ -509,6 +514,8 @@ handle_associated_event(struct cpu_hw_events *cpuc,
 		mipspmu->disable_event(idx);
 }
 
+#include "perf_event_mipsxx.c"
+
 /* Callchain handling code. */
 static inline void
 callchain_store(struct perf_callchain_entry *entry,
diff --git a/arch/mips/kernel/perf_event_mipsxx.c b/arch/mips/kernel/perf_event_mipsxx.c
new file mode 100644
index 0000000..135eae0
--- /dev/null
+++ b/arch/mips/kernel/perf_event_mipsxx.c
@@ -0,0 +1,802 @@
+#if defined(CONFIG_CPU_MIPS32) || defined(CONFIG_CPU_MIPS64) || \
+    defined(CONFIG_CPU_R10000) || defined(CONFIG_CPU_SB1)
+
+#ifdef CONFIG_MIPS_MT_SMP
+static DEFINE_RWLOCK(pmuint_rwlock);
+#endif
+
+/* 24K/34K/1004K cores can share the same event map. */
+static const struct mips_perf_event mipsxxcore_event_map
+				[PERF_COUNT_HW_MAX] = {
+	[PERF_COUNT_HW_CPU_CYCLES] = { 0x00, CNTR_EVEN | CNTR_ODD, P },
+	[PERF_COUNT_HW_INSTRUCTIONS] = { 0x01, CNTR_EVEN | CNTR_ODD, T },
+	[PERF_COUNT_HW_CACHE_REFERENCES] = { UNSUPPORTED_PERF_EVENT_ID },
+	[PERF_COUNT_HW_CACHE_MISSES] = { UNSUPPORTED_PERF_EVENT_ID },
+	[PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = { 0x02, CNTR_EVEN, T },
+	[PERF_COUNT_HW_BRANCH_MISSES] = { 0x02, CNTR_ODD, T },
+	[PERF_COUNT_HW_BUS_CYCLES] = { UNSUPPORTED_PERF_EVENT_ID },
+};
+
+/* 74K core has different branch event code. */
+static const struct mips_perf_event mipsxx74Kcore_event_map
+				[PERF_COUNT_HW_MAX] = {
+	[PERF_COUNT_HW_CPU_CYCLES] = { 0x00, CNTR_EVEN | CNTR_ODD, P },
+	[PERF_COUNT_HW_INSTRUCTIONS] = { 0x01, CNTR_EVEN | CNTR_ODD, T },
+	[PERF_COUNT_HW_CACHE_REFERENCES] = { UNSUPPORTED_PERF_EVENT_ID },
+	[PERF_COUNT_HW_CACHE_MISSES] = { UNSUPPORTED_PERF_EVENT_ID },
+	[PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = { 0x27, CNTR_EVEN, T },
+	[PERF_COUNT_HW_BRANCH_MISSES] = { 0x27, CNTR_ODD, T },
+	[PERF_COUNT_HW_BUS_CYCLES] = { UNSUPPORTED_PERF_EVENT_ID },
+};
+
+/* 24K/34K/1004K cores can share the same cache event map. */
+static const struct mips_perf_event mipsxxcore_cache_map
+				[PERF_COUNT_HW_CACHE_MAX]
+				[PERF_COUNT_HW_CACHE_OP_MAX]
+				[PERF_COUNT_HW_CACHE_RESULT_MAX] = {
+[C(L1D)] = {
+	/*
+	 * Like some other architectures (e.g. ARM), the performance
+	 * counters don't differentiate between read and write
+	 * accesses/misses, so this isn't strictly correct, but it's the
+	 * best we can do. Writes and reads get combined.
+	 */
+	[C(OP_READ)] = {
+		[C(RESULT_ACCESS)]	= { 0x0a, CNTR_EVEN, T },
+		[C(RESULT_MISS)]	= { 0x0b, CNTR_EVEN | CNTR_ODD, T },
+	},
+	[C(OP_WRITE)] = {
+		[C(RESULT_ACCESS)]	= { 0x0a, CNTR_EVEN, T },
+		[C(RESULT_MISS)]	= { 0x0b, CNTR_EVEN | CNTR_ODD, T },
+	},
+	[C(OP_PREFETCH)] = {
+		[C(RESULT_ACCESS)]	= { UNSUPPORTED_PERF_EVENT_ID },
+		[C(RESULT_MISS)]	= { UNSUPPORTED_PERF_EVENT_ID },
+	},
+},
+[C(L1I)] = {
+	[C(OP_READ)] = {
+		[C(RESULT_ACCESS)]	= { 0x09, CNTR_EVEN, T },
+		[C(RESULT_MISS)]	= { 0x09, CNTR_ODD, T },
+	},
+	[C(OP_WRITE)] = {
+		[C(RESULT_ACCESS)]	= { 0x09, CNTR_EVEN, T },
+		[C(RESULT_MISS)]	= { 0x09, CNTR_ODD, T },
+	},
+	[C(OP_PREFETCH)] = {
+		[C(RESULT_ACCESS)]	= { 0x14, CNTR_EVEN, T },
+		/*
+		 * Note that MIPS has only "hit" events countable for
+		 * the prefetch operation.
+		 */
+		[C(RESULT_MISS)]	= { UNSUPPORTED_PERF_EVENT_ID },
+	},
+},
+[C(LL)] = {
+	[C(OP_READ)] = {
+		[C(RESULT_ACCESS)]	= { 0x15, CNTR_ODD, P },
+		[C(RESULT_MISS)]	= { 0x16, CNTR_EVEN, P },
+	},
+	[C(OP_WRITE)] = {
+		[C(RESULT_ACCESS)]	= { 0x15, CNTR_ODD, P },
+		[C(RESULT_MISS)]	= { 0x16, CNTR_EVEN, P },
+	},
+	[C(OP_PREFETCH)] = {
+		[C(RESULT_ACCESS)]	= { UNSUPPORTED_PERF_EVENT_ID },
+		[C(RESULT_MISS)]	= { UNSUPPORTED_PERF_EVENT_ID },
+	},
+},
+[C(DTLB)] = {
+	[C(OP_READ)] = {
+		[C(RESULT_ACCESS)]	= { 0x06, CNTR_EVEN, T },
+		[C(RESULT_MISS)]	= { 0x06, CNTR_ODD, T },
+	},
+	[C(OP_WRITE)] = {
+		[C(RESULT_ACCESS)]	= { 0x06, CNTR_EVEN, T },
+		[C(RESULT_MISS)]	= { 0x06, CNTR_ODD, T },
+	},
+	[C(OP_PREFETCH)] = {
+		[C(RESULT_ACCESS)]	= { UNSUPPORTED_PERF_EVENT_ID },
+		[C(RESULT_MISS)]	= { UNSUPPORTED_PERF_EVENT_ID },
+	},
+},
+[C(ITLB)] = {
+	[C(OP_READ)] = {
+		[C(RESULT_ACCESS)]	= { 0x05, CNTR_EVEN, T },
+		[C(RESULT_MISS)]	= { 0x05, CNTR_ODD, T },
+	},
+	[C(OP_WRITE)] = {
+		[C(RESULT_ACCESS)]	= { 0x05, CNTR_EVEN, T },
+		[C(RESULT_MISS)]	= { 0x05, CNTR_ODD, T },
+	},
+	[C(OP_PREFETCH)] = {
+		[C(RESULT_ACCESS)]	= { UNSUPPORTED_PERF_EVENT_ID },
+		[C(RESULT_MISS)]	= { UNSUPPORTED_PERF_EVENT_ID },
+	},
+},
+[C(BPU)] = {
+	/* Using the same code for *HW_BRANCH* */
+	[C(OP_READ)] = {
+		[C(RESULT_ACCESS)]	= { 0x02, CNTR_EVEN, T },
+		[C(RESULT_MISS)]	= { 0x02, CNTR_ODD, T },
+	},
+	[C(OP_WRITE)] = {
+		[C(RESULT_ACCESS)]	= { 0x02, CNTR_EVEN, T },
+		[C(RESULT_MISS)]	= { 0x02, CNTR_ODD, T },
+	},
+	[C(OP_PREFETCH)] = {
+		[C(RESULT_ACCESS)]	= { UNSUPPORTED_PERF_EVENT_ID },
+		[C(RESULT_MISS)]	= { UNSUPPORTED_PERF_EVENT_ID },
+	},
+},
+};
+
+/* 74K core has completely different cache event map. */
+static const struct mips_perf_event mipsxx74Kcore_cache_map
+				[PERF_COUNT_HW_CACHE_MAX]
+				[PERF_COUNT_HW_CACHE_OP_MAX]
+				[PERF_COUNT_HW_CACHE_RESULT_MAX] = {
+[C(L1D)] = {
+	/*
+	 * Like some other architectures (e.g. ARM), the performance
+	 * counters don't differentiate between read and write
+	 * accesses/misses, so this isn't strictly correct, but it's the
+	 * best we can do. Writes and reads get combined.
+	 */
+	[C(OP_READ)] = {
+		[C(RESULT_ACCESS)]	= { 0x17, CNTR_ODD, T },
+		[C(RESULT_MISS)]	= { 0x18, CNTR_ODD, T },
+	},
+	[C(OP_WRITE)] = {
+		[C(RESULT_ACCESS)]	= { 0x17, CNTR_ODD, T },
+		[C(RESULT_MISS)]	= { 0x18, CNTR_ODD, T },
+	},
+	[C(OP_PREFETCH)] = {
+		[C(RESULT_ACCESS)]	= { UNSUPPORTED_PERF_EVENT_ID },
+		[C(RESULT_MISS)]	= { UNSUPPORTED_PERF_EVENT_ID },
+	},
+},
+[C(L1I)] = {
+	[C(OP_READ)] = {
+		[C(RESULT_ACCESS)]	= { 0x06, CNTR_EVEN, T },
+		[C(RESULT_MISS)]	= { 0x06, CNTR_ODD, T },
+	},
+	[C(OP_WRITE)] = {
+		[C(RESULT_ACCESS)]	= { 0x06, CNTR_EVEN, T },
+		[C(RESULT_MISS)]	= { 0x06, CNTR_ODD, T },
+	},
+	[C(OP_PREFETCH)] = {
+		[C(RESULT_ACCESS)]	= { 0x34, CNTR_EVEN, T },
+		/*
+		 * Note that MIPS has only "hit" events countable for
+		 * the prefetch operation.
+		 */
+		[C(RESULT_MISS)]	= { UNSUPPORTED_PERF_EVENT_ID },
+	},
+},
+[C(LL)] = {
+	[C(OP_READ)] = {
+		[C(RESULT_ACCESS)]	= { 0x1c, CNTR_ODD, P },
+		[C(RESULT_MISS)]	= { 0x1d, CNTR_EVEN | CNTR_ODD, P },
+	},
+	[C(OP_WRITE)] = {
+		[C(RESULT_ACCESS)]	= { 0x1c, CNTR_ODD, P },
+		[C(RESULT_MISS)]	= { 0x1d, CNTR_EVEN | CNTR_ODD, P },
+	},
+	[C(OP_PREFETCH)] = {
+		[C(RESULT_ACCESS)]	= { UNSUPPORTED_PERF_EVENT_ID },
+		[C(RESULT_MISS)]	= { UNSUPPORTED_PERF_EVENT_ID },
+	},
+},
+[C(DTLB)] = {
+	/* 74K core does not have specific DTLB events. */
+	[C(OP_READ)] = {
+		[C(RESULT_ACCESS)]	= { UNSUPPORTED_PERF_EVENT_ID },
+		[C(RESULT_MISS)]	= { UNSUPPORTED_PERF_EVENT_ID },
+	},
+	[C(OP_WRITE)] = {
+		[C(RESULT_ACCESS)]	= { UNSUPPORTED_PERF_EVENT_ID },
+		[C(RESULT_MISS)]	= { UNSUPPORTED_PERF_EVENT_ID },
+	},
+	[C(OP_PREFETCH)] = {
+		[C(RESULT_ACCESS)]	= { UNSUPPORTED_PERF_EVENT_ID },
+		[C(RESULT_MISS)]	= { UNSUPPORTED_PERF_EVENT_ID },
+	},
+},
+[C(ITLB)] = {
+	[C(OP_READ)] = {
+		[C(RESULT_ACCESS)]	= { 0x04, CNTR_EVEN, T },
+		[C(RESULT_MISS)]	= { 0x04, CNTR_ODD, T },
+	},
+	[C(OP_WRITE)] = {
+		[C(RESULT_ACCESS)]	= { 0x04, CNTR_EVEN, T },
+		[C(RESULT_MISS)]	= { 0x04, CNTR_ODD, T },
+	},
+	[C(OP_PREFETCH)] = {
+		[C(RESULT_ACCESS)]	= { UNSUPPORTED_PERF_EVENT_ID },
+		[C(RESULT_MISS)]	= { UNSUPPORTED_PERF_EVENT_ID },
+	},
+},
+[C(BPU)] = {
+	/* Using the same code for *HW_BRANCH* */
+	[C(OP_READ)] = {
+		[C(RESULT_ACCESS)]	= { 0x27, CNTR_EVEN, T },
+		[C(RESULT_MISS)]	= { 0x27, CNTR_ODD, T },
+	},
+	[C(OP_WRITE)] = {
+		[C(RESULT_ACCESS)]	= { 0x27, CNTR_EVEN, T },
+		[C(RESULT_MISS)]	= { 0x27, CNTR_ODD, T },
+	},
+	[C(OP_PREFETCH)] = {
+		[C(RESULT_ACCESS)]	= { UNSUPPORTED_PERF_EVENT_ID },
+		[C(RESULT_MISS)]	= { UNSUPPORTED_PERF_EVENT_ID },
+	},
+},
+};
+
+#ifdef CONFIG_MIPS_MT_SMP
+static void
+check_and_calc_range(struct perf_event *event,
+			const struct mips_perf_event *pev)
+{
+	struct hw_perf_event *hwc = &event->hw;
+
+	if (event->cpu >= 0) {
+		if (pev->range > V) {
+			/*
+			 * The user selected an event that is processor
+			 * wide, while expecting it to be VPE wide.
+			 */
+			hwc->config_base |= M_TC_EN_ALL;
+		} else {
+			/*
+			 * FIXME: cpu_data[event->cpu].vpe_id reports 0
+			 * for both CPUs.
+			 */
+			hwc->config_base |= M_PERFCTL_VPEID(event->cpu);
+			hwc->config_base |= M_TC_EN_VPE;
+		}
+	} else
+		hwc->config_base |= M_TC_EN_ALL;
+}
+#else
+static void
+check_and_calc_range(struct perf_event *event,
+			const struct mips_perf_event *pev)
+{
+}
+#endif
+
+static int __hw_perf_event_init(struct perf_event *event)
+{
+	struct perf_event_attr *attr = &event->attr;
+	struct hw_perf_event *hwc = &event->hw;
+	const struct mips_perf_event *pev;
+	int err;
+
+	/* Returning MIPS event descriptor for generic perf event. */
+	if (PERF_TYPE_HARDWARE == event->attr.type) {
+		if (event->attr.config >= PERF_COUNT_HW_MAX)
+			return -EINVAL;
+		pev = mipspmu_map_general_event(event->attr.config);
+	} else if (PERF_TYPE_HW_CACHE == event->attr.type) {
+		pev = mipspmu_map_cache_event(event->attr.config);
+	} else if (PERF_TYPE_RAW == event->attr.type) {
+		/* We are working on the global raw event. */
+		mutex_lock(&raw_event_mutex);
+		pev = mipspmu->map_raw_event(event->attr.config);
+	} else {
+		/* The event type is not (yet) supported. */
+		return -EOPNOTSUPP;
+	}
+
+	if (IS_ERR(pev)) {
+		if (PERF_TYPE_RAW == event->attr.type)
+			mutex_unlock(&raw_event_mutex);
+		return PTR_ERR(pev);
+	}
+
+	/*
+	 * We allow max flexibility on how each individual counter shared
+	 * by the single CPU operates (the mode exclusion and the range).
+	 */
+	hwc->config_base = M_PERFCTL_INTERRUPT_ENABLE;
+
+	/* Calculate range bits and validate it. */
+	if (num_possible_cpus() > 1)
+		check_and_calc_range(event, pev);
+
+	hwc->event_base = mipspmu_perf_event_encode(pev);
+	if (PERF_TYPE_RAW == event->attr.type)
+		mutex_unlock(&raw_event_mutex);
+
+	if (!attr->exclude_user)
+		hwc->config_base |= M_PERFCTL_USER;
+	if (!attr->exclude_kernel) {
+		hwc->config_base |= M_PERFCTL_KERNEL;
+		/* MIPS kernel mode: KSU == 00b || EXL == 1 || ERL == 1 */
+		hwc->config_base |= M_PERFCTL_EXL;
+	}
+	if (!attr->exclude_hv)
+		hwc->config_base |= M_PERFCTL_SUPERVISOR;
+
+	hwc->config_base &= M_PERFCTL_CONFIG_MASK;
+	/*
+	 * The event can belong to another cpu. We do not assign a local
+	 * counter for it for now.
+	 */
+	hwc->idx = -1;
+	hwc->config = 0;
+
+	if (!hwc->sample_period) {
+		hwc->sample_period  = MAX_PERIOD;
+		hwc->last_period    = hwc->sample_period;
+		atomic64_set(&hwc->period_left, hwc->sample_period);
+	}
+
+	err = 0;
+	if (event->group_leader != event) {
+		err = validate_group(event);
+		if (err)
+			return -EINVAL;
+	}
+
+	event->destroy = hw_perf_event_destroy;
+
+	return err;
+}
+
+static void pause_local_counters(void)
+{
+	struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
+	int counters = mipspmu->num_counters;
+	unsigned long flags;
+
+	local_irq_save(flags);
+	switch (counters) {
+	case 4:
+		cpuc->saved_ctrl[3] = r_c0_perfctrl3();
+		w_c0_perfctrl3(cpuc->saved_ctrl[3] &
+			~M_PERFCTL_COUNT_EVENT_WHENEVER);
+	case 3:
+		cpuc->saved_ctrl[2] = r_c0_perfctrl2();
+		w_c0_perfctrl2(cpuc->saved_ctrl[2] &
+			~M_PERFCTL_COUNT_EVENT_WHENEVER);
+	case 2:
+		cpuc->saved_ctrl[1] = r_c0_perfctrl1();
+		w_c0_perfctrl1(cpuc->saved_ctrl[1] &
+			~M_PERFCTL_COUNT_EVENT_WHENEVER);
+	case 1:
+		cpuc->saved_ctrl[0] = r_c0_perfctrl0();
+		w_c0_perfctrl0(cpuc->saved_ctrl[0] &
+			~M_PERFCTL_COUNT_EVENT_WHENEVER);
+	}
+	local_irq_restore(flags);
+}
+
+static void resume_local_counters(void)
+{
+	struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
+	int counters = mipspmu->num_counters;
+	unsigned long flags;
+
+	local_irq_save(flags);
+	switch (counters) {
+	case 4:
+		w_c0_perfctrl3(cpuc->saved_ctrl[3]);
+	case 3:
+		w_c0_perfctrl2(cpuc->saved_ctrl[2]);
+	case 2:
+		w_c0_perfctrl1(cpuc->saved_ctrl[1]);
+	case 1:
+		w_c0_perfctrl0(cpuc->saved_ctrl[0]);
+	}
+	local_irq_restore(flags);
+}
+
+static int mipsxx_pmu_handle_shared_irq(void)
+{
+	struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
+	struct perf_sample_data data;
+	unsigned int counters = mipspmu->num_counters;
+	unsigned int counter;
+	int handled = IRQ_NONE;
+	struct pt_regs *regs;
+
+	if (cpu_has_mips_r2 && !(read_c0_cause() & (1 << 26)))
+		return handled;
+
+	/*
+	 * First we pause the local counters, so that when we are locked
+	 * here, the counters are all paused. When it gets locked due to
+	 * perf_disable(), the timer interrupt handler will be delayed.
+	 *
+	 * See also mipsxx_pmu_start().
+	 */
+	pause_local_counters();
+#ifdef CONFIG_MIPS_MT_SMP
+	read_lock(&pmuint_rwlock);
+#endif
+
+	regs = get_irq_regs();
+
+	perf_sample_data_init(&data, 0);
+
+	switch (counters) {
+#define HANDLE_COUNTER(n)						\
+	case n + 1:							\
+		if (test_bit(n, cpuc->used_mask)) {			\
+			counter = r_c0_perfcntr ## n();			\
+			if (counter & M_COUNTER_OVERFLOW) {		\
+				w_c0_perfcntr ## n(counter &		\
+						VALID_COUNT);		\
+				if (test_and_change_bit(n, cpuc->msbs))	\
+					handle_associated_event(cpuc,	\
+						n, &data, regs);	\
+				handled = IRQ_HANDLED;			\
+			}						\
+		}
+	HANDLE_COUNTER(3)
+	HANDLE_COUNTER(2)
+	HANDLE_COUNTER(1)
+	HANDLE_COUNTER(0)
+	}
+
+	/*
+	 * Do all the work for the pending perf events. We can do this
+	 * in here because the performance counter interrupt is a regular
+	 * interrupt, not NMI.
+	 */
+	if (handled == IRQ_HANDLED)
+		perf_event_do_pending();
+
+#ifdef CONFIG_MIPS_MT_SMP
+	read_unlock(&pmuint_rwlock);
+#endif
+	resume_local_counters();
+	return handled;
+}
+
+static irqreturn_t
+mipsxx_pmu_handle_irq(int irq, void *dev)
+{
+	return mipsxx_pmu_handle_shared_irq();
+}
+
+static void mipsxx_pmu_start(void)
+{
+#ifdef CONFIG_MIPS_MT_SMP
+	write_unlock(&pmuint_rwlock);
+#endif
+	resume_local_counters();
+}
+
+/*
+ * MIPS performance counters can be per-TC. The control registers can
+ * not be directly accessed accross CPUs. Hence if we want to do global
+ * control, we need cross CPU calls. on_each_cpu() can help us, but we
+ * can not make sure this function is called with interrupts enabled. So
+ * here we pause local counters and then grab a rwlock and leave the
+ * counters on other CPUs alone. If any counter interrupt raises while
+ * we own the write lock, simply pause local counters on that CPU and
+ * spin in the handler. Also we know we won't be switched to another
+ * CPU after pausing local counters and before grabbing the lock.
+ */
+static void mipsxx_pmu_stop(void)
+{
+	pause_local_counters();
+#ifdef CONFIG_MIPS_MT_SMP
+	write_lock(&pmuint_rwlock);
+#endif
+}
+
+static int
+mipsxx_pmu_alloc_counter(struct cpu_hw_events *cpuc,
+			struct hw_perf_event *hwc)
+{
+	int i;
+
+	/*
+	 * We only need to care the counter mask. The range has been
+	 * checked definitely.
+	 */
+	unsigned long cntr_mask = (hwc->event_base >> 8) & 0xffff;
+
+	for (i = mipspmu->num_counters - 1; i >= 0; i--) {
+		/*
+		 * Note that some MIPS perf events can be counted by both
+		 * even and odd counters, wheresas many other are only by
+		 * even _or_ odd counters. This introduces an issue that
+		 * when the former kind of event takes the counter the
+		 * latter kind of event wants to use, then the "counter
+		 * allocation" for the latter event will fail. In fact if
+		 * they can be dynamically swapped, they both feel happy.
+		 * But here we leave this issue alone for now.
+		 */
+		if (test_bit(i, &cntr_mask) &&
+			!test_and_set_bit(i, cpuc->used_mask))
+			return i;
+	}
+
+	return -EAGAIN;
+}
+
+static void
+mipsxx_pmu_enable_event(struct hw_perf_event *evt, int idx)
+{
+	struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
+	unsigned long flags;
+
+	WARN_ON(idx < 0 || idx >= mipspmu->num_counters);
+
+	local_irq_save(flags);
+	cpuc->saved_ctrl[idx] = M_PERFCTL_EVENT(evt->event_base & 0xff) |
+		(evt->config_base & M_PERFCTL_CONFIG_MASK) |
+		/* Make sure interrupt enabled. */
+		M_PERFCTL_INTERRUPT_ENABLE;
+	/*
+	 * We do not actually let the counter run. Leave it until start().
+	 */
+	local_irq_restore(flags);
+}
+
+static void
+mipsxx_pmu_disable_event(int idx)
+{
+	struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
+	unsigned long flags;
+
+	WARN_ON(idx < 0 || idx >= mipspmu->num_counters);
+
+	local_irq_save(flags);
+	cpuc->saved_ctrl[idx] = mipsxx_pmu_read_control(idx) &
+		~M_PERFCTL_COUNT_EVENT_WHENEVER;
+	mipsxx_pmu_write_control(idx, cpuc->saved_ctrl[idx]);
+	local_irq_restore(flags);
+}
+
+/* 24K */
+#define IS_UNSUPPORTED_24K_EVENT(r, b)					\
+	((b) == 12 || (r) == 151 || (r) == 152 || (b) == 26 ||		\
+	 (b) == 27 || (r) == 28 || (r) == 158 || (b) == 31 ||		\
+	 (b) == 32 || (b) == 34 || (b) == 36 || (r) == 168 ||		\
+	 (r) == 172 || (b) == 47 || ((b) >= 56 && (b) <= 63) ||		\
+	 ((b) >= 68 && (b) <= 127))
+#define IS_BOTH_COUNTERS_24K_EVENT(b)					\
+	((b) == 0 || (b) == 1 || (b) == 11)
+
+/* 34K */
+#define IS_UNSUPPORTED_34K_EVENT(r, b)					\
+	((b) == 12 || (r) == 27 || (r) == 158 || (b) == 36 ||		\
+	 (b) == 38 || (r) == 175 || ((b) >= 56 && (b) <= 63) ||		\
+	 ((b) >= 68 && (b) <= 127))
+#define IS_BOTH_COUNTERS_34K_EVENT(b)					\
+	((b) == 0 || (b) == 1 || (b) == 11)
+#ifdef CONFIG_MIPS_MT_SMP
+#define IS_RANGE_P_34K_EVENT(r, b)					\
+	((b) == 0 || (r) == 18 || (b) == 21 || (b) == 22 ||		\
+	 (b) == 25 || (b) == 39 || (r) == 44 || (r) == 174 ||		\
+	 (r) == 176 || ((b) >= 50 && (b) <= 55) ||			\
+	 ((b) >= 64 && (b) <= 67))
+#define IS_RANGE_V_34K_EVENT(r)	((r) == 47)
+#endif
+
+/* 74K */
+#define IS_UNSUPPORTED_74K_EVENT(r, b)					\
+	((r) == 5 || ((r) >= 135 && (r) <= 137) ||			\
+	 ((b) >= 10 && (b) <= 12) || (b) == 22 || (b) == 27 ||		\
+	 (b) == 33 || (b) == 34 || ((b) >= 47 && (b) <= 49) ||		\
+	 (r) == 178 || (b) == 55 || (b) == 57 || (b) == 60 ||		\
+	 (b) == 61 || (r) == 62 || (r) == 191 ||			\
+	 ((b) >= 64 && (b) <= 127))
+#define IS_BOTH_COUNTERS_74K_EVENT(b)					\
+	((b) == 0 || (b) == 1)
+
+/* 1004K */
+#define IS_UNSUPPORTED_1004K_EVENT(r, b)				\
+	((b) == 12 || (r) == 27 || (r) == 158 || (b) == 38 ||		\
+	 (r) == 175 || (b) == 63 || ((b) >= 68 && (b) <= 127))
+#define IS_BOTH_COUNTERS_1004K_EVENT(b)					\
+	((b) == 0 || (b) == 1 || (b) == 11)
+#ifdef CONFIG_MIPS_MT_SMP
+#define IS_RANGE_P_1004K_EVENT(r, b)					\
+	((b) == 0 || (r) == 18 || (b) == 21 || (b) == 22 ||		\
+	 (b) == 25 || (b) == 36 || (b) == 39 || (r) == 44 ||		\
+	 (r) == 174 || (r) == 176 || ((b) >= 50 && (b) <= 59) ||	\
+	 (r) == 188 || (b) == 61 || (b) == 62 ||			\
+	 ((b) >= 64 && (b) <= 67))
+#define IS_RANGE_V_1004K_EVENT(r)	((r) == 47)
+#endif
+
+/*
+ * User can use 0-255 raw events, where 0-127 for the events of even
+ * counters, and 128-255 for odd counters. Note that bit 7 is used to
+ * indicate the parity. So, for example, when user wants to take the
+ * Event Num of 15 for odd counters (by referring to the user manual),
+ * then 128 needs to be added to 15 as the input for the event config,
+ * i.e., 143 (0x8F) to be used.
+ */
+static const struct mips_perf_event *
+mipsxx_pmu_map_raw_event(u64 config)
+{
+	unsigned int raw_id = config & 0xff;
+	unsigned int base_id = raw_id & 0x7f;
+
+	switch (current_cpu_type()) {
+	case CPU_24K:
+		if (IS_UNSUPPORTED_24K_EVENT(raw_id, base_id))
+			return ERR_PTR(-EOPNOTSUPP);
+		raw_event.event_id = base_id;
+		if (IS_BOTH_COUNTERS_24K_EVENT(base_id))
+			raw_event.cntr_mask = CNTR_EVEN | CNTR_ODD;
+		else
+			raw_event.cntr_mask =
+				raw_id > 127 ? CNTR_ODD : CNTR_EVEN;
+#ifdef CONFIG_MIPS_MT_SMP
+		/*
+		 * This is actually doing nothing. Non-multithreading
+		 * CPUs will not check and calculate the range.
+		 */
+		raw_event.range = P;
+#endif
+		break;
+	case CPU_34K:
+		if (IS_UNSUPPORTED_34K_EVENT(raw_id, base_id))
+			return ERR_PTR(-EOPNOTSUPP);
+		raw_event.event_id = base_id;
+		if (IS_BOTH_COUNTERS_34K_EVENT(base_id))
+			raw_event.cntr_mask = CNTR_EVEN | CNTR_ODD;
+		else
+			raw_event.cntr_mask =
+				raw_id > 127 ? CNTR_ODD : CNTR_EVEN;
+#ifdef CONFIG_MIPS_MT_SMP
+		if (IS_RANGE_P_34K_EVENT(raw_id, base_id))
+			raw_event.range = P;
+		else if (unlikely(IS_RANGE_V_34K_EVENT(raw_id)))
+			raw_event.range = V;
+		else
+			raw_event.range = T;
+#endif
+		break;
+	case CPU_74K:
+		if (IS_UNSUPPORTED_74K_EVENT(raw_id, base_id))
+			return ERR_PTR(-EOPNOTSUPP);
+		raw_event.event_id = base_id;
+		if (IS_BOTH_COUNTERS_74K_EVENT(base_id))
+			raw_event.cntr_mask = CNTR_EVEN | CNTR_ODD;
+		else
+			raw_event.cntr_mask =
+				raw_id > 127 ? CNTR_ODD : CNTR_EVEN;
+#ifdef CONFIG_MIPS_MT_SMP
+		raw_event.range = P;
+#endif
+		break;
+	case CPU_1004K:
+		if (IS_UNSUPPORTED_1004K_EVENT(raw_id, base_id))
+			return ERR_PTR(-EOPNOTSUPP);
+		raw_event.event_id = base_id;
+		if (IS_BOTH_COUNTERS_1004K_EVENT(base_id))
+			raw_event.cntr_mask = CNTR_EVEN | CNTR_ODD;
+		else
+			raw_event.cntr_mask =
+				raw_id > 127 ? CNTR_ODD : CNTR_EVEN;
+#ifdef CONFIG_MIPS_MT_SMP
+		if (IS_RANGE_P_1004K_EVENT(raw_id, base_id))
+			raw_event.range = P;
+		else if (unlikely(IS_RANGE_V_1004K_EVENT(raw_id)))
+			raw_event.range = V;
+		else
+			raw_event.range = T;
+#endif
+		break;
+	}
+
+	return &raw_event;
+}
+
+static struct mips_pmu mipsxxcore_pmu = {
+	.handle_irq = mipsxx_pmu_handle_irq,
+	.handle_shared_irq = mipsxx_pmu_handle_shared_irq,
+	.start = mipsxx_pmu_start,
+	.stop = mipsxx_pmu_stop,
+	.alloc_counter = mipsxx_pmu_alloc_counter,
+	.read_counter = mipsxx_pmu_read_counter,
+	.write_counter = mipsxx_pmu_write_counter,
+	.enable_event = mipsxx_pmu_enable_event,
+	.disable_event = mipsxx_pmu_disable_event,
+	.map_raw_event = mipsxx_pmu_map_raw_event,
+	.general_event_map = &mipsxxcore_event_map,
+	.cache_event_map = &mipsxxcore_cache_map,
+};
+
+static struct mips_pmu mipsxx74Kcore_pmu = {
+	.handle_irq = mipsxx_pmu_handle_irq,
+	.handle_shared_irq = mipsxx_pmu_handle_shared_irq,
+	.start = mipsxx_pmu_start,
+	.stop = mipsxx_pmu_stop,
+	.alloc_counter = mipsxx_pmu_alloc_counter,
+	.read_counter = mipsxx_pmu_read_counter,
+	.write_counter = mipsxx_pmu_write_counter,
+	.enable_event = mipsxx_pmu_enable_event,
+	.disable_event = mipsxx_pmu_disable_event,
+	.map_raw_event = mipsxx_pmu_map_raw_event,
+	.general_event_map = &mipsxx74Kcore_event_map,
+	.cache_event_map = &mipsxx74Kcore_cache_map,
+};
+
+static void reset_counters(void *arg)
+{
+	int counters = (int)(long)arg;
+	switch (counters) {
+	case 4:
+		w_c0_perfctrl3(0);
+		w_c0_perfcntr3(0);
+	case 3:
+		w_c0_perfctrl2(0);
+		w_c0_perfcntr2(0);
+	case 2:
+		w_c0_perfctrl1(0);
+		w_c0_perfcntr1(0);
+	case 1:
+		w_c0_perfctrl0(0);
+		w_c0_perfcntr0(0);
+	}
+}
+
+static int __init
+init_hw_perf_events(void)
+{
+	int counters;
+
+	pr_info("Performance counters: ");
+
+	counters = n_counters();
+	if (counters == 0) {
+		pr_cont("No available PMU.\n");
+		return -ENODEV;
+	}
+
+#ifdef CONFIG_MIPS_MT_SMP
+	cpu_has_mipsmt_pertccounters = read_c0_config7() & (1<<19);
+	if (!cpu_has_mipsmt_pertccounters)
+		counters = counters_total_to_per_cpu(counters);
+#endif
+
+	on_each_cpu(reset_counters, (void *)(long)counters, 1);
+
+	switch (current_cpu_type()) {
+	case CPU_24K:
+		mipsxxcore_pmu.name = "mips/24K";
+		mipsxxcore_pmu.num_counters = counters;
+		mipspmu = &mipsxxcore_pmu;
+		break;
+	case CPU_34K:
+		mipsxxcore_pmu.name = "mips/34K";
+		mipsxxcore_pmu.num_counters = counters;
+		mipspmu = &mipsxxcore_pmu;
+		break;
+	case CPU_74K:
+		mipsxx74Kcore_pmu.name = "mips/74K";
+		mipsxx74Kcore_pmu.num_counters = counters;
+		mipspmu = &mipsxx74Kcore_pmu;
+		break;
+	case CPU_1004K:
+		mipsxxcore_pmu.name = "mips/1004K";
+		mipsxxcore_pmu.num_counters = counters;
+		mipspmu = &mipsxxcore_pmu;
+		break;
+	default:
+		pr_cont("Either hardware does not support performance "
+			"counters, or not yet implemented.\n");
+		return -ENODEV;
+	}
+
+	if (mipspmu)
+		pr_cont("%s PMU enabled, %d counters available to each "
+			"CPU\n", mipspmu->name, mipspmu->num_counters);
+
+	return 0;
+}
+arch_initcall(init_hw_perf_events);
+
+#endif /* defined(CONFIG_CPU_MIPS32)... */
-- 
1.6.3.3

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH v6 7/7] MIPS: define local_xchg from xchg_local to atomic_long_xchg
  2010-06-09  4:35 [PATCH v6 0/7] MIPS performance event support v6 Deng-Cheng Zhu
                   ` (5 preceding siblings ...)
  2010-06-09  4:35 ` [PATCH v6 6/7] MIPS: add support for hardware performance events (mipsxx) Deng-Cheng Zhu
@ 2010-06-09  4:35 ` Deng-Cheng Zhu
  2010-09-22 12:32   ` Matt Fleming
  6 siblings, 1 reply; 17+ messages in thread
From: Deng-Cheng Zhu @ 2010-06-09  4:35 UTC (permalink / raw)
  To: linux-mips, ralf
  Cc: a.p.zijlstra, paulus, mingo, acme, jamie.iles, Deng-Cheng Zhu

Perf-events is now using local_t helper functions internally. There is a
use of local_xchg(). On MIPS, this is defined to xchg_local() which is
missing in asm/system.h. This patch re-defines local_xchg() in asm/local.h
to atomic_long_xchg(). Then Perf-events can pass the build.

Signed-off-by: Deng-Cheng Zhu <dengcheng.zhu@gmail.com>
---
 arch/mips/include/asm/local.h |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/arch/mips/include/asm/local.h b/arch/mips/include/asm/local.h
index bdcdef0..4d090a0 100644
--- a/arch/mips/include/asm/local.h
+++ b/arch/mips/include/asm/local.h
@@ -117,7 +117,7 @@ static __inline__ long local_sub_return(long i, local_t * l)
 
 #define local_cmpxchg(l, o, n) \
 	((long)cmpxchg_local(&((l)->a.counter), (o), (n)))
-#define local_xchg(l, n) (xchg_local(&((l)->a.counter), (n)))
+#define local_xchg(l, n) atomic_long_xchg((&(l)->a), (n))
 
 /**
  * local_add_unless - add unless the number is a given value
-- 
1.6.3.3

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* Re: [PATCH v6 2/7] MIPS: use generic atomic64 in non-64bit kernels
  2010-06-09  4:35 ` [PATCH v6 2/7] MIPS: use generic atomic64 in non-64bit kernels Deng-Cheng Zhu
@ 2010-06-09 16:58   ` David Daney
  2010-08-06 16:45   ` Ralf Baechle
  1 sibling, 0 replies; 17+ messages in thread
From: David Daney @ 2010-06-09 16:58 UTC (permalink / raw)
  To: Deng-Cheng Zhu, ralf
  Cc: linux-mips, a.p.zijlstra, paulus, mingo, acme, jamie.iles

On 06/08/2010 09:35 PM, Deng-Cheng Zhu wrote:
> 64bit kernel has already had its atomic64 functions. Except for that, we
> use the generic spinlocked version. The atomic64 types and related
> functions are needed for the Linux performance counter subsystem.
>
> Signed-off-by: Deng-Cheng Zhu<dengcheng.zhu@gmail.com>

I already acked this once, so you can (should) add my...

Acked-by: David Daney <ddaney@caviumnetworks.com>


Really, this part is correct and standalone, so I think Ralf should just 
go ahead and merge it to his queue.

David Daney.

> ---
>   arch/mips/Kconfig              |    1 +
>   arch/mips/include/asm/atomic.h |    4 ++++
>   2 files changed, 5 insertions(+), 0 deletions(-)
>
> diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
> index cdaae94..564e30b 100644
> --- a/arch/mips/Kconfig
> +++ b/arch/mips/Kconfig
> @@ -11,6 +11,7 @@ config MIPS
>   	select HAVE_FTRACE_MCOUNT_RECORD
>   	select HAVE_FUNCTION_GRAPH_TRACER
>   	select RTC_LIB if !MACH_LOONGSON
> +	select GENERIC_ATOMIC64 if !64BIT
>
>   mainmenu "Linux/MIPS Kernel Configuration"
>
> diff --git a/arch/mips/include/asm/atomic.h b/arch/mips/include/asm/atomic.h
> index 59dc0c7..485ec36 100644
> --- a/arch/mips/include/asm/atomic.h
> +++ b/arch/mips/include/asm/atomic.h
> @@ -782,6 +782,10 @@ static __inline__ int atomic64_add_unless(atomic64_t *v, long a, long u)
>    */
>   #define atomic64_add_negative(i, v) (atomic64_add_return(i, (v))<  0)
>
> +#else /* !CONFIG_64BIT */
> +
> +#include<asm-generic/atomic64.h>
> +
>   #endif /* CONFIG_64BIT */
>
>   /*

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v6 2/7] MIPS: use generic atomic64 in non-64bit kernels
  2010-06-09  4:35 ` [PATCH v6 2/7] MIPS: use generic atomic64 in non-64bit kernels Deng-Cheng Zhu
  2010-06-09 16:58   ` David Daney
@ 2010-08-06 16:45   ` Ralf Baechle
  1 sibling, 0 replies; 17+ messages in thread
From: Ralf Baechle @ 2010-08-06 16:45 UTC (permalink / raw)
  To: Deng-Cheng Zhu; +Cc: linux-mips, a.p.zijlstra, paulus, mingo, acme, jamie.iles

Applied.  Thanks Deng-Cheng!

  Ralf

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v6 3/7] MIPS: add support for software performance events
  2010-06-09  4:35 ` [PATCH v6 3/7] MIPS: add support for software performance events Deng-Cheng Zhu
@ 2010-09-22 11:38   ` Matt Fleming
  0 siblings, 0 replies; 17+ messages in thread
From: Matt Fleming @ 2010-09-22 11:38 UTC (permalink / raw)
  To: Deng-Cheng Zhu
  Cc: linux-mips, ralf, a.p.zijlstra, paulus, mingo, acme, jamie.iles

On Wed, Jun 09, 2010 at 12:35:26PM +0800, Deng-Cheng Zhu wrote:
> Software events are required as part of the measurable stuff by the
> Linux performance counter subsystem. Here is the list of events added by
> this patch:
> PERF_COUNT_SW_PAGE_FAULTS
> PERF_COUNT_SW_PAGE_FAULTS_MIN
> PERF_COUNT_SW_PAGE_FAULTS_MAJ
> PERF_COUNT_SW_ALIGNMENT_FAULTS
> PERF_COUNT_SW_EMULATION_FAULTS
> 
> Signed-off-by: Deng-Cheng Zhu <dengcheng.zhu@gmail.com>

This looks OK.

Reviewed-by: Matt Fleming <matt@console-pimps.org>

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v6 4/7] MIPS: add support for hardware performance events (skeleton)
  2010-06-09  4:35 ` [PATCH v6 4/7] MIPS: add support for hardware performance events (skeleton) Deng-Cheng Zhu
@ 2010-09-22 12:27   ` Matt Fleming
  2010-09-23  7:39     ` Deng-Cheng Zhu
  0 siblings, 1 reply; 17+ messages in thread
From: Matt Fleming @ 2010-09-22 12:27 UTC (permalink / raw)
  To: Deng-Cheng Zhu
  Cc: linux-mips, ralf, a.p.zijlstra, paulus, mingo, acme, jamie.iles

On Wed, Jun 09, 2010 at 12:35:27PM +0800, Deng-Cheng Zhu wrote:

[...]

> +static void mipspmu_event_update(struct perf_event *event,
> +			struct hw_perf_event *hwc,
> +			int idx)
> +{
> +	struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
> +	unsigned long flags;
> +	int shift = 64 - TOTAL_BITS;
> +	s64 prev_raw_count, new_raw_count;
> +	s64 delta;
> +
> +again:
> +	prev_raw_count = atomic64_read(&hwc->prev_count);
> +	local_irq_save(flags);
> +	/* Make the counter value be a "real" one. */
> +	new_raw_count = mipspmu->read_counter(idx);
> +	if (new_raw_count & (test_bit(idx, cpuc->msbs) << HIGHEST_BIT)) {
> +		new_raw_count &= VALID_COUNT;
> +		clear_bit(idx, cpuc->msbs);
> +	} else
> +		new_raw_count |= (test_bit(idx, cpuc->msbs) << HIGHEST_BIT);
> +	local_irq_restore(flags);

I'm probably just being stupid but I can't figure out what this snippet
of code is doing. You're checking to see if the counter has overflowed,
and if it has then you just clear the overflow bit? I would have
expected you to reset the counter to 0.

> +	if (atomic64_cmpxchg(&hwc->prev_count, prev_raw_count,
> +				new_raw_count) != prev_raw_count)
> +		goto again;
> +
> +	delta = (new_raw_count << shift) - (prev_raw_count << shift);
> +	delta >>= shift;
> +
> +	atomic64_add(delta, &event->count);
> +	atomic64_sub(delta, &hwc->period_left);
> +
> +	return;
> +}
> +
> +static void mipspmu_disable(struct perf_event *event)
> +{
> +	struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
> +	struct hw_perf_event *hwc = &event->hw;
> +	int idx = hwc->idx;
> +
> +
> +	WARN_ON(idx < 0 || idx >= mipspmu->num_counters);
> +
> +	/* We are working on a local event. */
> +	mipspmu->disable_event(idx);
> +
> +	barrier();
> +
> +	mipspmu_event_update(event, hwc, idx);
> +	cpuc->events[idx] = NULL;
> +	clear_bit(idx, cpuc->used_mask);

Shouldn't you also clear the overflow bit in ->msbs here?

> +
> +	perf_event_update_userpage(event);
> +}
> +
> +static void mipspmu_unthrottle(struct perf_event *event)
> +{
> +	struct hw_perf_event *hwc = &event->hw;
> +
> +	mipspmu->enable_event(hwc, hwc->idx);
> +}
> +
> +static void mipspmu_read(struct perf_event *event)
> +{
> +	struct hw_perf_event *hwc = &event->hw;
> +
> +	/* Don't read disabled counters! */
> +	if (hwc->idx < 0)
> +		return;
> +
> +	mipspmu_event_update(event, hwc, hwc->idx);
> +}
> +
> +static struct pmu pmu = {
> +	.enable		= mipspmu_enable,
> +	.disable	= mipspmu_disable,
> +	.unthrottle	= mipspmu_unthrottle,
> +	.read		= mipspmu_read,
> +};
> +
> +static atomic_t active_events = ATOMIC_INIT(0);
> +static DEFINE_MUTEX(pmu_reserve_mutex);
> +static int mips_pmu_irq = -1;
> +static int (*save_perf_irq)(void);
> +
> +static int mipspmu_get_irq(void)
> +{
> +	int err;
> +
> +	if (cpu_has_veic) {
> +		/*
> +		 * Using platform specific interrupt controller defines.
> +		 */
> +#ifdef MSC01E_INT_BASE
> +		mips_pmu_irq = MSC01E_INT_BASE + MSC01E_INT_PERFCTR;
> +#endif
> +	} else if (cp0_perfcount_irq >= 0) {
> +		/*
> +		 * Some CPUs have explicitly defined their perfcount irq.
> +		 */
> +#if defined(CONFIG_CPU_RM9000)
> +		mips_pmu_irq = rm9000_perfcount_irq;
> +#elif defined(CONFIG_CPU_LOONGSON2)
> +		mips_pmu_irq = LOONGSON2_PERFCNT_IRQ;
> +#else
> +		mips_pmu_irq = MIPS_CPU_IRQ_BASE + cp0_perfcount_irq;
> +#endif
> +	}

Having conditional code like this is a pretty sure sign that you haven't
separated support for the various performance hardware properly. Have
you had a look at how SH uses a registration interface to register
sh_pmus?  Ideally all the internals for each type of perfcounter
hardware should be in their own file.

> +
> +	if (mips_pmu_irq >= 0) {
> +		/* Request my own irq handler. */
> +		err = request_irq(mips_pmu_irq, mipspmu->handle_irq,
> +			IRQF_DISABLED | IRQF_NOBALANCING,
> +			"mips_perf_pmu", NULL);
> +		if (err) {
> +			pr_warning("Unable to request IRQ%d for MIPS "
> +			   "performance counters!\n", mips_pmu_irq);
> +		}
> +	} else if (cp0_perfcount_irq < 0) {
> +		/*
> +		 * We are sharing the irq number with the timer interrupt.
> +		 */
> +		save_perf_irq = perf_irq;
> +		perf_irq = mipspmu->handle_shared_irq;
> +		err = 0;

SH also has this problem that it doesn't have any sort of performance
counter interrupt and so can't check for overflow. This lack of
interrupt really needs to be solved generically in perf as it's a
problem that effects quite a few architectures. I suspect that using a
hrtimer instead of piggy-backing the timer interrupt would make the core
perf guys happier. Writing support for this is on my ever-growing list
of things todo. I've already started on some patches for the perf tool
so that it's possible to sample counters even though there is no
periodic interrupt (but that's a different problem).

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v6 7/7] MIPS: define local_xchg from xchg_local to atomic_long_xchg
  2010-06-09  4:35 ` [PATCH v6 7/7] MIPS: define local_xchg from xchg_local to atomic_long_xchg Deng-Cheng Zhu
@ 2010-09-22 12:32   ` Matt Fleming
  2010-09-23  7:56     ` Deng-Cheng Zhu
  0 siblings, 1 reply; 17+ messages in thread
From: Matt Fleming @ 2010-09-22 12:32 UTC (permalink / raw)
  To: Deng-Cheng Zhu
  Cc: linux-mips, ralf, a.p.zijlstra, paulus, mingo, acme, jamie.iles

On Wed, Jun 09, 2010 at 12:35:30PM +0800, Deng-Cheng Zhu wrote:
> Perf-events is now using local_t helper functions internally. There is a
> use of local_xchg(). On MIPS, this is defined to xchg_local() which is
> missing in asm/system.h. This patch re-defines local_xchg() in asm/local.h
> to atomic_long_xchg(). Then Perf-events can pass the build.
> 
> Signed-off-by: Deng-Cheng Zhu <dengcheng.zhu@gmail.com>

Is this a separate issue from the rest of this patch series? In any
event, it's probably worth moving this earlier in the patch series so
as not to break bisect.

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v6 4/7] MIPS: add support for hardware performance events (skeleton)
  2010-09-22 12:27   ` Matt Fleming
@ 2010-09-23  7:39     ` Deng-Cheng Zhu
  2010-09-24  8:36       ` Matt Fleming
  0 siblings, 1 reply; 17+ messages in thread
From: Deng-Cheng Zhu @ 2010-09-23  7:39 UTC (permalink / raw)
  To: Matt Fleming
  Cc: linux-mips, ralf, a.p.zijlstra, paulus, mingo, acme, jamie.iles

2010/9/22 Matt Fleming <matt@console-pimps.org>:
> I'm probably just being stupid but I can't figure out what this snippet
> of code is doing. You're checking to see if the counter has overflowed,
> and if it has then you just clear the overflow bit? I would have
> expected you to reset the counter to 0.
[DC]: Maybe my original code comment for the member msbs of the struct
cpu_hw_events is too simple. And here is more: Unlike the perf counters in
some other architectures, a 32bit MIPS perf counter, for example, will
generate an interrupt after 0x7fffffff. But we want the operation to look
like: 0x0 -> 0xffffffff -> interrupt. So there's a "pseudo" signal halfway.
Please also note that the counter value will be brought back to 0 soon
after it reaches 0x80000000 *each time*.


> Shouldn't you also clear the overflow bit in ->msbs here?
[DC]: See my comment above.


> Having conditional code like this is a pretty sure sign that you haven't
> separated support for the various performance hardware properly. Have
> you had a look at how SH uses a registration interface to register
> sh_pmus?  Ideally all the internals for each type of perfcounter
> hardware should be in their own file.
[DC]: It does look ugly. It should be easy to put conditional code into
perf_event_[mipsxx|loongson2|rm9000].c. I'll post a patch to fix it when
the whole thing is accepted.


> SH also has this problem that it doesn't have any sort of performance
> counter interrupt and so can't check for overflow. This lack of
> interrupt really needs to be solved generically in perf as it's a
> problem that effects quite a few architectures. I suspect that using a
> hrtimer instead of piggy-backing the timer interrupt would make the core
> perf guys happier. Writing support for this is on my ever-growing list
> of things todo. I've already started on some patches for the perf tool
> so that it's possible to sample counters even though there is no
> periodic interrupt (but that's a different problem).
[DC]: Please add me into your post list when your patches are ready :-)
Finally, thanks for your time to review the code.


Deng-Cheng

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v6 7/7] MIPS: define local_xchg from xchg_local to atomic_long_xchg
  2010-09-22 12:32   ` Matt Fleming
@ 2010-09-23  7:56     ` Deng-Cheng Zhu
  0 siblings, 0 replies; 17+ messages in thread
From: Deng-Cheng Zhu @ 2010-09-23  7:56 UTC (permalink / raw)
  To: Matt Fleming
  Cc: linux-mips, ralf, a.p.zijlstra, paulus, mingo, acme, jamie.iles

Yes, it's here only to pass the compiling (coz the Perf core had evolved
since the v5 of this patchset). Since the file it touches is stand-alone,
it can be applied earlier than the rest (like the patch #2).


Deng-Cheng


2010/9/22 Matt Fleming <matt@console-pimps.org>:
> On Wed, Jun 09, 2010 at 12:35:30PM +0800, Deng-Cheng Zhu wrote:
>> Perf-events is now using local_t helper functions internally. There is a
>> use of local_xchg(). On MIPS, this is defined to xchg_local() which is
>> missing in asm/system.h. This patch re-defines local_xchg() in asm/local.h
>> to atomic_long_xchg(). Then Perf-events can pass the build.
>>
>> Signed-off-by: Deng-Cheng Zhu <dengcheng.zhu@gmail.com>
>
> Is this a separate issue from the rest of this patch series? In any
> event, it's probably worth moving this earlier in the patch series so
> as not to break bisect.
>

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v6 4/7] MIPS: add support for hardware performance events (skeleton)
  2010-09-23  7:39     ` Deng-Cheng Zhu
@ 2010-09-24  8:36       ` Matt Fleming
  2010-09-25  2:53         ` Deng-Cheng Zhu
  0 siblings, 1 reply; 17+ messages in thread
From: Matt Fleming @ 2010-09-24  8:36 UTC (permalink / raw)
  To: Deng-Cheng Zhu
  Cc: linux-mips, ralf, a.p.zijlstra, paulus, mingo, acme, jamie.iles

On Thu, Sep 23, 2010 at 03:39:51PM +0800, Deng-Cheng Zhu wrote:
> 2010/9/22 Matt Fleming <matt@console-pimps.org>:
> > I'm probably just being stupid but I can't figure out what this snippet
> > of code is doing. You're checking to see if the counter has overflowed,
> > and if it has then you just clear the overflow bit? I would have
> > expected you to reset the counter to 0.
> [DC]: Maybe my original code comment for the member msbs of the struct
> cpu_hw_events is too simple. And here is more: Unlike the perf counters in
> some other architectures, a 32bit MIPS perf counter, for example, will
> generate an interrupt after 0x7fffffff. But we want the operation to look
> like: 0x0 -> 0xffffffff -> interrupt. So there's a "pseudo" signal halfway.
> Please also note that the counter value will be brought back to 0 soon
> after it reaches 0x80000000 *each time*.

Ah OK, thanks.

> > Having conditional code like this is a pretty sure sign that you haven't
> > separated support for the various performance hardware properly. Have
> > you had a look at how SH uses a registration interface to register
> > sh_pmus?  Ideally all the internals for each type of perfcounter
> > hardware should be in their own file.
> [DC]: It does look ugly. It should be easy to put conditional code into
> perf_event_[mipsxx|loongson2|rm9000].c. I'll post a patch to fix it when
> the whole thing is accepted.

The potential problem with doing a cleanup patch after this series has
been merged is that it will modify most of the code in this patch
series - essentially rewriting it. I don't have a strong opinion
either way but Ralf may.

> > SH also has this problem that it doesn't have any sort of performance
> > counter interrupt and so can't check for overflow. This lack of
> > interrupt really needs to be solved generically in perf as it's a
> > problem that effects quite a few architectures. I suspect that using a
> > hrtimer instead of piggy-backing the timer interrupt would make the core
> > perf guys happier. Writing support for this is on my ever-growing list
> > of things todo. I've already started on some patches for the perf tool
> > so that it's possible to sample counters even though there is no
> > periodic interrupt (but that's a different problem).
> [DC]: Please add me into your post list when your patches are ready :-)
> Finally, thanks for your time to review the code.

Sure, I will do. No problem!

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v6 4/7] MIPS: add support for hardware performance events (skeleton)
  2010-09-24  8:36       ` Matt Fleming
@ 2010-09-25  2:53         ` Deng-Cheng Zhu
  0 siblings, 0 replies; 17+ messages in thread
From: Deng-Cheng Zhu @ 2010-09-25  2:53 UTC (permalink / raw)
  To: Matt Fleming
  Cc: linux-mips, ralf, a.p.zijlstra, paulus, mingo, acme, jamie.iles

2010/9/24 Matt Fleming <matt@console-pimps.org>:
> The potential problem with doing a cleanup patch after this series has
> been merged is that it will modify most of the code in this patch
> series - essentially rewriting it. I don't have a strong opinion
> either way but Ralf may.
[DC]: No, the situation is not that serious. The mips_pmu registration
mechanism had already been implemented. You may take a look at the
bottom of perf_event_mipsxx.c (patch #6). The ugly conditional code
(mips_pmu_irq) could be fixed easily, I suppose.


Deng-Cheng

^ permalink raw reply	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2010-09-25  2:53 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-06-09  4:35 [PATCH v6 0/7] MIPS performance event support v6 Deng-Cheng Zhu
2010-06-09  4:35 ` [PATCH v6 1/7] MIPS/Oprofile: extract PMU defines/helper functions for sharing Deng-Cheng Zhu
2010-06-09  4:35 ` [PATCH v6 2/7] MIPS: use generic atomic64 in non-64bit kernels Deng-Cheng Zhu
2010-06-09 16:58   ` David Daney
2010-08-06 16:45   ` Ralf Baechle
2010-06-09  4:35 ` [PATCH v6 3/7] MIPS: add support for software performance events Deng-Cheng Zhu
2010-09-22 11:38   ` Matt Fleming
2010-06-09  4:35 ` [PATCH v6 4/7] MIPS: add support for hardware performance events (skeleton) Deng-Cheng Zhu
2010-09-22 12:27   ` Matt Fleming
2010-09-23  7:39     ` Deng-Cheng Zhu
2010-09-24  8:36       ` Matt Fleming
2010-09-25  2:53         ` Deng-Cheng Zhu
2010-06-09  4:35 ` [PATCH v6 5/7] MIPS/Perf-events: add callchain support Deng-Cheng Zhu
2010-06-09  4:35 ` [PATCH v6 6/7] MIPS: add support for hardware performance events (mipsxx) Deng-Cheng Zhu
2010-06-09  4:35 ` [PATCH v6 7/7] MIPS: define local_xchg from xchg_local to atomic_long_xchg Deng-Cheng Zhu
2010-09-22 12:32   ` Matt Fleming
2010-09-23  7:56     ` Deng-Cheng Zhu

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.