aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAbhishek Sagar <sagar.abhishek@gmail.com>2008-06-01 12:17:30 -0400
committerIngo Molnar <mingo@elte.hu>2008-06-10 05:56:57 -0400
commit0eb967012ea15e6e8cfab483d9fa37bc602d400c (patch)
tree0e9c026a2d83f313cdc3f9f235d58ff522cee090
parente0773410247f1e5fc6f7c52a4c5f3c6c9873d527 (diff)
ftrace: prevent freeing of all failed updates
Prevent freeing of records which cause problems and correspond to function from core kernel text. A new flag, FTRACE_FL_CONVERTED is used to mark a record as "converted". All other records are patched lazily to NOPs. Failed records now also remain on frace_hash table. Each invocation of ftrace_record_ip now checks whether the traced function has ever been recorded (including past failures) and doesn't re-record it again. Signed-off-by: Abhishek Sagar <sagar.abhishek@gmail.com> Signed-off-by: Ingo Molnar <mingo@elte.hu>
-rw-r--r--include/linux/ftrace.h1
-rw-r--r--kernel/trace/ftrace.c76
2 files changed, 47 insertions, 30 deletions
diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
index 623819433ed5..20e14d0093c7 100644
--- a/include/linux/ftrace.h
+++ b/include/linux/ftrace.h
@@ -49,6 +49,7 @@ enum {
49 FTRACE_FL_FILTER = (1 << 2), 49 FTRACE_FL_FILTER = (1 << 2),
50 FTRACE_FL_ENABLED = (1 << 3), 50 FTRACE_FL_ENABLED = (1 << 3),
51 FTRACE_FL_NOTRACE = (1 << 4), 51 FTRACE_FL_NOTRACE = (1 << 4),
52 FTRACE_FL_CONVERTED = (1 << 5),
52}; 53};
53 54
54struct dyn_ftrace { 55struct dyn_ftrace {
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index f762f5a2d331..ec54cb7d69d6 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -216,6 +216,12 @@ ftrace_add_hash(struct dyn_ftrace *node, unsigned long key)
216 hlist_add_head_rcu(&node->node, &ftrace_hash[key]); 216 hlist_add_head_rcu(&node->node, &ftrace_hash[key]);
217} 217}
218 218
219/* called from kstop_machine */
220static inline void ftrace_del_hash(struct dyn_ftrace *node)
221{
222 hlist_del(&node->node);
223}
224
219static void ftrace_free_rec(struct dyn_ftrace *rec) 225static void ftrace_free_rec(struct dyn_ftrace *rec)
220{ 226{
221 /* no locking, only called from kstop_machine */ 227 /* no locking, only called from kstop_machine */
@@ -332,12 +338,11 @@ ftrace_record_ip(unsigned long ip)
332#define FTRACE_ADDR ((long)(ftrace_caller)) 338#define FTRACE_ADDR ((long)(ftrace_caller))
333#define MCOUNT_ADDR ((long)(mcount)) 339#define MCOUNT_ADDR ((long)(mcount))
334 340
335static void 341static int
336__ftrace_replace_code(struct dyn_ftrace *rec, 342__ftrace_replace_code(struct dyn_ftrace *rec,
337 unsigned char *old, unsigned char *new, int enable) 343 unsigned char *old, unsigned char *new, int enable)
338{ 344{
339 unsigned long ip, fl; 345 unsigned long ip, fl;
340 int failed;
341 346
342 ip = rec->ip; 347 ip = rec->ip;
343 348
@@ -364,7 +369,7 @@ __ftrace_replace_code(struct dyn_ftrace *rec,
364 369
365 if ((fl == (FTRACE_FL_FILTER | FTRACE_FL_ENABLED)) || 370 if ((fl == (FTRACE_FL_FILTER | FTRACE_FL_ENABLED)) ||
366 (fl == 0) || (rec->flags & FTRACE_FL_NOTRACE)) 371 (fl == 0) || (rec->flags & FTRACE_FL_NOTRACE))
367 return; 372 return 0;
368 373
369 /* 374 /*
370 * If it is enabled disable it, 375 * If it is enabled disable it,
@@ -388,7 +393,7 @@ __ftrace_replace_code(struct dyn_ftrace *rec,
388 */ 393 */
389 fl = rec->flags & (FTRACE_FL_NOTRACE | FTRACE_FL_ENABLED); 394 fl = rec->flags & (FTRACE_FL_NOTRACE | FTRACE_FL_ENABLED);
390 if (fl == FTRACE_FL_NOTRACE) 395 if (fl == FTRACE_FL_NOTRACE)
391 return; 396 return 0;
392 397
393 new = ftrace_call_replace(ip, FTRACE_ADDR); 398 new = ftrace_call_replace(ip, FTRACE_ADDR);
394 } else 399 } else
@@ -396,34 +401,24 @@ __ftrace_replace_code(struct dyn_ftrace *rec,
396 401
397 if (enable) { 402 if (enable) {
398 if (rec->flags & FTRACE_FL_ENABLED) 403 if (rec->flags & FTRACE_FL_ENABLED)
399 return; 404 return 0;
400 rec->flags |= FTRACE_FL_ENABLED; 405 rec->flags |= FTRACE_FL_ENABLED;
401 } else { 406 } else {
402 if (!(rec->flags & FTRACE_FL_ENABLED)) 407 if (!(rec->flags & FTRACE_FL_ENABLED))
403 return; 408 return 0;
404 rec->flags &= ~FTRACE_FL_ENABLED; 409 rec->flags &= ~FTRACE_FL_ENABLED;
405 } 410 }
406 } 411 }
407 412
408 failed = ftrace_modify_code(ip, old, new); 413 return ftrace_modify_code(ip, old, new);
409 if (failed) {
410 unsigned long key;
411 /* It is possible that the function hasn't been converted yet */
412 key = hash_long(ip, FTRACE_HASHBITS);
413 if (!ftrace_ip_in_hash(ip, key)) {
414 rec->flags |= FTRACE_FL_FAILED;
415 ftrace_free_rec(rec);
416 }
417
418 }
419} 414}
420 415
421static void ftrace_replace_code(int enable) 416static void ftrace_replace_code(int enable)
422{ 417{
418 int i, failed;
423 unsigned char *new = NULL, *old = NULL; 419 unsigned char *new = NULL, *old = NULL;
424 struct dyn_ftrace *rec; 420 struct dyn_ftrace *rec;
425 struct ftrace_page *pg; 421 struct ftrace_page *pg;
426 int i;
427 422
428 if (enable) 423 if (enable)
429 old = ftrace_nop_replace(); 424 old = ftrace_nop_replace();
@@ -438,7 +433,15 @@ static void ftrace_replace_code(int enable)
438 if (rec->flags & FTRACE_FL_FAILED) 433 if (rec->flags & FTRACE_FL_FAILED)
439 continue; 434 continue;
440 435
441 __ftrace_replace_code(rec, old, new, enable); 436 failed = __ftrace_replace_code(rec, old, new, enable);
437 if (failed && (rec->flags & FTRACE_FL_CONVERTED)) {
438 rec->flags |= FTRACE_FL_FAILED;
439 if ((system_state == SYSTEM_BOOTING) ||
440 !kernel_text_address(rec->ip)) {
441 ftrace_del_hash(rec);
442 ftrace_free_rec(rec);
443 }
444 }
442 } 445 }
443 } 446 }
444} 447}
@@ -467,7 +470,6 @@ ftrace_code_disable(struct dyn_ftrace *rec)
467 failed = ftrace_modify_code(ip, call, nop); 470 failed = ftrace_modify_code(ip, call, nop);
468 if (failed) { 471 if (failed) {
469 rec->flags |= FTRACE_FL_FAILED; 472 rec->flags |= FTRACE_FL_FAILED;
470 ftrace_free_rec(rec);
471 return 0; 473 return 0;
472 } 474 }
473 return 1; 475 return 1;
@@ -621,8 +623,7 @@ unsigned long ftrace_update_tot_cnt;
621static int __ftrace_update_code(void *ignore) 623static int __ftrace_update_code(void *ignore)
622{ 624{
623 struct dyn_ftrace *p; 625 struct dyn_ftrace *p;
624 struct hlist_head head; 626 struct hlist_node *t, *n;
625 struct hlist_node *t;
626 int save_ftrace_enabled; 627 int save_ftrace_enabled;
627 cycle_t start, stop; 628 cycle_t start, stop;
628 int i; 629 int i;
@@ -637,18 +638,33 @@ static int __ftrace_update_code(void *ignore)
637 638
638 /* No locks needed, the machine is stopped! */ 639 /* No locks needed, the machine is stopped! */
639 for (i = 0; i < FTRACE_HASHSIZE; i++) { 640 for (i = 0; i < FTRACE_HASHSIZE; i++) {
640 if (hlist_empty(&ftrace_hash[i])) 641 /* all CPUS are stopped, we are safe to modify code */
641 continue; 642 hlist_for_each_entry_safe(p, t, n, &ftrace_hash[i], node) {
643 /* Skip over failed records which have not been
644 * freed. */
645 if (p->flags & FTRACE_FL_FAILED)
646 continue;
642 647
643 head = ftrace_hash[i]; 648 /* Unconverted records are always at the head of the
644 INIT_HLIST_HEAD(&ftrace_hash[i]); 649 * hash bucket. Once we encounter a converted record,
650 * simply skip over to the next bucket. Saves ftraced
651 * some processor cycles (ftrace does its bid for
652 * global warming :-p ). */
653 if (p->flags & (FTRACE_FL_CONVERTED))
654 break;
645 655
646 /* all CPUS are stopped, we are safe to modify code */ 656 if (ftrace_code_disable(p)) {
647 hlist_for_each_entry(p, t, &head, node) { 657 p->flags |= FTRACE_FL_CONVERTED;
648 if (ftrace_code_disable(p))
649 ftrace_update_cnt++; 658 ftrace_update_cnt++;
650 } 659 } else {
660 if ((system_state == SYSTEM_BOOTING) ||
661 !kernel_text_address(p->ip)) {
662 ftrace_del_hash(p);
663 ftrace_free_rec(p);
651 664
665 }
666 }
667 }
652 } 668 }
653 669
654 stop = ftrace_now(raw_smp_processor_id()); 670 stop = ftrace_now(raw_smp_processor_id());