diff options
Diffstat (limited to 'kernel/tracepoint.c')
| -rw-r--r-- | kernel/tracepoint.c | 169 |
1 files changed, 147 insertions, 22 deletions
diff --git a/kernel/tracepoint.c b/kernel/tracepoint.c index b219f1449c54..db110b8ae030 100644 --- a/kernel/tracepoint.c +++ b/kernel/tracepoint.c | |||
| @@ -34,11 +34,16 @@ extern struct tracepoint * const __stop___tracepoints_ptrs[]; | |||
| 34 | static const int tracepoint_debug; | 34 | static const int tracepoint_debug; |
| 35 | 35 | ||
| 36 | /* | 36 | /* |
| 37 | * tracepoints_mutex nests inside module_mutex. Tracepoints mutex protects the | 37 | * Tracepoints mutex protects the builtin and module tracepoints and the hash |
| 38 | * builtin and module tracepoints and the hash table. | 38 | * table, as well as the local module list. |
| 39 | */ | 39 | */ |
| 40 | static DEFINE_MUTEX(tracepoints_mutex); | 40 | static DEFINE_MUTEX(tracepoints_mutex); |
| 41 | 41 | ||
| 42 | #ifdef CONFIG_MODULES | ||
| 43 | /* Local list of struct module */ | ||
| 44 | static LIST_HEAD(tracepoint_module_list); | ||
| 45 | #endif /* CONFIG_MODULES */ | ||
| 46 | |||
| 42 | /* | 47 | /* |
| 43 | * Tracepoint hash table, containing the active tracepoints. | 48 | * Tracepoint hash table, containing the active tracepoints. |
| 44 | * Protected by tracepoints_mutex. | 49 | * Protected by tracepoints_mutex. |
| @@ -292,9 +297,10 @@ static void disable_tracepoint(struct tracepoint *elem) | |||
| 292 | * @end: end of the range | 297 | * @end: end of the range |
| 293 | * | 298 | * |
| 294 | * Updates the probe callback corresponding to a range of tracepoints. | 299 | * Updates the probe callback corresponding to a range of tracepoints. |
| 300 | * Called with tracepoints_mutex held. | ||
| 295 | */ | 301 | */ |
| 296 | void tracepoint_update_probe_range(struct tracepoint * const *begin, | 302 | static void tracepoint_update_probe_range(struct tracepoint * const *begin, |
| 297 | struct tracepoint * const *end) | 303 | struct tracepoint * const *end) |
| 298 | { | 304 | { |
| 299 | struct tracepoint * const *iter; | 305 | struct tracepoint * const *iter; |
| 300 | struct tracepoint_entry *mark_entry; | 306 | struct tracepoint_entry *mark_entry; |
| @@ -302,7 +308,6 @@ void tracepoint_update_probe_range(struct tracepoint * const *begin, | |||
| 302 | if (!begin) | 308 | if (!begin) |
| 303 | return; | 309 | return; |
| 304 | 310 | ||
| 305 | mutex_lock(&tracepoints_mutex); | ||
| 306 | for (iter = begin; iter < end; iter++) { | 311 | for (iter = begin; iter < end; iter++) { |
| 307 | mark_entry = get_tracepoint((*iter)->name); | 312 | mark_entry = get_tracepoint((*iter)->name); |
| 308 | if (mark_entry) { | 313 | if (mark_entry) { |
| @@ -312,11 +317,27 @@ void tracepoint_update_probe_range(struct tracepoint * const *begin, | |||
| 312 | disable_tracepoint(*iter); | 317 | disable_tracepoint(*iter); |
| 313 | } | 318 | } |
| 314 | } | 319 | } |
| 315 | mutex_unlock(&tracepoints_mutex); | ||
| 316 | } | 320 | } |
| 317 | 321 | ||
| 322 | #ifdef CONFIG_MODULES | ||
| 323 | void module_update_tracepoints(void) | ||
| 324 | { | ||
| 325 | struct tp_module *tp_mod; | ||
| 326 | |||
| 327 | list_for_each_entry(tp_mod, &tracepoint_module_list, list) | ||
| 328 | tracepoint_update_probe_range(tp_mod->tracepoints_ptrs, | ||
| 329 | tp_mod->tracepoints_ptrs + tp_mod->num_tracepoints); | ||
| 330 | } | ||
| 331 | #else /* CONFIG_MODULES */ | ||
| 332 | void module_update_tracepoints(void) | ||
| 333 | { | ||
| 334 | } | ||
| 335 | #endif /* CONFIG_MODULES */ | ||
| 336 | |||
| 337 | |||
| 318 | /* | 338 | /* |
| 319 | * Update probes, removing the faulty probes. | 339 | * Update probes, removing the faulty probes. |
| 340 | * Called with tracepoints_mutex held. | ||
| 320 | */ | 341 | */ |
| 321 | static void tracepoint_update_probes(void) | 342 | static void tracepoint_update_probes(void) |
| 322 | { | 343 | { |
| @@ -359,11 +380,12 @@ int tracepoint_probe_register(const char *name, void *probe, void *data) | |||
| 359 | 380 | ||
| 360 | mutex_lock(&tracepoints_mutex); | 381 | mutex_lock(&tracepoints_mutex); |
| 361 | old = tracepoint_add_probe(name, probe, data); | 382 | old = tracepoint_add_probe(name, probe, data); |
| 362 | mutex_unlock(&tracepoints_mutex); | 383 | if (IS_ERR(old)) { |
| 363 | if (IS_ERR(old)) | 384 | mutex_unlock(&tracepoints_mutex); |
| 364 | return PTR_ERR(old); | 385 | return PTR_ERR(old); |
| 365 | 386 | } | |
| 366 | tracepoint_update_probes(); /* may update entry */ | 387 | tracepoint_update_probes(); /* may update entry */ |
| 388 | mutex_unlock(&tracepoints_mutex); | ||
| 367 | release_probes(old); | 389 | release_probes(old); |
| 368 | return 0; | 390 | return 0; |
| 369 | } | 391 | } |
| @@ -402,11 +424,12 @@ int tracepoint_probe_unregister(const char *name, void *probe, void *data) | |||
| 402 | 424 | ||
| 403 | mutex_lock(&tracepoints_mutex); | 425 | mutex_lock(&tracepoints_mutex); |
| 404 | old = tracepoint_remove_probe(name, probe, data); | 426 | old = tracepoint_remove_probe(name, probe, data); |
| 405 | mutex_unlock(&tracepoints_mutex); | 427 | if (IS_ERR(old)) { |
| 406 | if (IS_ERR(old)) | 428 | mutex_unlock(&tracepoints_mutex); |
| 407 | return PTR_ERR(old); | 429 | return PTR_ERR(old); |
| 408 | 430 | } | |
| 409 | tracepoint_update_probes(); /* may update entry */ | 431 | tracepoint_update_probes(); /* may update entry */ |
| 432 | mutex_unlock(&tracepoints_mutex); | ||
| 410 | release_probes(old); | 433 | release_probes(old); |
| 411 | return 0; | 434 | return 0; |
| 412 | } | 435 | } |
| @@ -489,9 +512,8 @@ void tracepoint_probe_update_all(void) | |||
| 489 | if (!list_empty(&old_probes)) | 512 | if (!list_empty(&old_probes)) |
| 490 | list_replace_init(&old_probes, &release_probes); | 513 | list_replace_init(&old_probes, &release_probes); |
| 491 | need_update = 0; | 514 | need_update = 0; |
| 492 | mutex_unlock(&tracepoints_mutex); | ||
| 493 | |||
| 494 | tracepoint_update_probes(); | 515 | tracepoint_update_probes(); |
| 516 | mutex_unlock(&tracepoints_mutex); | ||
| 495 | list_for_each_entry_safe(pos, next, &release_probes, u.list) { | 517 | list_for_each_entry_safe(pos, next, &release_probes, u.list) { |
| 496 | list_del(&pos->u.list); | 518 | list_del(&pos->u.list); |
| 497 | call_rcu_sched(&pos->u.rcu, rcu_free_old_probes); | 519 | call_rcu_sched(&pos->u.rcu, rcu_free_old_probes); |
| @@ -509,7 +531,7 @@ EXPORT_SYMBOL_GPL(tracepoint_probe_update_all); | |||
| 509 | * Will return the first tracepoint in the range if the input tracepoint is | 531 | * Will return the first tracepoint in the range if the input tracepoint is |
| 510 | * NULL. | 532 | * NULL. |
| 511 | */ | 533 | */ |
| 512 | int tracepoint_get_iter_range(struct tracepoint * const **tracepoint, | 534 | static int tracepoint_get_iter_range(struct tracepoint * const **tracepoint, |
| 513 | struct tracepoint * const *begin, struct tracepoint * const *end) | 535 | struct tracepoint * const *begin, struct tracepoint * const *end) |
| 514 | { | 536 | { |
| 515 | if (!*tracepoint && begin != end) { | 537 | if (!*tracepoint && begin != end) { |
| @@ -520,11 +542,12 @@ int tracepoint_get_iter_range(struct tracepoint * const **tracepoint, | |||
| 520 | return 1; | 542 | return 1; |
| 521 | return 0; | 543 | return 0; |
| 522 | } | 544 | } |
| 523 | EXPORT_SYMBOL_GPL(tracepoint_get_iter_range); | ||
| 524 | 545 | ||
| 546 | #ifdef CONFIG_MODULES | ||
| 525 | static void tracepoint_get_iter(struct tracepoint_iter *iter) | 547 | static void tracepoint_get_iter(struct tracepoint_iter *iter) |
| 526 | { | 548 | { |
| 527 | int found = 0; | 549 | int found = 0; |
| 550 | struct tp_module *iter_mod; | ||
| 528 | 551 | ||
| 529 | /* Core kernel tracepoints */ | 552 | /* Core kernel tracepoints */ |
| 530 | if (!iter->module) { | 553 | if (!iter->module) { |
| @@ -534,12 +557,43 @@ static void tracepoint_get_iter(struct tracepoint_iter *iter) | |||
| 534 | if (found) | 557 | if (found) |
| 535 | goto end; | 558 | goto end; |
| 536 | } | 559 | } |
| 537 | /* tracepoints in modules. */ | 560 | /* Tracepoints in modules */ |
| 538 | found = module_get_iter_tracepoints(iter); | 561 | mutex_lock(&tracepoints_mutex); |
| 562 | list_for_each_entry(iter_mod, &tracepoint_module_list, list) { | ||
| 563 | /* | ||
| 564 | * Sorted module list | ||
| 565 | */ | ||
| 566 | if (iter_mod < iter->module) | ||
| 567 | continue; | ||
| 568 | else if (iter_mod > iter->module) | ||
| 569 | iter->tracepoint = NULL; | ||
| 570 | found = tracepoint_get_iter_range(&iter->tracepoint, | ||
| 571 | iter_mod->tracepoints_ptrs, | ||
| 572 | iter_mod->tracepoints_ptrs | ||
| 573 | + iter_mod->num_tracepoints); | ||
| 574 | if (found) { | ||
| 575 | iter->module = iter_mod; | ||
| 576 | break; | ||
| 577 | } | ||
| 578 | } | ||
| 579 | mutex_unlock(&tracepoints_mutex); | ||
| 539 | end: | 580 | end: |
| 540 | if (!found) | 581 | if (!found) |
| 541 | tracepoint_iter_reset(iter); | 582 | tracepoint_iter_reset(iter); |
| 542 | } | 583 | } |
| 584 | #else /* CONFIG_MODULES */ | ||
| 585 | static void tracepoint_get_iter(struct tracepoint_iter *iter) | ||
| 586 | { | ||
| 587 | int found = 0; | ||
| 588 | |||
| 589 | /* Core kernel tracepoints */ | ||
| 590 | found = tracepoint_get_iter_range(&iter->tracepoint, | ||
| 591 | __start___tracepoints_ptrs, | ||
| 592 | __stop___tracepoints_ptrs); | ||
| 593 | if (!found) | ||
| 594 | tracepoint_iter_reset(iter); | ||
| 595 | } | ||
| 596 | #endif /* CONFIG_MODULES */ | ||
| 543 | 597 | ||
| 544 | void tracepoint_iter_start(struct tracepoint_iter *iter) | 598 | void tracepoint_iter_start(struct tracepoint_iter *iter) |
| 545 | { | 599 | { |
| @@ -566,26 +620,98 @@ EXPORT_SYMBOL_GPL(tracepoint_iter_stop); | |||
| 566 | 620 | ||
| 567 | void tracepoint_iter_reset(struct tracepoint_iter *iter) | 621 | void tracepoint_iter_reset(struct tracepoint_iter *iter) |
| 568 | { | 622 | { |
| 623 | #ifdef CONFIG_MODULES | ||
| 569 | iter->module = NULL; | 624 | iter->module = NULL; |
| 625 | #endif /* CONFIG_MODULES */ | ||
| 570 | iter->tracepoint = NULL; | 626 | iter->tracepoint = NULL; |
| 571 | } | 627 | } |
| 572 | EXPORT_SYMBOL_GPL(tracepoint_iter_reset); | 628 | EXPORT_SYMBOL_GPL(tracepoint_iter_reset); |
| 573 | 629 | ||
| 574 | #ifdef CONFIG_MODULES | 630 | #ifdef CONFIG_MODULES |
| 631 | static int tracepoint_module_coming(struct module *mod) | ||
| 632 | { | ||
| 633 | struct tp_module *tp_mod, *iter; | ||
| 634 | int ret = 0; | ||
| 635 | |||
| 636 | /* | ||
| 637 | * We skip modules that tain the kernel, especially those with different | ||
| 638 | * module header (for forced load), to make sure we don't cause a crash. | ||
| 639 | */ | ||
| 640 | if (mod->taints) | ||
| 641 | return 0; | ||
| 642 | mutex_lock(&tracepoints_mutex); | ||
| 643 | tp_mod = kmalloc(sizeof(struct tp_module), GFP_KERNEL); | ||
| 644 | if (!tp_mod) { | ||
| 645 | ret = -ENOMEM; | ||
| 646 | goto end; | ||
| 647 | } | ||
| 648 | tp_mod->num_tracepoints = mod->num_tracepoints; | ||
| 649 | tp_mod->tracepoints_ptrs = mod->tracepoints_ptrs; | ||
| 650 | |||
| 651 | /* | ||
| 652 | * tracepoint_module_list is kept sorted by struct module pointer | ||
| 653 | * address for iteration on tracepoints from a seq_file that can release | ||
| 654 | * the mutex between calls. | ||
| 655 | */ | ||
| 656 | list_for_each_entry_reverse(iter, &tracepoint_module_list, list) { | ||
| 657 | BUG_ON(iter == tp_mod); /* Should never be in the list twice */ | ||
| 658 | if (iter < tp_mod) { | ||
| 659 | /* We belong to the location right after iter. */ | ||
| 660 | list_add(&tp_mod->list, &iter->list); | ||
| 661 | goto module_added; | ||
| 662 | } | ||
| 663 | } | ||
| 664 | /* We belong to the beginning of the list */ | ||
| 665 | list_add(&tp_mod->list, &tracepoint_module_list); | ||
| 666 | module_added: | ||
| 667 | tracepoint_update_probe_range(mod->tracepoints_ptrs, | ||
| 668 | mod->tracepoints_ptrs + mod->num_tracepoints); | ||
| 669 | end: | ||
| 670 | mutex_unlock(&tracepoints_mutex); | ||
| 671 | return ret; | ||
| 672 | } | ||
| 673 | |||
| 674 | static int tracepoint_module_going(struct module *mod) | ||
| 675 | { | ||
| 676 | struct tp_module *pos; | ||
| 677 | |||
| 678 | mutex_lock(&tracepoints_mutex); | ||
| 679 | tracepoint_update_probe_range(mod->tracepoints_ptrs, | ||
| 680 | mod->tracepoints_ptrs + mod->num_tracepoints); | ||
| 681 | list_for_each_entry(pos, &tracepoint_module_list, list) { | ||
| 682 | if (pos->tracepoints_ptrs == mod->tracepoints_ptrs) { | ||
| 683 | list_del(&pos->list); | ||
| 684 | kfree(pos); | ||
| 685 | break; | ||
| 686 | } | ||
| 687 | } | ||
| 688 | /* | ||
| 689 | * In the case of modules that were tainted at "coming", we'll simply | ||
| 690 | * walk through the list without finding it. We cannot use the "tainted" | ||
| 691 | * flag on "going", in case a module taints the kernel only after being | ||
| 692 | * loaded. | ||
| 693 | */ | ||
| 694 | mutex_unlock(&tracepoints_mutex); | ||
| 695 | return 0; | ||
| 696 | } | ||
| 575 | 697 | ||
| 576 | int tracepoint_module_notify(struct notifier_block *self, | 698 | int tracepoint_module_notify(struct notifier_block *self, |
| 577 | unsigned long val, void *data) | 699 | unsigned long val, void *data) |
| 578 | { | 700 | { |
| 579 | struct module *mod = data; | 701 | struct module *mod = data; |
| 702 | int ret = 0; | ||
| 580 | 703 | ||
| 581 | switch (val) { | 704 | switch (val) { |
| 582 | case MODULE_STATE_COMING: | 705 | case MODULE_STATE_COMING: |
| 706 | ret = tracepoint_module_coming(mod); | ||
| 707 | break; | ||
| 708 | case MODULE_STATE_LIVE: | ||
| 709 | break; | ||
| 583 | case MODULE_STATE_GOING: | 710 | case MODULE_STATE_GOING: |
| 584 | tracepoint_update_probe_range(mod->tracepoints_ptrs, | 711 | ret = tracepoint_module_going(mod); |
| 585 | mod->tracepoints_ptrs + mod->num_tracepoints); | ||
| 586 | break; | 712 | break; |
| 587 | } | 713 | } |
| 588 | return 0; | 714 | return ret; |
| 589 | } | 715 | } |
| 590 | 716 | ||
| 591 | struct notifier_block tracepoint_module_nb = { | 717 | struct notifier_block tracepoint_module_nb = { |
| @@ -598,7 +724,6 @@ static int init_tracepoints(void) | |||
| 598 | return register_module_notifier(&tracepoint_module_nb); | 724 | return register_module_notifier(&tracepoint_module_nb); |
| 599 | } | 725 | } |
| 600 | __initcall(init_tracepoints); | 726 | __initcall(init_tracepoints); |
| 601 | |||
| 602 | #endif /* CONFIG_MODULES */ | 727 | #endif /* CONFIG_MODULES */ |
| 603 | 728 | ||
| 604 | #ifdef CONFIG_HAVE_SYSCALL_TRACEPOINTS | 729 | #ifdef CONFIG_HAVE_SYSCALL_TRACEPOINTS |
