aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorSteven Rostedt <srostedt@redhat.com>2012-05-30 13:26:37 -0400
committerSteven Rostedt <rostedt@goodmis.org>2012-05-31 23:12:17 -0400
commita192cd0413b71c2a3e4e48dd365af704be72b748 (patch)
tree739c64a3fd4bf58b7c34a9eb5bc83aa844a06bf3 /arch
parentc985f7812331d79483beab932e8966477411a942 (diff)
ftrace: Synchronize variable setting with breakpoints
When the function tracer starts modifying the code via breakpoints it sets a variable (modifying_ftrace_code) to inform the breakpoint handler to call the ftrace int3 code. But there's no synchronization between setting this code and the handler, thus it is possible for the handler to be called on another CPU before it sees the variable. This will cause a kernel crash as the int3 handler will not know what to do with it. I originally added smp_mb()'s to force the visibility of the variable but H. Peter Anvin suggested that I just make it atomic. [ Added comments as suggested by Peter Zijlstra ] Suggested-by: H. Peter Anvin <hpa@zytor.com> Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Diffstat (limited to 'arch')
-rw-r--r--arch/x86/include/asm/ftrace.h2
-rw-r--r--arch/x86/kernel/ftrace.c38
-rw-r--r--arch/x86/kernel/traps.c8
3 files changed, 42 insertions, 6 deletions
diff --git a/arch/x86/include/asm/ftrace.h b/arch/x86/include/asm/ftrace.h
index 18d9005d9e4f..b0767bc08740 100644
--- a/arch/x86/include/asm/ftrace.h
+++ b/arch/x86/include/asm/ftrace.h
@@ -34,7 +34,7 @@
34 34
35#ifndef __ASSEMBLY__ 35#ifndef __ASSEMBLY__
36extern void mcount(void); 36extern void mcount(void);
37extern int modifying_ftrace_code; 37extern atomic_t modifying_ftrace_code;
38 38
39static inline unsigned long ftrace_call_adjust(unsigned long addr) 39static inline unsigned long ftrace_call_adjust(unsigned long addr)
40{ 40{
diff --git a/arch/x86/kernel/ftrace.c b/arch/x86/kernel/ftrace.c
index 32ff36596ab1..2407a6d81cb7 100644
--- a/arch/x86/kernel/ftrace.c
+++ b/arch/x86/kernel/ftrace.c
@@ -168,7 +168,38 @@ int ftrace_update_ftrace_func(ftrace_func_t func)
168 return ret; 168 return ret;
169} 169}
170 170
171int modifying_ftrace_code __read_mostly; 171/*
172 * The modifying_ftrace_code is used to tell the breakpoint
173 * handler to call ftrace_int3_handler(). If it fails to
174 * call this handler for a breakpoint added by ftrace, then
175 * the kernel may crash.
176 *
177 * As atomic_writes on x86 do not need a barrier, we do not
178 * need to add smp_mb()s for this to work. It is also considered
179 * that we can not read the modifying_ftrace_code before
180 * executing the breakpoint. That would be quite remarkable if
181 * it could do that. Here's the flow that is required:
182 *
183 * CPU-0 CPU-1
184 *
185 * atomic_inc(mfc);
186 * write int3s
187 * <trap-int3> // implicit (r)mb
188 * if (atomic_read(mfc))
189 * call ftrace_int3_handler()
190 *
191 * Then when we are finished:
192 *
193 * atomic_dec(mfc);
194 *
195 * If we hit a breakpoint that was not set by ftrace, it does not
196 * matter if ftrace_int3_handler() is called or not. It will
197 * simply be ignored. But it is crucial that a ftrace nop/caller
198 * breakpoint is handled. No other user should ever place a
199 * breakpoint on an ftrace nop/caller location. It must only
200 * be done by this code.
201 */
202atomic_t modifying_ftrace_code __read_mostly;
172 203
173/* 204/*
174 * A breakpoint was added to the code address we are about to 205 * A breakpoint was added to the code address we are about to
@@ -491,11 +522,12 @@ void ftrace_replace_code(int enable)
491 522
492void arch_ftrace_update_code(int command) 523void arch_ftrace_update_code(int command)
493{ 524{
494 modifying_ftrace_code++; 525 /* See comment above by declaration of modifying_ftrace_code */
526 atomic_inc(&modifying_ftrace_code);
495 527
496 ftrace_modify_all_code(command); 528 ftrace_modify_all_code(command);
497 529
498 modifying_ftrace_code--; 530 atomic_dec(&modifying_ftrace_code);
499} 531}
500 532
501int __init ftrace_dyn_arch_init(void *data) 533int __init ftrace_dyn_arch_init(void *data)
diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c
index ff08457a025d..05b31d92f69c 100644
--- a/arch/x86/kernel/traps.c
+++ b/arch/x86/kernel/traps.c
@@ -303,8 +303,12 @@ gp_in_kernel:
303dotraplinkage void __kprobes notrace do_int3(struct pt_regs *regs, long error_code) 303dotraplinkage void __kprobes notrace do_int3(struct pt_regs *regs, long error_code)
304{ 304{
305#ifdef CONFIG_DYNAMIC_FTRACE 305#ifdef CONFIG_DYNAMIC_FTRACE
306 /* ftrace must be first, everything else may cause a recursive crash */ 306 /*
307 if (unlikely(modifying_ftrace_code) && ftrace_int3_handler(regs)) 307 * ftrace must be first, everything else may cause a recursive crash.
308 * See note by declaration of modifying_ftrace_code in ftrace.c
309 */
310 if (unlikely(atomic_read(&modifying_ftrace_code)) &&
311 ftrace_int3_handler(regs))
308 return; 312 return;
309#endif 313#endif
310#ifdef CONFIG_KGDB_LOW_LEVEL_TRAP 314#ifdef CONFIG_KGDB_LOW_LEVEL_TRAP