diff options
-rw-r--r-- | include/linux/ftrace.h | 1 | ||||
-rw-r--r-- | include/linux/kernel.h | 1 | ||||
-rw-r--r-- | kernel/extable.c | 8 | ||||
-rw-r--r-- | kernel/trace/ftrace.c | 37 |
4 files changed, 40 insertions, 7 deletions
diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 4609c0ece79a..caba694a62b6 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h | |||
@@ -34,6 +34,7 @@ struct ftrace_hash; | |||
34 | enum { | 34 | enum { |
35 | FTRACE_OPS_FL_ENABLED = 1 << 0, | 35 | FTRACE_OPS_FL_ENABLED = 1 << 0, |
36 | FTRACE_OPS_FL_GLOBAL = 1 << 1, | 36 | FTRACE_OPS_FL_GLOBAL = 1 << 1, |
37 | FTRACE_OPS_FL_DYNAMIC = 1 << 2, | ||
37 | }; | 38 | }; |
38 | 39 | ||
39 | struct ftrace_ops { | 40 | struct ftrace_ops { |
diff --git a/include/linux/kernel.h b/include/linux/kernel.h index 00cec4dc0ae2..f37ba716ef8b 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h | |||
@@ -283,6 +283,7 @@ extern char *get_options(const char *str, int nints, int *ints); | |||
283 | extern unsigned long long memparse(const char *ptr, char **retptr); | 283 | extern unsigned long long memparse(const char *ptr, char **retptr); |
284 | 284 | ||
285 | extern int core_kernel_text(unsigned long addr); | 285 | extern int core_kernel_text(unsigned long addr); |
286 | extern int core_kernel_data(unsigned long addr); | ||
286 | extern int __kernel_text_address(unsigned long addr); | 287 | extern int __kernel_text_address(unsigned long addr); |
287 | extern int kernel_text_address(unsigned long addr); | 288 | extern int kernel_text_address(unsigned long addr); |
288 | extern int func_ptr_is_kernel_text(void *ptr); | 289 | extern int func_ptr_is_kernel_text(void *ptr); |
diff --git a/kernel/extable.c b/kernel/extable.c index 7f8f263f8524..c2d625fcda77 100644 --- a/kernel/extable.c +++ b/kernel/extable.c | |||
@@ -72,6 +72,14 @@ int core_kernel_text(unsigned long addr) | |||
72 | return 0; | 72 | return 0; |
73 | } | 73 | } |
74 | 74 | ||
75 | int core_kernel_data(unsigned long addr) | ||
76 | { | ||
77 | if (addr >= (unsigned long)_sdata && | ||
78 | addr < (unsigned long)_edata) | ||
79 | return 1; | ||
80 | return 0; | ||
81 | } | ||
82 | |||
75 | int __kernel_text_address(unsigned long addr) | 83 | int __kernel_text_address(unsigned long addr) |
76 | { | 84 | { |
77 | if (core_kernel_text(addr)) | 85 | if (core_kernel_text(addr)) |
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 6c7e1df39b57..5b3ee04e39d9 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c | |||
@@ -189,8 +189,14 @@ static void update_ftrace_function(void) | |||
189 | 189 | ||
190 | update_global_ops(); | 190 | update_global_ops(); |
191 | 191 | ||
192 | /* | ||
193 | * If we are at the end of the list and this ops is | ||
194 | * not dynamic, then have the mcount trampoline call | ||
195 | * the function directly | ||
196 | */ | ||
192 | if (ftrace_ops_list == &ftrace_list_end || | 197 | if (ftrace_ops_list == &ftrace_list_end || |
193 | ftrace_ops_list->next == &ftrace_list_end) | 198 | (ftrace_ops_list->next == &ftrace_list_end && |
199 | !(ftrace_ops_list->flags & FTRACE_OPS_FL_DYNAMIC))) | ||
194 | func = ftrace_ops_list->func; | 200 | func = ftrace_ops_list->func; |
195 | else | 201 | else |
196 | func = ftrace_ops_list_func; | 202 | func = ftrace_ops_list_func; |
@@ -250,6 +256,9 @@ static int __register_ftrace_function(struct ftrace_ops *ops) | |||
250 | if (WARN_ON(ops->flags & FTRACE_OPS_FL_ENABLED)) | 256 | if (WARN_ON(ops->flags & FTRACE_OPS_FL_ENABLED)) |
251 | return -EBUSY; | 257 | return -EBUSY; |
252 | 258 | ||
259 | if (!core_kernel_data((unsigned long)ops)) | ||
260 | ops->flags |= FTRACE_OPS_FL_DYNAMIC; | ||
261 | |||
253 | if (ops->flags & FTRACE_OPS_FL_GLOBAL) { | 262 | if (ops->flags & FTRACE_OPS_FL_GLOBAL) { |
254 | int first = ftrace_global_list == &ftrace_list_end; | 263 | int first = ftrace_global_list == &ftrace_list_end; |
255 | add_ftrace_ops(&ftrace_global_list, ops); | 264 | add_ftrace_ops(&ftrace_global_list, ops); |
@@ -293,6 +302,13 @@ static int __unregister_ftrace_function(struct ftrace_ops *ops) | |||
293 | if (ftrace_enabled) | 302 | if (ftrace_enabled) |
294 | update_ftrace_function(); | 303 | update_ftrace_function(); |
295 | 304 | ||
305 | /* | ||
306 | * Dynamic ops may be freed, we must make sure that all | ||
307 | * callers are done before leaving this function. | ||
308 | */ | ||
309 | if (ops->flags & FTRACE_OPS_FL_DYNAMIC) | ||
310 | synchronize_sched(); | ||
311 | |||
296 | return 0; | 312 | return 0; |
297 | } | 313 | } |
298 | 314 | ||
@@ -1225,6 +1241,9 @@ ftrace_hash_move(struct ftrace_hash **dst, struct ftrace_hash *src) | |||
1225 | * the filter_hash does not exist or is empty, | 1241 | * the filter_hash does not exist or is empty, |
1226 | * AND | 1242 | * AND |
1227 | * the ip is not in the ops->notrace_hash. | 1243 | * the ip is not in the ops->notrace_hash. |
1244 | * | ||
1245 | * This needs to be called with preemption disabled as | ||
1246 | * the hashes are freed with call_rcu_sched(). | ||
1228 | */ | 1247 | */ |
1229 | static int | 1248 | static int |
1230 | ftrace_ops_test(struct ftrace_ops *ops, unsigned long ip) | 1249 | ftrace_ops_test(struct ftrace_ops *ops, unsigned long ip) |
@@ -1233,9 +1252,6 @@ ftrace_ops_test(struct ftrace_ops *ops, unsigned long ip) | |||
1233 | struct ftrace_hash *notrace_hash; | 1252 | struct ftrace_hash *notrace_hash; |
1234 | int ret; | 1253 | int ret; |
1235 | 1254 | ||
1236 | /* The hashes are freed with call_rcu_sched() */ | ||
1237 | preempt_disable_notrace(); | ||
1238 | |||
1239 | filter_hash = rcu_dereference_raw(ops->filter_hash); | 1255 | filter_hash = rcu_dereference_raw(ops->filter_hash); |
1240 | notrace_hash = rcu_dereference_raw(ops->notrace_hash); | 1256 | notrace_hash = rcu_dereference_raw(ops->notrace_hash); |
1241 | 1257 | ||
@@ -1246,7 +1262,6 @@ ftrace_ops_test(struct ftrace_ops *ops, unsigned long ip) | |||
1246 | ret = 1; | 1262 | ret = 1; |
1247 | else | 1263 | else |
1248 | ret = 0; | 1264 | ret = 0; |
1249 | preempt_enable_notrace(); | ||
1250 | 1265 | ||
1251 | return ret; | 1266 | return ret; |
1252 | } | 1267 | } |
@@ -3425,14 +3440,20 @@ ftrace_ops_test(struct ftrace_ops *ops, unsigned long ip) | |||
3425 | static void | 3440 | static void |
3426 | ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip) | 3441 | ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip) |
3427 | { | 3442 | { |
3428 | /* see comment above ftrace_global_list_func */ | 3443 | struct ftrace_ops *op; |
3429 | struct ftrace_ops *op = rcu_dereference_raw(ftrace_ops_list); | ||
3430 | 3444 | ||
3445 | /* | ||
3446 | * Some of the ops may be dynamically allocated, | ||
3447 | * they must be freed after a synchronize_sched(). | ||
3448 | */ | ||
3449 | preempt_disable_notrace(); | ||
3450 | op = rcu_dereference_raw(ftrace_ops_list); | ||
3431 | while (op != &ftrace_list_end) { | 3451 | while (op != &ftrace_list_end) { |
3432 | if (ftrace_ops_test(op, ip)) | 3452 | if (ftrace_ops_test(op, ip)) |
3433 | op->func(ip, parent_ip); | 3453 | op->func(ip, parent_ip); |
3434 | op = rcu_dereference_raw(op->next); | 3454 | op = rcu_dereference_raw(op->next); |
3435 | }; | 3455 | }; |
3456 | preempt_enable_notrace(); | ||
3436 | } | 3457 | } |
3437 | 3458 | ||
3438 | static void clear_ftrace_swapper(void) | 3459 | static void clear_ftrace_swapper(void) |
@@ -3743,6 +3764,7 @@ int register_ftrace_function(struct ftrace_ops *ops) | |||
3743 | mutex_unlock(&ftrace_lock); | 3764 | mutex_unlock(&ftrace_lock); |
3744 | return ret; | 3765 | return ret; |
3745 | } | 3766 | } |
3767 | EXPORT_SYMBOL_GPL(register_ftrace_function); | ||
3746 | 3768 | ||
3747 | /** | 3769 | /** |
3748 | * unregister_ftrace_function - unregister a function for profiling. | 3770 | * unregister_ftrace_function - unregister a function for profiling. |
@@ -3762,6 +3784,7 @@ int unregister_ftrace_function(struct ftrace_ops *ops) | |||
3762 | 3784 | ||
3763 | return ret; | 3785 | return ret; |
3764 | } | 3786 | } |
3787 | EXPORT_SYMBOL_GPL(unregister_ftrace_function); | ||
3765 | 3788 | ||
3766 | int | 3789 | int |
3767 | ftrace_enable_sysctl(struct ctl_table *table, int write, | 3790 | ftrace_enable_sysctl(struct ctl_table *table, int write, |