Add support for DYNAMIC_FTRACE_WITH_REGS to 64-bit and 31-bit s390
architectures. This is required for kGraft and kpatch to work on s390.

It's done by adding a _regs variant of ftrace_caller that preserves
registers and puts them on stack in a struct pt_regs layout and
allows modification of return address by changing the PSW (instruction
pointer) member od struct pt_regs. 

Signed-off-by: Vojtech Pavlik <vojt...@suse.cz>
Reviewed-by: Jiri Kosina <jkos...@suse.cz>
Cc: Steven Rostedt <rost...@goodmis.org>

---

 arch/s390/Kconfig               |    1 
 arch/s390/include/asm/ftrace.h  |    4 ++
 arch/s390/include/asm/lowcore.h |   12 ++++---
 arch/s390/kernel/asm-offsets.c  |    1 
 arch/s390/kernel/early.c        |    3 +
 arch/s390/kernel/ftrace.c       |   68 +++++++++++++++++++++++++++++++++++-----
 arch/s390/kernel/mcount.S       |   43 +++++++++++++++++++++++++
 arch/s390/kernel/mcount64.S     |   38 ++++++++++++++++++++++
 arch/s390/kernel/setup.c        |    1 
 arch/s390/kernel/smp.c          |    1 
 include/linux/ftrace.h          |    1 
 11 files changed, 160 insertions(+), 13 deletions(-)

diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig
index bb63499..1de14d3 100644
--- a/arch/s390/Kconfig
+++ b/arch/s390/Kconfig
@@ -113,6 +113,7 @@ config S390
        select HAVE_C_RECORDMCOUNT
        select HAVE_DEBUG_KMEMLEAK
        select HAVE_DYNAMIC_FTRACE
+       select HAVE_DYNAMIC_FTRACE_WITH_REGS
        select HAVE_FTRACE_MCOUNT_RECORD
        select HAVE_FUNCTION_GRAPH_TRACER
        select HAVE_FUNCTION_TRACER
diff --git a/arch/s390/include/asm/ftrace.h b/arch/s390/include/asm/ftrace.h
index bf246da..df87fd2 100644
--- a/arch/s390/include/asm/ftrace.h
+++ b/arch/s390/include/asm/ftrace.h
@@ -17,6 +17,10 @@ static inline unsigned long ftrace_call_adjust(unsigned long 
addr)
 
 #endif /* __ASSEMBLY__ */
 
+#ifdef CONFIG_DYNAMIC_FTRACE
+#define ARCH_SUPPORTS_FTRACE_OPS 1
+#endif
+
 #ifdef CONFIG_64BIT
 #define MCOUNT_INSN_SIZE  12
 #else
