2015-07-06 13:33:20

by Max Filippov

[permalink] [raw]
Subject: [PATCH 0/8] Support hardware perf counters on xtensa

Hello,

this series adds support for hardware performance counters in counting and
sampling modes as well as for page fault counting. It reorganizes stack
stracing code to share it between old oprofile and new perf interfaces,
and improves kernel stack traces both for builtin debug functions and for
gdb.

Max Filippov (8):
xtensa: clean up Kconfig dependencies for custom cores
xtensa: keep exception/interrupt stack continuous
xtensa: move oprofile stack tracing to stacktrace.c
xtensa: select PERF_USE_VMALLOC for cache-aliasing configurations
xtensa: add profiling IRQ type to xtensa_irq_map
xtensa: count software page fault perf events
xtensa: implement counting and sampling perf events
perf tools: xtensa: add DWARF register names

arch/xtensa/Kconfig | 22 +-
arch/xtensa/include/asm/stacktrace.h | 8 +
arch/xtensa/kernel/Makefile | 1 +
arch/xtensa/kernel/entry.S | 60 +++--
arch/xtensa/kernel/irq.c | 6 +
arch/xtensa/kernel/perf_event.c | 450 +++++++++++++++++++++++++++++++
arch/xtensa/kernel/stacktrace.c | 167 +++++++++++-
arch/xtensa/mm/fault.c | 7 +
arch/xtensa/oprofile/backtrace.c | 158 +----------
tools/perf/arch/xtensa/Build | 1 +
tools/perf/arch/xtensa/Makefile | 3 +
tools/perf/arch/xtensa/util/Build | 1 +
tools/perf/arch/xtensa/util/dwarf-regs.c | 25 ++
13 files changed, 725 insertions(+), 184 deletions(-)
create mode 100644 arch/xtensa/kernel/perf_event.c
create mode 100644 tools/perf/arch/xtensa/Build
create mode 100644 tools/perf/arch/xtensa/Makefile
create mode 100644 tools/perf/arch/xtensa/util/Build
create mode 100644 tools/perf/arch/xtensa/util/dwarf-regs.c

--
1.8.1.4


2015-07-06 13:35:55

by Max Filippov

[permalink] [raw]
Subject: [PATCH 1/8] xtensa: clean up Kconfig dependencies for custom cores

Signed-off-by: Max Filippov <[email protected]>
---
arch/xtensa/Kconfig | 11 +++--------
1 file changed, 3 insertions(+), 8 deletions(-)

diff --git a/arch/xtensa/Kconfig b/arch/xtensa/Kconfig
index 87be10e..ba22699 100644
--- a/arch/xtensa/Kconfig
+++ b/arch/xtensa/Kconfig
@@ -61,9 +61,7 @@ config TRACE_IRQFLAGS_SUPPORT
def_bool y

config MMU
- bool
- default n if !XTENSA_VARIANT_CUSTOM
- default XTENSA_VARIANT_MMU if XTENSA_VARIANT_CUSTOM
+ def_bool n

config VARIANT_IRQ_SWITCH
def_bool n
@@ -71,9 +69,6 @@ config VARIANT_IRQ_SWITCH
config HAVE_XTENSA_GPIO32
def_bool n

-config MAY_HAVE_SMP
- def_bool n
-
menu "Processor type and features"

choice
@@ -100,7 +95,6 @@ config XTENSA_VARIANT_DC233C

config XTENSA_VARIANT_CUSTOM
bool "Custom Xtensa processor configuration"
- select MAY_HAVE_SMP
select HAVE_XTENSA_GPIO32
help
Select this variant to use a custom Xtensa processor configuration.
@@ -126,6 +120,7 @@ config XTENSA_VARIANT_MMU
bool "Core variant has a Full MMU (TLB, Pages, Protection, etc)"
depends on XTENSA_VARIANT_CUSTOM
default y
+ select MMU
help
Build a Conventional Kernel with full MMU support,
ie: it supports a TLB with auto-loading, page protection.
@@ -143,7 +138,7 @@ source "kernel/Kconfig.preempt"

config HAVE_SMP
bool "System Supports SMP (MX)"
- depends on MAY_HAVE_SMP
+ depends on XTENSA_VARIANT_CUSTOM
select XTENSA_MX
help
This option is use to indicate that the system-on-a-chip (SOC)
--
1.8.1.4

2015-07-06 13:35:32

by Max Filippov

[permalink] [raw]
Subject: [PATCH 2/8] xtensa: keep exception/interrupt stack continuous

Restore original a0 in the kernel exception stack frame. This way it
looks like the frame that got interrupt/exception did alloca (copy a0 and
a1 potentially spilled under old stack to the new location as well) to
save registers and then did a call to handler. The point where
interrupt/exception was taken is not in the stack chain, only in pt_regs
(call4 from that address can be simulated to keep it in the stack trace).

Signed-off-by: Max Filippov <[email protected]>
---
arch/xtensa/kernel/entry.S | 60 +++++++++++++++++++++++++++-------------------
1 file changed, 35 insertions(+), 25 deletions(-)

diff --git a/arch/xtensa/kernel/entry.S b/arch/xtensa/kernel/entry.S
index 82bbfa5..ab6a857 100644
--- a/arch/xtensa/kernel/entry.S
+++ b/arch/xtensa/kernel/entry.S
@@ -218,7 +218,7 @@ _user_exception:
/* We are back to the original stack pointer (a1) */

2: /* Now, jump to the common exception handler. */
-
+ movi a0, 0 # terminate user stack trace with 0
j common_exception

ENDPROC(user_exception)
@@ -264,6 +264,19 @@ ENTRY(kernel_exception)
.globl _kernel_exception
_kernel_exception:

+ /* Copy spill slots of a0 and a1 to imitate movsp
+ * in order to keep exception stack continuous
+ */
+ l32i a0, a1, PT_AREG1
+ addi a2, a1, -16
+ addi a0, a0, -16
+ l32i a3, a0, 0
+ l32i a0, a0, 4
+ s32i a3, a2, 0
+ s32i a0, a2, 4
+
+ l32i a0, a1, PT_AREG0 # restore saved a0
+
/* Save SAR and turn off single stepping */

movi a2, 0
@@ -338,52 +351,50 @@ common_exception:
xsr a2, lcount
s32i a2, a1, PT_LCOUNT

- /* It is now save to restore the EXC_TABLE_FIXUP variable. */
+ /* It is now safe to restore the EXC_TABLE_FIXUP variable. */

- rsr a0, exccause
movi a3, 0
rsr a2, excsave1
- s32i a0, a1, PT_EXCCAUSE
s32i a3, a2, EXC_TABLE_FIXUP

- /* All unrecoverable states are saved on stack, now, and a1 is valid,
- * so we can allow exceptions and interrupts (*) again.
- * Set PS(EXCM = 0, UM = 0, RING = 0, OWB = 0, WOE = 1, INTLEVEL = X)
+ /* Save remaining unrecoverable states (exccause, ps) on stack.
+ * Now we can allow exceptions again. In case we've got an interrupt
+ * PS.INTLEVEL is set to LOCKLEVEL disabling furhter interrupts,
+ * otherwise it's left unchanged.
*
- * (*) We only allow interrupts if they were previously enabled and
- * we're not handling an IRQ
+ * Set PS(EXCM = 0, UM = 0, RING = 0, OWB = 0, WOE = 1, INTLEVEL = X)
*/

rsr a3, ps
- addi a0, a0, -EXCCAUSE_LEVEL1_INTERRUPT
- movi a2, LOCKLEVEL
+ movi a2, (1 << PS_WOE_BIT)
extui a3, a3, PS_INTLEVEL_SHIFT, PS_INTLEVEL_WIDTH
- # a3 = PS.INTLEVEL
- moveqz a3, a2, a0 # a3 = LOCKLEVEL iff interrupt
- movi a2, 1 << PS_WOE_BIT
or a3, a3, a2
- rsr a0, exccause
- xsr a3, ps

+ rsr a2, exccause
+ bnei a2, EXCCAUSE_LEVEL1_INTERRUPT, 1f
+ movi a3, (1 << PS_WOE_BIT) | LOCKLEVEL
+1:
+ xsr a3, ps
+ s32i a2, a1, PT_EXCCAUSE
s32i a3, a1, PT_PS # save ps

/* Save lbeg, lend */

- rsr a2, lbeg
+ rsr a4, lbeg
rsr a3, lend
- s32i a2, a1, PT_LBEG
+ s32i a4, a1, PT_LBEG
s32i a3, a1, PT_LEND

/* Save SCOMPARE1 */

#if XCHAL_HAVE_S32C1I
- rsr a2, scompare1
- s32i a2, a1, PT_SCOMPARE1
+ rsr a3, scompare1
+ s32i a3, a1, PT_SCOMPARE1
#endif

/* Save optional registers. */

