diff options
-rw-r--r-- | arch/x86/include/asm/ftrace.h | 8 | ||||
-rw-r--r-- | arch/x86/kernel/ftrace.c | 29 | ||||
-rw-r--r-- | include/linux/ftrace.h | 53 | ||||
-rw-r--r-- | kernel/module.c | 2 | ||||
-rw-r--r-- | kernel/trace/ftrace.c | 62 |
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 | |||
23 | struct 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 | ||
169 | unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr) | 169 | static 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 | ||
312 | static unsigned char ftrace_nop[MCOUNT_INSN_SIZE]; | 312 | static unsigned char ftrace_nop[MCOUNT_INSN_SIZE]; |
313 | 313 | ||
314 | unsigned char *ftrace_nop_replace(void) | 314 | static unsigned char *ftrace_nop_replace(void) |
315 | { | 315 | { |
316 | return ftrace_nop; | 316 | return ftrace_nop; |
317 | } | 317 | } |
318 | 318 | ||
319 | int | 319 | static int |
320 | ftrace_modify_code(unsigned long ip, unsigned char *old_code, | 320 | ftrace_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 | ||
352 | int 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 | |||
364 | int 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 | |||
352 | int ftrace_update_ftrace_func(ftrace_func_t func) | 375 | int 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 | |||
77 | enum { | 80 | enum { |
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 | ||
93 | int ftrace_force_update(void); | 97 | int 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 */ |
97 | extern int ftrace_ip_converted(unsigned long ip); | 101 | extern int ftrace_ip_converted(unsigned long ip); |
98 | extern unsigned char *ftrace_nop_replace(void); | ||
99 | extern unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr); | ||
100 | extern int ftrace_dyn_arch_init(void *data); | 102 | extern int ftrace_dyn_arch_init(void *data); |
101 | extern int ftrace_update_ftrace_func(ftrace_func_t func); | 103 | extern int ftrace_update_ftrace_func(ftrace_func_t func); |
102 | extern void ftrace_caller(void); | 104 | extern void ftrace_caller(void); |
103 | extern void ftrace_call(void); | 105 | extern void ftrace_call(void); |
104 | extern void mcount_call(void); | 106 | extern void mcount_call(void); |
105 | 107 | ||
106 | /* May be defined in arch */ | 108 | /** |
107 | extern 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 | */ | ||
129 | extern 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 | */ |
128 | extern int ftrace_modify_code(unsigned long ip, unsigned char *old_code, | 152 | extern int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr); |
129 | unsigned char *new_code); | 153 | |
154 | |||
155 | /* May be defined in arch */ | ||
156 | extern int ftrace_arch_read_dyn_info(char *buf, int size); | ||
130 | 157 | ||
131 | extern int skip_trace(unsigned long ip); | 158 | extern 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 |
261 | extern void ftrace_init(void); | 288 | extern void ftrace_init(void); |
262 | extern void ftrace_init_module(unsigned long *start, unsigned long *end); | 289 | extern void ftrace_init_module(struct module *mod, |
290 | unsigned long *start, unsigned long *end); | ||
263 | #else | 291 | #else |
264 | static inline void ftrace_init(void) { } | 292 | static inline void ftrace_init(void) { } |
265 | static inline void | 293 | static inline void |
266 | ftrace_init_module(unsigned long *start, unsigned long *end) { } | 294 | ftrace_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 | ||
361 | static void ftrace_bug(int failed, unsigned long ip, | 361 | static 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 | ||
394 | static int | 390 | static 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 | ||
475 | static void ftrace_replace_code(int enable) | 468 | static 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 | ||
520 | static int | 507 | static int |
521 | ftrace_code_disable(struct dyn_ftrace *rec) | 508 | ftrace_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; | |||
650 | static unsigned long ftrace_update_cnt; | 633 | static unsigned long ftrace_update_cnt; |
651 | unsigned long ftrace_update_tot_cnt; | 634 | unsigned long ftrace_update_tot_cnt; |
652 | 635 | ||
653 | static int ftrace_update_code(void) | 636 | static 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 | ||
1310 | fs_initcall(ftrace_init_debugfs); | 1293 | fs_initcall(ftrace_init_debugfs); |
1311 | 1294 | ||
1312 | static int ftrace_convert_nops(unsigned long *start, | 1295 | static 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 | ||
1335 | void ftrace_init_module(unsigned long *start, unsigned long *end) | 1319 | void 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 | ||
1342 | extern unsigned long __start_mcount_loc[]; | 1327 | extern 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; |