diff options
-rw-r--r-- | include/linux/ftrace.h | 3 | ||||
-rw-r--r-- | kernel/trace/ftrace.c | 112 | ||||
-rw-r--r-- | kernel/trace/trace_selftest.c | 4 |
3 files changed, 110 insertions, 9 deletions
diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 61e757bd2350..4650a3160b7f 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h | |||
@@ -58,6 +58,9 @@ struct dyn_ftrace { | |||
58 | int ftrace_force_update(void); | 58 | int ftrace_force_update(void); |
59 | void ftrace_set_filter(unsigned char *buf, int len, int reset); | 59 | void ftrace_set_filter(unsigned char *buf, int len, int reset); |
60 | 60 | ||
61 | /* totally disable ftrace - can not re-enable after this */ | ||
62 | void ftrace_kill(void); | ||
63 | |||
61 | /* defined in arch */ | 64 | /* defined in arch */ |
62 | extern int ftrace_ip_converted(unsigned long ip); | 65 | extern int ftrace_ip_converted(unsigned long ip); |
63 | extern unsigned char *ftrace_nop_replace(void); | 66 | extern unsigned char *ftrace_nop_replace(void); |
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 8e02aa690b2b..ff42345dd78e 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c | |||
@@ -29,9 +29,16 @@ | |||
29 | 29 | ||
30 | #include "trace.h" | 30 | #include "trace.h" |
31 | 31 | ||
32 | int ftrace_enabled; | 32 | /* ftrace_enabled is a method to turn ftrace on or off */ |
33 | int ftrace_enabled __read_mostly; | ||
33 | static int last_ftrace_enabled; | 34 | static int last_ftrace_enabled; |
34 | 35 | ||
36 | /* | ||
37 | * ftrace_disabled is set when an anomaly is discovered. | ||
38 | * ftrace_disabled is much stronger than ftrace_enabled. | ||
39 | */ | ||
40 | static int ftrace_disabled __read_mostly; | ||
41 | |||
35 | static DEFINE_SPINLOCK(ftrace_lock); | 42 | static DEFINE_SPINLOCK(ftrace_lock); |
36 | static DEFINE_MUTEX(ftrace_sysctl_lock); | 43 | static DEFINE_MUTEX(ftrace_sysctl_lock); |
37 | 44 | ||
@@ -230,10 +237,11 @@ static notrace struct dyn_ftrace *ftrace_alloc_dyn_node(unsigned long ip) | |||
230 | if (ftrace_free_records) { | 237 | if (ftrace_free_records) { |
231 | rec = ftrace_free_records; | 238 | rec = ftrace_free_records; |
232 | 239 | ||
233 | /* todo, disable tracing altogether on this warning */ | ||
234 | if (unlikely(!(rec->flags & FTRACE_FL_FREE))) { | 240 | if (unlikely(!(rec->flags & FTRACE_FL_FREE))) { |
235 | WARN_ON_ONCE(1); | 241 | WARN_ON_ONCE(1); |
236 | ftrace_free_records = NULL; | 242 | ftrace_free_records = NULL; |
243 | ftrace_disabled = 1; | ||
244 | ftrace_enabled = 0; | ||
237 | return NULL; | 245 | return NULL; |
238 | } | 246 | } |
239 | 247 | ||
@@ -260,7 +268,7 @@ ftrace_record_ip(unsigned long ip) | |||
260 | int resched; | 268 | int resched; |
261 | int atomic; | 269 | int atomic; |
262 | 270 | ||
263 | if (!ftrace_enabled) | 271 | if (!ftrace_enabled || ftrace_disabled) |
264 | return; | 272 | return; |
265 | 273 | ||
266 | resched = need_resched(); | 274 | resched = need_resched(); |
@@ -485,6 +493,9 @@ static void notrace ftrace_startup(void) | |||
485 | { | 493 | { |
486 | int command = 0; | 494 | int command = 0; |
487 | 495 | ||
496 | if (unlikely(ftrace_disabled)) | ||
497 | return; | ||
498 | |||
488 | mutex_lock(&ftraced_lock); | 499 | mutex_lock(&ftraced_lock); |
489 | ftraced_suspend++; | 500 | ftraced_suspend++; |
490 | if (ftraced_suspend == 1) | 501 | if (ftraced_suspend == 1) |
@@ -507,6 +518,9 @@ static void notrace ftrace_shutdown(void) | |||
507 | { | 518 | { |
508 | int command = 0; | 519 | int command = 0; |
509 | 520 | ||
521 | if (unlikely(ftrace_disabled)) | ||
522 | return; | ||
523 | |||
510 | mutex_lock(&ftraced_lock); | 524 | mutex_lock(&ftraced_lock); |
511 | ftraced_suspend--; | 525 | ftraced_suspend--; |
512 | if (!ftraced_suspend) | 526 | if (!ftraced_suspend) |
@@ -529,6 +543,9 @@ static void notrace ftrace_startup_sysctl(void) | |||
529 | { | 543 | { |
530 | int command = FTRACE_ENABLE_MCOUNT; | 544 | int command = FTRACE_ENABLE_MCOUNT; |
531 | 545 | ||
546 | if (unlikely(ftrace_disabled)) | ||
547 | return; | ||
548 | |||
532 | mutex_lock(&ftraced_lock); | 549 | mutex_lock(&ftraced_lock); |
533 | /* Force update next time */ | 550 | /* Force update next time */ |
534 | saved_ftrace_func = NULL; | 551 | saved_ftrace_func = NULL; |
@@ -544,6 +561,9 @@ static void notrace ftrace_shutdown_sysctl(void) | |||
544 | { | 561 | { |
545 | int command = FTRACE_DISABLE_MCOUNT; | 562 | int command = FTRACE_DISABLE_MCOUNT; |
546 | 563 | ||
564 | if (unlikely(ftrace_disabled)) | ||
565 | return; | ||
566 | |||
547 | mutex_lock(&ftraced_lock); | 567 | mutex_lock(&ftraced_lock); |
548 | /* ftraced_suspend is true if ftrace is running */ | 568 | /* ftraced_suspend is true if ftrace is running */ |
549 | if (ftraced_suspend) | 569 | if (ftraced_suspend) |
@@ -600,6 +620,9 @@ static int notrace __ftrace_update_code(void *ignore) | |||
600 | 620 | ||
601 | static void notrace ftrace_update_code(void) | 621 | static void notrace ftrace_update_code(void) |
602 | { | 622 | { |
623 | if (unlikely(ftrace_disabled)) | ||
624 | return; | ||
625 | |||
603 | stop_machine_run(__ftrace_update_code, NULL, NR_CPUS); | 626 | stop_machine_run(__ftrace_update_code, NULL, NR_CPUS); |
604 | } | 627 | } |
605 | 628 | ||
@@ -614,6 +637,9 @@ static int notrace ftraced(void *ignore) | |||
614 | /* check once a second */ | 637 | /* check once a second */ |
615 | schedule_timeout(HZ); | 638 | schedule_timeout(HZ); |
616 | 639 | ||
640 | if (unlikely(ftrace_disabled)) | ||
641 | continue; | ||
642 | |||
617 | mutex_lock(&ftrace_sysctl_lock); | 643 | mutex_lock(&ftrace_sysctl_lock); |
618 | mutex_lock(&ftraced_lock); | 644 | mutex_lock(&ftraced_lock); |
619 | if (ftrace_enabled && ftraced_trigger && !ftraced_suspend) { | 645 | if (ftrace_enabled && ftraced_trigger && !ftraced_suspend) { |
@@ -628,6 +654,7 @@ static int notrace ftraced(void *ignore) | |||
628 | ftrace_update_cnt != 1 ? "s" : "", | 654 | ftrace_update_cnt != 1 ? "s" : "", |
629 | ftrace_update_tot_cnt, | 655 | ftrace_update_tot_cnt, |
630 | usecs, usecs != 1 ? "s" : ""); | 656 | usecs, usecs != 1 ? "s" : ""); |
657 | ftrace_disabled = 1; | ||
631 | WARN_ON_ONCE(1); | 658 | WARN_ON_ONCE(1); |
632 | } | 659 | } |
633 | ftraced_trigger = 0; | 660 | ftraced_trigger = 0; |
@@ -785,6 +812,9 @@ ftrace_avail_open(struct inode *inode, struct file *file) | |||
785 | struct ftrace_iterator *iter; | 812 | struct ftrace_iterator *iter; |
786 | int ret; | 813 | int ret; |
787 | 814 | ||
815 | if (unlikely(ftrace_disabled)) | ||
816 | return -ENODEV; | ||
817 | |||
788 | iter = kzalloc(sizeof(*iter), GFP_KERNEL); | 818 | iter = kzalloc(sizeof(*iter), GFP_KERNEL); |
789 | if (!iter) | 819 | if (!iter) |
790 | return -ENOMEM; | 820 | return -ENOMEM; |
@@ -843,6 +873,9 @@ ftrace_filter_open(struct inode *inode, struct file *file) | |||
843 | struct ftrace_iterator *iter; | 873 | struct ftrace_iterator *iter; |
844 | int ret = 0; | 874 | int ret = 0; |
845 | 875 | ||
876 | if (unlikely(ftrace_disabled)) | ||
877 | return -ENODEV; | ||
878 | |||
846 | iter = kzalloc(sizeof(*iter), GFP_KERNEL); | 879 | iter = kzalloc(sizeof(*iter), GFP_KERNEL); |
847 | if (!iter) | 880 | if (!iter) |
848 | return -ENOMEM; | 881 | return -ENOMEM; |
@@ -1063,6 +1096,9 @@ ftrace_filter_write(struct file *file, const char __user *ubuf, | |||
1063 | */ | 1096 | */ |
1064 | notrace void ftrace_set_filter(unsigned char *buf, int len, int reset) | 1097 | notrace void ftrace_set_filter(unsigned char *buf, int len, int reset) |
1065 | { | 1098 | { |
1099 | if (unlikely(ftrace_disabled)) | ||
1100 | return; | ||
1101 | |||
1066 | mutex_lock(&ftrace_filter_lock); | 1102 | mutex_lock(&ftrace_filter_lock); |
1067 | if (reset) | 1103 | if (reset) |
1068 | ftrace_filter_reset(); | 1104 | ftrace_filter_reset(); |
@@ -1133,7 +1169,7 @@ int ftrace_force_update(void) | |||
1133 | DECLARE_WAITQUEUE(wait, current); | 1169 | DECLARE_WAITQUEUE(wait, current); |
1134 | int ret = 0; | 1170 | int ret = 0; |
1135 | 1171 | ||
1136 | if (!ftraced_task) | 1172 | if (unlikely(ftrace_disabled)) |
1137 | return -ENODEV; | 1173 | return -ENODEV; |
1138 | 1174 | ||
1139 | mutex_lock(&ftraced_lock); | 1175 | mutex_lock(&ftraced_lock); |
@@ -1142,6 +1178,11 @@ int ftrace_force_update(void) | |||
1142 | set_current_state(TASK_INTERRUPTIBLE); | 1178 | set_current_state(TASK_INTERRUPTIBLE); |
1143 | add_wait_queue(&ftraced_waiters, &wait); | 1179 | add_wait_queue(&ftraced_waiters, &wait); |
1144 | 1180 | ||
1181 | if (unlikely(!ftraced_task)) { | ||
1182 | ret = -ENODEV; | ||
1183 | goto out; | ||
1184 | } | ||
1185 | |||
1145 | do { | 1186 | do { |
1146 | mutex_unlock(&ftraced_lock); | 1187 | mutex_unlock(&ftraced_lock); |
1147 | wake_up_process(ftraced_task); | 1188 | wake_up_process(ftraced_task); |
@@ -1154,6 +1195,7 @@ int ftrace_force_update(void) | |||
1154 | set_current_state(TASK_INTERRUPTIBLE); | 1195 | set_current_state(TASK_INTERRUPTIBLE); |
1155 | } while (last_counter == ftraced_iteration_counter); | 1196 | } while (last_counter == ftraced_iteration_counter); |
1156 | 1197 | ||
1198 | out: | ||
1157 | mutex_unlock(&ftraced_lock); | 1199 | mutex_unlock(&ftraced_lock); |
1158 | remove_wait_queue(&ftraced_waiters, &wait); | 1200 | remove_wait_queue(&ftraced_waiters, &wait); |
1159 | set_current_state(TASK_RUNNING); | 1201 | set_current_state(TASK_RUNNING); |
@@ -1161,6 +1203,22 @@ int ftrace_force_update(void) | |||
1161 | return ret; | 1203 | return ret; |
1162 | } | 1204 | } |
1163 | 1205 | ||
1206 | static void ftrace_force_shutdown(void) | ||
1207 | { | ||
1208 | struct task_struct *task; | ||
1209 | int command = FTRACE_DISABLE_CALLS | FTRACE_UPDATE_TRACE_FUNC; | ||
1210 | |||
1211 | mutex_lock(&ftraced_lock); | ||
1212 | task = ftraced_task; | ||
1213 | ftraced_task = NULL; | ||
1214 | ftraced_suspend = -1; | ||
1215 | ftrace_run_update_code(command); | ||
1216 | mutex_unlock(&ftraced_lock); | ||
1217 | |||
1218 | if (task) | ||
1219 | kthread_stop(task); | ||
1220 | } | ||
1221 | |||
1164 | static __init int ftrace_init_debugfs(void) | 1222 | static __init int ftrace_init_debugfs(void) |
1165 | { | 1223 | { |
1166 | struct dentry *d_tracer; | 1224 | struct dentry *d_tracer; |
@@ -1194,21 +1252,29 @@ static int __init notrace ftrace_dynamic_init(void) | |||
1194 | stop_machine_run(ftrace_dyn_arch_init, &addr, NR_CPUS); | 1252 | stop_machine_run(ftrace_dyn_arch_init, &addr, NR_CPUS); |
1195 | 1253 | ||
1196 | /* ftrace_dyn_arch_init places the return code in addr */ | 1254 | /* ftrace_dyn_arch_init places the return code in addr */ |
1197 | if (addr) | 1255 | if (addr) { |
1198 | return addr; | 1256 | ret = (int)addr; |
1257 | goto failed; | ||
1258 | } | ||
1199 | 1259 | ||
1200 | ret = ftrace_dyn_table_alloc(); | 1260 | ret = ftrace_dyn_table_alloc(); |
1201 | if (ret) | 1261 | if (ret) |
1202 | return ret; | 1262 | goto failed; |
1203 | 1263 | ||
1204 | p = kthread_run(ftraced, NULL, "ftraced"); | 1264 | p = kthread_run(ftraced, NULL, "ftraced"); |
1205 | if (IS_ERR(p)) | 1265 | if (IS_ERR(p)) { |
1206 | return -1; | 1266 | ret = -1; |
1267 | goto failed; | ||
1268 | } | ||
1207 | 1269 | ||
1208 | last_ftrace_enabled = ftrace_enabled = 1; | 1270 | last_ftrace_enabled = ftrace_enabled = 1; |
1209 | ftraced_task = p; | 1271 | ftraced_task = p; |
1210 | 1272 | ||
1211 | return 0; | 1273 | return 0; |
1274 | |||
1275 | failed: | ||
1276 | ftrace_disabled = 1; | ||
1277 | return ret; | ||
1212 | } | 1278 | } |
1213 | 1279 | ||
1214 | core_initcall(ftrace_dynamic_init); | 1280 | core_initcall(ftrace_dynamic_init); |
@@ -1217,9 +1283,31 @@ core_initcall(ftrace_dynamic_init); | |||
1217 | # define ftrace_shutdown() do { } while (0) | 1283 | # define ftrace_shutdown() do { } while (0) |
1218 | # define ftrace_startup_sysctl() do { } while (0) | 1284 | # define ftrace_startup_sysctl() do { } while (0) |
1219 | # define ftrace_shutdown_sysctl() do { } while (0) | 1285 | # define ftrace_shutdown_sysctl() do { } while (0) |
1286 | # define ftrace_force_shutdown() do { } while (0) | ||
1220 | #endif /* CONFIG_DYNAMIC_FTRACE */ | 1287 | #endif /* CONFIG_DYNAMIC_FTRACE */ |
1221 | 1288 | ||
1222 | /** | 1289 | /** |
1290 | * ftrace_kill - totally shutdown ftrace | ||
1291 | * | ||
1292 | * This is a safety measure. If something was detected that seems | ||
1293 | * wrong, calling this function will keep ftrace from doing | ||
1294 | * any more modifications, and updates. | ||
1295 | * used when something went wrong. | ||
1296 | */ | ||
1297 | void ftrace_kill(void) | ||
1298 | { | ||
1299 | mutex_lock(&ftrace_sysctl_lock); | ||
1300 | ftrace_disabled = 1; | ||
1301 | ftrace_enabled = 0; | ||
1302 | |||
1303 | clear_ftrace_function(); | ||
1304 | mutex_unlock(&ftrace_sysctl_lock); | ||
1305 | |||
1306 | /* Try to totally disable ftrace */ | ||
1307 | ftrace_force_shutdown(); | ||
1308 | } | ||
1309 | |||
1310 | /** | ||
1223 | * register_ftrace_function - register a function for profiling | 1311 | * register_ftrace_function - register a function for profiling |
1224 | * @ops - ops structure that holds the function for profiling. | 1312 | * @ops - ops structure that holds the function for profiling. |
1225 | * | 1313 | * |
@@ -1234,6 +1322,9 @@ int register_ftrace_function(struct ftrace_ops *ops) | |||
1234 | { | 1322 | { |
1235 | int ret; | 1323 | int ret; |
1236 | 1324 | ||
1325 | if (unlikely(ftrace_disabled)) | ||
1326 | return -1; | ||
1327 | |||
1237 | mutex_lock(&ftrace_sysctl_lock); | 1328 | mutex_lock(&ftrace_sysctl_lock); |
1238 | ret = __register_ftrace_function(ops); | 1329 | ret = __register_ftrace_function(ops); |
1239 | ftrace_startup(); | 1330 | ftrace_startup(); |
@@ -1267,6 +1358,9 @@ ftrace_enable_sysctl(struct ctl_table *table, int write, | |||
1267 | { | 1358 | { |
1268 | int ret; | 1359 | int ret; |
1269 | 1360 | ||
1361 | if (unlikely(ftrace_disabled)) | ||
1362 | return -ENODEV; | ||
1363 | |||
1270 | mutex_lock(&ftrace_sysctl_lock); | 1364 | mutex_lock(&ftrace_sysctl_lock); |
1271 | 1365 | ||
1272 | ret = proc_dointvec(table, write, file, buffer, lenp, ppos); | 1366 | ret = proc_dointvec(table, write, file, buffer, lenp, ppos); |
diff --git a/kernel/trace/trace_selftest.c b/kernel/trace/trace_selftest.c index a6f1ed75f836..85715b86a342 100644 --- a/kernel/trace/trace_selftest.c +++ b/kernel/trace/trace_selftest.c | |||
@@ -248,6 +248,10 @@ trace_selftest_startup_function(struct tracer *trace, struct trace_array *tr) | |||
248 | ftrace_enabled = save_ftrace_enabled; | 248 | ftrace_enabled = save_ftrace_enabled; |
249 | tracer_enabled = save_tracer_enabled; | 249 | tracer_enabled = save_tracer_enabled; |
250 | 250 | ||
251 | /* kill ftrace totally if we failed */ | ||
252 | if (ret) | ||
253 | ftrace_kill(); | ||
254 | |||
251 | return ret; | 255 | return ret; |
252 | } | 256 | } |
253 | #endif /* CONFIG_FTRACE */ | 257 | #endif /* CONFIG_FTRACE */ |