]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
s390/ftrace: Avoid trampolines if possible
authorVasily Gorbik <gor@linux.ibm.com>
Wed, 28 Aug 2024 17:06:55 +0000 (19:06 +0200)
committerVasily Gorbik <gor@linux.ibm.com>
Thu, 29 Aug 2024 20:56:34 +0000 (22:56 +0200)
When a sequential instruction fetching facility is present, it is safe
to patch ftrace NOPs in function prologues. All of them are 8-byte
aligned.

Reviewed-by: Heiko Carstens <hca@linux.ibm.com>
Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
arch/s390/kernel/ftrace.c

index 9552b1e7121fad499ef952db62276e7bc231e3a0..03eadf7d098e350ba8af1fe50fa07df9d0fe5213 100644 (file)
@@ -69,7 +69,7 @@ static const char *ftrace_shared_hotpatch_trampoline(const char **end)
 
 bool ftrace_need_init_nop(void)
 {
-       return true;
+       return !MACHINE_HAS_SEQ_INSN;
 }
 
 int ftrace_init_nop(struct module *mod, struct dyn_ftrace *rec)
@@ -139,8 +139,35 @@ static struct ftrace_hotpatch_trampoline *ftrace_get_trampoline(struct dyn_ftrac
        return trampoline;
 }
 
-int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr,
-                      unsigned long addr)
+static inline struct ftrace_insn
+ftrace_generate_branch_insn(unsigned long ip, unsigned long target)
+{
+       /* brasl r0,target or brcl 0,0 */
+       return (struct ftrace_insn){ .opc = target ? 0xc005 : 0xc004,
+                                    .disp = target ? (target - ip) / 2 : 0 };
+}
+
+static int ftrace_patch_branch_insn(unsigned long ip, unsigned long old_target,
+                                   unsigned long target)
+{
+       struct ftrace_insn orig = ftrace_generate_branch_insn(ip, old_target);
+       struct ftrace_insn new = ftrace_generate_branch_insn(ip, target);
+       struct ftrace_insn old;
+
+       if (!IS_ALIGNED(ip, 8))
+               return -EINVAL;
+       if (copy_from_kernel_nofault(&old, (void *)ip, sizeof(old)))
+               return -EFAULT;
+       /* Verify that the to be replaced code matches what we expect. */
+       if (memcmp(&orig, &old, sizeof(old)))
+               return -EINVAL;
+       s390_kernel_write((void *)ip, &new, sizeof(new));
+       return 0;
+}
+
+static int ftrace_modify_trampoline_call(struct dyn_ftrace *rec,
+                                        unsigned long old_addr,
+                                        unsigned long addr)
 {
        struct ftrace_hotpatch_trampoline *trampoline;
        u64 old;
@@ -156,6 +183,15 @@ int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr,
        return 0;
 }
 
+int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr,
+                      unsigned long addr)
+{
+       if (MACHINE_HAS_SEQ_INSN)
+               return ftrace_patch_branch_insn(rec->ip, old_addr, addr);
+       else
+               return ftrace_modify_trampoline_call(rec, old_addr, addr);
+}
+
 static int ftrace_patch_branch_mask(void *addr, u16 expected, bool enable)
 {
        u16 old;
@@ -174,11 +210,14 @@ static int ftrace_patch_branch_mask(void *addr, u16 expected, bool enable)
 int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec,
                    unsigned long addr)
 {
-       /* Expect brcl 0xf,... */
-       return ftrace_patch_branch_mask((void *)rec->ip, 0xc0f4, false);
+       /* Expect brcl 0xf,... for the !MACHINE_HAS_SEQ_INSN case */
+       if (MACHINE_HAS_SEQ_INSN)
+               return ftrace_patch_branch_insn(rec->ip, addr, 0);
+       else
+               return ftrace_patch_branch_mask((void *)rec->ip, 0xc0f4, false);
 }
 
-int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
+static int ftrace_make_trampoline_call(struct dyn_ftrace *rec, unsigned long addr)
 {
        struct ftrace_hotpatch_trampoline *trampoline;
 
@@ -190,6 +229,14 @@ int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
        return ftrace_patch_branch_mask((void *)rec->ip, 0xc004, true);
 }
 
+int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
+{
+       if (MACHINE_HAS_SEQ_INSN)
+               return ftrace_patch_branch_insn(rec->ip, 0, addr);
+       else
+               return ftrace_make_trampoline_call(rec, addr);
+}
+
 int ftrace_update_ftrace_func(ftrace_func_t func)
 {
        ftrace_func = func;