- save_xtregs_opt a1 a2 a4 a5 a6 a7 PT_XTREGS_OPT
+ save_xtregs_opt a1 a3 a4 a5 a6 a7 PT_XTREGS_OPT

#ifdef CONFIG_TRACE_IRQFLAGS
l32i a4, a1, PT_DEPC
@@ -391,8 +402,7 @@ common_exception:
* while PS.EXCM was set, i.e. interrupts disabled.
*/
bgeui a4, VALID_DOUBLE_EXCEPTION_ADDRESS, 1f
- l32i a4, a1, PT_EXCCAUSE
- bnei a4, EXCCAUSE_LEVEL1_INTERRUPT, 1f
+ bnei a2, EXCCAUSE_LEVEL1_INTERRUPT, 1f
/* We came here with an interrupt means interrupts were enabled
* and we've just disabled them.
*/
@@ -407,8 +417,8 @@ common_exception:

rsr a4, excsave1
mov a6, a1 # pass stack frame
- mov a7, a0 # pass EXCCAUSE
- addx4 a4, a0, a4
+ mov a7, a2 # pass EXCCAUSE
+ addx4 a4, a2, a4
l32i a4, a4, EXC_TABLE_DEFAULT # load handler

/* Call the second-level handler */
--
1.8.1.4

2015-07-06 13:33:26

by Max Filippov

[permalink] [raw]
Subject: [PATCH 3/8] xtensa: move oprofile stack tracing to stacktrace.c

Old oprofile interface will share user stack tracing with new perf
interface. Move oprofile user/kernel stack tracing to stacktrace.c to
make it possible.

Signed-off-by: Max Filippov <[email protected]>
---
arch/xtensa/include/asm/stacktrace.h | 8 ++
arch/xtensa/kernel/stacktrace.c | 167 ++++++++++++++++++++++++++++++++++-
arch/xtensa/oprofile/backtrace.c | 158 ++-------------------------------
3 files changed, 182 insertions(+), 151 deletions(-)

diff --git a/arch/xtensa/include/asm/stacktrace.h b/arch/xtensa/include/asm/stacktrace.h
index 6a05fcb..fe06e8e 100644
--- a/arch/xtensa/include/asm/stacktrace.h
+++ b/arch/xtensa/include/asm/stacktrace.h
@@ -33,4 +33,12 @@ void walk_stackframe(unsigned long *sp,
int (*fn)(struct stackframe *frame, void *data),
void *data);

+void xtensa_backtrace_kernel(struct pt_regs *regs, unsigned int depth,
+ int (*kfn)(struct stackframe *frame, void *data),
+ int (*ufn)(struct stackframe *frame, void *data),
+ void *data);
+void xtensa_backtrace_user(struct pt_regs *regs, unsigned int depth,
+ int (*ufn)(struct stackframe *frame, void *data),
+ void *data);
+
#endif /* _XTENSA_STACKTRACE_H */
diff --git a/arch/xtensa/kernel/stacktrace.c b/arch/xtensa/kernel/stacktrace.c
index 7d2c317..7538d80 100644
--- a/arch/xtensa/kernel/stacktrace.c
+++ b/arch/xtensa/kernel/stacktrace.c
@@ -1,11 +1,12 @@
/*
- * arch/xtensa/kernel/stacktrace.c
+ * Kernel and userspace stack tracing.
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 2001 - 2013 Tensilica Inc.
+ * Copyright (C) 2015 Cadence Design Systems Inc.
*/
#include <linux/export.h>
#include <linux/sched.h>
@@ -13,6 +14,170 @@

#include <asm/stacktrace.h>
#include <asm/traps.h>
+#include <asm/uaccess.h>
+
+#if IS_ENABLED(CONFIG_OPROFILE) || IS_ENABLED(CONFIG_PERF_EVENTS)
+
+/* Address of common_exception_return, used to check the
+ * transition from kernel to user space.
+ */
+extern int common_exception_return;
+
+/* A struct that maps to the part of the frame containing the a0 and
+ * a1 registers.
+ */
+struct frame_start {
+ unsigned long a0;
+ unsigned long a1;
+};
+
+void xtensa_backtrace_user(struct pt_regs *regs, unsigned int depth,
+ int (*ufn)(struct stackframe *frame, void *data),
+ void *data)
+{
+ unsigned long windowstart = regs->windowstart;
+ unsigned long windowbase = regs->windowbase;
+ unsigned long a0 = regs->areg[0];
+ unsigned long a1 = regs->areg[1];
+ unsigned long pc = regs->pc;
+ struct stackframe frame;
+ int index;
+
+ if (!depth--)
+ return;
+
+ frame.pc = pc;
+ frame.sp = a1;
+
+ if (pc == 0 || pc >= TASK_SIZE || ufn(&frame, data))
+ return;
+
+ /* Two steps:
+ *
+ * 1. Look through the register window for the
+ * previous PCs in the call trace.
+ *
+ * 2. Look on the stack.
+ */
+
+ /* Step 1. */
+ /* Rotate WINDOWSTART to move the bit corresponding to
+ * the current window to the bit #0.
+ */
+ windowstart = (windowstart << WSBITS | windowstart) >> windowbase;
+
+ /* Look for bits that are set, they correspond to
+ * valid windows.
+ */
+ for (index = WSBITS - 1; (index > 0) && depth; depth--, index--)
+ if (windowstart & (1 << index)) {
+ /* Get the PC from a0 and a1. */
+ pc = MAKE_PC_FROM_RA(a0, pc);
+ /* Read a0 and a1 from the
+ * corresponding position in AREGs.
+ */
+ a0 = regs->areg[index * 4];
+ a1 = regs->areg[index * 4 + 1];
+
+ frame.pc = pc;
+ frame.sp = a1;
+
+ if (pc == 0 || pc >= TASK_SIZE || ufn(&frame, data))
+ return;
+ }
+
+ /* Step 2. */
+ /* We are done with the register window, we need to
+ * look through the stack.
+ */
+ if (!depth)
+ return;
+
+ /* Start from the a1 register. */
+ /* a1 = regs->areg[1]; */
+ while (a0 != 0 && depth--) {
+ struct frame_start frame_start;
+ /* Get the location for a1, a0 for the
+ * previous frame from the current a1.
+ */
+ unsigned long *psp = (unsigned long *)a1;
+
+ psp -= 4;
+
+ /* Check if the region is OK to access. */
+ if (!access_ok(VERIFY_READ, psp, sizeof(frame_start)))
+ return;
+ /* Copy a1, a0 from user space stack frame. */
+ if (__copy_from_user_inatomic(&frame_start, psp,
+ sizeof(frame_start)))
+ return;
+
+ pc = MAKE_PC_FROM_RA(a0, pc);
+ a0 = frame_start.a0;
+ a1 = frame_start.a1;
+
+ frame.pc = pc;
+ frame.sp = a1;
+
+ if (pc == 0 || pc >= TASK_SIZE || ufn(&frame, data))
+ return;
+ }
+}
+EXPORT_SYMBOL(xtensa_backtrace_user);
+
+void xtensa_backtrace_kernel(struct pt_regs *regs, unsigned int depth,
+ int (*kfn)(struct stackframe *frame, void *data),
+ int (*ufn)(struct stackframe *frame, void *data),
+ void *data)
+{
+ unsigned long pc = regs->depc > VALID_DOUBLE_EXCEPTION_ADDRESS ?
+ regs->depc : regs->pc;
+ unsigned long sp_start, sp_end;
+ unsigned long a0 = regs->areg[0];
+ unsigned long a1 = regs->areg[1];
+
+ sp_start = a1 & ~(THREAD_SIZE - 1);
+ sp_end = sp_start + THREAD_SIZE;
+
+ /* Spill the register window to the stack first. */
+ spill_registers();
+
+ /* Read the stack frames one by one and create the PC
+ * from the a0 and a1 registers saved there.
+ */
+ while (a1 > sp_start && a1 < sp_end && depth--) {
+ struct stackframe frame;
+ unsigned long *psp = (unsigned long *)a1;
+
+ frame.pc = pc;
+ frame.sp = a1;
+
+ if (kernel_text_address(pc) && kfn(&frame, data))
+ return;
+
+ if (pc == (unsigned long)&common_exception_return) {
+ regs = (struct pt_regs *)a1;
+ if (user_mode(regs)) {
+ if (ufn == NULL)
+ return;
+ xtensa_backtrace_user(regs, depth, ufn, data);
+ return;
+ }
+ a0 = regs->areg[0];
+ a1 = regs->areg[1];
+ continue;
+ }
+
+ sp_start = a1;
+
+ pc = MAKE_PC_FROM_RA(a0, pc);
+ a0 = *(psp - 4);
+ a1 = *(psp - 3);
+ }
+}
+EXPORT_SYMBOL(xtensa_backtrace_kernel);
+
+#endif

