diff options
Diffstat (limited to 'kernel/trace/trace.c')
| -rw-r--r-- | kernel/trace/trace.c | 71 |
1 files changed, 71 insertions, 0 deletions
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 6757561d9617..68a6f78f6862 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c | |||
| @@ -1424,6 +1424,28 @@ static int wait_on_pipe(struct trace_iterator *iter, bool full) | |||
| 1424 | } | 1424 | } |
| 1425 | 1425 | ||
| 1426 | #ifdef CONFIG_FTRACE_STARTUP_TEST | 1426 | #ifdef CONFIG_FTRACE_STARTUP_TEST |
| 1427 | static bool selftests_can_run; | ||
| 1428 | |||
| 1429 | struct trace_selftests { | ||
| 1430 | struct list_head list; | ||
| 1431 | struct tracer *type; | ||
| 1432 | }; | ||
| 1433 | |||
| 1434 | static LIST_HEAD(postponed_selftests); | ||
| 1435 | |||
| 1436 | static int save_selftest(struct tracer *type) | ||
| 1437 | { | ||
| 1438 | struct trace_selftests *selftest; | ||
| 1439 | |||
| 1440 | selftest = kmalloc(sizeof(*selftest), GFP_KERNEL); | ||
| 1441 | if (!selftest) | ||
| 1442 | return -ENOMEM; | ||
| 1443 | |||
| 1444 | selftest->type = type; | ||
| 1445 | list_add(&selftest->list, &postponed_selftests); | ||
| 1446 | return 0; | ||
| 1447 | } | ||
| 1448 | |||
| 1427 | static int run_tracer_selftest(struct tracer *type) | 1449 | static int run_tracer_selftest(struct tracer *type) |
| 1428 | { | 1450 | { |
| 1429 | struct trace_array *tr = &global_trace; | 1451 | struct trace_array *tr = &global_trace; |
| @@ -1434,6 +1456,14 @@ static int run_tracer_selftest(struct tracer *type) | |||
| 1434 | return 0; | 1456 | return 0; |
| 1435 | 1457 | ||
| 1436 | /* | 1458 | /* |
| 1459 | * If a tracer registers early in boot up (before scheduling is | ||
| 1460 | * initialized and such), then do not run its selftests yet. | ||
| 1461 | * Instead, run it a little later in the boot process. | ||
| 1462 | */ | ||
| 1463 | if (!selftests_can_run) | ||
| 1464 | return save_selftest(type); | ||
| 1465 | |||
| 1466 | /* | ||
| 1437 | * Run a selftest on this tracer. | 1467 | * Run a selftest on this tracer. |
| 1438 | * Here we reset the trace buffer, and set the current | 1468 | * Here we reset the trace buffer, and set the current |
| 1439 | * tracer to be this tracer. The tracer can then run some | 1469 | * tracer to be this tracer. The tracer can then run some |
| @@ -1482,6 +1512,47 @@ static int run_tracer_selftest(struct tracer *type) | |||
| 1482 | printk(KERN_CONT "PASSED\n"); | 1512 | printk(KERN_CONT "PASSED\n"); |
| 1483 | return 0; | 1513 | return 0; |
| 1484 | } | 1514 | } |
| 1515 | |||
| 1516 | static __init int init_trace_selftests(void) | ||
| 1517 | { | ||
| 1518 | struct trace_selftests *p, *n; | ||
| 1519 | struct tracer *t, **last; | ||
| 1520 | int ret; | ||
| 1521 | |||
| 1522 | selftests_can_run = true; | ||
| 1523 | |||
| 1524 | mutex_lock(&trace_types_lock); | ||
| 1525 | |||
| 1526 | if (list_empty(&postponed_selftests)) | ||
| 1527 | goto out; | ||
| 1528 | |||
| 1529 | pr_info("Running postponed tracer tests:\n"); | ||
| 1530 | |||
| 1531 | list_for_each_entry_safe(p, n, &postponed_selftests, list) { | ||
| 1532 | ret = run_tracer_selftest(p->type); | ||
| 1533 | /* If the test fails, then warn and remove from available_tracers */ | ||
| 1534 | if (ret < 0) { | ||
| 1535 | WARN(1, "tracer: %s failed selftest, disabling\n", | ||
| 1536 | p->type->name); | ||
| 1537 | last = &trace_types; | ||
| 1538 | for (t = trace_types; t; t = t->next) { | ||
| 1539 | if (t == p->type) { | ||
| 1540 | *last = t->next; | ||
| 1541 | break; | ||
| 1542 | } | ||
| 1543 | last = &t->next; | ||
| 1544 | } | ||
| 1545 | } | ||
| 1546 | list_del(&p->list); | ||
| 1547 | kfree(p); | ||
| 1548 | } | ||
| 1549 | |||
| 1550 | out: | ||
| 1551 | mutex_unlock(&trace_types_lock); | ||
| 1552 | |||
| 1553 | return 0; | ||
| 1554 | } | ||
| 1555 | early_initcall(init_trace_selftests); | ||
| 1485 | #else | 1556 | #else |
| 1486 | static inline int run_tracer_selftest(struct tracer *type) | 1557 | static inline int run_tracer_selftest(struct tracer *type) |
| 1487 | { | 1558 | { |
