diff options
author | Steven Rostedt <srostedt@redhat.com> | 2011-12-16 14:42:37 -0500 |
---|---|---|
committer | Steven Rostedt <rostedt@goodmis.org> | 2011-12-21 07:17:57 -0500 |
commit | 3208230983a0ee3d95be22d463257e530c684956 (patch) | |
tree | fdba765e348c53a4fd65094ac17e66061f0b8932 /kernel/trace | |
parent | c88fd8634ea68e74c7d19fd2621b4078fd22864c (diff) |
ftrace: Remove usage of "freed" records
Records that are added to the function trace table are
permanently there, except for modules. By separating out the
modules to their own pages that can be freed in one shot
we can remove the "freed" flag and simplify some of the record
management.
Another benefit of doing this is that we can also move the
records around; sort them.
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Diffstat (limited to 'kernel/trace')
-rw-r--r-- | kernel/trace/ftrace.c | 100 |
1 files changed, 49 insertions, 51 deletions
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 655b432fb890..be6888f40d2b 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c | |||
@@ -996,8 +996,6 @@ struct ftrace_page { | |||
996 | static struct ftrace_page *ftrace_pages_start; | 996 | static struct ftrace_page *ftrace_pages_start; |
997 | static struct ftrace_page *ftrace_pages; | 997 | static struct ftrace_page *ftrace_pages; |
998 | 998 | ||
999 | static struct dyn_ftrace *ftrace_free_records; | ||
1000 | |||
1001 | static struct ftrace_func_entry * | 999 | static struct ftrace_func_entry * |
1002 | ftrace_lookup_ip(struct ftrace_hash *hash, unsigned long ip) | 1000 | ftrace_lookup_ip(struct ftrace_hash *hash, unsigned long ip) |
1003 | { | 1001 | { |
@@ -1421,32 +1419,8 @@ static void ftrace_hash_rec_enable(struct ftrace_ops *ops, | |||
1421 | __ftrace_hash_rec_update(ops, filter_hash, 1); | 1419 | __ftrace_hash_rec_update(ops, filter_hash, 1); |
1422 | } | 1420 | } |
1423 | 1421 | ||
1424 | static void ftrace_free_rec(struct dyn_ftrace *rec) | ||
1425 | { | ||
1426 | rec->freelist = ftrace_free_records; | ||
1427 | ftrace_free_records = rec; | ||
1428 | rec->flags |= FTRACE_FL_FREE; | ||
1429 | } | ||
1430 | |||
1431 | static struct dyn_ftrace *ftrace_alloc_dyn_node(unsigned long ip) | 1422 | static struct dyn_ftrace *ftrace_alloc_dyn_node(unsigned long ip) |
1432 | { | 1423 | { |
1433 | struct dyn_ftrace *rec; | ||
1434 | |||
1435 | /* First check for freed records */ | ||
1436 | if (ftrace_free_records) { | ||
1437 | rec = ftrace_free_records; | ||
1438 | |||
1439 | if (unlikely(!(rec->flags & FTRACE_FL_FREE))) { | ||
1440 | FTRACE_WARN_ON_ONCE(1); | ||
1441 | ftrace_free_records = NULL; | ||
1442 | return NULL; | ||
1443 | } | ||
1444 | |||
1445 | ftrace_free_records = rec->freelist; | ||
1446 | memset(rec, 0, sizeof(*rec)); | ||
1447 | return rec; | ||
1448 | } | ||
1449 | |||
1450 | if (ftrace_pages->index == ENTRIES_PER_PAGE) { | 1424 | if (ftrace_pages->index == ENTRIES_PER_PAGE) { |
1451 | if (!ftrace_pages->next) { | 1425 | if (!ftrace_pages->next) { |
1452 | /* allocate another page */ | 1426 | /* allocate another page */ |
@@ -1639,10 +1613,6 @@ static void ftrace_replace_code(int update) | |||
1639 | return; | 1613 | return; |
1640 | 1614 | ||
1641 | do_for_each_ftrace_rec(pg, rec) { | 1615 | do_for_each_ftrace_rec(pg, rec) { |
1642 | /* Skip over free records */ | ||
1643 | if (rec->flags & FTRACE_FL_FREE) | ||
1644 | continue; | ||
1645 | |||
1646 | failed = __ftrace_replace_code(rec, update); | 1616 | failed = __ftrace_replace_code(rec, update); |
1647 | if (failed) { | 1617 | if (failed) { |
1648 | ftrace_bug(failed, rec->ip); | 1618 | ftrace_bug(failed, rec->ip); |
@@ -2007,11 +1977,8 @@ static int ftrace_update_code(struct module *mod) | |||
2007 | * Do the initial record conversion from mcount jump | 1977 | * Do the initial record conversion from mcount jump |
2008 | * to the NOP instructions. | 1978 | * to the NOP instructions. |
2009 | */ | 1979 | */ |
2010 | if (!ftrace_code_disable(mod, p)) { | 1980 | if (!ftrace_code_disable(mod, p)) |
2011 | ftrace_free_rec(p); | ||
2012 | /* Game over */ | ||
2013 | break; | 1981 | break; |
2014 | } | ||
2015 | 1982 | ||
2016 | ftrace_update_cnt++; | 1983 | ftrace_update_cnt++; |
2017 | 1984 | ||
@@ -2026,10 +1993,8 @@ static int ftrace_update_code(struct module *mod) | |||
2026 | */ | 1993 | */ |
2027 | if (ftrace_start_up && ref) { | 1994 | if (ftrace_start_up && ref) { |
2028 | int failed = __ftrace_replace_code(p, 1); | 1995 | int failed = __ftrace_replace_code(p, 1); |
2029 | if (failed) { | 1996 | if (failed) |
2030 | ftrace_bug(failed, p->ip); | 1997 | ftrace_bug(failed, p->ip); |
2031 | ftrace_free_rec(p); | ||
2032 | } | ||
2033 | } | 1998 | } |
2034 | } | 1999 | } |
2035 | 2000 | ||
@@ -2223,9 +2188,7 @@ t_next(struct seq_file *m, void *v, loff_t *pos) | |||
2223 | } | 2188 | } |
2224 | } else { | 2189 | } else { |
2225 | rec = &iter->pg->records[iter->idx++]; | 2190 | rec = &iter->pg->records[iter->idx++]; |
2226 | if ((rec->flags & FTRACE_FL_FREE) || | 2191 | if (((iter->flags & FTRACE_ITER_FILTER) && |
2227 | |||
2228 | ((iter->flags & FTRACE_ITER_FILTER) && | ||
2229 | !(ftrace_lookup_ip(ops->filter_hash, rec->ip))) || | 2192 | !(ftrace_lookup_ip(ops->filter_hash, rec->ip))) || |
2230 | 2193 | ||
2231 | ((iter->flags & FTRACE_ITER_NOTRACE) && | 2194 | ((iter->flags & FTRACE_ITER_NOTRACE) && |
@@ -2602,7 +2565,6 @@ match_records(struct ftrace_hash *hash, char *buff, | |||
2602 | goto out_unlock; | 2565 | goto out_unlock; |
2603 | 2566 | ||
2604 | do_for_each_ftrace_rec(pg, rec) { | 2567 | do_for_each_ftrace_rec(pg, rec) { |
2605 | |||
2606 | if (ftrace_match_record(rec, mod, search, search_len, type)) { | 2568 | if (ftrace_match_record(rec, mod, search, search_len, type)) { |
2607 | ret = enter_record(hash, rec, not); | 2569 | ret = enter_record(hash, rec, not); |
2608 | if (ret < 0) { | 2570 | if (ret < 0) { |
@@ -3446,9 +3408,6 @@ ftrace_set_func(unsigned long *array, int *idx, char *buffer) | |||
3446 | 3408 | ||
3447 | do_for_each_ftrace_rec(pg, rec) { | 3409 | do_for_each_ftrace_rec(pg, rec) { |
3448 | 3410 | ||
3449 | if (rec->flags & FTRACE_FL_FREE) | ||
3450 | continue; | ||
3451 | |||
3452 | if (ftrace_match_record(rec, NULL, search, search_len, type)) { | 3411 | if (ftrace_match_record(rec, NULL, search, search_len, type)) { |
3453 | /* if it is in the array */ | 3412 | /* if it is in the array */ |
3454 | exists = false; | 3413 | exists = false; |
@@ -3566,6 +3525,27 @@ static int ftrace_process_locs(struct module *mod, | |||
3566 | unsigned long flags = 0; /* Shut up gcc */ | 3525 | unsigned long flags = 0; /* Shut up gcc */ |
3567 | 3526 | ||
3568 | mutex_lock(&ftrace_lock); | 3527 | mutex_lock(&ftrace_lock); |
3528 | /* | ||
3529 | * Core and each module needs their own pages, as | ||
3530 | * modules will free them when they are removed. | ||
3531 | * Force a new page to be allocated for modules. | ||
3532 | */ | ||
3533 | if (mod) { | ||
3534 | if (!ftrace_pages) | ||
3535 | return -ENOMEM; | ||
3536 | |||
3537 | /* | ||
3538 | * If the last page was full, it will be | ||
3539 | * allocated anyway. | ||
3540 | */ | ||
3541 | if (ftrace_pages->index != ENTRIES_PER_PAGE) { | ||
3542 | ftrace_pages->next = (void *)get_zeroed_page(GFP_KERNEL); | ||
3543 | if (!ftrace_pages->next) | ||
3544 | return -ENOMEM; | ||
3545 | ftrace_pages = ftrace_pages->next; | ||
3546 | } | ||
3547 | } | ||
3548 | |||
3569 | p = start; | 3549 | p = start; |
3570 | while (p < end) { | 3550 | while (p < end) { |
3571 | addr = ftrace_call_adjust(*p++); | 3551 | addr = ftrace_call_adjust(*p++); |
@@ -3599,9 +3579,13 @@ static int ftrace_process_locs(struct module *mod, | |||
3599 | } | 3579 | } |
3600 | 3580 | ||
3601 | #ifdef CONFIG_MODULES | 3581 | #ifdef CONFIG_MODULES |
3582 | |||
3583 | #define next_to_ftrace_page(p) container_of(p, struct ftrace_page, next) | ||
3584 | |||
3602 | void ftrace_release_mod(struct module *mod) | 3585 | void ftrace_release_mod(struct module *mod) |
3603 | { | 3586 | { |
3604 | struct dyn_ftrace *rec; | 3587 | struct dyn_ftrace *rec; |
3588 | struct ftrace_page **last_pg; | ||
3605 | struct ftrace_page *pg; | 3589 | struct ftrace_page *pg; |
3606 | 3590 | ||
3607 | mutex_lock(&ftrace_lock); | 3591 | mutex_lock(&ftrace_lock); |
@@ -3609,16 +3593,30 @@ void ftrace_release_mod(struct module *mod) | |||
3609 | if (ftrace_disabled) | 3593 | if (ftrace_disabled) |
3610 | goto out_unlock; | 3594 | goto out_unlock; |
3611 | 3595 | ||
3612 | do_for_each_ftrace_rec(pg, rec) { | 3596 | /* |
3597 | * Each module has its own ftrace_pages, remove | ||
3598 | * them from the list. | ||
3599 | */ | ||
3600 | last_pg = &ftrace_pages_start; | ||
3601 | for (pg = ftrace_pages_start; pg; pg = *last_pg) { | ||
3602 | rec = &pg->records[0]; | ||
3613 | if (within_module_core(rec->ip, mod)) { | 3603 | if (within_module_core(rec->ip, mod)) { |
3614 | /* | 3604 | /* |
3615 | * rec->ip is changed in ftrace_free_rec() | 3605 | * As core pages are first, the first |
3616 | * It should not between s and e if record was freed. | 3606 | * page should never be a module page. |
3617 | */ | 3607 | */ |
3618 | FTRACE_WARN_ON(rec->flags & FTRACE_FL_FREE); | 3608 | if (WARN_ON(pg == ftrace_pages_start)) |
3619 | ftrace_free_rec(rec); | 3609 | goto out_unlock; |
3620 | } | 3610 | |
3621 | } while_for_each_ftrace_rec(); | 3611 | /* Check if we are deleting the last page */ |
3612 | if (pg == ftrace_pages) | ||
3613 | ftrace_pages = next_to_ftrace_page(last_pg); | ||
3614 | |||
3615 | *last_pg = pg->next; | ||
3616 | free_page((unsigned long)pg); | ||
3617 | } else | ||
3618 | last_pg = &pg->next; | ||
3619 | } | ||
3622 | out_unlock: | 3620 | out_unlock: |
3623 | mutex_unlock(&ftrace_lock); | 3621 | mutex_unlock(&ftrace_lock); |
3624 | } | 3622 | } |