void walk_stackframe(unsigned long *sp,
int (*fn)(struct stackframe *frame, void *data),
diff --git a/arch/xtensa/oprofile/backtrace.c b/arch/xtensa/oprofile/backtrace.c
index 5f03a59..8f95203 100644
--- a/arch/xtensa/oprofile/backtrace.c
+++ b/arch/xtensa/oprofile/backtrace.c
@@ -2,168 +2,26 @@
* @file backtrace.c
*
* @remark Copyright 2008 Tensilica Inc.
+ * Copyright (C) 2015 Cadence Design Systems Inc.
* @remark Read the file COPYING
*
*/

#include <linux/oprofile.h>
-#include <linux/sched.h>
-#include <linux/mm.h>
#include <asm/ptrace.h>
-#include <asm/uaccess.h>
-#include <asm/traps.h>
+#include <asm/stacktrace.h>

-/* Address of common_exception_return, used to check the
- * transition from kernel to user space.
- */
-extern int common_exception_return;
-
-/* A struct that maps to the part of the frame containing the a0 and
- * a1 registers.
- */
-struct frame_start {
- unsigned long a0;
- unsigned long a1;
-};
-
-static void xtensa_backtrace_user(struct pt_regs *regs, unsigned int depth)
-{
- unsigned long windowstart = regs->windowstart;
- unsigned long windowbase = regs->windowbase;
- unsigned long a0 = regs->areg[0];
- unsigned long a1 = regs->areg[1];
- unsigned long pc = MAKE_PC_FROM_RA(a0, regs->pc);
- int index;
-
- /* First add the current PC to the trace. */
- if (pc != 0 && pc <= TASK_SIZE)
- oprofile_add_trace(pc);
- else
- return;
-
- /* Two steps:
- *
- * 1. Look through the register window for the
- * previous PCs in the call trace.
- *
- * 2. Look on the stack.
- */
-
- /* Step 1. */
- /* Rotate WINDOWSTART to move the bit corresponding to
- * the current window to the bit #0.
- */
- windowstart = (windowstart << WSBITS | windowstart) >> windowbase;
-
- /* Look for bits that are set, they correspond to
- * valid windows.
- */
- for (index = WSBITS - 1; (index > 0) && depth; depth--, index--)
- if (windowstart & (1 << index)) {
- /* Read a0 and a1 from the
- * corresponding position in AREGs.
- */
- a0 = regs->areg[index * 4];
- a1 = regs->areg[index * 4 + 1];
- /* Get the PC from a0 and a1. */
- pc = MAKE_PC_FROM_RA(a0, pc);
-
- /* Add the PC to the trace. */
- if (pc != 0 && pc <= TASK_SIZE)
- oprofile_add_trace(pc);
- else
- return;
- }
-
- /* Step 2. */
- /* We are done with the register window, we need to
- * look through the stack.
- */
- if (depth > 0) {
- /* Start from the a1 register. */
- /* a1 = regs->areg[1]; */
- while (a0 != 0 && depth--) {
-
- struct frame_start frame_start;
- /* Get the location for a1, a0 for the
- * previous frame from the current a1.
- */
- unsigned long *psp = (unsigned long *)a1;
- psp -= 4;
-
- /* Check if the region is OK to access. */
- if (!access_ok(VERIFY_READ, psp, sizeof(frame_start)))
- return;
- /* Copy a1, a0 from user space stack frame. */
- if (__copy_from_user_inatomic(&frame_start, psp,
- sizeof(frame_start)))
- return;
-
- a0 = frame_start.a0;
- a1 = frame_start.a1;
- pc = MAKE_PC_FROM_RA(a0, pc);
-
- if (pc != 0 && pc <= TASK_SIZE)
- oprofile_add_trace(pc);
- else
- return;
- }
- }
-}
-
-static void xtensa_backtrace_kernel(struct pt_regs *regs, unsigned int depth)
+static int xtensa_backtrace_cb(struct stackframe *frame, void *data)
{
- unsigned long pc = regs->pc;
- unsigned long *psp;
- unsigned long sp_start, sp_end;
- unsigned long a0 = regs->areg[0];
- unsigned long a1 = regs->areg[1];
-
- sp_start = a1 & ~(THREAD_SIZE-1);
- sp_end = sp_start + THREAD_SIZE;
-
- /* Spill the register window to the stack first. */
- spill_registers();
-
- /* Read the stack frames one by one and create the PC
- * from the a0 and a1 registers saved there.
- */
- while (a1 > sp_start && a1 < sp_end && depth--) {
- pc = MAKE_PC_FROM_RA(a0, pc);
-
- /* Add the PC to the trace. */
- oprofile_add_trace(pc);
- if (pc == (unsigned long) &common_exception_return) {
- regs = (struct pt_regs *)a1;
- if (user_mode(regs)) {
- pc = regs->pc;
- if (pc != 0 && pc <= TASK_SIZE)
- oprofile_add_trace(pc);
- else
- return;
- return xtensa_backtrace_user(regs, depth);
- }
- a0 = regs->areg[0];
- a1 = regs->areg[1];
- continue;
- }
-
- psp = (unsigned long *)a1;
-
- a0 = *(psp - 4);
- a1 = *(psp - 3);
-
- if (a1 <= (unsigned long)psp)
- return;
-
- }
- return;
+ oprofile_add_trace(frame->pc);
+ return 0;
}

void xtensa_backtrace(struct pt_regs * const regs, unsigned int depth)
{
if (user_mode(regs))
- xtensa_backtrace_user(regs, depth);
+ xtensa_backtrace_user(regs, depth, xtensa_backtrace_cb, NULL);
else
- xtensa_backtrace_kernel(regs, depth);
+ xtensa_backtrace_kernel(regs, depth, xtensa_backtrace_cb,
+ xtensa_backtrace_cb, NULL);
}
--
1.8.1.4

2015-07-06 13:35:10

by Max Filippov

[permalink] [raw]
Subject: [PATCH 4/8] xtensa: select PERF_USE_VMALLOC for cache-aliasing configurations

Signed-off-by: Max Filippov <[email protected]>
---
arch/xtensa/Kconfig | 1 +
1 file changed, 1 insertion(+)

diff --git a/arch/xtensa/Kconfig b/arch/xtensa/Kconfig
index ba22699..3c57934 100644
--- a/arch/xtensa/Kconfig
+++ b/arch/xtensa/Kconfig
@@ -20,6 +20,7 @@ config XTENSA
select HAVE_PERF_EVENTS
select IRQ_DOMAIN
select MODULES_USE_ELF_RELA
+ select PERF_USE_VMALLOC
select VIRT_TO_BUS
help
Xtensa processors are 32-bit RISC machines designed by Tensilica
--
1.8.1.4

2015-07-06 13:34:46

by Max Filippov

[permalink] [raw]
Subject: [PATCH 5/8] xtensa: add profiling IRQ type to xtensa_irq_map

Signed-off-by: Max Filippov <[email protected]>
---
arch/xtensa/kernel/irq.c | 6 ++++++
1 file changed, 6 insertions(+)

diff --git a/arch/xtensa/kernel/irq.c b/arch/xtensa/kernel/irq.c
index 3eee94f..32b6056 100644
--- a/arch/xtensa/kernel/irq.c
+++ b/arch/xtensa/kernel/irq.c
@@ -106,6 +106,12 @@ int xtensa_irq_map(struct irq_domain *d, unsigned int irq,
irq_set_chip_and_handler_name(irq, irq_chip,
handle_percpu_irq, "timer");
irq_clear_status_flags(irq, IRQ_LEVEL);
+#ifdef XCHAL_INTTYPE_MASK_PROFILING
+ } else if (mask & XCHAL_INTTYPE_MASK_PROFILING) {
+ irq_set_chip_and_handler_name(irq, irq_chip,
+ handle_percpu_irq, "profiling");
+ irq_set_status_flags(irq, IRQ_LEVEL);
+#endif
} else {/* XCHAL_INTTYPE_MASK_WRITE_ERROR */
/* XCHAL_INTTYPE_MASK_NMI */
irq_set_chip_and_handler_name(irq, irq_chip,
--
1.8.1.4

2015-07-06 13:34:12

by Max Filippov

[permalink] [raw]
Subject: [PATCH 6/8] xtensa: count software page fault perf events

Signed-off-by: Max Filippov <[email protected]>
---
arch/xtensa/mm/fault.c | 7 +++++++
1 file changed, 7 insertions(+)

diff --git a/arch/xtensa/mm/fault.c b/arch/xtensa/mm/fault.c
index 9e3571a..76360a2 100644
--- a/arch/xtensa/mm/fault.c
+++ b/arch/xtensa/mm/fault.c
@@ -15,6 +15,7 @@
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/hardirq.h>
+#include <linux/perf_event.h>
#include <asm/mmu_context.h>
#include <asm/cacheflush.h>
#include <asm/hardirq.h>
@@ -142,6 +143,12 @@ good_area:
}

up_read(&mm->mmap_sem);
+ perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address);
+ if (flags & VM_FAULT_MAJOR)
+ perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, regs, address);
+ else if (flags & VM_FAULT_MINOR)
+ perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, regs, address);
+
return;

