Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

livepatch: Use the default ftrace_ops instead of REGS when ARGS is available

When CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS is available, the ftrace call
will be able to set the ip of the calling function. This will improve the
performance of live kernel patching where it does not need all the regs to
be stored just to change the instruction pointer.

If all archs that support live kernel patching also support
HAVE_DYNAMIC_FTRACE_WITH_ARGS, then the architecture specific function
klp_arch_set_pc() could be made generic.

It is possible that an arch can support HAVE_DYNAMIC_FTRACE_WITH_ARGS but
not HAVE_DYNAMIC_FTRACE_WITH_REGS and then have access to live patching.

Cc: Josh Poimboeuf <jpoimboe@redhat.com>
Cc: Jiri Kosina <jikos@kernel.org>
Cc: live-patching@vger.kernel.org
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Acked-by: Miroslav Benes <mbenes@suse.cz>
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>

+29 -9
+3 -1
arch/powerpc/include/asm/livepatch.h
··· 12 12 #include <linux/sched/task_stack.h> 13 13 14 14 #ifdef CONFIG_LIVEPATCH 15 - static inline void klp_arch_set_pc(struct pt_regs *regs, unsigned long ip) 15 + static inline void klp_arch_set_pc(struct ftrace_regs *fregs, unsigned long ip) 16 16 { 17 + struct pt_regs *regs = ftrace_get_regs(fregs); 18 + 17 19 regs->nip = ip; 18 20 } 19 21
+4 -1
arch/s390/include/asm/livepatch.h
··· 11 11 #ifndef ASM_LIVEPATCH_H 12 12 #define ASM_LIVEPATCH_H 13 13 14 + #include <linux/ftrace.h> 14 15 #include <asm/ptrace.h> 15 16 16 - static inline void klp_arch_set_pc(struct pt_regs *regs, unsigned long ip) 17 + static inline void klp_arch_set_pc(struct ftrace_regs *fregs, unsigned long ip) 17 18 { 19 + struct pt_regs *regs = ftrace_get_regs(fregs); 20 + 18 21 regs->psw.addr = ip; 19 22 } 20 23
+3
arch/x86/include/asm/ftrace.h
··· 54 54 return NULL; 55 55 return &fregs->regs; 56 56 } 57 + 58 + #define ftrace_instruction_pointer_set(fregs, _ip) \ 59 + do { (fregs)->regs.ip = (_ip); } while (0) 57 60 #endif 58 61 59 62 #ifdef CONFIG_DYNAMIC_FTRACE
+2 -2
arch/x86/include/asm/livepatch.h
··· 12 12 #include <asm/setup.h> 13 13 #include <linux/ftrace.h> 14 14 15 - static inline void klp_arch_set_pc(struct pt_regs *regs, unsigned long ip) 15 + static inline void klp_arch_set_pc(struct ftrace_regs *fregs, unsigned long ip) 16 16 { 17 - regs->ip = ip; 17 + ftrace_instruction_pointer_set(fregs, ip); 18 18 } 19 19 20 20 #endif /* _ASM_X86_LIVEPATCH_H */
+4
arch/x86/kernel/ftrace_64.S
··· 157 157 SYM_INNER_LABEL(ftrace_call, SYM_L_GLOBAL) 158 158 call ftrace_stub 159 159 160 + /* Handlers can change the RIP */ 161 + movq RIP(%rsp), %rax 162 + movq %rax, MCOUNT_REG_SIZE(%rsp) 163 + 160 164 restore_mcount_regs 161 165 162 166 /*
+7
include/linux/ftrace.h
··· 97 97 }; 98 98 #define arch_ftrace_get_regs(fregs) (&(fregs)->regs) 99 99 100 + /* 101 + * ftrace_instruction_pointer_set() is to be defined by the architecture 102 + * if to allow setting of the instruction pointer from the ftrace_regs 103 + * when HAVE_DYNAMIC_FTRACE_WITH_ARGS is set and it supports 104 + * live kernel patching. 105 + */ 106 + #define ftrace_instruction_pointer_set(fregs, ip) do { } while (0) 100 107 #endif /* CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS */ 101 108 102 109 static __always_inline struct pt_regs *ftrace_get_regs(struct ftrace_regs *fregs)
+1 -1
kernel/livepatch/Kconfig
··· 6 6 7 7 config LIVEPATCH 8 8 bool "Kernel Live Patching" 9 - depends on DYNAMIC_FTRACE_WITH_REGS 9 + depends on DYNAMIC_FTRACE_WITH_REGS || DYNAMIC_FTRACE_WITH_ARGS 10 10 depends on MODULES 11 11 depends on SYSFS 12 12 depends on KALLSYMS_ALL
+5 -4
kernel/livepatch/patch.c
··· 42 42 struct ftrace_ops *fops, 43 43 struct ftrace_regs *fregs) 44 44 { 45 - struct pt_regs *regs = ftrace_get_regs(fregs); 46 45 struct klp_ops *ops; 47 46 struct klp_func *func; 48 47 int patch_state; ··· 117 118 if (func->nop) 118 119 goto unlock; 119 120 120 - klp_arch_set_pc(regs, (unsigned long)func->new_func); 121 + klp_arch_set_pc(fregs, (unsigned long)func->new_func); 121 122 122 123 unlock: 123 124 preempt_enable_notrace(); ··· 199 200 return -ENOMEM; 200 201 201 202 ops->fops.func = klp_ftrace_handler; 202 - ops->fops.flags = FTRACE_OPS_FL_SAVE_REGS | 203 - FTRACE_OPS_FL_DYNAMIC | 203 + ops->fops.flags = FTRACE_OPS_FL_DYNAMIC | 204 + #ifndef CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS 205 + FTRACE_OPS_FL_SAVE_REGS | 206 + #endif 204 207 FTRACE_OPS_FL_IPMODIFY | 205 208 FTRACE_OPS_FL_PERMANENT; 206 209