diff --git a/arch/s390/include/asm/lowcore.h b/arch/s390/include/asm/lowcore.h
index 4349197..31b064a 100644
--- a/arch/s390/include/asm/lowcore.h
+++ b/arch/s390/include/asm/lowcore.h
@@ -142,9 +142,10 @@ struct _lowcore {
        __u32   percpu_offset;                  /* 0x02f0 */
        __u32   machine_flags;                  /* 0x02f4 */
        __u32   ftrace_func;                    /* 0x02f8 */
-       __u32   spinlock_lockval;               /* 0x02fc */
+       __u32   ftrace_regs_func;               /* 0x02fc */
+       __u32   spinlock_lockval;               /* 0x0300 */
 
-       __u8    pad_0x0300[0x0e00-0x0300];      /* 0x0300 */
+       __u8    pad_0x0304[0x0e00-0x0304];      /* 0x0304 */
 
        /*
         * 0xe00 contains the address of the IPL Parameter Information
@@ -287,9 +288,10 @@ struct _lowcore {
        __u64   vdso_per_cpu_data;              /* 0x0380 */
        __u64   machine_flags;                  /* 0x0388 */
        __u64   ftrace_func;                    /* 0x0390 */
-       __u64   gmap;                           /* 0x0398 */
-       __u32   spinlock_lockval;               /* 0x03a0 */
-       __u8    pad_0x03a0[0x0400-0x03a4];      /* 0x03a4 */
+       __u64   ftrace_regs_func;               /* 0x0398 */
+       __u64   gmap;                           /* 0x03a0 */
+       __u32   spinlock_lockval;               /* 0x03a8 */
+       __u8    pad_0x03ac[0x0400-0x03ac];      /* 0x03ac */
 
        /* Per cpu primary space access list */
        __u32   paste[16];                      /* 0x0400 */
diff --git a/arch/s390/kernel/asm-offsets.c b/arch/s390/kernel/asm-offsets.c
index afe1715..12ef6ce 100644
--- a/arch/s390/kernel/asm-offsets.c
+++ b/arch/s390/kernel/asm-offsets.c
@@ -150,6 +150,7 @@ int main(void)
        DEFINE(__LC_MCCK_CLOCK, offsetof(struct _lowcore, mcck_clock));
        DEFINE(__LC_MACHINE_FLAGS, offsetof(struct _lowcore, machine_flags));
        DEFINE(__LC_FTRACE_FUNC, offsetof(struct _lowcore, ftrace_func));
+       DEFINE(__LC_FTRACE_REGS_FUNC, offsetof(struct _lowcore, 
ftrace_regs_func));
        DEFINE(__LC_DUMP_REIPL, offsetof(struct _lowcore, ipib));
        BLANK();
        DEFINE(__LC_CPU_TIMER_SAVE_AREA, offsetof(struct _lowcore, 
cpu_timer_save_area));
diff --git a/arch/s390/kernel/early.c b/arch/s390/kernel/early.c
index 0dff972..2ab52d2 100644
--- a/arch/s390/kernel/early.c
+++ b/arch/s390/kernel/early.c
@@ -493,5 +493,8 @@ void __init startup_init(void)
 #ifdef CONFIG_DYNAMIC_FTRACE
        S390_lowcore.ftrace_func = (unsigned long)ftrace_caller;
 #endif
+#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
+       S390_lowcore.ftrace_regs_func = (unsigned long)ftrace_regs_caller;
+#endif
        lockdep_on();
 }
diff --git a/arch/s390/kernel/ftrace.c b/arch/s390/kernel/ftrace.c
index 54d6493..7d1ec79 100644
--- a/arch/s390/kernel/ftrace.c
+++ b/arch/s390/kernel/ftrace.c
@@ -21,6 +21,7 @@
 
 void ftrace_disable_code(void);
 void ftrace_enable_insn(void);