/* Something tried to access memory that isn't in our memory map..
--
1.8.1.4

2015-07-06 13:33:39

by Max Filippov

[permalink] [raw]
Subject: [PATCH 7/8] xtensa: implement counting and sampling perf events

Xtensa Performance Monitor Module has up to 8 32 bit wide performance
counters. Each counter may be enabled independently and can count any
single type of hardware performance events. Event counting may be enabled
and disabled globally (per PMM).
Each counter has status register with bits indicating if the counter has
been overflown and may be programmed to raise profiling IRQ on overflow.
This IRQ is used to rewind counters and allow for counting more than 2^32
samples for counting events and to report samples for sampling events.

For more details see Tensilica Debug User's Guide, chapter 8
"Performance monitor module".

Cc: Peter Zijlstra <[email protected]>
Cc: Paul Mackerras <[email protected]>
Cc: Ingo Molnar <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Signed-off-by: Max Filippov <[email protected]>
---
arch/xtensa/Kconfig | 10 +
arch/xtensa/kernel/Makefile | 1 +
arch/xtensa/kernel/perf_event.c | 450 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 461 insertions(+)
create mode 100644 arch/xtensa/kernel/perf_event.c

diff --git a/arch/xtensa/Kconfig b/arch/xtensa/Kconfig
index 3c57934..0e92885 100644
--- a/arch/xtensa/Kconfig
+++ b/arch/xtensa/Kconfig
@@ -126,6 +126,16 @@ config XTENSA_VARIANT_MMU
Build a Conventional Kernel with full MMU support,
ie: it supports a TLB with auto-loading, page protection.

+config XTENSA_VARIANT_HAVE_PERF_EVENTS
+ bool "Core variant has Performance Monitor Module"
+ depends on XTENSA_VARIANT_CUSTOM
+ default n
+ help
+ Enable if core variant has Performance Monitor Module with
+ External Registers Interface.
+
+ If unsure, say N.
+
config XTENSA_UNALIGNED_USER
bool "Unaligned memory access in use space"
help
diff --git a/arch/xtensa/kernel/Makefile b/arch/xtensa/kernel/Makefile
index d3a0f0f..547a757 100644
--- a/arch/xtensa/kernel/Makefile
+++ b/arch/xtensa/kernel/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_PCI) += pci.o
obj-$(CONFIG_MODULES) += xtensa_ksyms.o module.o
obj-$(CONFIG_FUNCTION_TRACER) += mcount.o
obj-$(CONFIG_SMP) += smp.o mxhead.o
+obj-$(CONFIG_XTENSA_VARIANT_HAVE_PERF_EVENTS) += perf_event.o

AFLAGS_head.o += -mtext-section-literals

diff --git a/arch/xtensa/kernel/perf_event.c b/arch/xtensa/kernel/perf_event.c
new file mode 100644
index 0000000..0458e0a
--- /dev/null
+++ b/arch/xtensa/kernel/perf_event.c
@@ -0,0 +1,450 @@
+/*
+ * Xtensa Performance Monitor Module driver
+ * See Tensilica Debug User's Guide for PMU registers documentation.
+ *
+ * Copyright (c) 2015 Cadence Design Systems Inc.
+ *
+ * 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/interrupt.h>
+#include <linux/irqdomain.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/perf_event.h>
+#include <linux/platform_device.h>
+
+#include <asm/processor.h>
+#include <asm/stacktrace.h>
+
+/* Global control/status for all perf counters */
+#define XTENSA_PMU_PMG 0x1000
+/* Perf counter values */
+#define XTENSA_PMU_PM(i) (0x1080 + (i) * 4)
+/* Perf counter control registers */
+#define XTENSA_PMU_PMCTRL(i) (0x1100 + (i) * 4)
+/* Perf counter status registers */
+#define XTENSA_PMU_PMSTAT(i) (0x1180 + (i) * 4)
+
+#define XTENSA_PMU_PMG_PMEN 0x1
+
+#define XTENSA_PMU_COUNTER_MASK 0xffffffffULL
+#define XTENSA_PMU_COUNTER_MAX 0x7fffffff
+
+#define XTENSA_PMU_PMCTRL_INTEN 0x00000001
+#define XTENSA_PMU_PMCTRL_KRNLCNT 0x00000008
+#define XTENSA_PMU_PMCTRL_TRACELEVEL 0x000000f0
+#define XTENSA_PMU_PMCTRL_SELECT_SHIFT 8
+#define XTENSA_PMU_PMCTRL_SELECT 0x00001f00
+#define XTENSA_PMU_PMCTRL_MASK_SHIFT 16
+#define XTENSA_PMU_PMCTRL_MASK 0xffff0000
+
+#define XTENSA_PMU_MASK(select, mask) \
+ (((select) << XTENSA_PMU_PMCTRL_SELECT_SHIFT) | \
+ ((mask) << XTENSA_PMU_PMCTRL_MASK_SHIFT) | \
+ XTENSA_PMU_PMCTRL_TRACELEVEL | \
+ XTENSA_PMU_PMCTRL_INTEN)
+
+#define XTENSA_PMU_PMSTAT_OVFL 0x00000001
+#define XTENSA_PMU_PMSTAT_INTASRT 0x00000010
+
+struct xtensa_pmu_events {
+ /* Array of events currently on this core */
+ struct perf_event *event[XCHAL_NUM_PERF_COUNTERS];
+ /* Bitmap of used hardware counters */
+ unsigned long used_mask[BITS_TO_LONGS(XCHAL_NUM_PERF_COUNTERS)];
+};
+static DEFINE_PER_CPU(struct xtensa_pmu_events, xtensa_pmu_events);
+
+static const u32 xtensa_hw_ctl[] = {
+ [PERF_COUNT_HW_CPU_CYCLES] = XTENSA_PMU_MASK(0, 0x1),
+ [PERF_COUNT_HW_INSTRUCTIONS] = XTENSA_PMU_MASK(2, 0xffff),
+ [PERF_COUNT_HW_CACHE_REFERENCES] = XTENSA_PMU_MASK(10, 0x1),
+ [PERF_COUNT_HW_CACHE_MISSES] = XTENSA_PMU_MASK(12, 0x1),
+ /* Taken and non-taken branches + taken loop ends */
+ [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = XTENSA_PMU_MASK(2, 0x490),
+ /* Instruction-related + other global stall cycles */
+ [PERF_COUNT_HW_STALLED_CYCLES_FRONTEND] = XTENSA_PMU_MASK(4, 0x1ff),
+ /* Data-related global stall cycles */
+ [PERF_COUNT_HW_STALLED_CYCLES_BACKEND] = XTENSA_PMU_MASK(3, 0x1ff),
+};
+
+#define C(_x) PERF_COUNT_HW_CACHE_##_x
+
+static const u32 xtensa_cache_ctl[][C(OP_MAX)][C(RESULT_MAX)] = {
+ [C(L1D)] = {
+ [C(OP_READ)] = {
+ [C(RESULT_ACCESS)] = XTENSA_PMU_MASK(10, 0x1),
+ [C(RESULT_MISS)] = XTENSA_PMU_MASK(10, 0x2),
+ },
+ [C(OP_WRITE)] = {
+ [C(RESULT_ACCESS)] = XTENSA_PMU_MASK(11, 0x1),
+ [C(RESULT_MISS)] = XTENSA_PMU_MASK(11, 0x2),
+ },
+ },
+ [C(L1I)] = {
+ [C(OP_READ)] = {
+ [C(RESULT_ACCESS)] = XTENSA_PMU_MASK(8, 0x1),
+ [C(RESULT_MISS)] = XTENSA_PMU_MASK(8, 0x2),
+ },
+ },
+ [C(DTLB)] = {
+ [C(OP_READ)] = {
+ [C(RESULT_ACCESS)] = XTENSA_PMU_MASK(9, 0x1),
+ [C(RESULT_MISS)] = XTENSA_PMU_MASK(9, 0x8),
+ },
+ },
+ [C(ITLB)] = {
+ [C(OP_READ)] = {
+ [C(RESULT_ACCESS)] = XTENSA_PMU_MASK(7, 0x1),
+ [C(RESULT_MISS)] = XTENSA_PMU_MASK(7, 0x8),
+ },
+ },
+};
+
+static int xtensa_pmu_cache_event(u64 config)
+{
+ unsigned int cache_type, cache_op, cache_result;
+ int ret;
+
+ cache_type = (config >> 0) & 0xff;
+ cache_op = (config >> 8) & 0xff;
+ cache_result = (config >> 16) & 0xff;
+
+ if (cache_type >= ARRAY_SIZE(xtensa_cache_ctl) ||
+ cache_op >= C(OP_MAX) ||
+ cache_result >= C(RESULT_MAX))
+ return -EINVAL;
+
+ ret = xtensa_cache_ctl[cache_type][cache_op][cache_result];
+
+ if (ret == 0)
+ return -ENOENT;
+
+ return ret;
+}
+
+static inline uint32_t xtensa_pmu_read_counter(int idx)
+{
+ return get_er(XTENSA_PMU_PM(idx));
+}
+
+static inline void xtensa_pmu_write_counter(int idx, uint32_t v)
+{
+ set_er(v, XTENSA_PMU_PM(idx));
+}
+
+static void xtensa_perf_event_update(struct perf_event *event,
+ struct hw_perf_event *hwc, int idx)
+{
+ uint64_t prev_raw_count, new_raw_count;
+ int64_t delta;
+
+ do {
+ prev_raw_count = local64_read(&hwc->prev_count);
+ new_raw_count = xtensa_pmu_read_counter(event->hw.idx);
+ } while (local64_cmpxchg(&hwc->prev_count, prev_raw_count,
+ new_raw_count) != prev_raw_count);
+
+ delta = (new_raw_count - prev_raw_count) & XTENSA_PMU_COUNTER_MASK;
+
+ local64_add(delta, &event->count);
+ local64_sub(delta, &hwc->period_left);
+}
+
+static bool xtensa_perf_event_set_period(struct perf_event *event,
+ struct hw_perf_event *hwc, int idx)
+{
+ bool rc = false;
+ s64 left;
+
+ if (!is_sampling_event(event)) {
+ left = XTENSA_PMU_COUNTER_MAX;
+ } else {
+ s64 period = hwc->sample_period;
+
+ left = local64_read(&hwc->period_left);
+ if (left <= -period) {
+ left = period;
+ local64_set(&hwc->period_left, left);
+ hwc->last_period = period;
+ rc = true;
+ } else if (left <= 0) {
+ left += period;
+ local64_set(&hwc->period_left, left);
+ hwc->last_period = period;
+ rc = true;
+ }
+ if (left > XTENSA_PMU_COUNTER_MAX)
+ left = XTENSA_PMU_COUNTER_MAX;
+ }
+
+ local64_set(&hwc->prev_count, -left);
+ xtensa_pmu_write_counter(idx, -left);
+ perf_event_update_userpage(event);
+
+ return rc;
+}
+
+static void xtensa_pmu_enable(struct pmu *pmu)
+{
+ set_er(get_er(XTENSA_PMU_PMG) | XTENSA_PMU_PMG_PMEN, XTENSA_PMU_PMG);
+}
+
+static void xtensa_pmu_disable(struct pmu *pmu)
+{
+ set_er(get_er(XTENSA_PMU_PMG) & ~XTENSA_PMU_PMG_PMEN, XTENSA_PMU_PMG);
+}
+
+static int xtensa_pmu_event_init(struct perf_event *event)
+{
+ int ret;
+
+ switch (event->attr.type) {
+ case PERF_TYPE_HARDWARE:
+ if (event->attr.config >= ARRAY_SIZE(xtensa_hw_ctl) ||
+ xtensa_hw_ctl[event->attr.config] == 0)
+ return -ENOENT;
+ event->hw.config = xtensa_hw_ctl[event->attr.config];
+ return 0;
+
+ case PERF_TYPE_HW_CACHE:
+ ret = xtensa_pmu_cache_event(event->attr.config);
+ if (ret < 0)
+ return ret;
+ event->hw.config = ret;
+ return 0;
+
+ case PERF_TYPE_RAW:
+ /* Not 'previous counter' select */
+ if ((event->attr.config & XTENSA_PMU_PMCTRL_SELECT) ==
+ (1 << XTENSA_PMU_PMCTRL_SELECT_SHIFT))
+ return -ENOENT;
+ event->hw.config = (event->attr.config &
+ (XTENSA_PMU_PMCTRL_KRNLCNT |
+ XTENSA_PMU_PMCTRL_TRACELEVEL |
+ XTENSA_PMU_PMCTRL_SELECT |
+ XTENSA_PMU_PMCTRL_MASK)) |
+ XTENSA_PMU_PMCTRL_INTEN;
+ return 0;
+
+ default:
+ return -ENOENT;
+ }
+}
+
+/*
+ * Starts/Stops a counter present on the PMU. The PMI handler
+ * should stop the counter when perf_event_overflow() returns
+ * !0. ->start() will be used to continue.
+ */
+static void xtensa_pmu_start(struct perf_event *event, int flags)
+{
+ struct hw_perf_event *hwc = &event->hw;
+ int idx = hwc->idx;
+
+ if (WARN_ON_ONCE(idx == -1))
+ return;
+
+ if (flags & PERF_EF_RELOAD) {
+ WARN_ON_ONCE(!(event->hw.state & PERF_HES_UPTODATE));
+ xtensa_perf_event_set_period(event, hwc, idx);
+ }
+
+ hwc->state = 0;
+
+ set_er(hwc->config, XTENSA_PMU_PMCTRL(idx));
+}
+
+static void xtensa_pmu_stop(struct perf_event *event, int flags)
+{
+ struct hw_perf_event *hwc = &event->hw;
+ int idx = hwc->idx;
+
+ if (!(hwc->state & PERF_HES_STOPPED)) {
+ set_er(0, XTENSA_PMU_PMCTRL(idx));
+ set_er(get_er(XTENSA_PMU_PMSTAT(idx)),
+ XTENSA_PMU_PMSTAT(idx));
+ hwc->state |= PERF_HES_STOPPED;
+ }
+
+ if ((flags & PERF_EF_UPDATE) &&
+ !(event->hw.state & PERF_HES_UPTODATE)) {
+ xtensa_perf_event_update(event, &event->hw, idx);
+ event->hw.state |= PERF_HES_UPTODATE;
+ }
+}
+
+/*
+ * Adds/Removes a counter to/from the PMU, can be done inside
+ * a transaction, see the ->*_txn() methods.
+ */
+static int xtensa_pmu_add(struct perf_event *event, int flags)
+{
+ struct xtensa_pmu_events *ev = this_cpu_ptr(&xtensa_pmu_events);
+ struct hw_perf_event *hwc = &event->hw;
+ int idx = hwc->idx;
+
+ if (__test_and_set_bit(idx, ev->used_mask)) {
+ idx = find_first_zero_bit(ev->used_mask,
+ XCHAL_NUM_PERF_COUNTERS);
+ if (idx == XCHAL_NUM_PERF_COUNTERS)
+ return -EAGAIN;
+
+ __set_bit(idx, ev->used_mask);
+ hwc->idx = idx;
+ }
+ ev->event[idx] = event;
+
+ hwc->state = PERF_HES_UPTODATE | PERF_HES_STOPPED;
+
+ if (flags & PERF_EF_START)
+ xtensa_pmu_start(event, PERF_EF_RELOAD);
+
+ perf_event_update_userpage(event);
+ return 0;
+}
+
+static void xtensa_pmu_del(struct perf_event *event, int flags)
+{
+ struct xtensa_pmu_events *ev = this_cpu_ptr(&xtensa_pmu_events);
+
+ xtensa_pmu_stop(event, PERF_EF_UPDATE);
+ __clear_bit(event->hw.idx, ev->used_mask);
+ perf_event_update_userpage(event);
+}
+
+static void xtensa_pmu_read(struct perf_event *event)
+{
+ xtensa_perf_event_update(event, &event->hw, event->hw.idx);
+}
+
+static int callchain_trace(struct stackframe *frame, void *data)
+{
+ struct perf_callchain_entry *entry = data;
+
+ perf_callchain_store(entry, frame->pc);
+ return 0;
+}
+
+void perf_callchain_kernel(struct perf_callchain_entry *entry,
+ struct pt_regs *regs)
+{
+ xtensa_backtrace_kernel(regs, PERF_MAX_STACK_DEPTH,
+ callchain_trace, NULL, entry);
+}
+
+void perf_callchain_user(struct perf_callchain_entry *entry,
+ struct pt_regs *regs)
+{
+ xtensa_backtrace_user(regs, PERF_MAX_STACK_DEPTH,
+ callchain_trace, entry);
+}
+
+void perf_event_print_debug(void)
+{
+ unsigned long flags;
+ unsigned i;
+
+ local_irq_save(flags);
+ pr_info("CPU#%d: PMG: 0x%08lx\n", smp_processor_id(),
+ get_er(XTENSA_PMU_PMG));
+ for (i = 0; i < XCHAL_NUM_PERF_COUNTERS; ++i)
+ pr_info("PM%d: 0x%08lx, PMCTRL%d: 0x%08lx, PMSTAT%d: 0x%08lx\n",
+ i, get_er(XTENSA_PMU_PM(i)),
+ i, get_er(XTENSA_PMU_PMCTRL(i)),
+ i, get_er(XTENSA_PMU_PMSTAT(i)));
+ local_irq_restore(flags);
+}
+
+static irqreturn_t xtensa_pmu_irq_handler(int irq, void *dev_id)
+{
+ irqreturn_t rc = IRQ_NONE;
+ struct xtensa_pmu_events *ev = this_cpu_ptr(&xtensa_pmu_events);
+ unsigned i;
+
+ for (i = find_first_bit(ev->used_mask, XCHAL_NUM_PERF_COUNTERS);
+ i < XCHAL_NUM_PERF_COUNTERS;
+ i = find_next_bit(ev->used_mask, XCHAL_NUM_PERF_COUNTERS, i + 1)) {
+ uint32_t v = get_er(XTENSA_PMU_PMSTAT(i));
+ struct perf_event *event = ev->event[i];
+ struct hw_perf_event *hwc = &event->hw;
+ u64 last_period;
+
+ if (!(v & XTENSA_PMU_PMSTAT_OVFL))
+ continue;
+
+ set_er(v, XTENSA_PMU_PMSTAT(i));
+ xtensa_perf_event_update(event, hwc, i);
+ last_period = hwc->last_period;
+ if (xtensa_perf_event_set_period(event, hwc, i)) {
+ struct perf_sample_data data;
+ struct pt_regs *regs = get_irq_regs();
+
+ perf_sample_data_init(&data, 0, last_period);
+ if (perf_event_overflow(event, &data, regs))
+ xtensa_pmu_stop(event, 0);
+ }
+
+ rc = IRQ_HANDLED;
+ }
+ return rc;
+}
+
+static struct pmu xtensa_pmu = {
+ .pmu_enable = xtensa_pmu_enable,
+ .pmu_disable = xtensa_pmu_disable,
+ .event_init = xtensa_pmu_event_init,
+ .add = xtensa_pmu_add,
+ .del = xtensa_pmu_del,
+ .start = xtensa_pmu_start,
+ .stop = xtensa_pmu_stop,
+ .read = xtensa_pmu_read,
+};
+
+static void xtensa_pmu_setup(void)
+{
+ unsigned i;
+
+ set_er(0, XTENSA_PMU_PMG);
+ for (i = 0; i < XCHAL_NUM_PERF_COUNTERS; ++i) {
+ set_er(0, XTENSA_PMU_PMCTRL(i));
+ set_er(get_er(XTENSA_PMU_PMSTAT(i)), XTENSA_PMU_PMSTAT(i));
+ }
+}
+
+static int xtensa_pmu_notifier(struct notifier_block *self,
+ unsigned long action, void *data)
+{
+ switch (action & ~CPU_TASKS_FROZEN) {
+ case CPU_STARTING:
+ xtensa_pmu_setup();
+ break;
+
+ default:
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+static int __init xtensa_pmu_init(void)
+{
+ int ret;
+ int irq = irq_create_mapping(NULL, XCHAL_PROFILING_INTERRUPT);
+
+ perf_cpu_notifier(xtensa_pmu_notifier);
+ ret = request_irq(irq, xtensa_pmu_irq_handler, IRQF_PERCPU,
+ "pmu", NULL);
+ if (ret < 0)
+ return ret;
+
+ ret = perf_pmu_register(&xtensa_pmu, "cpu", PERF_TYPE_RAW);
+ if (ret)
+ free_irq(irq, NULL);
+
+ return ret;
+}
+early_initcall(xtensa_pmu_init);
--
1.8.1.4

2015-07-06 13:33:36

by Max Filippov

[permalink] [raw]
Subject: [PATCH 8/8] perf tools: xtensa: add DWARF register names

Cc: Peter Zijlstra <[email protected]>
Cc: Paul Mackerras <[email protected]>
Cc: Ingo Molnar <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Signed-off-by: Max Filippov <[email protected]>
---
tools/perf/arch/xtensa/Build | 1 +
tools/perf/arch/xtensa/Makefile | 3 +++
tools/perf/arch/xtensa/util/Build | 1 +
tools/perf/arch/xtensa/util/dwarf-regs.c | 25 +++++++++++++++++++++++++
4 files changed, 30 insertions(+)
create mode 100644 tools/perf/arch/xtensa/Build
create mode 100644 tools/perf/arch/xtensa/Makefile
create mode 100644 tools/perf/arch/xtensa/util/Build
create mode 100644 tools/perf/arch/xtensa/util/dwarf-regs.c

diff --git a/tools/perf/arch/xtensa/Build b/tools/perf/arch/xtensa/Build
new file mode 100644
index 0000000..54afe4a
--- /dev/null
+++ b/tools/perf/arch/xtensa/Build
@@ -0,0 +1 @@
+libperf-y += util/
diff --git a/tools/perf/arch/xtensa/Makefile b/tools/perf/arch/xtensa/Makefile
new file mode 100644
index 0000000..7fbca17
--- /dev/null
+++ b/tools/perf/arch/xtensa/Makefile
@@ -0,0 +1,3 @@
+ifndef NO_DWARF
+PERF_HAVE_DWARF_REGS := 1
+endif
diff --git a/tools/perf/arch/xtensa/util/Build b/tools/perf/arch/xtensa/util/Build
new file mode 100644
index 0000000..954e287
--- /dev/null
+++ b/tools/perf/arch/xtensa/util/Build
@@ -0,0 +1 @@
+libperf-$(CONFIG_DWARF) += dwarf-regs.o
diff --git a/tools/perf/arch/xtensa/util/dwarf-regs.c b/tools/perf/arch/xtensa/util/dwarf-regs.c
new file mode 100644
index 0000000..4dba76b
--- /dev/null
+++ b/tools/perf/arch/xtensa/util/dwarf-regs.c
@@ -0,0 +1,25 @@
+/*
+ * Mapping of DWARF debug register numbers into register names.
+ *
+ * Copyright (c) 2015 Cadence Design Systems Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <stddef.h>
+#include <dwarf-regs.h>
+
+#define XTENSA_MAX_REGS 16
+
+const char *xtensa_regs_table[XTENSA_MAX_REGS] = {
+ "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7",
+ "a8", "a9", "a10", "a11", "a12", "a13", "a14", "a15",
+};
+
+const char *get_arch_regstr(unsigned int n)
+{
+ return n < XTENSA_MAX_REGS ? xtensa_regs_table[n] : NULL;
+}
--
1.8.1.4

2015-07-06 13:42:13

by Peter Zijlstra

[permalink] [raw]
Subject: Re: [PATCH 7/8] xtensa: implement counting and sampling perf events

On Mon, Jul 06, 2015 at 04:32:48PM +0300, Max Filippov wrote:
> +static int xtensa_pmu_event_init(struct perf_event *event)
> +{
> + int ret;
> +
> + switch (event->attr.type) {
> + case PERF_TYPE_HARDWARE:
> + if (event->attr.config >= ARRAY_SIZE(xtensa_hw_ctl) ||
> + xtensa_hw_ctl[event->attr.config] == 0)
> + return -ENOENT;
> + event->hw.config = xtensa_hw_ctl[event->attr.config];
> + return 0;
> +
> + case PERF_TYPE_HW_CACHE:
> + ret = xtensa_pmu_cache_event(event->attr.config);
> + if (ret < 0)
> + return ret;
> + event->hw.config = ret;
> + return 0;
> +
> + case PERF_TYPE_RAW:
> + /* Not 'previous counter' select */
> + if ((event->attr.config & XTENSA_PMU_PMCTRL_SELECT) ==
> + (1 << XTENSA_PMU_PMCTRL_SELECT_SHIFT))
> + return -ENOENT;
> + event->hw.config = (event->attr.config &
> + (XTENSA_PMU_PMCTRL_KRNLCNT |
> + XTENSA_PMU_PMCTRL_TRACELEVEL |
> + XTENSA_PMU_PMCTRL_SELECT |
> + XTENSA_PMU_PMCTRL_MASK)) |
> + XTENSA_PMU_PMCTRL_INTEN;
> + return 0;

Since this PMU implements TYPE_HARDWARE/TYPE_HW_CACHE/TYPE_RAW,
returning -ENOENT for those is wrong.

If the configuration is invalid, return -EINVAL.

> +
> + default:
> + return -ENOENT;
> + }
> +}

2015-07-06 13:47:18

by Peter Zijlstra

[permalink] [raw]
Subject: Re: [PATCH 7/8] xtensa: implement counting and sampling perf events

On Mon, Jul 06, 2015 at 04:32:48PM +0300, Max Filippov wrote:
> +static int __init xtensa_pmu_init(void)
> +{
> + int ret;
> + int irq = irq_create_mapping(NULL, XCHAL_PROFILING_INTERRUPT);

Does this platform have interrupt priorities which you can partially
mask in order to create NMI like behaviour?

2015-07-06 13:49:35

by Max Filippov

[permalink] [raw]
Subject: Re: [PATCH 7/8] xtensa: implement counting and sampling perf events

On Mon, Jul 6, 2015 at 4:41 PM, Peter Zijlstra <[email protected]> wrote:
> Since this PMU implements TYPE_HARDWARE/TYPE_HW_CACHE/TYPE_RAW,
> returning -ENOENT for those is wrong.
>
> If the configuration is invalid, return -EINVAL.

Ok.

--
Thanks.
-- Max

2015-07-06 13:56:12

by Max Filippov

[permalink] [raw]
Subject: Re: [PATCH 7/8] xtensa: implement counting and sampling perf events

On Mon, Jul 6, 2015 at 4:47 PM, Peter Zijlstra <[email protected]> wrote:
> On Mon, Jul 06, 2015 at 04:32:48PM +0300, Max Filippov wrote:
>> +static int __init xtensa_pmu_init(void)
>> +{
>> + int ret;
>> + int irq = irq_create_mapping(NULL, XCHAL_PROFILING_INTERRUPT);
>
> Does this platform have interrupt priorities which you can partially
> mask in order to create NMI like behaviour?

Not sure what you mean by "NMI like".
Interrupt priorities are fixed in the current xtensa architecture, and
we can in theory
mask certain level and below, but practically we always mask all low-
and medium-
level interrupts.

Also we currently can't have handlers for high priority interrupts written in C.

--
Thanks.
-- Max

2015-07-06 14:05:11

by Peter Zijlstra

[permalink] [raw]
Subject: Re: [PATCH 7/8] xtensa: implement counting and sampling perf events

On Mon, Jul 06, 2015 at 04:56:09PM +0300, Max Filippov wrote:
> On Mon, Jul 6, 2015 at 4:47 PM, Peter Zijlstra <[email protected]> wrote:
> > On Mon, Jul 06, 2015 at 04:32:48PM +0300, Max Filippov wrote:
> >> +static int __init xtensa_pmu_init(void)
> >> +{
> >> + int ret;
> >> + int irq = irq_create_mapping(NULL, XCHAL_PROFILING_INTERRUPT);
> >
> > Does this platform have interrupt priorities which you can partially
> > mask in order to create NMI like behaviour?
>
> Not sure what you mean by "NMI like".

There's a number of archs where we implement NMIs by having
local_irq_disable() only disable part of the interrupt priority range
and making sure all 'normal' IRQs are mapped in that priority range.

We then map our NMI handlers to a priority above the 'normal' range,
such that these interrupts can indeed happen when interrupts are
'disabled.

See for example:

b4f4372f96e0 ("sparc64: Make %pil level 15 a pseudo-NMI.")
0c25e9e6cbe7 ("sparc64: Adjust __raw_local_irq_save() to cooperate in NMIs.")
c011f80ba091 ("sparc64: Add some more commentary to __raw_local_irq_save()")

> Interrupt priorities are fixed in the current xtensa architecture, and
> we can in theory mask certain level and below, but practically we
> always mask all low- and medium- level interrupts.
>
> Also we currently can't have handlers for high priority interrupts written in C.

Why not? Surely this can be cured with an assembly stub?

The advantage of having NMIs is that profiling information for the
kernel becomes much more useful. Without this local_irq_enable() will be
a very 'hot' function.

2015-07-06 14:22:36

by Max Filippov

[permalink] [raw]
Subject: Re: [PATCH 7/8] xtensa: implement counting and sampling perf events

On Mon, Jul 6, 2015 at 5:05 PM, Peter Zijlstra <[email protected]> wrote:
> On Mon, Jul 06, 2015 at 04:56:09PM +0300, Max Filippov wrote:
>> On Mon, Jul 6, 2015 at 4:47 PM, Peter Zijlstra <[email protected]> wrote:
>> > On Mon, Jul 06, 2015 at 04:32:48PM +0300, Max Filippov wrote:
>> >> +static int __init xtensa_pmu_init(void)
>> >> +{
>> >> + int ret;
>> >> + int irq = irq_create_mapping(NULL, XCHAL_PROFILING_INTERRUPT);
>> >
>> > Does this platform have interrupt priorities which you can partially
>> > mask in order to create NMI like behaviour?
>>
>> Not sure what you mean by "NMI like".
>
> There's a number of archs where we implement NMIs by having
> local_irq_disable() only disable part of the interrupt priority range
> and making sure all 'normal' IRQs are mapped in that priority range.
>
> We then map our NMI handlers to a priority above the 'normal' range,
> such that these interrupts can indeed happen when interrupts are
> 'disabled.
>
> See for example:
>
> b4f4372f96e0 ("sparc64: Make %pil level 15 a pseudo-NMI.")
> 0c25e9e6cbe7 ("sparc64: Adjust __raw_local_irq_save() to cooperate in NMIs.")
> c011f80ba091 ("sparc64: Add some more commentary to __raw_local_irq_save()")

Ok, I see. I guess I can change IRQ disabling logic to not mask perf IRQ
in case it's configured as the only interrupt on its level and it's the highest
medium-level IRQ.

>> Interrupt priorities are fixed in the current xtensa architecture, and
>> we can in theory mask certain level and below, but practically we
>> always mask all low- and medium- level interrupts.
>>
>> Also we currently can't have handlers for high priority interrupts written in C.
>
> Why not? Surely this can be cured with an assembly stub?

IIUC that was a deliberate architecture design choice and working around
it penalizes all interrupt handlers. But let me take another close look.

> The advantage of having NMIs is that profiling information for the
> kernel becomes much more useful. Without this local_irq_enable() will be
> a very 'hot' function.

I haven't noticed that in my testing.

--
Thanks.
-- Max

2015-07-07 03:57:03

by Chris Zankel

[permalink] [raw]
Subject: Re: [PATCH 7/8] xtensa: implement counting and sampling perf events

Hi Max,

We could probably still use NMI with a separate stack. However, for
exception handling while in NMI, we might have to implement something
similar to x86_64 (https://lwn.net/Articles/484932/).

Cheers!
-Chris

On Mon, Jul 6, 2015 at 7:22 AM, Max Filippov <[email protected]> wrote:
> On Mon, Jul 6, 2015 at 5:05 PM, Peter Zijlstra <[email protected]> wrote:
>> On Mon, Jul 06, 2015 at 04:56:09PM +0300, Max Filippov wrote:
>>> On Mon, Jul 6, 2015 at 4:47 PM, Peter Zijlstra <[email protected]> wrote:
>>> > On Mon, Jul 06, 2015 at 04:32:48PM +0300, Max Filippov wrote:
>>> >> +static int __init xtensa_pmu_init(void)
>>> >> +{
>>> >> + int ret;
>>> >> + int irq = irq_create_mapping(NULL, XCHAL_PROFILING_INTERRUPT);
>>> >
>>> > Does this platform have interrupt priorities which you can partially
>>> > mask in order to create NMI like behaviour?
>>>
>>> Not sure what you mean by "NMI like".
>>
>> There's a number of archs where we implement NMIs by having
>> local_irq_disable() only disable part of the interrupt priority range
>> and making sure all 'normal' IRQs are mapped in that priority range.
>>
>> We then map our NMI handlers to a priority above the 'normal' range,
>> such that these interrupts can indeed happen when interrupts are
>> 'disabled.
>>
>> See for example:
>>
>> b4f4372f96e0 ("sparc64: Make %pil level 15 a pseudo-NMI.")
>> 0c25e9e6cbe7 ("sparc64: Adjust __raw_local_irq_save() to cooperate in NMIs.")
>> c011f80ba091 ("sparc64: Add some more commentary to __raw_local_irq_save()")
>
> Ok, I see. I guess I can change IRQ disabling logic to not mask perf IRQ
> in case it's configured as the only interrupt on its level and it's the highest
> medium-level IRQ.
>
>>> Interrupt priorities are fixed in the current xtensa architecture, and
>>> we can in theory mask certain level and below, but practically we
>>> always mask all low- and medium- level interrupts.
>>>
>>> Also we currently can't have handlers for high priority interrupts written in C.
>>
>> Why not? Surely this can be cured with an assembly stub?
>
> IIUC that was a deliberate architecture design choice and working around
> it penalizes all interrupt handlers. But let me take another close look.
>
>> The advantage of having NMIs is that profiling information for the
>> kernel becomes much more useful. Without this local_irq_enable() will be
>> a very 'hot' function.
>
> I haven't noticed that in my testing.
>
> --
> Thanks.
> -- Max

2015-07-07 09:18:55

by Paul Bolle

[permalink] [raw]
Subject: Re: [PATCH 1/8] xtensa: clean up Kconfig dependencies for custom cores

On ma, 2015-07-06 at 16:32 +0300, Max Filippov wrote:
> --- a/arch/xtensa/Kconfig
> +++ b/arch/xtensa/Kconfig

> config MMU
> - bool
> - default n if !XTENSA_VARIANT_CUSTOM
> - default XTENSA_VARIANT_MMU if XTENSA_VARIANT_CUSTOM
> + def_bool n

Doesn't
config MMU
bool

(ie, drop the 'n' default) work just as well?


Paul Bolle

2015-07-07 18:47:46

by Chris Zankel

[permalink] [raw]
Subject: Re: [PATCH 2/8] xtensa: keep exception/interrupt stack continuous

Hi Max,

Not such a big fan about these changes as they introduce additional
load and stores and a branch.

Copy spill area:
- is this only for debugging? Could the debugger identify the 'kernel
exception' frame and handle it appropriately?
- if we need it for debugging/perf, maybe move these line under a
kernel option (could be enabled by default)
- since we know the kernel stack frame size, why can't we just load
from that offset (instead of the l32i and add). Also, can't we use
s32e (need to look up the spec again).

Branch:
- is there any logical change in the code or are these only to avoid using a0?
- if for a0, couldn't we load a0 just before the 'xsr a3, ps' and keep
the code as before?

Thanks,
-Chris

On Mon, Jul 6, 2015 at 6:32 AM, Max Filippov <[email protected]> wrote:
> Restore original a0 in the kernel exception stack frame. This way it
> looks like the frame that got interrupt/exception did alloca (copy a0 and
> a1 potentially spilled under old stack to the new location as well) to
> save registers and then did a call to handler. The point where
> interrupt/exception was taken is not in the stack chain, only in pt_regs
> (call4 from that address can be simulated to keep it in the stack trace).
>
> Signed-off-by: Max Filippov <[email protected]>
> ---
> arch/xtensa/kernel/entry.S | 60 +++++++++++++++++++++++++++-------------------
> 1 file changed, 35 insertions(+), 25 deletions(-)
>
> diff --git a/arch/xtensa/kernel/entry.S b/arch/xtensa/kernel/entry.S
> index 82bbfa5..ab6a857 100644
> --- a/arch/xtensa/kernel/entry.S
> +++ b/arch/xtensa/kernel/entry.S
> @@ -218,7 +218,7 @@ _user_exception:
> /* We are back to the original stack pointer (a1) */
>
> 2: /* Now, jump to the common exception handler. */
> -
> + movi a0, 0 # terminate user stack trace with 0
> j common_exception
>
> ENDPROC(user_exception)
> @@ -264,6 +264,19 @@ ENTRY(kernel_exception)
> .globl _kernel_exception
> _kernel_exception:
>
> + /* Copy spill slots of a0 and a1 to imitate movsp
> + * in order to keep exception stack continuous
> + */
> + l32i a0, a1, PT_AREG1
> + addi a2, a1, -16
> + addi a0, a0, -16
> + l32i a3, a0, 0
> + l32i a0, a0, 4
> + s32i a3, a2, 0
> + s32i a0, a2, 4
> +
> + l32i a0, a1, PT_AREG0 # restore saved a0
> +
> /* Save SAR and turn off single stepping */
>
> movi a2, 0
> @@ -338,52 +351,50 @@ common_exception:
> xsr a2, lcount
> s32i a2, a1, PT_LCOUNT
>
> - /* It is now save to restore the EXC_TABLE_FIXUP variable. */
> + /* It is now safe to restore the EXC_TABLE_FIXUP variable. */
>
> - rsr a0, exccause
> movi a3, 0
> rsr a2, excsave1
> - s32i a0, a1, PT_EXCCAUSE
> s32i a3, a2, EXC_TABLE_FIXUP
>
> - /* All unrecoverable states are saved on stack, now, and a1 is valid,
> - * so we can allow exceptions and interrupts (*) again.
> - * Set PS(EXCM = 0, UM = 0, RING = 0, OWB = 0, WOE = 1, INTLEVEL = X)
> + /* Save remaining unrecoverable states (exccause, ps) on stack.
> + * Now we can allow exceptions again. In case we've got an interrupt
> + * PS.INTLEVEL is set to LOCKLEVEL disabling furhter interrupts,
> + * otherwise it's left unchanged.
> *
> - * (*) We only allow interrupts if they were previously enabled and
> - * we're not handling an IRQ
> + * Set PS(EXCM = 0, UM = 0, RING = 0, OWB = 0, WOE = 1, INTLEVEL = X)
> */
>
> rsr a3, ps
> - addi a0, a0, -EXCCAUSE_LEVEL1_INTERRUPT
> - movi a2, LOCKLEVEL
> + movi a2, (1 << PS_WOE_BIT)
> extui a3, a3, PS_INTLEVEL_SHIFT, PS_INTLEVEL_WIDTH
> - # a3 = PS.INTLEVEL
> - moveqz a3, a2, a0 # a3 = LOCKLEVEL iff interrupt
> - movi a2, 1 << PS_WOE_BIT
> or a3, a3, a2
> - rsr a0, exccause
> - xsr a3, ps
>
> + rsr a2, exccause
> + bnei a2, EXCCAUSE_LEVEL1_INTERRUPT, 1f
> + movi a3, (1 << PS_WOE_BIT) | LOCKLEVEL
> +1:
> + xsr a3, ps
> + s32i a2, a1, PT_EXCCAUSE
> s32i a3, a1, PT_PS # save ps
>
> /* Save lbeg, lend */
>
> - rsr a2, lbeg
> + rsr a4, lbeg
> rsr a3, lend
> - s32i a2, a1, PT_LBEG
> + s32i a4, a1, PT_LBEG
> s32i a3, a1, PT_LEND
>
> /* Save SCOMPARE1 */
>
> #if XCHAL_HAVE_S32C1I
> - rsr a2, scompare1
> - s32i a2, a1, PT_SCOMPARE1
> + rsr a3, scompare1
> + s32i a3, a1, PT_SCOMPARE1
> #endif
>
> /* Save optional registers. */
>
> - save_xtregs_opt a1 a2 a4 a5 a6 a7 PT_XTREGS_OPT
> + save_xtregs_opt a1 a3 a4 a5 a6 a7 PT_XTREGS_OPT
>
> #ifdef CONFIG_TRACE_IRQFLAGS
> l32i a4, a1, PT_DEPC
> @@ -391,8 +402,7 @@ common_exception:
> * while PS.EXCM was set, i.e. interrupts disabled.
> */
> bgeui a4, VALID_DOUBLE_EXCEPTION_ADDRESS, 1f
> - l32i a4, a1, PT_EXCCAUSE
> - bnei a4, EXCCAUSE_LEVEL1_INTERRUPT, 1f
> + bnei a2, EXCCAUSE_LEVEL1_INTERRUPT, 1f
> /* We came here with an interrupt means interrupts were enabled
> * and we've just disabled them.
> */
> @@ -407,8 +417,8 @@ common_exception:
>
> rsr a4, excsave1
> mov a6, a1 # pass stack frame
> - mov a7, a0 # pass EXCCAUSE
> - addx4 a4, a0, a4
> + mov a7, a2 # pass EXCCAUSE
> + addx4 a4, a2, a4
> l32i a4, a4, EXC_TABLE_DEFAULT # load handler
>
> /* Call the second-level handler */
> --
> 1.8.1.4
>

2015-07-07 19:46:39

by Max Filippov

[permalink] [raw]
Subject: Re: [PATCH 2/8] xtensa: keep exception/interrupt stack continuous

Hi Chris,

On Tue, Jul 7, 2015 at 9:47 PM, Chris Zankel <[email protected]> wrote:
> Not such a big fan about these changes as they introduce additional
> load and stores and a branch.
>
> Copy spill area:
> - is this only for debugging? Could the debugger identify the 'kernel
> exception' frame and handle it appropriately?

For the debugger and all in-kernel stack tracers: perf, ftrace, oprofile.
I'm not sure how the kernel exception frame can be identified,
especially for the nested kernel re-entries.

> - if we need it for debugging/perf, maybe move these line under a
> kernel option (could be enabled by default)

Ok.

> - since we know the kernel stack frame size, why can't we just load
> from that offset (instead of the l32i and add). Also, can't we use
> s32e (need to look up the spec again).

Oh, right, l32e and s32e make perfect sense here, I thought about
it, but somehow I was under impression we could get here from
userspace and CRING would be wrong for the kernel stack. We
obviously can't.

> Branch:
> - is there any logical change in the code or are these only to avoid using a0?

Yes.

> - if for a0, couldn't we load a0 just before the 'xsr a3, ps' and keep
> the code as before?

Sure.

--
Thanks.
-- Max

2015-07-07 19:47:47

by Max Filippov

[permalink] [raw]
Subject: Re: [PATCH 2/8] xtensa: keep exception/interrupt stack continuous

On Tue, Jul 7, 2015 at 10:46 PM, Max Filippov <[email protected]> wrote:
>> Branch:
>> - is there any logical change in the code or are these only to avoid using a0?
>
> Yes.

Meant the latter.

--
Thanks.
-- Max