aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/x86/include/asm/ftrace.h8
-rw-r--r--arch/x86/kernel/ftrace.c29
-rw-r--r--include/linux/ftrace.h53
-rw-r--r--kernel/module.c2
-rw-r--r--kernel/trace/ftrace.c62
5 files changed, 100 insertions, 54 deletions
diff --git a/arch/x86/include/asm/ftrace.h b/arch/x86/include/asm/ftrace.h
index 9b6a1fa19e70..2bb43b433e07 100644
--- a/arch/x86/include/asm/ftrace.h
+++ b/arch/x86/include/asm/ftrace.h
@@ -17,6 +17,14 @@ static inline unsigned long ftrace_call_adjust(unsigned long addr)
17 */ 17 */
18 return addr - 1; 18 return addr - 1;
19} 19}
20
21#ifdef CONFIG_DYNAMIC_FTRACE
22
23struct dyn_arch_ftrace {
24 /* No extra data needed for x86 */
25};
26
27#endif /* CONFIG_DYNAMIC_FTRACE */
20#endif /* __ASSEMBLY__ */ 28#endif /* __ASSEMBLY__ */
21#endif /* CONFIG_FUNCTION_TRACER */ 29#endif /* CONFIG_FUNCTION_TRACER */
22 30
diff --git a/arch/x86/kernel/ftrace.c b/arch/x86/kernel/ftrace.c
index fe832738e1e2..762222ad1387 100644
--- a/arch/x86/kernel/ftrace.c
+++ b/arch/x86/kernel/ftrace.c
@@ -166,7 +166,7 @@ static int ftrace_calc_offset(long ip, long addr)
166 return (int)(addr - ip); 166 return (int)(addr - ip);
167} 167}
168 168
169unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr) 169static unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr)
170{ 170{
171 static union ftrace_code_union calc; 171 static union ftrace_code_union calc;
172 172
@@ -311,12 +311,12 @@ do_ftrace_mod_code(unsigned long ip, void *new_code)
311 311
312static unsigned char ftrace_nop[MCOUNT_INSN_SIZE]; 312static unsigned char ftrace_nop[MCOUNT_INSN_SIZE];
313 313
314unsigned char *ftrace_nop_replace(void) 314static unsigned char *ftrace_nop_replace(void)
315{ 315{
316 return ftrace_nop; 316 return ftrace_nop;
317} 317}
318 318
319int 319static int
320ftrace_modify_code(unsigned long ip, unsigned char *old_code, 320ftrace_modify_code(unsigned long ip, unsigned char *old_code,
321 unsigned char *new_code) 321 unsigned char *new_code)
322{ 322{
@@ -349,6 +349,29 @@ ftrace_modify_code(unsigned long ip, unsigned char *old_code,
349 return 0; 349 return 0;
350} 350}
351 351
352int ftrace_make_nop(struct module *mod,
353 struct dyn_ftrace *rec, unsigned long addr)
354{
355 unsigned char *new, *old;
356 unsigned long ip = rec->ip;
357
358 old = ftrace_call_replace(ip, addr);
359 new = ftrace_nop_replace();
360
361 return ftrace_modify_code(rec->ip, old, new);
362}
363
364int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
365{
366 unsigned char *new, *old;
367 unsigned long ip = rec->ip;
368
369 old = ftrace_nop_replace();
370 new = ftrace_call_replace(ip, addr);
371
372 return ftrace_modify_code(rec->ip, old, new);
373}
374
352int ftrace_update_ftrace_func(ftrace_func_t func) 375int ftrace_update_ftrace_func(ftrace_func_t func)
353{ 376{
354 unsigned long ip = (unsigned long)(&ftrace_call); 377 unsigned long ip = (unsigned long)(&ftrace_call);
diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
index 4fbc4a8b86a5..166a2070ef65 100644
--- a/include/linux/ftrace.h
+++ b/include/linux/ftrace.h
@@ -74,6 +74,9 @@ static inline void ftrace_start(void) { }
74#endif /* CONFIG_FUNCTION_TRACER */ 74#endif /* CONFIG_FUNCTION_TRACER */
75 75
76#ifdef CONFIG_DYNAMIC_FTRACE 76#ifdef CONFIG_DYNAMIC_FTRACE
77/* asm/ftrace.h must be defined for archs supporting dynamic ftrace */
78#include <asm/ftrace.h>
79
77enum { 80enum {
78 FTRACE_FL_FREE = (1 << 0), 81 FTRACE_FL_FREE = (1 << 0),
79 FTRACE_FL_FAILED = (1 << 1), 82 FTRACE_FL_FAILED = (1 << 1),
@@ -88,6 +91,7 @@ struct dyn_ftrace {
88 struct list_head list; 91 struct list_head list;
89 unsigned long ip; /* address of mcount call-site */ 92 unsigned long ip; /* address of mcount call-site */
90 unsigned long flags; 93 unsigned long flags;
94 struct dyn_arch_ftrace arch;
91}; 95};
92 96
93int ftrace_force_update(void); 97int ftrace_force_update(void);
@@ -95,22 +99,40 @@ void ftrace_set_filter(unsigned char *buf, int len, int reset);
95 99
96/* defined in arch */ 100/* defined in arch */
97extern int ftrace_ip_converted(unsigned long ip); 101extern int ftrace_ip_converted(unsigned long ip);
98extern unsigned char *ftrace_nop_replace(void);
99extern unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr);
100extern int ftrace_dyn_arch_init(void *data); 102extern int ftrace_dyn_arch_init(void *data);
101extern int ftrace_update_ftrace_func(ftrace_func_t func); 103extern int ftrace_update_ftrace_func(ftrace_func_t func);
102extern void ftrace_caller(void); 104extern void ftrace_caller(void);
103extern void ftrace_call(void); 105extern void ftrace_call(void);
104extern void mcount_call(void); 106extern void mcount_call(void);
105 107
106/* May be defined in arch */ 108/**
107extern int ftrace_arch_read_dyn_info(char *buf, int size); 109 * ftrace_make_nop - convert code into top
110 * @mod: module structure if called by module load initialization
111 * @rec: the mcount call site record
112 * @addr: the address that the call site should be calling
113 *
114 * This is a very sensitive operation and great care needs
115 * to be taken by the arch. The operation should carefully
116 * read the location, check to see if what is read is indeed
117 * what we expect it to be, and then on success of the compare,
118 * it should write to the location.
119 *
120 * The code segment at @rec->ip should be a caller to @addr
121 *
122 * Return must be:
123 * 0 on success
124 * -EFAULT on error reading the location
125 * -EINVAL on a failed compare of the contents
126 * -EPERM on error writing to the location
127 * Any other value will be considered a failure.
128 */
129extern int ftrace_make_nop(struct module *mod,
130 struct dyn_ftrace *rec, unsigned long addr);
108 131
109/** 132/**
110 * ftrace_modify_code - modify code segment 133 * ftrace_make_call - convert a nop call site into a call to addr
111 * @ip: the address of the code segment 134 * @rec: the mcount call site record
112 * @old_code: the contents of what is expected to be there 135 * @addr: the address that the call site should call
113 * @new_code: the code to patch in
114 * 136 *
115 * This is a very sensitive operation and great care needs 137 * This is a very sensitive operation and great care needs
116 * to be taken by the arch. The operation should carefully 138 * to be taken by the arch. The operation should carefully
@@ -118,6 +140,8 @@ extern int ftrace_arch_read_dyn_info(char *buf, int size);
118 * what we expect it to be, and then on success of the compare, 140 * what we expect it to be, and then on success of the compare,
119 * it should write to the location. 141 * it should write to the location.
120 * 142 *
143 * The code segment at @rec->ip should be a nop
144 *
121 * Return must be: 145 * Return must be:
122 * 0 on success 146 * 0 on success
123 * -EFAULT on error reading the location 147 * -EFAULT on error reading the location
@@ -125,8 +149,11 @@ extern int ftrace_arch_read_dyn_info(char *buf, int size);
125 * -EPERM on error writing to the location 149 * -EPERM on error writing to the location
126 * Any other value will be considered a failure. 150 * Any other value will be considered a failure.
127 */ 151 */
128extern int ftrace_modify_code(unsigned long ip, unsigned char *old_code, 152extern int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr);
129 unsigned char *new_code); 153
154
155/* May be defined in arch */
156extern int ftrace_arch_read_dyn_info(char *buf, int size);
130 157
131extern int skip_trace(unsigned long ip); 158extern int skip_trace(unsigned long ip);
132 159
@@ -259,11 +286,13 @@ static inline void ftrace_dump(void) { }
259 286
260#ifdef CONFIG_FTRACE_MCOUNT_RECORD 287#ifdef CONFIG_FTRACE_MCOUNT_RECORD
261extern void ftrace_init(void); 288extern void ftrace_init(void);
262extern void ftrace_init_module(unsigned long *start, unsigned long *end); 289extern void ftrace_init_module(struct module *mod,
290 unsigned long *start, unsigned long *end);
263#else 291#else
264static inline void ftrace_init(void) { } 292static inline void ftrace_init(void) { }
265static inline void 293static inline void
266ftrace_init_module(unsigned long *start, unsigned long *end) { } 294ftrace_init_module(struct module *mod,
295 unsigned long *start, unsigned long *end) { }
267#endif 296#endif
268 297
269 298
diff --git a/kernel/module.c b/kernel/module.c
index 1f4cc00e0c20..69791274e899 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -2201,7 +2201,7 @@ static noinline struct module *load_module(void __user *umod,
2201 /* sechdrs[0].sh_size is always zero */ 2201 /* sechdrs[0].sh_size is always zero */
2202 mseg = section_objs(hdr, sechdrs, secstrings, "__mcount_loc", 2202 mseg = section_objs(hdr, sechdrs, secstrings, "__mcount_loc",
2203 sizeof(*mseg), &num_mcount); 2203 sizeof(*mseg), &num_mcount);
2204 ftrace_init_module(mseg, mseg + num_mcount); 2204 ftrace_init_module(mod, mseg, mseg + num_mcount);
2205 2205
2206 err = module_finalize(hdr, sechdrs, mod); 2206 err = module_finalize(hdr, sechdrs, mod);
2207 if (err < 0) 2207 if (err < 0)
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index 3940c71ac2a2..e9a5fbfce08e 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -358,9 +358,7 @@ static void print_ip_ins(const char *fmt, unsigned char *p)
358 printk(KERN_CONT "%s%02x", i ? ":" : "", p[i]); 358 printk(KERN_CONT "%s%02x", i ? ":" : "", p[i]);
359} 359}
360 360
361static void ftrace_bug(int failed, unsigned long ip, 361static void ftrace_bug(int failed, unsigned long ip)
362 unsigned char *expected,
363 unsigned char *replace)
364{ 362{
365 switch (failed) { 363 switch (failed) {
366 case -EFAULT: 364 case -EFAULT:
@@ -372,9 +370,7 @@ static void ftrace_bug(int failed, unsigned long ip,
372 FTRACE_WARN_ON_ONCE(1); 370 FTRACE_WARN_ON_ONCE(1);
373 pr_info("ftrace failed to modify "); 371 pr_info("ftrace failed to modify ");
374 print_ip_sym(ip); 372 print_ip_sym(ip);
375 print_ip_ins(" expected: ", expected);
376 print_ip_ins(" actual: ", (unsigned char *)ip); 373 print_ip_ins(" actual: ", (unsigned char *)ip);
377 print_ip_ins(" replace: ", replace);
378 printk(KERN_CONT "\n"); 374 printk(KERN_CONT "\n");
379 break; 375 break;
380 case -EPERM: 376 case -EPERM:
@@ -392,8 +388,7 @@ static void ftrace_bug(int failed, unsigned long ip,
392#define FTRACE_ADDR ((long)(ftrace_caller)) 388#define FTRACE_ADDR ((long)(ftrace_caller))
393 389
394static int 390static int
395__ftrace_replace_code(struct dyn_ftrace *rec, 391__ftrace_replace_code(struct dyn_ftrace *rec, int enable)
396 unsigned char *old, unsigned char *new, int enable)
397{ 392{
398 unsigned long ip, fl; 393 unsigned long ip, fl;
399 394
@@ -435,12 +430,10 @@ __ftrace_replace_code(struct dyn_ftrace *rec,
435 * otherwise enable it! 430 * otherwise enable it!
436 */ 431 */
437 if (fl & FTRACE_FL_ENABLED) { 432 if (fl & FTRACE_FL_ENABLED) {
438 /* swap new and old */ 433 enable = 0;
439 new = old;
440 old = ftrace_call_replace(ip, FTRACE_ADDR);
441 rec->flags &= ~FTRACE_FL_ENABLED; 434 rec->flags &= ~FTRACE_FL_ENABLED;
442 } else { 435 } else {
443 new = ftrace_call_replace(ip, FTRACE_ADDR); 436 enable = 1;
444 rec->flags |= FTRACE_FL_ENABLED; 437 rec->flags |= FTRACE_FL_ENABLED;
445 } 438 }
446 } else { 439 } else {
@@ -453,10 +446,7 @@ __ftrace_replace_code(struct dyn_ftrace *rec,
453 fl = rec->flags & (FTRACE_FL_NOTRACE | FTRACE_FL_ENABLED); 446 fl = rec->flags & (FTRACE_FL_NOTRACE | FTRACE_FL_ENABLED);
454 if (fl == FTRACE_FL_NOTRACE) 447 if (fl == FTRACE_FL_NOTRACE)
455 return 0; 448 return 0;
456 449 }
457 new = ftrace_call_replace(ip, FTRACE_ADDR);
458 } else
459 old = ftrace_call_replace(ip, FTRACE_ADDR);
460 450
461 if (enable) { 451 if (enable) {
462 if (rec->flags & FTRACE_FL_ENABLED) 452 if (rec->flags & FTRACE_FL_ENABLED)
@@ -469,21 +459,18 @@ __ftrace_replace_code(struct dyn_ftrace *rec,
469 } 459 }
470 } 460 }
471 461
472 return ftrace_modify_code(ip, old, new); 462 if (enable)
463 return ftrace_make_call(rec, FTRACE_ADDR);
464 else
465 return ftrace_make_nop(NULL, rec, FTRACE_ADDR);
473} 466}
474 467
475static void ftrace_replace_code(int enable) 468static void ftrace_replace_code(int enable)
476{ 469{
477 int i, failed; 470 int i, failed;
478 unsigned char *new = NULL, *old = NULL;
479 struct dyn_ftrace *rec; 471 struct dyn_ftrace *rec;
480 struct ftrace_page *pg; 472 struct ftrace_page *pg;
481 473
482 if (enable)
483 old = ftrace_nop_replace();
484 else
485 new = ftrace_nop_replace();
486
487 for (pg = ftrace_pages_start; pg; pg = pg->next) { 474 for (pg = ftrace_pages_start; pg; pg = pg->next) {
488 for (i = 0; i < pg->index; i++) { 475 for (i = 0; i < pg->index; i++) {
489 rec = &pg->records[i]; 476 rec = &pg->records[i];
@@ -504,34 +491,30 @@ static void ftrace_replace_code(int enable)
504 unfreeze_record(rec); 491 unfreeze_record(rec);
505 } 492 }
506 493
507 failed = __ftrace_replace_code(rec, old, new, enable); 494 failed = __ftrace_replace_code(rec, enable);
508 if (failed && (rec->flags & FTRACE_FL_CONVERTED)) { 495 if (failed && (rec->flags & FTRACE_FL_CONVERTED)) {
509 rec->flags |= FTRACE_FL_FAILED; 496 rec->flags |= FTRACE_FL_FAILED;
510 if ((system_state == SYSTEM_BOOTING) || 497 if ((system_state == SYSTEM_BOOTING) ||
511 !core_kernel_text(rec->ip)) { 498 !core_kernel_text(rec->ip)) {
512 ftrace_free_rec(rec); 499 ftrace_free_rec(rec);
513 } else 500 } else
514 ftrace_bug(failed, rec->ip, old, new); 501 ftrace_bug(failed, rec->ip);
515 } 502 }
516 } 503 }
517 } 504 }
518} 505}
519 506
520static int 507static int
521ftrace_code_disable(struct dyn_ftrace *rec) 508ftrace_code_disable(struct module *mod, struct dyn_ftrace *rec)
522{ 509{
523 unsigned long ip; 510 unsigned long ip;
524 unsigned char *nop, *call;
525 int ret; 511 int ret;
526 512
527 ip = rec->ip; 513 ip = rec->ip;
528 514
529 nop = ftrace_nop_replace(); 515 ret = ftrace_make_nop(mod, rec, mcount_addr);
530 call = ftrace_call_replace(ip, mcount_addr);
531
532 ret = ftrace_modify_code(ip, call, nop);
533 if (ret) { 516 if (ret) {
534 ftrace_bug(ret, ip, call, nop); 517 ftrace_bug(ret, ip);
535 rec->flags |= FTRACE_FL_FAILED; 518 rec->flags |= FTRACE_FL_FAILED;
536 return 0; 519 return 0;
537 } 520 }
@@ -650,7 +633,7 @@ static cycle_t ftrace_update_time;
650static unsigned long ftrace_update_cnt; 633static unsigned long ftrace_update_cnt;
651unsigned long ftrace_update_tot_cnt; 634unsigned long ftrace_update_tot_cnt;
652 635
653static int ftrace_update_code(void) 636static int ftrace_update_code(struct module *mod)
654{ 637{
655 struct dyn_ftrace *p, *t; 638 struct dyn_ftrace *p, *t;
656 cycle_t start, stop; 639 cycle_t start, stop;
@@ -667,7 +650,7 @@ static int ftrace_update_code(void)
667 list_del_init(&p->list); 650 list_del_init(&p->list);
668 651
669 /* convert record (i.e, patch mcount-call with NOP) */ 652 /* convert record (i.e, patch mcount-call with NOP) */
670 if (ftrace_code_disable(p)) { 653 if (ftrace_code_disable(mod, p)) {
671 p->flags |= FTRACE_FL_CONVERTED; 654 p->flags |= FTRACE_FL_CONVERTED;
672 ftrace_update_cnt++; 655 ftrace_update_cnt++;
673 } else 656 } else
@@ -1309,7 +1292,8 @@ static __init int ftrace_init_debugfs(void)
1309 1292
1310fs_initcall(ftrace_init_debugfs); 1293fs_initcall(ftrace_init_debugfs);
1311 1294
1312static int ftrace_convert_nops(unsigned long *start, 1295static int ftrace_convert_nops(struct module *mod,
1296 unsigned long *start,
1313 unsigned long *end) 1297 unsigned long *end)
1314{ 1298{
1315 unsigned long *p; 1299 unsigned long *p;
@@ -1325,18 +1309,19 @@ static int ftrace_convert_nops(unsigned long *start,
1325 1309
1326 /* disable interrupts to prevent kstop machine */ 1310 /* disable interrupts to prevent kstop machine */
1327 local_irq_save(flags); 1311 local_irq_save(flags);
1328 ftrace_update_code(); 1312 ftrace_update_code(mod);
1329 local_irq_restore(flags); 1313 local_irq_restore(flags);
1330 mutex_unlock(&ftrace_start_lock); 1314 mutex_unlock(&ftrace_start_lock);
1331 1315
1332 return 0; 1316 return 0;
1333} 1317}
1334 1318
1335void ftrace_init_module(unsigned long *start, unsigned long *end) 1319void ftrace_init_module(struct module *mod,
1320 unsigned long *start, unsigned long *end)
1336{ 1321{
1337 if (ftrace_disabled || start == end) 1322 if (ftrace_disabled || start == end)
1338 return; 1323 return;
1339 ftrace_convert_nops(start, end); 1324 ftrace_convert_nops(mod, start, end);
1340} 1325}
1341 1326
1342extern unsigned long __start_mcount_loc[]; 1327extern unsigned long __start_mcount_loc[];
@@ -1366,7 +1351,8 @@ void __init ftrace_init(void)
1366 1351
1367 last_ftrace_enabled = ftrace_enabled = 1; 1352 last_ftrace_enabled = ftrace_enabled = 1;
1368 1353
1369 ret = ftrace_convert_nops(__start_mcount_loc, 1354 ret = ftrace_convert_nops(NULL,
1355 __start_mcount_loc,
1370 __stop_mcount_loc); 1356 __stop_mcount_loc);
1371 1357
1372 return; 1358 return;