History

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.

Steven Rostedt (VMware) committed 6 years ago

Commit 2860cd81 parent 02a474c8 files · +29 −9Browse files

Unified · Split

8 files
  1. arch/powerpc/include/asm/livepatch.h +3 −1
  2. arch/s390/include/asm/livepatch.h +4 −1
  3. arch/x86/include/asm/ftrace.h +3 −0
  4. arch/x86/include/asm/livepatch.h +2 −2
  5. arch/x86/kernel/ftrace_64.S +4 −0
  6. include/linux/ftrace.h +7 −0
  7. kernel/livepatch/Kconfig +1 −1
  8. kernel/livepatch/patch.c +5 −4
arch/powerpc/include/asm/livepatch.h+3−1
@@ -12,8 +12,10 @@ · 11 unmodified lines
12#include <linux/sched/task_stack.h>12#include <linux/sched/task_stack.h>
13 13
14#ifdef CONFIG_LIVEPATCH14#ifdef CONFIG_LIVEPATCH
15static inline void klp_arch_set_pc(struct pt_regs *regs, unsigned long ip)15static 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 regs->nip = ip;19 regs->nip = ip;
18}20}
19 21
arch/s390/include/asm/livepatch.h+4−1
@@ -11,10 +11,13 @@ · 10 unmodified lines
11#ifndef ASM_LIVEPATCH_H11#ifndef ASM_LIVEPATCH_H
12#define ASM_LIVEPATCH_H12#define ASM_LIVEPATCH_H
13 13
14#include <linux/ftrace.h>
14#include <asm/ptrace.h>15#include <asm/ptrace.h>
15 16
16static inline void klp_arch_set_pc(struct pt_regs *regs, unsigned long ip)17static 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 regs->psw.addr = ip;21 regs->psw.addr = ip;
19}22}
20 23
arch/x86/include/asm/ftrace.h+3−0
@@ -54,6 +54,9 @@ arch_ftrace_get_regs(struct ftrace_regs *fregs) · 53 unmodified lines
54 return NULL;54 return NULL;
55 return &fregs->regs;55 return &fregs->regs;
56}56}
57
58#define ftrace_instruction_pointer_set(fregs, _ip) \
59 do { (fregs)->regs.ip = (_ip); } while (0)
57#endif60#endif
58 61
59#ifdef CONFIG_DYNAMIC_FTRACE62#ifdef CONFIG_DYNAMIC_FTRACE
arch/x86/include/asm/livepatch.h+2−2
@@ -12,9 +12,9 @@ · 11 unmodified lines
12#include <asm/setup.h>12#include <asm/setup.h>
13#include <linux/ftrace.h>13#include <linux/ftrace.h>
14 14
15static inline void klp_arch_set_pc(struct pt_regs *regs, unsigned long ip)15static 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#endif /* _ASM_X86_LIVEPATCH_H */20#endif /* _ASM_X86_LIVEPATCH_H */
arch/x86/kernel/ftrace_64.S+4−0
@@ -157,6 +157,10 @@ SYM_INNER_LABEL(ftrace_caller_op_ptr, SYM_L_GLOBAL) · 156 unmodified lines
157SYM_INNER_LABEL(ftrace_call, SYM_L_GLOBAL)157SYM_INNER_LABEL(ftrace_call, SYM_L_GLOBAL)
158 call ftrace_stub158 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 restore_mcount_regs164 restore_mcount_regs
161 165
162 /*166 /*
include/linux/ftrace.h+7−0
@@ -97,6 +97,13 @@ struct ftrace_regs { · 96 unmodified lines
97};97};
98#define arch_ftrace_get_regs(fregs) (&(fregs)->regs)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#endif /* CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS */107#endif /* CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS */
101 108
102static __always_inline struct pt_regs *ftrace_get_regs(struct ftrace_regs *fregs)109static __always_inline struct pt_regs *ftrace_get_regs(struct ftrace_regs *fregs)
kernel/livepatch/Kconfig+1−1
@@ -6,7 +6,7 @@ config HAVE_LIVEPATCH · 5 unmodified lines
6 6
7config LIVEPATCH7config LIVEPATCH
8 bool "Kernel Live Patching"8 bool "Kernel Live Patching"
9 depends on DYNAMIC_FTRACE_WITH_REGS9 depends on DYNAMIC_FTRACE_WITH_REGS || DYNAMIC_FTRACE_WITH_ARGS
10 depends on MODULES10 depends on MODULES
11 depends on SYSFS11 depends on SYSFS
12 depends on KALLSYMS_ALL12 depends on KALLSYMS_ALL
kernel/livepatch/patch.c+5−4
@@ -42,7 +42,6 @@ static void notrace klp_ftrace_handler(unsigned long ip, · 41 unmodified lines
42 struct ftrace_ops *fops,42 struct ftrace_ops *fops,
43 struct ftrace_regs *fregs)43 struct ftrace_regs *fregs)
44{44{
45 struct pt_regs *regs = ftrace_get_regs(fregs);
46 struct klp_ops *ops;45 struct klp_ops *ops;
47 struct klp_func *func;46 struct klp_func *func;
48 int patch_state;47 int patch_state;
@@ -118,7 +117,7 @@ static void notrace klp_ftrace_handler(unsigned long ip, · 69 unmodified lines
118 if (func->nop)117 if (func->nop)
119 goto unlock;118 goto unlock;
120 119
121 klp_arch_set_pc(regs, (unsigned long)func->new_func);120 klp_arch_set_pc(fregs, (unsigned long)func->new_func);
122 121
123unlock:122unlock:
124 preempt_enable_notrace();123 preempt_enable_notrace();
@@ -200,8 +199,10 @@ static int klp_patch_func(struct klp_func *func) · 75 unmodified lines
200 return -ENOMEM;199 return -ENOMEM;
201 200
202 ops->fops.func = klp_ftrace_handler;201 ops->fops.func = klp_ftrace_handler;
203 ops->fops.flags = FTRACE_OPS_FL_SAVE_REGS |202 ops->fops.flags = FTRACE_OPS_FL_DYNAMIC |
204 FTRACE_OPS_FL_DYNAMIC |203#ifndef CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS
204 FTRACE_OPS_FL_SAVE_REGS |
205#endif
205 FTRACE_OPS_FL_IPMODIFY |206 FTRACE_OPS_FL_IPMODIFY |
206 FTRACE_OPS_FL_PERMANENT;207 FTRACE_OPS_FL_PERMANENT;
207 208