Skip to contentSkip to conversation

MIPS performance event support v6

PATCH Applied Deng-Cheng Zhu 4 participants 17 messages Jun 9, 2010 – Sep 25, 2010

Subsystems (3)

v6 of 8 Applied Latest is v8 →

lore
Share & tools
lore
#0 lore raw

v6 cover MIPS performance event support v6

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
#1 lore raw

PATCH v6 1/7 MIPS/Oprofile: extract PMU defines/helper functions for sharing

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

---
 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
arch/mips/include/asm/pmu.h new file +212 -0
new file mode 100644
index 0000000..c84d70c
@@ -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 <[email protected]>
+ * Author: Wu Zhangjin <[email protected]>
+ * 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__ */
arch/mips/oprofile/op_model_loongson2.c +1 -17
index 60d3ea6..665e994 100644
@@ -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;
arch/mips/oprofile/op_model_mipsxx.c +3 -144

168 lines not on this page · raw message

arch/mips/oprofile/op_model_rm9000.c +1 -15
index 3aa8138..48e7487 100644
@@ -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;
Signature · 1 line
1.6.3.3
#2 lore raw

PATCH v6 2/7 MIPS: use generic atomic64 in non-64bit kernels

In reply to 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.

---
 arch/mips/Kconfig              |    1 +
 arch/mips/include/asm/atomic.h |    4 ++++
 2 files changed, 5 insertions(+), 0 deletions(-)
arch/mips/Kconfig +1 -0
index cdaae94..564e30b 100644
@@ -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"
 
arch/mips/include/asm/atomic.h +4 -0
index 59dc0c7..485ec36 100644
@@ -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 */
 
 /*
Signature · 1 line
1.6.3.3
Acked-by on v6 2/7
#3 lore raw

Re: [PATCH v6 2/7] MIPS: use generic atomic64 in non-64bit kernels

Quoted 5 lines · 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.

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

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

David Daney.

Quoted 32 lines
---
  arch/mips/Kconfig              |    1 +
  arch/mips/include/asm/atomic.h |    4 ++++
  2 files changed, 5 insertions(+), 0 deletions(-)
arch/mips/Kconfig +1 -0
index cdaae94..564e30b 100644
@@ -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"
 
arch/mips/include/asm/atomic.h +4 -0
index 59dc0c7..485ec36 100644
@@ -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 */
 
  /*
#5 lore raw

PATCH v6 3/7 MIPS: add support for software performance events

In reply to 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

---
 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(-)
arch/mips/Kconfig +2 -0
index 564e30b..378c25d 100644
@@ -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
arch/mips/kernel/traps.c +15 -3
index 8bdd6a6..d16b3fc 100644
@@ -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 ... */
 }
arch/mips/kernel/unaligned.c +5 -0
index 69b039c..a319c7a 100644
@@ -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?
arch/mips/math-emu/cp1emu.c +3 -0
index 47842b7..fd180ea 100644
@@ -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:{
arch/mips/mm/fault.c +9 -2
index b4aac42..1887ff6 100644
@@ -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;
Signature · 1 line
1.6.3.3
Reviewed-by on v6 3/7
#6 lore raw

Re: [PATCH v6 3/7] MIPS: add support for software performance events

Quoted 10 lines · 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

This looks OK.

#7 lore raw

PATCH v6 4/7 MIPS: add support for hardware performance events (skeleton)

In reply to 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.

---
 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
arch/mips/Kconfig +8 -0
index 378c25d..653d574 100644
@@ -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
arch/mips/include/asm/perf_event.h new file +26 -0
new file mode 100644
index 0000000..ad8f986
@@ -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__ */
+
arch/mips/kernel/Makefile +2 -0
index ff5ec2e..c037121 100644
@@ -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)
arch/mips/kernel/perf_event.c new file +510 -0

510 lines not on this page · raw message

Signature · 1 line
1.6.3.3
#8 lore raw

Re: [PATCH v6 4/7] MIPS: add support for hardware performance events (skeleton)

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

[...]

Quoted 11 lines
arch/mips/kernel/perf_event.c new file +11 -0
+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:

arch/mips/kernel/perf_event.c line 212

Quoted 10 lines
arch/mips/kernel/perf_event.c new file +10 -0
+	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.

Quoted 20 lines
arch/mips/kernel/perf_event.c new file +20 -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;
+
+

arch/mips/kernel/perf_event.c line 243

Quoted 10 lines
arch/mips/kernel/perf_event.c new file +10 -0
+	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?

Quoted 47 lines
arch/mips/kernel/perf_event.c new file +47 -0
+
+	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) {
+		/*

arch/mips/kernel/perf_event.c line 300

Quoted 10 lines
arch/mips/kernel/perf_event.c new file +10 -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.

Quoted 7 lines
arch/mips/kernel/perf_event.c new file +7 -0
+
+	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) {

arch/mips/kernel/perf_event.c line 317

Quoted 10 lines
arch/mips/kernel/perf_event.c new file +10 -0
+			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).

#9 lore raw

Re: [PATCH v6 4/7] MIPS: add support for hardware performance events (skeleton)

2010/9/22 Matt Fleming <[email protected]>:

Quoted 4 lines

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*.

Quoted 1 line

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

[DC]: See my comment above.

Quoted 5 lines

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.

Quoted 9 lines

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

#10 lore raw

Re: [PATCH v6 4/7] MIPS: add support for hardware performance events (skeleton)

Quoted 5 lines · Deng-Cheng Zhu

2010/9/22 Matt Fleming <[email protected]>:

Quoted 4 lines

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.

Quoted 7 lines

[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.

Quoted 8 lines
Quoted 5 lines

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.

Quoted 11 lines
Quoted 9 lines

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!

#11 lore raw

Re: [PATCH v6 4/7] MIPS: add support for hardware performance events (skeleton)

2010/9/24 Matt Fleming <[email protected]>:

Quoted 4 lines

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

#12 lore raw

PATCH v6 5/7 MIPS/Perf-events: add callchain support

In reply to 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.

---
 arch/mips/kernel/perf_event.c |  108 ++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 107 insertions(+), 1 deletions(-)
arch/mips/kernel/perf_event.c +107 -1
index 51e31e2..7e371ae 100644
@@ -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;
+}
+
Signature · 1 line
1.6.3.3
#13 lore raw

PATCH v6 6/7 MIPS: add support for hardware performance events (mipsxx)

In reply to 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

---
 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
arch/mips/include/asm/pmu.h +87 -0
index c84d70c..be057ca 100644
@@ -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)
arch/mips/kernel/perf_event.c +7 -0
index 7e371ae..fb7a040 100644
@@ -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,
arch/mips/kernel/perf_event_mipsxx.c new file +802 -0

802 lines not on this page · raw message

Signature · 1 line
1.6.3.3
#14 lore raw

PATCH v6 7/7 MIPS: define local_xchg from xchg_local to atomic_long_xchg

In reply to 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.

---
 arch/mips/include/asm/local.h |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)
arch/mips/include/asm/local.h +1 -1
index bdcdef0..4d090a0 100644
@@ -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
Signature · 1 line
1.6.3.3
#15 lore raw

Re: [PATCH v6 7/7] MIPS: define local_xchg from xchg_local to atomic_long_xchg

Quoted 6 lines · 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.

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.

#16 lore raw

Re: [PATCH v6 7/7] MIPS: define local_xchg from xchg_local to atomic_long_xchg

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 <[email protected]>:

Quoted 12 lines · Deng-Cheng Zhu
Quoted 6 lines

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.

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.