diff options
Diffstat (limited to 'kernel/trace/ftrace.c')
-rw-r--r-- | kernel/trace/ftrace.c | 98 |
1 files changed, 69 insertions, 29 deletions
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index b3fde6d7b7fc..8a5c017bb50c 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c | |||
@@ -486,7 +486,6 @@ struct ftrace_profile_stat { | |||
486 | #define PROFILES_PER_PAGE \ | 486 | #define PROFILES_PER_PAGE \ |
487 | (PROFILE_RECORDS_SIZE / sizeof(struct ftrace_profile)) | 487 | (PROFILE_RECORDS_SIZE / sizeof(struct ftrace_profile)) |
488 | 488 | ||
489 | static int ftrace_profile_bits __read_mostly; | ||
490 | static int ftrace_profile_enabled __read_mostly; | 489 | static int ftrace_profile_enabled __read_mostly; |
491 | 490 | ||
492 | /* ftrace_profile_lock - synchronize the enable and disable of the profiler */ | 491 | /* ftrace_profile_lock - synchronize the enable and disable of the profiler */ |
@@ -494,7 +493,8 @@ static DEFINE_MUTEX(ftrace_profile_lock); | |||
494 | 493 | ||
495 | static DEFINE_PER_CPU(struct ftrace_profile_stat, ftrace_profile_stats); | 494 | static DEFINE_PER_CPU(struct ftrace_profile_stat, ftrace_profile_stats); |
496 | 495 | ||
497 | #define FTRACE_PROFILE_HASH_SIZE 1024 /* must be power of 2 */ | 496 | #define FTRACE_PROFILE_HASH_BITS 10 |
497 | #define FTRACE_PROFILE_HASH_SIZE (1 << FTRACE_PROFILE_HASH_BITS) | ||
498 | 498 | ||
499 | static void * | 499 | static void * |
500 | function_stat_next(void *v, int idx) | 500 | function_stat_next(void *v, int idx) |
@@ -676,7 +676,7 @@ int ftrace_profile_pages_init(struct ftrace_profile_stat *stat) | |||
676 | 676 | ||
677 | pages = DIV_ROUND_UP(functions, PROFILES_PER_PAGE); | 677 | pages = DIV_ROUND_UP(functions, PROFILES_PER_PAGE); |
678 | 678 | ||
679 | for (i = 0; i < pages; i++) { | 679 | for (i = 1; i < pages; i++) { |
680 | pg->next = (void *)get_zeroed_page(GFP_KERNEL); | 680 | pg->next = (void *)get_zeroed_page(GFP_KERNEL); |
681 | if (!pg->next) | 681 | if (!pg->next) |
682 | goto out_free; | 682 | goto out_free; |
@@ -724,13 +724,6 @@ static int ftrace_profile_init_cpu(int cpu) | |||
724 | if (!stat->hash) | 724 | if (!stat->hash) |
725 | return -ENOMEM; | 725 | return -ENOMEM; |
726 | 726 | ||
727 | if (!ftrace_profile_bits) { | ||
728 | size--; | ||
729 | |||
730 | for (; size; size >>= 1) | ||
731 | ftrace_profile_bits++; | ||
732 | } | ||
733 | |||
734 | /* Preallocate the function profiling pages */ | 727 | /* Preallocate the function profiling pages */ |
735 | if (ftrace_profile_pages_init(stat) < 0) { | 728 | if (ftrace_profile_pages_init(stat) < 0) { |
736 | kfree(stat->hash); | 729 | kfree(stat->hash); |
@@ -763,7 +756,7 @@ ftrace_find_profiled_func(struct ftrace_profile_stat *stat, unsigned long ip) | |||
763 | struct hlist_head *hhd; | 756 | struct hlist_head *hhd; |
764 | unsigned long key; | 757 | unsigned long key; |
765 | 758 | ||
766 | key = hash_long(ip, ftrace_profile_bits); | 759 | key = hash_long(ip, FTRACE_PROFILE_HASH_BITS); |
767 | hhd = &stat->hash[key]; | 760 | hhd = &stat->hash[key]; |
768 | 761 | ||
769 | if (hlist_empty(hhd)) | 762 | if (hlist_empty(hhd)) |
@@ -782,7 +775,7 @@ static void ftrace_add_profile(struct ftrace_profile_stat *stat, | |||
782 | { | 775 | { |
783 | unsigned long key; | 776 | unsigned long key; |
784 | 777 | ||
785 | key = hash_long(rec->ip, ftrace_profile_bits); | 778 | key = hash_long(rec->ip, FTRACE_PROFILE_HASH_BITS); |
786 | hlist_add_head_rcu(&rec->node, &stat->hash[key]); | 779 | hlist_add_head_rcu(&rec->node, &stat->hash[key]); |
787 | } | 780 | } |
788 | 781 | ||
@@ -1079,7 +1072,7 @@ struct ftrace_func_probe { | |||
1079 | unsigned long flags; | 1072 | unsigned long flags; |
1080 | unsigned long ip; | 1073 | unsigned long ip; |
1081 | void *data; | 1074 | void *data; |
1082 | struct rcu_head rcu; | 1075 | struct list_head free_list; |
1083 | }; | 1076 | }; |
1084 | 1077 | ||
1085 | struct ftrace_func_entry { | 1078 | struct ftrace_func_entry { |
@@ -1329,7 +1322,6 @@ ftrace_hash_move(struct ftrace_ops *ops, int enable, | |||
1329 | struct hlist_head *hhd; | 1322 | struct hlist_head *hhd; |
1330 | struct ftrace_hash *old_hash; | 1323 | struct ftrace_hash *old_hash; |
1331 | struct ftrace_hash *new_hash; | 1324 | struct ftrace_hash *new_hash; |
1332 | unsigned long key; | ||
1333 | int size = src->count; | 1325 | int size = src->count; |
1334 | int bits = 0; | 1326 | int bits = 0; |
1335 | int ret; | 1327 | int ret; |
@@ -1372,10 +1364,6 @@ ftrace_hash_move(struct ftrace_ops *ops, int enable, | |||
1372 | for (i = 0; i < size; i++) { | 1364 | for (i = 0; i < size; i++) { |
1373 | hhd = &src->buckets[i]; | 1365 | hhd = &src->buckets[i]; |
1374 | hlist_for_each_entry_safe(entry, tn, hhd, hlist) { | 1366 | hlist_for_each_entry_safe(entry, tn, hhd, hlist) { |
1375 | if (bits > 0) | ||
1376 | key = hash_long(entry->ip, bits); | ||
1377 | else | ||
1378 | key = 0; | ||
1379 | remove_hash_entry(src, entry); | 1367 | remove_hash_entry(src, entry); |
1380 | __add_hash_entry(new_hash, entry); | 1368 | __add_hash_entry(new_hash, entry); |
1381 | } | 1369 | } |
@@ -2973,28 +2961,27 @@ static void __disable_ftrace_function_probe(void) | |||
2973 | } | 2961 | } |
2974 | 2962 | ||
2975 | 2963 | ||
2976 | static void ftrace_free_entry_rcu(struct rcu_head *rhp) | 2964 | static void ftrace_free_entry(struct ftrace_func_probe *entry) |
2977 | { | 2965 | { |
2978 | struct ftrace_func_probe *entry = | ||
2979 | container_of(rhp, struct ftrace_func_probe, rcu); | ||
2980 | |||
2981 | if (entry->ops->free) | 2966 | if (entry->ops->free) |
2982 | entry->ops->free(&entry->data); | 2967 | entry->ops->free(entry->ops, entry->ip, &entry->data); |
2983 | kfree(entry); | 2968 | kfree(entry); |
2984 | } | 2969 | } |
2985 | 2970 | ||
2986 | |||
2987 | int | 2971 | int |
2988 | register_ftrace_function_probe(char *glob, struct ftrace_probe_ops *ops, | 2972 | register_ftrace_function_probe(char *glob, struct ftrace_probe_ops *ops, |
2989 | void *data) | 2973 | void *data) |
2990 | { | 2974 | { |
2991 | struct ftrace_func_probe *entry; | 2975 | struct ftrace_func_probe *entry; |
2976 | struct ftrace_hash **orig_hash = &trace_probe_ops.filter_hash; | ||
2977 | struct ftrace_hash *hash; | ||
2992 | struct ftrace_page *pg; | 2978 | struct ftrace_page *pg; |
2993 | struct dyn_ftrace *rec; | 2979 | struct dyn_ftrace *rec; |
2994 | int type, len, not; | 2980 | int type, len, not; |
2995 | unsigned long key; | 2981 | unsigned long key; |
2996 | int count = 0; | 2982 | int count = 0; |
2997 | char *search; | 2983 | char *search; |
2984 | int ret; | ||
2998 | 2985 | ||
2999 | type = filter_parse_regex(glob, strlen(glob), &search, ¬); | 2986 | type = filter_parse_regex(glob, strlen(glob), &search, ¬); |
3000 | len = strlen(search); | 2987 | len = strlen(search); |
@@ -3005,8 +2992,16 @@ register_ftrace_function_probe(char *glob, struct ftrace_probe_ops *ops, | |||
3005 | 2992 | ||
3006 | mutex_lock(&ftrace_lock); | 2993 | mutex_lock(&ftrace_lock); |
3007 | 2994 | ||
3008 | if (unlikely(ftrace_disabled)) | 2995 | hash = alloc_and_copy_ftrace_hash(FTRACE_HASH_DEFAULT_BITS, *orig_hash); |
2996 | if (!hash) { | ||
2997 | count = -ENOMEM; | ||
3009 | goto out_unlock; | 2998 | goto out_unlock; |
2999 | } | ||
3000 | |||
3001 | if (unlikely(ftrace_disabled)) { | ||
3002 | count = -ENODEV; | ||
3003 | goto out_unlock; | ||
3004 | } | ||
3010 | 3005 | ||
3011 | do_for_each_ftrace_rec(pg, rec) { | 3006 | do_for_each_ftrace_rec(pg, rec) { |
3012 | 3007 | ||
@@ -3030,14 +3025,21 @@ register_ftrace_function_probe(char *glob, struct ftrace_probe_ops *ops, | |||
3030 | * for each function we find. We call the callback | 3025 | * for each function we find. We call the callback |
3031 | * to give the caller an opportunity to do so. | 3026 | * to give the caller an opportunity to do so. |
3032 | */ | 3027 | */ |
3033 | if (ops->callback) { | 3028 | if (ops->init) { |
3034 | if (ops->callback(rec->ip, &entry->data) < 0) { | 3029 | if (ops->init(ops, rec->ip, &entry->data) < 0) { |
3035 | /* caller does not like this func */ | 3030 | /* caller does not like this func */ |
3036 | kfree(entry); | 3031 | kfree(entry); |
3037 | continue; | 3032 | continue; |
3038 | } | 3033 | } |
3039 | } | 3034 | } |
3040 | 3035 | ||
3036 | ret = enter_record(hash, rec, 0); | ||
3037 | if (ret < 0) { | ||
3038 | kfree(entry); | ||
3039 | count = ret; | ||
3040 | goto out_unlock; | ||
3041 | } | ||
3042 | |||
3041 | entry->ops = ops; | 3043 | entry->ops = ops; |
3042 | entry->ip = rec->ip; | 3044 | entry->ip = rec->ip; |
3043 | 3045 | ||
@@ -3045,10 +3047,16 @@ register_ftrace_function_probe(char *glob, struct ftrace_probe_ops *ops, | |||
3045 | hlist_add_head_rcu(&entry->node, &ftrace_func_hash[key]); | 3047 | hlist_add_head_rcu(&entry->node, &ftrace_func_hash[key]); |
3046 | 3048 | ||
3047 | } while_for_each_ftrace_rec(); | 3049 | } while_for_each_ftrace_rec(); |
3050 | |||
3051 | ret = ftrace_hash_move(&trace_probe_ops, 1, orig_hash, hash); | ||
3052 | if (ret < 0) | ||
3053 | count = ret; | ||
3054 | |||
3048 | __enable_ftrace_function_probe(); | 3055 | __enable_ftrace_function_probe(); |
3049 | 3056 | ||
3050 | out_unlock: | 3057 | out_unlock: |
3051 | mutex_unlock(&ftrace_lock); | 3058 | mutex_unlock(&ftrace_lock); |
3059 | free_ftrace_hash(hash); | ||
3052 | 3060 | ||
3053 | return count; | 3061 | return count; |
3054 | } | 3062 | } |
@@ -3062,7 +3070,12 @@ static void | |||
3062 | __unregister_ftrace_function_probe(char *glob, struct ftrace_probe_ops *ops, | 3070 | __unregister_ftrace_function_probe(char *glob, struct ftrace_probe_ops *ops, |
3063 | void *data, int flags) | 3071 | void *data, int flags) |
3064 | { | 3072 | { |
3073 | struct ftrace_func_entry *rec_entry; | ||
3065 | struct ftrace_func_probe *entry; | 3074 | struct ftrace_func_probe *entry; |
3075 | struct ftrace_func_probe *p; | ||
3076 | struct ftrace_hash **orig_hash = &trace_probe_ops.filter_hash; | ||
3077 | struct list_head free_list; | ||
3078 | struct ftrace_hash *hash; | ||
3066 | struct hlist_node *tmp; | 3079 | struct hlist_node *tmp; |
3067 | char str[KSYM_SYMBOL_LEN]; | 3080 | char str[KSYM_SYMBOL_LEN]; |
3068 | int type = MATCH_FULL; | 3081 | int type = MATCH_FULL; |
@@ -3083,6 +3096,14 @@ __unregister_ftrace_function_probe(char *glob, struct ftrace_probe_ops *ops, | |||
3083 | } | 3096 | } |
3084 | 3097 | ||
3085 | mutex_lock(&ftrace_lock); | 3098 | mutex_lock(&ftrace_lock); |
3099 | |||
3100 | hash = alloc_and_copy_ftrace_hash(FTRACE_HASH_DEFAULT_BITS, *orig_hash); | ||
3101 | if (!hash) | ||
3102 | /* Hmm, should report this somehow */ | ||
3103 | goto out_unlock; | ||
3104 | |||
3105 | INIT_LIST_HEAD(&free_list); | ||
3106 | |||
3086 | for (i = 0; i < FTRACE_FUNC_HASHSIZE; i++) { | 3107 | for (i = 0; i < FTRACE_FUNC_HASHSIZE; i++) { |
3087 | struct hlist_head *hhd = &ftrace_func_hash[i]; | 3108 | struct hlist_head *hhd = &ftrace_func_hash[i]; |
3088 | 3109 | ||
@@ -3103,12 +3124,30 @@ __unregister_ftrace_function_probe(char *glob, struct ftrace_probe_ops *ops, | |||
3103 | continue; | 3124 | continue; |
3104 | } | 3125 | } |
3105 | 3126 | ||
3127 | rec_entry = ftrace_lookup_ip(hash, entry->ip); | ||
3128 | /* It is possible more than one entry had this ip */ | ||
3129 | if (rec_entry) | ||
3130 | free_hash_entry(hash, rec_entry); | ||
3131 | |||
3106 | hlist_del_rcu(&entry->node); | 3132 | hlist_del_rcu(&entry->node); |
3107 | call_rcu_sched(&entry->rcu, ftrace_free_entry_rcu); | 3133 | list_add(&entry->free_list, &free_list); |
3108 | } | 3134 | } |
3109 | } | 3135 | } |
3110 | __disable_ftrace_function_probe(); | 3136 | __disable_ftrace_function_probe(); |
3137 | /* | ||
3138 | * Remove after the disable is called. Otherwise, if the last | ||
3139 | * probe is removed, a null hash means *all enabled*. | ||
3140 | */ | ||
3141 | ftrace_hash_move(&trace_probe_ops, 1, orig_hash, hash); | ||
3142 | synchronize_sched(); | ||
3143 | list_for_each_entry_safe(entry, p, &free_list, free_list) { | ||
3144 | list_del(&entry->free_list); | ||
3145 | ftrace_free_entry(entry); | ||
3146 | } | ||
3147 | |||
3148 | out_unlock: | ||
3111 | mutex_unlock(&ftrace_lock); | 3149 | mutex_unlock(&ftrace_lock); |
3150 | free_ftrace_hash(hash); | ||
3112 | } | 3151 | } |
3113 | 3152 | ||
3114 | void | 3153 | void |
@@ -3736,7 +3775,8 @@ out: | |||
3736 | if (fail) | 3775 | if (fail) |
3737 | return -EINVAL; | 3776 | return -EINVAL; |
3738 | 3777 | ||
3739 | ftrace_graph_filter_enabled = 1; | 3778 | ftrace_graph_filter_enabled = !!(*idx); |
3779 | |||
3740 | return 0; | 3780 | return 0; |
3741 | } | 3781 | } |
3742 | 3782 | ||