+void ftrace_enable_regs_insn(void);
 
 #ifdef CONFIG_64BIT
 /*
@@ -56,7 +57,13 @@ asm(
        "0:\n"
        "       .align  4\n"
        "ftrace_enable_insn:\n"
-       "       lg      %r1,"__stringify(__LC_FTRACE_FUNC)"\n");
+       "       lg      %r1,"__stringify(__LC_FTRACE_FUNC)"\n"
+#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
+       "       .align  4\n"
+       "ftrace_enable_regs_insn:\n"
+       "       lg      %r1,"__stringify(__LC_FTRACE_REGS_FUNC)"\n"
+#endif
+);
 
 #define FTRACE_INSN_SIZE       6
 
@@ -101,7 +108,13 @@ asm(
        "1:\n"
        "       .align  4\n"
        "ftrace_enable_insn:\n"
-       "       l       %r14,"__stringify(__LC_FTRACE_FUNC)"\n");
+       "       l       %r14,"__stringify(__LC_FTRACE_FUNC)"\n"
+#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
+       "       .align  4\n"
+       "ftrace_enable_regs_insn:\n"
+       "       l       %r14,"__stringify(__LC_FTRACE_REGS_FUNC)"\n"
+#endif
+);
 
 #define FTRACE_INSN_SIZE       4
 
@@ -119,12 +132,29 @@ int ftrace_make_nop(struct module *mod, struct dyn_ftrace 
*rec,
 
 int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
 {
-       if (probe_kernel_write((void *) rec->ip, ftrace_enable_insn,
-                              FTRACE_INSN_SIZE))
+#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
+       if (rec->flags & FTRACE_FL_REGS) {
+               if (probe_kernel_write((void *) rec->ip, 
ftrace_enable_regs_insn,
+                                      FTRACE_INSN_SIZE))
                return -EPERM;
+       } else
+#endif
+       {
+               if (probe_kernel_write((void *) rec->ip, ftrace_enable_insn,
+                                      FTRACE_INSN_SIZE))
+               return -EPERM;
+       }
        return 0;
 }
 
+#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
+int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr,
+                               unsigned long addr)
+{
+       return ftrace_make_call(rec, addr);
+}
+#endif
+
 int ftrace_update_ftrace_func(ftrace_func_t func)
 {
        return 0;
@@ -173,19 +203,41 @@ out:
 int ftrace_enable_ftrace_graph_caller(void)
 {
        unsigned short offset;
+       int ret;
+
+#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
+       offset = ((void *) prepare_ftrace_return -
+                 (void *) ftrace_regs_graph_caller) / 2;
+       if ((ret = probe_kernel_write((void *) ftrace_regs_graph_caller + 2,
+                                 &offset, sizeof(offset))))
+               return ret;
+#endif
 
        offset = ((void *) prepare_ftrace_return -
                  (void *) ftrace_graph_caller) / 2;
-       return probe_kernel_write((void *) ftrace_graph_caller + 2,
-                                 &offset, sizeof(offset));
+       if ((ret = probe_kernel_write((void *) ftrace_graph_caller + 2,
+                                 &offset, sizeof(offset))))
+               return ret;
+
+       return 0;
 }
 
 int ftrace_disable_ftrace_graph_caller(void)
 {
        static unsigned short offset = 0x0002;
+       int ret;
 
-       return probe_kernel_write((void *) ftrace_graph_caller + 2,
-                                 &offset, sizeof(offset));
+#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
+       if ((ret = probe_kernel_write((void *) ftrace_regs_graph_caller + 2,
+                         &offset, sizeof(offset))))
+               return ret;
+#endif
+
+       if ((ret = probe_kernel_write((void *) ftrace_graph_caller + 2,
+                         &offset, sizeof(offset))))
+               return ret;
+
+       return 0;
 }
 
 #endif /* CONFIG_DYNAMIC_FTRACE */
diff --git a/arch/s390/kernel/mcount.S b/arch/s390/kernel/mcount.S
index 08dcf21..e4322cf 100644
--- a/arch/s390/kernel/mcount.S
+++ b/arch/s390/kernel/mcount.S
@@ -53,6 +53,49 @@ ENTRY(ftrace_graph_caller)
 3:     lm      %r2,%r5,16(%r15)
        br      %r14
 
+#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
+ENTRY(ftrace_regs_caller)
+       bras    %r1,1f
+0:     .long   function_trace_stop
+1:     l       %r1,0(%r1)
+       icm     %r1,0xf,0(%r1)
+       bnzr    %r14
+       lr      %r1,%r15
+       ahi     %r15,-188
+       st      %r1,__SF_BACKCHAIN(%r15)
+       st      %r1,168(%r15)
+       stm     %r0,%r13,108(%r15)
+       st      %r14,104(%r15)
+       lr      %r2,%r14
+       ahi     %r2,-MCOUNT_INSN_SIZE
+       l       %r3,192(%r15)
+       st      %r3,164(%r15)
+       bras    %r1,1f
+0:     .long   ftrace_trace_function
+1:     .long   function_trace_op
+       l       %r4,1b-0b(%r1)
+       l       %r4,0(%r4)
+       lr      %r5, %r15
+       ahi     %r5, 96
+       l       %r14,0(%r1)
+       l       %r14,0(%r14)
+       basr    %r14,%r14
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+       l       %r2,192(%r15)
+       l       %r3,244(%r15)
+ENTRY(ftrace_regs_graph_caller)
+# The bras instruction gets runtime patched to call prepare_ftrace_return.
+# See ftrace_enable_ftrace_graph_caller. The patched instruction is:
+#      bras    %r14,prepare_ftrace_return
+       bras    %r14,0f
+0:     st      %r2,192(%r15)
+#endif
+       lm      %r0,%r13,108(%r15)
+       l       %r14,104(%r15)
+       ahi     %r15,188
+       br      %r14
+#endif
+
 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
 
 ENTRY(return_to_handler)
