aboutsummaryrefslogtreecommitdiff
path: root/arch/s390
diff options
context:
space:
mode:
Diffstat (limited to 'arch/s390')
-rw-r--r--arch/s390/Kconfig1
-rw-r--r--arch/s390/include/asm/ftrace.h10
-rw-r--r--arch/s390/kernel/Makefile6
-rw-r--r--arch/s390/kernel/ftrace.c72
-rw-r--r--arch/s390/kernel/mcount.S79
-rw-r--r--arch/s390/kernel/s390_ext.c3
-rw-r--r--arch/s390/kernel/time.c2
-rw-r--r--arch/s390/kernel/vmlinux.lds.S1
8 files changed, 164 insertions, 10 deletions
diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig
index 480590f2157..9023cc900bd 100644
--- a/arch/s390/Kconfig
+++ b/arch/s390/Kconfig
@@ -85,6 +85,7 @@ config S390
select HAVE_FUNCTION_TRACE_MCOUNT_TEST
select HAVE_FTRACE_MCOUNT_RECORD
select HAVE_DYNAMIC_FTRACE
+ select HAVE_FUNCTION_GRAPH_TRACER
select HAVE_DEFAULT_NO_SPIN_MUTEXES
select HAVE_OPROFILE
select HAVE_KPROBES
diff --git a/arch/s390/include/asm/ftrace.h b/arch/s390/include/asm/ftrace.h
index ba23d8f97d0..96c14a9102b 100644
--- a/arch/s390/include/asm/ftrace.h
+++ b/arch/s390/include/asm/ftrace.h
@@ -11,11 +11,13 @@ struct dyn_arch_ftrace { };
#define MCOUNT_ADDR ((long)_mcount)
#ifdef CONFIG_64BIT
-#define MCOUNT_INSN_SIZE 24
-#define MCOUNT_OFFSET 14
+#define MCOUNT_OFFSET_RET 18
+#define MCOUNT_INSN_SIZE 24
+#define MCOUNT_OFFSET 14
#else
-#define MCOUNT_INSN_SIZE 30
-#define MCOUNT_OFFSET 8
+#define MCOUNT_OFFSET_RET 26
+#define MCOUNT_INSN_SIZE 30
+#define MCOUNT_OFFSET 8
#endif
static inline unsigned long ftrace_call_adjust(unsigned long addr)
diff --git a/arch/s390/kernel/Makefile b/arch/s390/kernel/Makefile
index ce172bfaab8..c75ed43b1a1 100644
--- a/arch/s390/kernel/Makefile
+++ b/arch/s390/kernel/Makefile
@@ -3,11 +3,8 @@
#
ifdef CONFIG_FUNCTION_TRACER
-# Do not trace early boot code
+# Don't trace early setup code and tracing code
CFLAGS_REMOVE_early.o = -pg
-endif
-
-ifdef CONFIG_DYNAMIC_FTRACE
CFLAGS_REMOVE_ftrace.o = -pg
endif
@@ -46,6 +43,7 @@ obj-$(CONFIG_STACKTRACE) += stacktrace.o
obj-$(CONFIG_KPROBES) += kprobes.o
obj-$(CONFIG_FUNCTION_TRACER) += mcount.o
obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o
+obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o
# Kexec part
S390_KEXEC_OBJS := machine_kexec.o crash.o
diff --git a/arch/s390/kernel/ftrace.c b/arch/s390/kernel/ftrace.c
index 0b81a784e03..c92a1095327 100644
--- a/arch/s390/kernel/ftrace.c
+++ b/arch/s390/kernel/ftrace.c
@@ -7,13 +7,17 @@
*
*/
+#include <linux/hardirq.h>
#include <linux/uaccess.h>
#include <linux/ftrace.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <asm/lowcore.h>
+#ifdef CONFIG_DYNAMIC_FTRACE
+
void ftrace_disable_code(void);
+void ftrace_disable_return(void);
void ftrace_call_code(void);
void ftrace_nop_code(void);
@@ -28,6 +32,7 @@ asm(
" .word 0x0024\n"
" lg %r1,"__stringify(__LC_FTRACE_FUNC)"\n"
" basr %r14,%r1\n"
+ "ftrace_disable_return:\n"
" lg %r14,8(15)\n"
" lgr %r0,%r0\n"
"0:\n");
@@ -50,6 +55,7 @@ asm(
" j 0f\n"
" l %r1,"__stringify(__LC_FTRACE_FUNC)"\n"
" basr %r14,%r1\n"
+ "ftrace_disable_return:\n"
" l %r14,4(%r15)\n"
" j 0f\n"
" bcr 0,%r7\n"
@@ -130,3 +136,69 @@ int __init ftrace_dyn_arch_init(void *data)
*(unsigned long *)data = 0;
return 0;
}
+
+#endif /* CONFIG_DYNAMIC_FTRACE */
+
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+#ifdef CONFIG_DYNAMIC_FTRACE
+/*
+ * Patch the kernel code at ftrace_graph_caller location:
+ * The instruction there is branch relative on condition. The condition mask
+ * is either all ones (always branch aka disable ftrace_graph_caller) or all
+ * zeroes (nop aka enable ftrace_graph_caller).
+ * Instruction format for brc is a7m4xxxx where m is the condition mask.
+ */
+int ftrace_enable_ftrace_graph_caller(void)
+{
+ unsigned short opcode = 0xa704;
+
+ return probe_kernel_write(ftrace_graph_caller, &opcode, sizeof(opcode));
+}
+
+int ftrace_disable_ftrace_graph_caller(void)
+{
+ unsigned short opcode = 0xa7f4;
+
+ return probe_kernel_write(ftrace_graph_caller, &opcode, sizeof(opcode));
+}
+
+static inline unsigned long ftrace_mcount_call_adjust(unsigned long addr)
+{
+ return addr - (ftrace_disable_return - ftrace_disable_code);
+}
+
+#else /* CONFIG_DYNAMIC_FTRACE */
+
+static inline unsigned long ftrace_mcount_call_adjust(unsigned long addr)
+{
+ return addr - MCOUNT_OFFSET_RET;
+}
+
+#endif /* CONFIG_DYNAMIC_FTRACE */
+
+/*
+ * Hook the return address and push it in the stack of return addresses
+ * in current thread info.
+ */
+unsigned long prepare_ftrace_return(unsigned long ip, unsigned long parent)
+{
+ struct ftrace_graph_ent trace;
+
+ /* Nmi's are currently unsupported. */
+ if (unlikely(in_nmi()))
+ goto out;
+ if (unlikely(atomic_read(&current->tracing_graph_pause)))
+ goto out;
+ if (ftrace_push_return_trace(parent, ip, &trace.depth) == -EBUSY)
+ goto out;
+ trace.func = ftrace_mcount_call_adjust(ip) & PSW_ADDR_INSN;
+ /* Only trace if the calling function expects to. */
+ if (!ftrace_graph_entry(&trace)) {
+ current->curr_ret_stack--;
+ goto out;
+ }
+ parent = (unsigned long)return_to_handler;
+out:
+ return parent;
+}
+#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
diff --git a/arch/s390/kernel/mcount.S b/arch/s390/kernel/mcount.S
index 0aa85ec94d0..2a0a5e97ba8 100644
--- a/arch/s390/kernel/mcount.S
+++ b/arch/s390/kernel/mcount.S
@@ -34,6 +34,18 @@ ftrace_caller:
larl %r14,ftrace_dyn_func
lg %r14,0(%r14)
basr %r14,%r14
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+ .globl ftrace_graph_caller
+ftrace_graph_caller:
+ # This unconditional branch gets runtime patched. Change only if
+ # you know what you are doing. See ftrace_enable_graph_caller().
+ j 0f
+ lg %r2,272(%r15)
+ lg %r3,168(%r15)
+ brasl %r14,prepare_ftrace_return
+ stg %r2,168(%r15)
+0:
+#endif
aghi %r15,160
lmg %r2,%r5,32(%r15)
lg %r14,112(%r15)
@@ -62,6 +74,12 @@ _mcount:
larl %r14,ftrace_trace_function
lg %r14,0(%r14)
basr %r14,%r14
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+ lg %r2,272(%r15)
+ lg %r3,168(%r15)
+ brasl %r14,prepare_ftrace_return
+ stg %r2,168(%r15)
+#endif
aghi %r15,160
lmg %r2,%r5,32(%r15)
lg %r14,112(%r15)
@@ -69,6 +87,22 @@ _mcount:
#endif /* CONFIG_DYNAMIC_FTRACE */
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+
+ .globl return_to_handler
+return_to_handler:
+ stmg %r2,%r5,32(%r15)
+ lgr %r1,%r15
+ aghi %r15,-160
+ stg %r1,__SF_BACKCHAIN(%r15)
+ brasl %r14,ftrace_return_to_handler
+ aghi %r15,160
+ lgr %r14,%r2
+ lmg %r2,%r5,32(%r15)
+ br %r14
+
+#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
+
#else /* CONFIG_64BIT */
#ifdef CONFIG_DYNAMIC_FTRACE
@@ -96,6 +130,21 @@ ftrace_caller:
l %r14,0b-0b(%r1)
l %r14,0(%r14)
basr %r14,%r14
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+ .globl ftrace_graph_caller
+ftrace_graph_caller:
+ # This unconditional branch gets runtime patched. Change only if
+ # you know what you are doing. See ftrace_enable_graph_caller().
+ j 1f
+ bras %r1,0f
+ .long prepare_ftrace_return
+0: l %r2,152(%r15)
+ l %r4,0(%r1)
+ l %r3,100(%r15)
+ basr %r14,%r4
+ st %r2,100(%r15)
+1:
+#endif
ahi %r15,96
l %r14,56(%r15)
3: lm %r2,%r5,16(%r15)
@@ -128,10 +177,40 @@ _mcount:
l %r14,0b-0b(%r1)
l %r14,0(%r14)
basr %r14,%r14
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+ bras %r1,0f
+ .long prepare_ftrace_return
+0: l %r2,152(%r15)
+ l %r4,0(%r1)
+ l %r3,100(%r15)
+ basr %r14,%r4
+ st %r2,100(%r15)
+#endif
ahi %r15,96
l %r14,56(%r15)
3: lm %r2,%r5,16(%r15)
br %r14
#endif /* CONFIG_DYNAMIC_FTRACE */
+
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+
+ .globl return_to_handler
+return_to_handler:
+ stm %r2,%r5,16(%r15)
+ st %r14,56(%r15)
+ lr %r0,%r15
+ ahi %r15,-96
+ st %r0,__SF_BACKCHAIN(%r15)
+ bras %r1,0f
+ .long ftrace_return_to_handler
+0: l %r2,0b-0b(%r1)
+ basr %r14,%r2
+ lr %r14,%r2
+ ahi %r15,96
+ lm %r2,%r5,16(%r15)
+ br %r14
+
+#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
+
#endif /* CONFIG_64BIT */
diff --git a/arch/s390/kernel/s390_ext.c b/arch/s390/kernel/s390_ext.c
index 6b0686d78fc..0de305b598c 100644
--- a/arch/s390/kernel/s390_ext.c
+++ b/arch/s390/kernel/s390_ext.c
@@ -10,6 +10,7 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
+#include <linux/ftrace.h>
#include <linux/errno.h>
#include <linux/kernel_stat.h>
#include <linux/interrupt.h>
@@ -112,7 +113,7 @@ int unregister_early_external_interrupt(__u16 code, ext_int_handler_t handler,
return 0;
}
-void do_extint(struct pt_regs *regs, unsigned short code)
+void __irq_entry do_extint(struct pt_regs *regs, unsigned short code)
{
ext_int_info_t *p;
int index;
diff --git a/arch/s390/kernel/time.c b/arch/s390/kernel/time.c
index ad9a999aaa9..215330a2c12 100644
--- a/arch/s390/kernel/time.c
+++ b/arch/s390/kernel/time.c
@@ -70,7 +70,7 @@ static DEFINE_PER_CPU(struct clock_event_device, comparators);
/*
* Scheduler clock - returns current time in nanosec units.
*/
-unsigned long long sched_clock(void)
+unsigned long long notrace sched_clock(void)
{
return ((get_clock_xt() - sched_clock_base_cc) * 125) >> 9;
}
diff --git a/arch/s390/kernel/vmlinux.lds.S b/arch/s390/kernel/vmlinux.lds.S
index 89399b8756c..a53db23ee09 100644
--- a/arch/s390/kernel/vmlinux.lds.S
+++ b/arch/s390/kernel/vmlinux.lds.S
@@ -34,6 +34,7 @@ SECTIONS
SCHED_TEXT
LOCK_TEXT
KPROBES_TEXT
+ IRQENTRY_TEXT
*(.fixup)
*(.gnu.warning)
} :text = 0x0700