diff options
Diffstat (limited to 'kernel/trace/ftrace.c')
-rw-r--r-- | kernel/trace/ftrace.c | 157 |
1 files changed, 110 insertions, 47 deletions
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 1843edc098a6..f762f5a2d331 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c | |||
@@ -151,8 +151,6 @@ static int __unregister_ftrace_function(struct ftrace_ops *ops) | |||
151 | #ifdef CONFIG_DYNAMIC_FTRACE | 151 | #ifdef CONFIG_DYNAMIC_FTRACE |
152 | 152 | ||
153 | static struct task_struct *ftraced_task; | 153 | static struct task_struct *ftraced_task; |
154 | static DECLARE_WAIT_QUEUE_HEAD(ftraced_waiters); | ||
155 | static unsigned long ftraced_iteration_counter; | ||
156 | 154 | ||
157 | enum { | 155 | enum { |
158 | FTRACE_ENABLE_CALLS = (1 << 0), | 156 | FTRACE_ENABLE_CALLS = (1 << 0), |
@@ -189,6 +187,7 @@ static struct ftrace_page *ftrace_pages; | |||
189 | 187 | ||
190 | static int ftraced_trigger; | 188 | static int ftraced_trigger; |
191 | static int ftraced_suspend; | 189 | static int ftraced_suspend; |
190 | static int ftraced_stop; | ||
192 | 191 | ||
193 | static int ftrace_record_suspend; | 192 | static int ftrace_record_suspend; |
194 | 193 | ||
@@ -474,14 +473,21 @@ ftrace_code_disable(struct dyn_ftrace *rec) | |||
474 | return 1; | 473 | return 1; |
475 | } | 474 | } |
476 | 475 | ||
476 | static int __ftrace_update_code(void *ignore); | ||
477 | |||
477 | static int __ftrace_modify_code(void *data) | 478 | static int __ftrace_modify_code(void *data) |
478 | { | 479 | { |
479 | unsigned long addr; | 480 | unsigned long addr; |
480 | int *command = data; | 481 | int *command = data; |
481 | 482 | ||
482 | if (*command & FTRACE_ENABLE_CALLS) | 483 | if (*command & FTRACE_ENABLE_CALLS) { |
484 | /* | ||
485 | * Update any recorded ips now that we have the | ||
486 | * machine stopped | ||
487 | */ | ||
488 | __ftrace_update_code(NULL); | ||
483 | ftrace_replace_code(1); | 489 | ftrace_replace_code(1); |
484 | else if (*command & FTRACE_DISABLE_CALLS) | 490 | } else if (*command & FTRACE_DISABLE_CALLS) |
485 | ftrace_replace_code(0); | 491 | ftrace_replace_code(0); |
486 | 492 | ||
487 | if (*command & FTRACE_UPDATE_TRACE_FUNC) | 493 | if (*command & FTRACE_UPDATE_TRACE_FUNC) |
@@ -503,6 +509,25 @@ static void ftrace_run_update_code(int command) | |||
503 | stop_machine_run(__ftrace_modify_code, &command, NR_CPUS); | 509 | stop_machine_run(__ftrace_modify_code, &command, NR_CPUS); |
504 | } | 510 | } |
505 | 511 | ||
512 | void ftrace_disable_daemon(void) | ||
513 | { | ||
514 | /* Stop the daemon from calling kstop_machine */ | ||
515 | mutex_lock(&ftraced_lock); | ||
516 | ftraced_stop = 1; | ||
517 | mutex_unlock(&ftraced_lock); | ||
518 | |||
519 | ftrace_force_update(); | ||
520 | } | ||
521 | |||
522 | void ftrace_enable_daemon(void) | ||
523 | { | ||
524 | mutex_lock(&ftraced_lock); | ||
525 | ftraced_stop = 0; | ||
526 | mutex_unlock(&ftraced_lock); | ||
527 | |||
528 | ftrace_force_update(); | ||
529 | } | ||
530 | |||
506 | static ftrace_func_t saved_ftrace_func; | 531 | static ftrace_func_t saved_ftrace_func; |
507 | 532 | ||
508 | static void ftrace_startup(void) | 533 | static void ftrace_startup(void) |
@@ -603,6 +628,7 @@ static int __ftrace_update_code(void *ignore) | |||
603 | int i; | 628 | int i; |
604 | 629 | ||
605 | /* Don't be recording funcs now */ | 630 | /* Don't be recording funcs now */ |
631 | ftrace_record_suspend++; | ||
606 | save_ftrace_enabled = ftrace_enabled; | 632 | save_ftrace_enabled = ftrace_enabled; |
607 | ftrace_enabled = 0; | 633 | ftrace_enabled = 0; |
608 | 634 | ||
@@ -628,18 +654,23 @@ static int __ftrace_update_code(void *ignore) | |||
628 | stop = ftrace_now(raw_smp_processor_id()); | 654 | stop = ftrace_now(raw_smp_processor_id()); |
629 | ftrace_update_time = stop - start; | 655 | ftrace_update_time = stop - start; |
630 | ftrace_update_tot_cnt += ftrace_update_cnt; | 656 | ftrace_update_tot_cnt += ftrace_update_cnt; |
657 | ftraced_trigger = 0; | ||
631 | 658 | ||
632 | ftrace_enabled = save_ftrace_enabled; | 659 | ftrace_enabled = save_ftrace_enabled; |
660 | ftrace_record_suspend--; | ||
633 | 661 | ||
634 | return 0; | 662 | return 0; |
635 | } | 663 | } |
636 | 664 | ||
637 | static void ftrace_update_code(void) | 665 | static int ftrace_update_code(void) |
638 | { | 666 | { |
639 | if (unlikely(ftrace_disabled)) | 667 | if (unlikely(ftrace_disabled) || |
640 | return; | 668 | !ftrace_enabled || !ftraced_trigger) |
669 | return 0; | ||
641 | 670 | ||
642 | stop_machine_run(__ftrace_update_code, NULL, NR_CPUS); | 671 | stop_machine_run(__ftrace_update_code, NULL, NR_CPUS); |
672 | |||
673 | return 1; | ||
643 | } | 674 | } |
644 | 675 | ||
645 | static int ftraced(void *ignore) | 676 | static int ftraced(void *ignore) |
@@ -658,14 +689,13 @@ static int ftraced(void *ignore) | |||
658 | 689 | ||
659 | mutex_lock(&ftrace_sysctl_lock); | 690 | mutex_lock(&ftrace_sysctl_lock); |
660 | mutex_lock(&ftraced_lock); | 691 | mutex_lock(&ftraced_lock); |
661 | if (ftrace_enabled && ftraced_trigger && !ftraced_suspend) { | 692 | if (!ftraced_suspend && !ftraced_stop && |
662 | ftrace_record_suspend++; | 693 | ftrace_update_code()) { |
663 | ftrace_update_code(); | ||
664 | usecs = nsecs_to_usecs(ftrace_update_time); | 694 | usecs = nsecs_to_usecs(ftrace_update_time); |
665 | if (ftrace_update_tot_cnt > 100000) { | 695 | if (ftrace_update_tot_cnt > 100000) { |
666 | ftrace_update_tot_cnt = 0; | 696 | ftrace_update_tot_cnt = 0; |
667 | pr_info("hm, dftrace overflow: %lu change%s" | 697 | pr_info("hm, dftrace overflow: %lu change%s" |
668 | " (%lu total) in %lu usec%s\n", | 698 | " (%lu total) in %lu usec%s\n", |
669 | ftrace_update_cnt, | 699 | ftrace_update_cnt, |
670 | ftrace_update_cnt != 1 ? "s" : "", | 700 | ftrace_update_cnt != 1 ? "s" : "", |
671 | ftrace_update_tot_cnt, | 701 | ftrace_update_tot_cnt, |
@@ -673,15 +703,10 @@ static int ftraced(void *ignore) | |||
673 | ftrace_disabled = 1; | 703 | ftrace_disabled = 1; |
674 | WARN_ON_ONCE(1); | 704 | WARN_ON_ONCE(1); |
675 | } | 705 | } |
676 | ftraced_trigger = 0; | ||
677 | ftrace_record_suspend--; | ||
678 | } | 706 | } |
679 | ftraced_iteration_counter++; | ||
680 | mutex_unlock(&ftraced_lock); | 707 | mutex_unlock(&ftraced_lock); |
681 | mutex_unlock(&ftrace_sysctl_lock); | 708 | mutex_unlock(&ftrace_sysctl_lock); |
682 | 709 | ||
683 | wake_up_interruptible(&ftraced_waiters); | ||
684 | |||
685 | ftrace_shutdown_replenish(); | 710 | ftrace_shutdown_replenish(); |
686 | } | 711 | } |
687 | __set_current_state(TASK_RUNNING); | 712 | __set_current_state(TASK_RUNNING); |
@@ -1219,6 +1244,55 @@ ftrace_notrace_release(struct inode *inode, struct file *file) | |||
1219 | return ftrace_regex_release(inode, file, 0); | 1244 | return ftrace_regex_release(inode, file, 0); |
1220 | } | 1245 | } |
1221 | 1246 | ||
1247 | static ssize_t | ||
1248 | ftraced_read(struct file *filp, char __user *ubuf, | ||
1249 | size_t cnt, loff_t *ppos) | ||
1250 | { | ||
1251 | /* don't worry about races */ | ||
1252 | char *buf = ftraced_stop ? "disabled\n" : "enabled\n"; | ||
1253 | int r = strlen(buf); | ||
1254 | |||
1255 | return simple_read_from_buffer(ubuf, cnt, ppos, buf, r); | ||
1256 | } | ||
1257 | |||
1258 | static ssize_t | ||
1259 | ftraced_write(struct file *filp, const char __user *ubuf, | ||
1260 | size_t cnt, loff_t *ppos) | ||
1261 | { | ||
1262 | char buf[64]; | ||
1263 | long val; | ||
1264 | int ret; | ||
1265 | |||
1266 | if (cnt >= sizeof(buf)) | ||
1267 | return -EINVAL; | ||
1268 | |||
1269 | if (copy_from_user(&buf, ubuf, cnt)) | ||
1270 | return -EFAULT; | ||
1271 | |||
1272 | if (strncmp(buf, "enable", 6) == 0) | ||
1273 | val = 1; | ||
1274 | else if (strncmp(buf, "disable", 7) == 0) | ||
1275 | val = 0; | ||
1276 | else { | ||
1277 | buf[cnt] = 0; | ||
1278 | |||
1279 | ret = strict_strtoul(buf, 10, &val); | ||
1280 | if (ret < 0) | ||
1281 | return ret; | ||
1282 | |||
1283 | val = !!val; | ||
1284 | } | ||
1285 | |||
1286 | if (val) | ||
1287 | ftrace_enable_daemon(); | ||
1288 | else | ||
1289 | ftrace_disable_daemon(); | ||
1290 | |||
1291 | filp->f_pos += cnt; | ||
1292 | |||
1293 | return cnt; | ||
1294 | } | ||
1295 | |||
1222 | static struct file_operations ftrace_avail_fops = { | 1296 | static struct file_operations ftrace_avail_fops = { |
1223 | .open = ftrace_avail_open, | 1297 | .open = ftrace_avail_open, |
1224 | .read = seq_read, | 1298 | .read = seq_read, |
@@ -1242,51 +1316,34 @@ static struct file_operations ftrace_notrace_fops = { | |||
1242 | .release = ftrace_notrace_release, | 1316 | .release = ftrace_notrace_release, |
1243 | }; | 1317 | }; |
1244 | 1318 | ||
1319 | static struct file_operations ftraced_fops = { | ||
1320 | .open = tracing_open_generic, | ||
1321 | .read = ftraced_read, | ||
1322 | .write = ftraced_write, | ||
1323 | }; | ||
1324 | |||
1245 | /** | 1325 | /** |
1246 | * ftrace_force_update - force an update to all recording ftrace functions | 1326 | * ftrace_force_update - force an update to all recording ftrace functions |
1247 | * | ||
1248 | * The ftrace dynamic update daemon only wakes up once a second. | ||
1249 | * There may be cases where an update needs to be done immediately | ||
1250 | * for tests or internal kernel tracing to begin. This function | ||
1251 | * wakes the daemon to do an update and will not return until the | ||
1252 | * update is complete. | ||
1253 | */ | 1327 | */ |
1254 | int ftrace_force_update(void) | 1328 | int ftrace_force_update(void) |
1255 | { | 1329 | { |
1256 | unsigned long last_counter; | ||
1257 | DECLARE_WAITQUEUE(wait, current); | ||
1258 | int ret = 0; | 1330 | int ret = 0; |
1259 | 1331 | ||
1260 | if (unlikely(ftrace_disabled)) | 1332 | if (unlikely(ftrace_disabled)) |
1261 | return -ENODEV; | 1333 | return -ENODEV; |
1262 | 1334 | ||
1335 | mutex_lock(&ftrace_sysctl_lock); | ||
1263 | mutex_lock(&ftraced_lock); | 1336 | mutex_lock(&ftraced_lock); |
1264 | last_counter = ftraced_iteration_counter; | ||
1265 | |||
1266 | set_current_state(TASK_INTERRUPTIBLE); | ||
1267 | add_wait_queue(&ftraced_waiters, &wait); | ||
1268 | 1337 | ||
1269 | if (unlikely(!ftraced_task)) { | 1338 | /* |
1270 | ret = -ENODEV; | 1339 | * If ftraced_trigger is not set, then there is nothing |
1271 | goto out; | 1340 | * to update. |
1272 | } | 1341 | */ |
1273 | 1342 | if (ftraced_trigger && !ftrace_update_code()) | |
1274 | do { | 1343 | ret = -EBUSY; |
1275 | mutex_unlock(&ftraced_lock); | ||
1276 | wake_up_process(ftraced_task); | ||
1277 | schedule(); | ||
1278 | mutex_lock(&ftraced_lock); | ||
1279 | if (signal_pending(current)) { | ||
1280 | ret = -EINTR; | ||
1281 | break; | ||
1282 | } | ||
1283 | set_current_state(TASK_INTERRUPTIBLE); | ||
1284 | } while (last_counter == ftraced_iteration_counter); | ||
1285 | 1344 | ||
1286 | out: | ||
1287 | mutex_unlock(&ftraced_lock); | 1345 | mutex_unlock(&ftraced_lock); |
1288 | remove_wait_queue(&ftraced_waiters, &wait); | 1346 | mutex_unlock(&ftrace_sysctl_lock); |
1289 | set_current_state(TASK_RUNNING); | ||
1290 | 1347 | ||
1291 | return ret; | 1348 | return ret; |
1292 | } | 1349 | } |
@@ -1331,6 +1388,12 @@ static __init int ftrace_init_debugfs(void) | |||
1331 | if (!entry) | 1388 | if (!entry) |
1332 | pr_warning("Could not create debugfs " | 1389 | pr_warning("Could not create debugfs " |
1333 | "'set_ftrace_notrace' entry\n"); | 1390 | "'set_ftrace_notrace' entry\n"); |
1391 | |||
1392 | entry = debugfs_create_file("ftraced_enabled", 0644, d_tracer, | ||
1393 | NULL, &ftraced_fops); | ||
1394 | if (!entry) | ||
1395 | pr_warning("Could not create debugfs " | ||
1396 | "'ftraced_enabled' entry\n"); | ||
1334 | return 0; | 1397 | return 0; |
1335 | } | 1398 | } |
1336 | 1399 | ||