diff --git a/arch/s390/kernel/mcount64.S b/arch/s390/kernel/mcount64.S
index 1c52eae..bcad958 100644
--- a/arch/s390/kernel/mcount64.S
+++ b/arch/s390/kernel/mcount64.S
@@ -49,6 +49,44 @@ ENTRY(ftrace_graph_caller)
        lg      %r14,112(%r15)
        br      %r14
 
+#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
+ENTRY(ftrace_regs_caller)
+       larl    %r1,function_trace_stop
+       icm     %r1,0xf,0(%r1)
+       bnzr    %r14
+       lgr     %r1,%r15
+       aghi    %r15,-336
+       stg     %r1,__SF_BACKCHAIN(%r15)
+       stg     %r1,304(%r15)
+       stmg    %r0,%r13,184(%r15)
+       stg     %r14,176(%r15)
+       lgr     %r2,%r14
+       aghi    %r2,-MCOUNT_INSN_SIZE
+       lg      %r3,344(%r15)
+       stg     %r3,296(%r15)
+       larl    %r4,function_trace_op
+       lg      %r4,0(%r4)
+       lgr     %r5, %r15
+       aghi    %r5, 160
+       larl    %r14,ftrace_trace_function
+       lg      %r14,0(%r14)
+       basr    %r14,%r14
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+       lg      %r2,344(%r15)
+       lg      %r3,448(%r15)
+ENTRY(ftrace_regs_graph_caller)
+# The bras instruction gets runtime patched to call prepare_ftrace_return.
+# See ftrace_enable_ftrace_graph_caller. The patched instruction is:
+#      bras    %r14,prepare_ftrace_return
+       bras    %r14,0f
+0:     stg     %r2,344(%r15)
+#endif
+       lmg     %r0,%r13,184(%r15)
+       lg      %r14,176(%r15)
+       aghi    %r15,336
+       br      %r14
+#endif
+
 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
 
 ENTRY(return_to_handler)
diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c
index 1e2264b..6cdf2a1 100644
--- a/arch/s390/kernel/setup.c
+++ b/arch/s390/kernel/setup.c
@@ -352,6 +352,7 @@ static void __init setup_lowcore(void)
        lc->last_update_timer = S390_lowcore.last_update_timer;
        lc->last_update_clock = S390_lowcore.last_update_clock;
        lc->ftrace_func = S390_lowcore.ftrace_func;
+       lc->ftrace_regs_func = S390_lowcore.ftrace_regs_func;
 
        restart_stack = __alloc_bootmem(ASYNC_SIZE, ASYNC_SIZE, 0);
        restart_stack += ASYNC_SIZE;
diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c
index 243c7e5..152967a 100644
--- a/arch/s390/kernel/smp.c
+++ b/arch/s390/kernel/smp.c
@@ -232,6 +232,7 @@ static void pcpu_prepare_secondary(struct pcpu *pcpu, int 
cpu)
        lc->kernel_asce = S390_lowcore.kernel_asce;
        lc->machine_flags = S390_lowcore.machine_flags;
        lc->ftrace_func = S390_lowcore.ftrace_func;
+       lc->ftrace_regs_func = S390_lowcore.ftrace_regs_func;
        lc->user_timer = lc->system_timer = lc->steal_timer = 0;
        __ctl_store(lc->cregs_save_area, 0, 15);
        save_access_regs((unsigned int *) lc->access_regs_save_area);
diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
index 404a686..70ccc82 100644
--- a/include/linux/ftrace.h
+++ b/include/linux/ftrace.h
@@ -441,6 +441,7 @@ void ftrace_modify_all_code(int command);
 
 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
 extern void ftrace_graph_caller(void);
+extern void ftrace_regs_graph_caller(void);
 extern int ftrace_enable_ftrace_graph_caller(void);
 extern int ftrace_disable_ftrace_graph_caller(void);
 #else
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to