aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteven Rostedt <srostedt@redhat.com>2012-05-30 13:36:38 -0400
committerSteven Rostedt <rostedt@goodmis.org>2012-05-31 23:12:19 -0400
commit8a4d0a687a599f39b7df3fe15f2d51d2157caf44 (patch)
tree0a110234b8109154a0ffbe9bc4eb0d545da85102
parenta192cd0413b71c2a3e4e48dd365af704be72b748 (diff)
ftrace: Use breakpoint method to update ftrace caller
On boot up and module load, it is fine to modify the code directly, without the use of breakpoints. This is because boot up modification is done before SMP is initialized, thus the modification is serial, and module load is done before the module executes. But after that we must use a SMP safe method to modify running code. Otherwise, if we are running the function tracer and update its function (by starting off the stack tracer, or perf tracing) the change of the function called by the ftrace trampoline is done directly. If this is being executed on another CPU, that CPU may take a GPF and crash the kernel. The breakpoint method is used to change the nops at all the functions, but the change of the ftrace callback handler itself was still using a direct modification. If tracing was enabled and the function callback was changed then another CPU could fault if it was currently calling the original callback. This modification must use the breakpoint method too. Note, the direct method is still used for boot up and module load. Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
-rw-r--r--arch/x86/kernel/ftrace.c88
1 files changed, 72 insertions, 16 deletions
diff --git a/arch/x86/kernel/ftrace.c b/arch/x86/kernel/ftrace.c
index 2407a6d81cb7..c3a7cb4bf6e6 100644
--- a/arch/x86/kernel/ftrace.c
+++ b/arch/x86/kernel/ftrace.c
@@ -100,7 +100,7 @@ static const unsigned char *ftrace_nop_replace(void)
100} 100}
101 101
102static int 102static int
103ftrace_modify_code(unsigned long ip, unsigned const char *old_code, 103ftrace_modify_code_direct(unsigned long ip, unsigned const char *old_code,
104 unsigned const char *new_code) 104 unsigned const char *new_code)
105{ 105{
106 unsigned char replaced[MCOUNT_INSN_SIZE]; 106 unsigned char replaced[MCOUNT_INSN_SIZE];
@@ -141,7 +141,20 @@ int ftrace_make_nop(struct module *mod,
141 old = ftrace_call_replace(ip, addr); 141 old = ftrace_call_replace(ip, addr);
142 new = ftrace_nop_replace(); 142 new = ftrace_nop_replace();
143 143
144 return ftrace_modify_code(rec->ip, old, new); 144 /*
145 * On boot up, and when modules are loaded, the MCOUNT_ADDR
146 * is converted to a nop, and will never become MCOUNT_ADDR
147 * again. This code is either running before SMP (on boot up)
148 * or before the code will ever be executed (module load).
149 * We do not want to use the breakpoint version in this case,
150 * just modify the code directly.
151 */
152 if (addr == MCOUNT_ADDR)
153 return ftrace_modify_code_direct(rec->ip, old, new);
154
155 /* Normal cases use add_brk_on_nop */
156 WARN_ONCE(1, "invalid use of ftrace_make_nop");
157 return -EINVAL;
145} 158}
146 159
147int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) 160int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
@@ -152,20 +165,8 @@ int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
152 old = ftrace_nop_replace(); 165 old = ftrace_nop_replace();
153 new = ftrace_call_replace(ip, addr); 166 new = ftrace_call_replace(ip, addr);
154 167
155 return ftrace_modify_code(rec->ip, old, new); 168 /* Should only be called when module is loaded */
156} 169 return ftrace_modify_code_direct(rec->ip, old, new);
157
158int ftrace_update_ftrace_func(ftrace_func_t func)
159{
160 unsigned long ip = (unsigned long)(&ftrace_call);
161 unsigned char old[MCOUNT_INSN_SIZE], *new;
162 int ret;
163
164 memcpy(old, &ftrace_call, MCOUNT_INSN_SIZE);
165 new = ftrace_call_replace(ip, (unsigned long)func);
166 ret = ftrace_modify_code(ip, old, new);
167
168 return ret;
169} 170}
170 171
171/* 172/*
@@ -201,6 +202,29 @@ int ftrace_update_ftrace_func(ftrace_func_t func)
201 */ 202 */
202atomic_t modifying_ftrace_code __read_mostly; 203atomic_t modifying_ftrace_code __read_mostly;
203 204
205static int
206ftrace_modify_code(unsigned long ip, unsigned const char *old_code,
207 unsigned const char *new_code);
208
209int ftrace_update_ftrace_func(ftrace_func_t func)
210{
211 unsigned long ip = (unsigned long)(&ftrace_call);
212 unsigned char old[MCOUNT_INSN_SIZE], *new;
213 int ret;
214
215 memcpy(old, &ftrace_call, MCOUNT_INSN_SIZE);
216 new = ftrace_call_replace(ip, (unsigned long)func);
217
218 /* See comment above by declaration of modifying_ftrace_code */
219 atomic_inc(&modifying_ftrace_code);
220
221 ret = ftrace_modify_code(ip, old, new);
222
223 atomic_dec(&modifying_ftrace_code);
224
225 return ret;
226}
227
204/* 228/*
205 * A breakpoint was added to the code address we are about to 229 * A breakpoint was added to the code address we are about to
206 * modify, and this is the handle that will just skip over it. 230 * modify, and this is the handle that will just skip over it.
@@ -520,6 +544,38 @@ void ftrace_replace_code(int enable)
520 } 544 }
521} 545}
522 546
547static int
548ftrace_modify_code(unsigned long ip, unsigned const char *old_code,
549 unsigned const char *new_code)
550{
551 int ret;
552
553 ret = add_break(ip, old_code);
554 if (ret)
555 goto out;
556
557 run_sync();
558
559 ret = add_update_code(ip, new_code);
560 if (ret)
561 goto fail_update;
562
563 run_sync();
564
565 ret = ftrace_write(ip, new_code, 1);
566 if (ret) {
567 ret = -EPERM;
568 goto out;
569 }
570 run_sync();
571 out:
572 return ret;
573
574 fail_update:
575 probe_kernel_write((void *)ip, &old_code[0], 1);
576 goto out;
577}
578
523void arch_ftrace_update_code(int command) 579void arch_ftrace_update_code(int command)
524{ 580{
525 /* See comment above by declaration of modifying_ftrace_code */ 581 /* See comment above by declaration of modifying_ftrace_code */